この vignette は、新しい関数 pivot_longer()
と pivot_wider()
の使用法を説明する。その目的は。gather()
と spread()
の使い勝手を向上させ、他のパッケージで見られるような最先端のフィーチャを取り入れることである。
以前から。spread()
と gather()
のデザインには何か根本的な問題があることは明らかであった。多くの人がその名前を直感的に理解できず、どの方向が拡散に相当し、どの方向が収集に相当するかを覚えるのは困難である。また、これらの関数の引数を覚えるのも意外と大変なようで、多くの人が (私も含めて!) 毎回ドキュメントを参照しなければならないことになる。
Rでリシェイプを進めている他のRパッケージからヒントを得た重要な新機能が2つある。
pivot_longer()
は、Matt Dowle と Arun Srinivasan による data.table data.table パッケージが提供する拡張 melt()
と dcast()
関数に触発されて、異なる型を持ちうる複数の値変数を扱えるようになった。
pivot_longer()
と pivot_wider()
は、John Mount と Nina Zumel による cdata cdata パッケージに触発されて、列名に格納されたメタデータがデータ変数になる方法(およびその逆)を正確に指定するデータフレームを取ることができる。
この vignette では。pivot_longer()
と pivot_wider()
を使って、単純なものから複雑なものまで、さまざまなデータ再形成の課題を解決する様子を見て、その鍵となる考え方を学んでいただく。
はじめに、必要なパッケージをいくつかロードする。実際の解析コードでは。library(tidyverse)
、で行うのだろうが、この vignette はパッケージに組み込まれているので、ここではできない。
library(tidyr)
library(dplyr)
library(readr)
pivot_longer()
は、行数を増やし列数を減らすことで、データセットを__longer__にする。データセットが「長い形」であると表現することに意味があるとは思えない。長さとは相対的な言葉であり、(例えば)データセットAはデータセットBよりも長いとしか言えないのである。
pivot_longer()
は、野生で捕獲されたデータセットを整理するために一般的に必要とされるもので、分析の容易さよりもデータ入力の容易さや比較の容易さに最適化されていることが多いことがある。以下のセクションでは、現実的なデータセットに幅広く対応するために、pivot_longer()
を使用する方法を紹介する。
relig_income
データセットには、宗教と年収を尋ねたアンケートに基づくカウントが格納されている。
relig_income#> # A tibble: 18 × 11
#> religion `<$10k` $10-2…¹ $20-3…² $30-4…³ $40-5…⁴ $50-7…⁵ $75-1…⁶ $100-…⁷
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 Agnostic 27 34 60 81 76 137 122 109
#> 2 Atheist 12 27 37 52 35 70 73 59
#> 3 Buddhist 27 21 30 34 33 58 62 39
#> 4 Catholic 418 617 732 670 638 1116 949 792
#> 5 Don’t know/… 15 14 15 11 10 35 21 17
#> 6 Evangelical … 575 869 1064 982 881 1486 949 723
#> 7 Hindu 1 9 7 9 11 34 47 48
#> 8 Historically… 228 244 236 238 197 223 131 81
#> 9 Jehovah's Wi… 20 27 24 24 21 30 15 11
#> 10 Jewish 19 19 25 25 30 95 69 87
#> # … with 8 more rows, 2 more variables: `>150k` <dbl>,
#> # `Don't know/refused` <dbl>, and abbreviated variable names ¹`$10-20k`,
#> # ²`$20-30k`, ³`$30-40k`, ⁴`$40-50k`, ⁵`$50-75k`, ⁶`$75-100k`, ⁷`$100-150k`
このデータセットには3つの変数が含まれている。
religion
, 行に格納されている。income
列名で広がっている。count
セルの値に格納されている。これを整理するために。pivot_longer()
を使用する。
%>%
relig_income pivot_longer(!religion, names_to = "income", values_to = "count")
#> # A tibble: 180 × 3
#> religion income count
#> <chr> <chr> <dbl>
#> 1 Agnostic <$10k 27
#> 2 Agnostic $10-20k 34
#> 3 Agnostic $20-30k 60
#> 4 Agnostic $30-40k 81
#> 5 Agnostic $40-50k 76
#> 6 Agnostic $50-75k 137
#> 7 Agnostic $75-100k 122
#> 8 Agnostic $100-150k 109
#> 9 Agnostic >150k 84
#> 10 Agnostic Don't know/refused 96
#> # … with 170 more rows
最初の引数は、リシェイプするデータセットである。relig_income
.
2番目の引数は、どの列を整形する必要があるかを記述する。この場合。religion
以外のすべての列が対象となる。
names_to
は、列名に格納されたデータから作成される変数名、すなわち。income
を指定する。
values_to
には、セル値に格納されたデータから作成される変数名、すなわち count
を指定する。
names_to
と values_to
の両列は relig_income
には存在しないので、引用符で囲まれた文字列として提供する。
billboard
データセットは2000年の曲のビルボードランクを記録している。relig_income
データと似たような形式だが、列名にエンコードされているデータは文字列ではなく本当に数字である。
billboard#> # A tibble: 317 × 79
#> artist track date.ent…¹ wk1 wk2 wk3 wk4 wk5 wk6 wk7 wk8 wk9
#> <chr> <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 2 Pac Baby… 2000-02-26 87 82 72 77 87 94 99 NA NA
#> 2 2Ge+h… The … 2000-09-02 91 87 92 NA NA NA NA NA NA
#> 3 3 Doo… Kryp… 2000-04-08 81 70 68 67 66 57 54 53 51
#> 4 3 Doo… Loser 2000-10-21 76 76 72 69 67 65 55 59 62
#> 5 504 B… Wobb… 2000-04-15 57 34 25 17 17 31 36 49 53
#> 6 98^0 Give… 2000-08-19 51 39 34 26 26 19 2 2 3
#> 7 A*Tee… Danc… 2000-07-08 97 97 96 95 100 NA NA NA NA
#> 8 Aaliy… I Do… 2000-01-29 84 62 51 41 38 35 35 38 38
#> 9 Aaliy… Try … 2000-03-18 59 53 38 28 21 18 16 14 12
#> 10 Adams… Open… 2000-08-26 76 76 74 69 68 67 61 58 57
#> # … with 307 more rows, 67 more variables: wk10 <dbl>, wk11 <dbl>, wk12 <dbl>,
#> # wk13 <dbl>, wk14 <dbl>, wk15 <dbl>, wk16 <dbl>, wk17 <dbl>, wk18 <dbl>,
#> # wk19 <dbl>, wk20 <dbl>, wk21 <dbl>, wk22 <dbl>, wk23 <dbl>, wk24 <dbl>,
#> # wk25 <dbl>, wk26 <dbl>, wk27 <dbl>, wk28 <dbl>, wk29 <dbl>, wk30 <dbl>,
#> # wk31 <dbl>, wk32 <dbl>, wk33 <dbl>, wk34 <dbl>, wk35 <dbl>, wk36 <dbl>,
#> # wk37 <dbl>, wk38 <dbl>, wk39 <dbl>, wk40 <dbl>, wk41 <dbl>, wk42 <dbl>,
#> # wk43 <dbl>, wk44 <dbl>, wk45 <dbl>, wk46 <dbl>, wk47 <dbl>, wk48 <dbl>, …
relig_income
のデータセットと同じ基本仕様で始めることができる。ここでは、名前を week
という変数に、値を rank
という変数にしたい。また。values_drop_na
を使って、欠損値に対応する行を削除する。すべての曲が76週すべてチャートにとどまるわけではないので、入力データの構造上、不必要な明示的な NA
を作成せざるを得ない。
%>%
billboard pivot_longer(
cols = starts_with("wk"),
names_to = "week",
values_to = "rank",
values_drop_na = TRUE
)#> # A tibble: 5,307 × 5
#> artist track date.entered week rank
#> <chr> <chr> <date> <chr> <dbl>
#> 1 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk1 87
#> 2 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk2 82
#> 3 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk3 72
#> 4 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk4 77
#> 5 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk5 87
#> 6 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk6 94
#> 7 2 Pac Baby Don't Cry (Keep... 2000-02-26 wk7 99
#> 8 2Ge+her The Hardest Part Of ... 2000-09-02 wk1 91
#> 9 2Ge+her The Hardest Part Of ... 2000-09-02 wk2 87
#> 10 2Ge+her The Hardest Part Of ... 2000-09-02 wk3 92
#> # … with 5,297 more rows
各曲がチャートにどれくらいの期間とどまったかを簡単に判断できたらいいのであるが、そのためには week
変数を整数に変換する必要がある。それには2つの追加引数を使う。names_prefix
は wk
の接頭辞を除去し。names_transform
は week
を整数に変換する。
%>%
billboard pivot_longer(
cols = starts_with("wk"),
names_to = "week",
names_prefix = "wk",
names_transform = list(week = as.integer),
values_to = "rank",
values_drop_na = TRUE,
)
あるいは、数値以外の成分を自動的に取り除く readr::parse_number()
を使って、1つの引数でこれを行うこともできる。
%>%
billboard pivot_longer(
cols = starts_with("wk"),
names_to = "week",
names_transform = list(week = readr::parse_number),
values_to = "rank",
values_drop_na = TRUE,
)
より困難な状況は、複数の変数を列名に詰め込んだ場合に発生する。例えば。who
のデータセットを見てみよう。
who#> # A tibble: 7,240 × 60
#> country iso2 iso3 year new_s…¹ new_s…² new_s…³ new_s…⁴ new_s…⁵ new_s…⁶
#> <chr> <chr> <chr> <int> <int> <int> <int> <int> <int> <int>
#> 1 Afghanistan AF AFG 1980 NA NA NA NA NA NA
#> 2 Afghanistan AF AFG 1981 NA NA NA NA NA NA
#> 3 Afghanistan AF AFG 1982 NA NA NA NA NA NA
#> 4 Afghanistan AF AFG 1983 NA NA NA NA NA NA
#> 5 Afghanistan AF AFG 1984 NA NA NA NA NA NA
#> 6 Afghanistan AF AFG 1985 NA NA NA NA NA NA
#> 7 Afghanistan AF AFG 1986 NA NA NA NA NA NA
#> 8 Afghanistan AF AFG 1987 NA NA NA NA NA NA
#> 9 Afghanistan AF AFG 1988 NA NA NA NA NA NA
#> 10 Afghanistan AF AFG 1989 NA NA NA NA NA NA
#> # … with 7,230 more rows, 50 more variables: new_sp_m65 <int>,
#> # new_sp_f014 <int>, new_sp_f1524 <int>, new_sp_f2534 <int>,
#> # new_sp_f3544 <int>, new_sp_f4554 <int>, new_sp_f5564 <int>,
#> # new_sp_f65 <int>, new_sn_m014 <int>, new_sn_m1524 <int>,
#> # new_sn_m2534 <int>, new_sn_m3544 <int>, new_sn_m4554 <int>,
#> # new_sn_m5564 <int>, new_sn_m65 <int>, new_sn_f014 <int>,
#> # new_sn_f1524 <int>, new_sn_f2534 <int>, new_sn_f3544 <int>, …
country
, iso2
, iso3
, year
はすでに変数なので、そのままでもよい。しかし。new_sp_m014
から newrel_f65
までの列は、その名前に4つの変数をエンコードしている。
new_
/ new
の接頭辞は、新規案件のカウントであることを示している。この データセットには新しいケースしか含まれていないので、ここでは無視することにする。
の定数である。
sp
/ rel
/ ep
には、症例がどのように診断されたかを記述している。
m
/ f
は、性別を表する。
014
/ 1524
/ 2535
/ 3544
/ 4554
/ 65
は、年齢層を表している。
names_to
で複数の列名を指定し。names_sep
または names_pattern
を提供することで、これらの変数を分割することができる。ここでは names_pattern
が最も自然に適合している。これは extract
と同様のインターフェイスを持っている。 グループを含む正規表現 ( ()
で定義) を与えると、各グループを1つの列に配置する。
%>% pivot_longer(
who cols = new_sp_m014:newrel_f65,
names_to = c("diagnosis", "gender", "age"),
names_pattern = "new_?(.*)_(.)(.*)",
values_to = "count"
)#> # A tibble: 405,440 × 8
#> country iso2 iso3 year diagnosis gender age count
#> <chr> <chr> <chr> <int> <chr> <chr> <chr> <int>
#> 1 Afghanistan AF AFG 1980 sp m 014 NA
#> 2 Afghanistan AF AFG 1980 sp m 1524 NA
#> 3 Afghanistan AF AFG 1980 sp m 2534 NA
#> 4 Afghanistan AF AFG 1980 sp m 3544 NA
#> 5 Afghanistan AF AFG 1980 sp m 4554 NA
#> 6 Afghanistan AF AFG 1980 sp m 5564 NA
#> 7 Afghanistan AF AFG 1980 sp m 65 NA
#> 8 Afghanistan AF AFG 1980 sp f 014 NA
#> 9 Afghanistan AF AFG 1980 sp f 1524 NA
#> 10 Afghanistan AF AFG 1980 sp f 2534 NA
#> # … with 405,430 more rows
さらに一歩進んで、性別と年齢を因数に変換するためにreadr関数を使用することもできる。これは、既知の値のセットを持つカテゴリ変数がある場合の良い練習になると思われる。
%>% pivot_longer(
who cols = new_sp_m014:newrel_f65,
names_to = c("diagnosis", "gender", "age"),
names_pattern = "new_?(.*)_(.)(.*)",
names_transform = list(
gender = ~ readr::parse_factor(.x, levels = c("f", "m")),
age = ~ readr::parse_factor(
.x,levels = c("014", "1524", "2534", "3544", "4554", "5564", "65"),
ordered = TRUE
)
),values_to = "count",
)
これまで、1行に1つのオブザベーションを持つデータフレームを扱ってきましたが、重要なピボット問題の多くには、1行に複数のオブザベーションが含まれる。このような場合、出力に表示したい列の名前が入力の列名の一部であるため、通常は認識することができる。このセクションでは、このようなデータをピボットする方法について学ぶ。
以下の例は、tidyrがこの問題を解決するためのインスピレーションとして、 data.table vignette から引用したものである。
<- tribble(
family ~family, ~dob_child1, ~dob_child2, ~gender_child1, ~gender_child2,
"1998-11-26", "2000-01-29", 1L, 2L,
1L, "1996-06-22", NA, 2L, NA,
2L, "2002-07-11", "2004-04-05", 2L, 2L,
3L, "2004-10-10", "2009-08-27", 1L, 1L,
4L, "2000-12-05", "2005-02-28", 2L, 1L,
5L,
)<- family %>% mutate_at(vars(starts_with("dob")), parse_date)
family
family#> # A tibble: 5 × 5
#> family dob_child1 dob_child2 gender_child1 gender_child2
#> <int> <date> <date> <int> <int>
#> 1 1 1998-11-26 2000-01-29 1 2
#> 2 2 1996-06-22 NA 2 NA
#> 3 3 2002-07-11 2004-04-05 2 2
#> 4 4 2004-10-10 2009-08-27 1 1
#> 5 5 2000-12-05 2005-02-28 2 1
各子供について。gender
と dob
(生年月日) という 2 つの情報 (または値) があることに注意。これらは、結果の中の別々の列に入れる必要がある。ここでも。names_to
に複数の変数を与え。names_sep
を使って各変数名を分割している。.value
という特別な名前に注意。これは、列名のその部分が測定される「値」(出力では変数になる)を指定することを pivot_longer()
に伝える。
%>%
family pivot_longer(
!family,
names_to = c(".value", "child"),
names_sep = "_",
values_drop_na = TRUE
)#> # A tibble: 9 × 4
#> family child dob gender
#> <int> <chr> <date> <int>
#> 1 1 child1 1998-11-26 1
#> 2 1 child2 2000-01-29 2
#> 3 2 child1 1996-06-22 2
#> 4 3 child1 2002-07-11 2
#> 5 3 child2 2004-04-05 2
#> 6 4 child1 2004-10-10 1
#> 7 4 child2 2009-08-27 1
#> 8 5 child1 2000-12-05 2
#> 9 5 child2 2005-02-28 1
values_drop_na = TRUE
の使用に注意 : 入力形状は、存在しないオブザベーションのために明示的に欠損変数を作成することを強制する。
この問題は、ベースRに組み込まれた anscombe
データセットにも存在する。
anscombe#> x1 x2 x3 x4 y1 y2 y3 y4
#> 1 10 10 10 8 8.04 9.14 7.46 6.58
#> 2 8 8 8 8 6.95 8.14 6.77 5.76
#> 3 13 13 13 8 7.58 8.74 12.74 7.71
#> 4 9 9 9 8 8.81 8.77 7.11 8.84
#> 5 11 11 11 8 8.33 9.26 7.81 8.47
#> 6 14 14 14 8 9.96 8.10 8.84 7.04
#> 7 6 6 6 8 7.24 6.13 6.08 5.25
#> 8 4 4 4 19 4.26 3.10 5.39 12.50
#> 9 12 12 12 8 10.84 9.13 8.15 5.56
#> 10 7 7 7 8 4.82 7.26 6.42 7.91
#> 11 5 5 5 8 5.68 4.74 5.73 6.89
このデータセットには4組の変数( x1
and y1
, x2
and y2
, etc)が含まれており、Anscombeのカルテット(要約統計量(平均、sd、相関など)は同じだが、データは全く異なる4つのデータセットの集合)の根底をなすものである。私たちは。set
。x
。y
の列を持つデータセットを作りたい。
%>%
anscombe pivot_longer(everything(),
names_to = c(".value", "set"),
names_pattern = "(.)(.)"
%>%
) arrange(set)
#> # A tibble: 44 × 3
#> set x y
#> <chr> <dbl> <dbl>
#> 1 1 10 8.04
#> 2 1 8 6.95
#> 3 1 13 7.58
#> 4 1 9 8.81
#> 5 1 11 8.33
#> 6 1 14 9.96
#> 7 1 6 7.24
#> 8 1 4 4.26
#> 9 1 12 10.8
#> 10 1 7 4.82
#> # … with 34 more rows
パネルデータでも同じような状況が起こりうる。たとえば、 Thomas Leeperが提供するこのデータセットの例を見てみよう。anscombe
と同じ方法で整頓することができる。
<- tibble(
pnl x = 1:4,
a = c(1, 1,0, 0),
b = c(0, 1, 1, 1),
y1 = rnorm(4),
y2 = rnorm(4),
z1 = rep(3, 4),
z2 = rep(-2, 4),
)
%>%
pnl pivot_longer(
!c(x, a, b),
names_to = c(".value", "time"),
names_pattern = "(.)(.)"
)#> # A tibble: 8 × 6
#> x a b time y z
#> <int> <dbl> <dbl> <chr> <dbl> <dbl>
#> 1 1 1 0 1 1.52 3
#> 2 1 1 0 2 -1.37 -2
#> 3 2 1 1 1 -0.224 3
#> 4 2 1 1 2 -0.910 -2
#> 5 3 0 1 1 -2.03 3
#> 6 3 0 1 2 0.451 -2
#> 7 4 0 1 1 1.47 3
#> 8 4 0 1 2 0.942 -2
時々、列名が重複しているデータセットを見かけることがある。一般に、このようなデータセットはRで扱うのが難しい。なぜなら、ある列を名前で参照したときに、最初にマッチしたものしか見つけられないからである。名前が重複している tibble を作成するには、名前の修復を明示的に拒否する必要がある。
<- tibble(id = 1:3, y = 4:6, y = 5:7, y = 7:9, .name_repair = "minimal")
df
df#> # A tibble: 3 × 4
#> id y y y
#> <int> <int> <int> <int>
#> 1 1 4 5 7
#> 2 2 5 6 8
#> 3 3 6 7 9
pivot_longer()
は、このようなデータに出会うと、自動的に別の列を出力に追加する。
%>% pivot_longer(!id, names_to = "name", values_to = "value")
df #> # A tibble: 9 × 3
#> id name value
#> <int> <chr> <int>
#> 1 1 y 4
#> 2 1 y 5
#> 3 1 y 7
#> 4 2 y 5
#> 5 2 y 6
#> 6 2 y 8
#> 7 3 y 6
#> 8 3 y 7
#> 9 3 y 9
次の例のように、複数の入力列が同じ出力列にマップされる場合にも、同様の処理が行われる。ここでは、各列名の数字のサフィックスを無視する。
<- tibble(id = 1:3, x1 = 4:6, x2 = 5:7, y1 = 7:9, y2 = 10:12)
df %>% pivot_longer(!id, names_to = ".value", names_pattern = "(.).")
df #> # A tibble: 6 × 3
#> id x y
#> <int> <int> <int>
#> 1 1 4 7
#> 2 1 5 10
#> 3 2 5 8
#> 4 2 6 11
#> 5 3 6 9
#> 6 3 7 12
pivot_wider()
は の逆で、列数を増やし行数を減らすことでデータセットをより広く__するものである。 を使って整頓されたデータを作成することは比較的稀であるが、プレゼンテーション用の要約表や、他のツールが必要とする形式のデータを作成する際にはよく役に立つ。 pivot_longer()
pivot_wider()
### 捕獲-再捕獲データ
Myfanwy Johnston が提供する fish_encounters
データセットは、川を泳ぐ魚が自動監視局で検出されるタイミングを記述したものである。
fish_encounters#> # A tibble: 114 × 3
#> fish station seen
#> <fct> <fct> <int>
#> 1 4842 Release 1
#> 2 4842 I80_1 1
#> 3 4842 Lisbon 1
#> 4 4842 Rstr 1
#> 5 4842 Base_TD 1
#> 6 4842 BCE 1
#> 7 4842 BCW 1
#> 8 4842 BCE2 1
#> 9 4842 BCW2 1
#> 10 4842 MAE 1
#> # … with 104 more rows
このデータを分析するための多くのツールは、各ステーションが列となる形式を必要とする。
%>% pivot_wider(names_from = station, values_from = seen)
fish_encounters #> # A tibble: 19 × 12
#> fish Release I80_1 Lisbon Rstr Base_TD BCE BCW BCE2 BCW2 MAE MAW
#> <fct> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
#> 1 4842 1 1 1 1 1 1 1 1 1 1 1
#> 2 4843 1 1 1 1 1 1 1 1 1 1 1
#> 3 4844 1 1 1 1 1 1 1 1 1 1 1
#> 4 4845 1 1 1 1 1 NA NA NA NA NA NA
#> 5 4847 1 1 1 NA NA NA NA NA NA NA NA
#> 6 4848 1 1 1 1 NA NA NA NA NA NA NA
#> 7 4849 1 1 NA NA NA NA NA NA NA NA NA
#> 8 4850 1 1 NA 1 1 1 1 NA NA NA NA
#> 9 4851 1 1 NA NA NA NA NA NA NA NA NA
#> 10 4854 1 1 NA NA NA NA NA NA NA NA NA
#> # … with 9 more rows
このデータセットでは、魚がステーションで検出されたときのみ記録され、検出されなかったときは記録されていない(これはこの種のデータではよくあることである)。つまり、出力データは NA
s で埋め尽くされる。しかし、この場合、記録がないということは、魚が seen
されなかったということだとわかるので、これらの欠損値をゼロで埋めるように pivot_wider()
に依頼することができる。
%>% pivot_wider(
fish_encounters names_from = station,
values_from = seen,
values_fill = 0
)#> # A tibble: 19 × 12
#> fish Release I80_1 Lisbon Rstr Base_TD BCE BCW BCE2 BCW2 MAE MAW
#> <fct> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
#> 1 4842 1 1 1 1 1 1 1 1 1 1 1
#> 2 4843 1 1 1 1 1 1 1 1 1 1 1
#> 3 4844 1 1 1 1 1 1 1 1 1 1 1
#> 4 4845 1 1 1 1 1 0 0 0 0 0 0
#> 5 4847 1 1 1 0 0 0 0 0 0 0 0
#> 6 4848 1 1 1 1 0 0 0 0 0 0 0
#> 7 4849 1 1 0 0 0 0 0 0 0 0 0
#> 8 4850 1 1 0 1 1 1 1 0 0 0 0
#> 9 4851 1 1 0 0 0 0 0 0 0 0 0
#> 10 4854 1 1 0 0 0 0 0 0 0 0 0
#> # … with 9 more rows
また。pivot_wider()
を使って、簡単な集計を行うこともできる。例えば、ベースRに組み込まれた warpbreaks
データセット(印刷方法を良くするためにtibbleに変換されている)を見てみよう。
<- warpbreaks %>% as_tibble() %>% select(wool, tension, breaks)
warpbreaks
warpbreaks#> # A tibble: 54 × 3
#> wool tension breaks
#> <fct> <fct> <dbl>
#> 1 A L 26
#> 2 A L 30
#> 3 A L 54
#> 4 A L 25
#> 5 A L 70
#> 6 A L 52
#> 7 A L 51
#> 8 A L 26
#> 9 A L 67
#> 10 A M 18
#> # … with 44 more rows
これは。wool
( A
と B
) と tension
( L
, M
, H
) の組み合わせごとに、9回の複製を行う設計実験である。
%>% count(wool, tension)
warpbreaks #> # A tibble: 6 × 3
#> wool tension n
#> <fct> <fct> <int>
#> 1 A L 9
#> 2 A M 9
#> 3 A H 9
#> 4 B L 9
#> 5 B M 9
#> 6 B H 9
wool
のレベルを列にピボットしようとすると、どうなるだろうか。
%>% pivot_wider(names_from = wool, values_from = breaks)
warpbreaks #> Warning: Values from `breaks` are not uniquely identified; output will contain list-cols.
#> * Use `values_fn = list` to suppress this warning.
#> * Use `values_fn = {summary_fun}` to summarise duplicates.
#> * Use the following dplyr code to identify duplicates.
#> {data} %>%
#> dplyr::group_by(tension, wool) %>%
#> dplyr::summarise(n = dplyr::n(), .groups = "drop") %>%
#> dplyr::filter(n > 1L)
#> # A tibble: 3 × 3
#> tension A B
#> <fct> <list> <list>
#> 1 L <dbl [9]> <dbl [9]>
#> 2 M <dbl [9]> <dbl [9]>
#> 3 H <dbl [9]> <dbl [9]>
出力の各セルが入力の複数のセルに対応しているという警告が表示される。デフォルトの動作は、すべての個々の値を含むリスト列を生成する。より有用な出力は、要約統計、例えば、ウールとテンションの組み合わせごとの mean
breakだろう。
%>%
warpbreaks pivot_wider(
names_from = wool,
values_from = breaks,
values_fn = list(breaks = mean)
)#> # A tibble: 3 × 3
#> tension A B
#> <fct> <dbl> <dbl>
#> 1 L 44.6 28.2
#> 2 M 24 28.8
#> 3 H 24.6 18.8
より複雑な要約操作を行う場合は、リシェイプの前に要約することを勧めるが、単純なケースでは。pivot_wider()
の中で要約するのが便利なことがよくある。
<https://stackoverflow.com/questions/24929954>のように、製品、国、年の組み合わせを含む情報があると想像してみよう。整頓すると次のようになる。
<- expand_grid(
production product = c("A", "B"),
country = c("AI", "EI"),
year = 2000:2014
%>%
) filter((product == "A" & country == "AI") | product == "B") %>%
mutate(production = rnorm(nrow(.)))
production#> # A tibble: 45 × 4
#> product country year production
#> <chr> <chr> <int> <dbl>
#> 1 A AI 2000 0.958
#> 2 A AI 2001 1.29
#> 3 A AI 2002 0.117
#> 4 A AI 2003 -0.755
#> 5 A AI 2004 0.608
#> 6 A AI 2005 0.532
#> 7 A AI 2006 0.168
#> 8 A AI 2007 -0.629
#> 9 A AI 2008 0.908
#> 10 A AI 2009 0.790
#> # … with 35 more rows
product
と country
の組み合わせごとに1列ずつになるように、データを広げたい。重要なのは。names_from
に複数の変数を指定することである。
%>% pivot_wider(
production names_from = c(product, country),
values_from = production
)#> # A tibble: 15 × 4
#> year A_AI B_AI B_EI
#> <int> <dbl> <dbl> <dbl>
#> 1 2000 0.958 -0.733 0.560
#> 2 2001 1.29 -1.67 -0.164
#> 3 2002 0.117 1.25 0.989
#> 4 2003 -0.755 2.48 -1.38
#> 5 2004 0.608 -0.0895 -1.02
#> 6 2005 0.532 1.42 -0.838
#> 7 2006 0.168 0.389 2.60
#> 8 2007 -0.629 -0.208 0.945
#> 9 2008 0.908 -0.888 1.37
#> 10 2009 0.790 -0.834 0.872
#> # … with 5 more rows
names_from
または values_from
が複数の変数を選択する場合。names_sep
と names_prefix
、または主力製品である names_glue
で、出力で構築される列名を制御することができる。
%>% pivot_wider(
production names_from = c(product, country),
values_from = production,
names_sep = ".",
names_prefix = "prod."
)#> # A tibble: 15 × 4
#> year prod.A.AI prod.B.AI prod.B.EI
#> <int> <dbl> <dbl> <dbl>
#> 1 2000 0.958 -0.733 0.560
#> 2 2001 1.29 -1.67 -0.164
#> 3 2002 0.117 1.25 0.989
#> 4 2003 -0.755 2.48 -1.38
#> 5 2004 0.608 -0.0895 -1.02
#> 6 2005 0.532 1.42 -0.838
#> 7 2006 0.168 0.389 2.60
#> 8 2007 -0.629 -0.208 0.945
#> 9 2008 0.908 -0.888 1.37
#> 10 2009 0.790 -0.834 0.872
#> # … with 5 more rows
%>% pivot_wider(
production names_from = c(product, country),
values_from = production,
names_glue = "prod_{product}_{country}"
)#> # A tibble: 15 × 4
#> year prod_A_AI prod_B_AI prod_B_EI
#> <int> <dbl> <dbl> <dbl>
#> 1 2000 0.958 -0.733 0.560
#> 2 2001 1.29 -1.67 -0.164
#> 3 2002 0.117 1.25 0.989
#> 4 2003 -0.755 2.48 -1.38
#> 5 2004 0.608 -0.0895 -1.02
#> 6 2005 0.532 1.42 -0.838
#> 7 2006 0.168 0.389 2.60
#> 8 2007 -0.629 -0.208 0.945
#> 9 2008 0.908 -0.888 1.37
#> 10 2009 0.790 -0.834 0.872
#> # … with 5 more rows
us_rent_income
データセットは、2017年のアメリカの各州の所得と家賃の中央値の情報を含んでいる(American Community Surveyより、 tidycensus tidycensus パッケージで取得)。
us_rent_income#> # A tibble: 104 × 5
#> GEOID NAME variable estimate moe
#> <chr> <chr> <chr> <dbl> <dbl>
#> 1 01 Alabama income 24476 136
#> 2 01 Alabama rent 747 3
#> 3 02 Alaska income 32940 508
#> 4 02 Alaska rent 1200 13
#> 5 04 Arizona income 27517 148
#> 6 04 Arizona rent 972 4
#> 7 05 Arkansas income 23789 165
#> 8 05 Arkansas rent 709 5
#> 9 06 California income 29454 109
#> 10 06 California rent 1358 3
#> # … with 94 more rows
ここでは。estimate
と moe
の両方が値列なので。values_from
に供給することができる。
%>%
us_rent_income pivot_wider(names_from = variable, values_from = c(estimate, moe))
#> # A tibble: 52 × 6
#> GEOID NAME estimate_income estimate_rent moe_income moe_rent
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 01 Alabama 24476 747 136 3
#> 2 02 Alaska 32940 1200 508 13
#> 3 04 Arizona 27517 972 148 4
#> 4 05 Arkansas 23789 709 165 5
#> 5 06 California 29454 1358 109 3
#> 6 08 Colorado 32401 1125 109 5
#> 7 09 Connecticut 35326 1123 195 5
#> 8 10 Delaware 31560 1076 247 10
#> 9 11 District of Columbia 43198 1424 681 17
#> 10 12 Florida 25952 1077 70 3
#> # … with 42 more rows
なお、出力列には自動的に変数名が付加される。
時折、名前変数が因子としてエンコードされているデータに出会うことがあるが、すべてのデータが表現されるわけではない。
<- c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
weekdays
<- tibble(
daily day = factor(c("Tue", "Thu", "Fri", "Mon"), levels = weekdays),
value = c(2, 3, 1, 5)
)
daily#> # A tibble: 4 × 2
#> day value
#> <fct> <dbl>
#> 1 Tue 2
#> 2 Thu 3
#> 3 Fri 1
#> 4 Mon 5
pivot_wider()
のデフォルトでは、実際にデータで表される値から列を生成するが、将来的にデータが変更された場合に備えて、可能性のある各レベルの列を含めるようにするとよいだろう。
pivot_wider(daily, names_from = day, values_from = value)
#> # A tibble: 1 × 4
#> Tue Thu Fri Mon
#> <dbl> <dbl> <dbl> <dbl>
#> 1 2 3 1 5
names_expand
引数は、暗黙的な因子レベルを明示的なものに変え、結果で表現することを強制する。また、レベルの順序を使用して列名をソートし、この場合、より直感的な結果を生成する。
pivot_wider(daily, names_from = day, values_from = value, names_expand = TRUE)
#> # A tibble: 1 × 7
#> Mon Tue Wed Thu Fri Sat Sun
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 5 2 NA 3 1 NA NA
複数の列 ( names_from
) が指定された場合。names_expand
は。names_from
の値のすべての可能な組み合わせのデカルト積を生成する。以下のデータでは、パーセント値が 0
となる行がいくつか省略されていることに注意。names_expand
を使用すると、ピボット時にこれらを明示すことができる。
<- tibble(
percentages year = c(2018, 2019, 2020, 2020),
type = factor(c("A", "B", "A", "B"), levels = c("A", "B")),
percentage = c(100, 100, 40, 60)
)
percentages#> # A tibble: 4 × 3
#> year type percentage
#> <dbl> <fct> <dbl>
#> 1 2018 A 100
#> 2 2019 B 100
#> 3 2020 A 40
#> 4 2020 B 60
pivot_wider(
percentages,names_from = c(year, type),
values_from = percentage,
names_expand = TRUE,
values_fill = 0
)#> # A tibble: 1 × 6
#> `2018_A` `2018_B` `2019_A` `2019_B` `2020_A` `2020_B`
#> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 100 0 0 100 40 60
id_cols
この場合、明示的に表現したい行 (列ではなく) が欠落していることになる。この例では。daily
データに type
列を追加し、その代わりに day
を ID 列としてピボットすることにする。
<- mutate(daily, type = factor(c("A", "B", "B", "A")))
daily
daily#> # A tibble: 4 × 3
#> day value type
#> <fct> <dbl> <fct>
#> 1 Tue 2 A
#> 2 Thu 3 B
#> 3 Fri 1 B
#> 4 Mon 5 A
type
のレベルはすべて列で表現されているが、表現されていない day
の因子レベルに関連するいくつかの行が欠けている。
pivot_wider(
daily, names_from = type,
values_from = value,
values_fill = 0
)#> # A tibble: 4 × 3
#> day A B
#> <fct> <dbl> <dbl>
#> 1 Tue 2 0
#> 2 Thu 0 3
#> 3 Fri 0 1
#> 4 Mon 5 0
id_expand
を names_expand
と同じように使えば。id_cols
の暗黙の欠落行を展開(ソート)することができる。
pivot_wider(
daily, names_from = type,
values_from = value,
values_fill = 0,
id_expand = TRUE
)#> # A tibble: 7 × 3
#> day A B
#> <fct> <dbl> <dbl>
#> 1 Mon 5 0
#> 2 Tue 2 0
#> 3 Wed 0 0
#> 4 Thu 0 3
#> 5 Fri 0 1
#> 6 Sat 0 0
#> 7 Sun 0 0
ピボット処理とはまったく関係のない列がデータ内に存在する場合、その列の情報を何らかの方法で保持したいと想像してみよう。たとえば。updates
で。system
列をピボットして、各郡のシステム更新の 1 行サマリーを作成したいとする。
<- tibble(
updates county = c("Wake", "Wake", "Wake", "Guilford", "Guilford"),
date = c(as.Date("2020-01-01") + 0:2, as.Date("2020-01-03") + 0:1),
system = c("A", "B", "C", "A", "C"),
value = c(3.2, 4, 5.5, 2, 1.2)
)
updates#> # A tibble: 5 × 4
#> county date system value
#> <chr> <date> <chr> <dbl>
#> 1 Wake 2020-01-01 A 3.2
#> 2 Wake 2020-01-02 B 4
#> 3 Wake 2020-01-03 C 5.5
#> 4 Guilford 2020-01-03 A 2
#> 5 Guilford 2020-01-04 C 1.2
一般的な pivot_wider()
の呼び出しでも可能であるが。date
の列に関するすべての情報を完全に失いる。
pivot_wider(
updates, id_cols = county,
names_from = system,
values_from = value
)#> # A tibble: 2 × 4
#> county A B C
#> <chr> <dbl> <dbl> <dbl>
#> 1 Wake 3.2 4 5.5
#> 2 Guilford 2 NA 1.2
この例では、特定の郡のすべてのシステムで、最新の更新日を保持したいと思われる。これを実現するには。unused_fn
引数を使用する。これにより、ピボット処理で使用されない列の値を要約することができる。
pivot_wider(
updates, id_cols = county,
names_from = system,
values_from = value,
unused_fn = list(date = max)
)#> # A tibble: 2 × 5
#> county A B C date
#> <chr> <dbl> <dbl> <dbl> <date>
#> 1 Wake 3.2 4 5.5 2020-01-03
#> 2 Guilford 2 NA 1.2 2020-01-04
また。list()
を要約関数として使用することで、データは保持しつつ、集計を完全に遅らせることができる。
pivot_wider(
updates, id_cols = county,
names_from = system,
values_from = value,
unused_fn = list(date = list)
)#> # A tibble: 2 × 5
#> county A B C date
#> <chr> <dbl> <dbl> <dbl> <list>
#> 1 Wake 3.2 4 5.5 <date [3]>
#> 2 Guilford 2 NA 1.2 <date [2]>
最後の課題は、 Jiena Guに触発されたものである。ウェブサイトからコピー&ペーストした連絡先リストがあると想像してみよう。
<- tribble(
contacts ~field, ~value,
"name", "Jiena McLellan",
"company", "Toyota",
"name", "John Smith",
"company", "google",
"email", "john@google.com",
"name", "Huxley Ratcliffe"
)
これは、どのオブザベーションが一緒になっているかを識別する変数がないので、困難である。すべてのコンタクトは名前で始まるので。field
として “name”を見るたびにカウントすることによってユニークなIDを作成することによってこれを修正することがでく :
<- contacts %>%
contacts mutate(
person_id = cumsum(field == "name")
)
contacts#> # A tibble: 6 × 3
#> field value person_id
#> <chr> <chr> <int>
#> 1 name Jiena McLellan 1
#> 2 company Toyota 1
#> 3 name John Smith 2
#> 4 company google 2
#> 5 email john@google.com 2
#> 6 name Huxley Ratcliffe 3
各人に一意の識別子があるので。field
と value
を列にピボットすることができる。
%>%
contacts pivot_wider(names_from = field, values_from = value)
#> # A tibble: 3 × 4
#> person_id name company email
#> <int> <chr> <chr> <chr>
#> 1 1 Jiena McLellan Toyota <NA>
#> 2 2 John Smith google john@google.com
#> 3 3 Huxley Ratcliffe <NA> <NA>
問題によっては、単一方向のピボットでは解決できないものもある。このセクションの例では。pivot_longer()
と pivot_wider()
を組み合わせて、より複雑な問題を解決する方法を紹介する。
world_bank_pop
は、2000年から2018年までの国別人口について世界銀行のデータを収録している。
world_bank_pop#> # A tibble: 1,056 × 20
#> country indic…¹ `2000` `2001` `2002` `2003` `2004` `2005` `2006` `2007`
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 ABW SP.URB… 4.24e4 4.30e4 4.37e4 4.42e4 4.47e+4 4.49e+4 4.49e+4 4.47e+4
#> 2 ABW SP.URB… 1.18e0 1.41e0 1.43e0 1.31e0 9.51e-1 4.91e-1 -1.78e-2 -4.35e-1
#> 3 ABW SP.POP… 9.09e4 9.29e4 9.50e4 9.70e4 9.87e+4 1.00e+5 1.01e+5 1.01e+5
#> 4 ABW SP.POP… 2.06e0 2.23e0 2.23e0 2.11e0 1.76e+0 1.30e+0 7.98e-1 3.84e-1
#> 5 AFG SP.URB… 4.44e6 4.65e6 4.89e6 5.16e6 5.43e+6 5.69e+6 5.93e+6 6.15e+6
#> 6 AFG SP.URB… 3.91e0 4.66e0 5.13e0 5.23e0 5.12e+0 4.77e+0 4.12e+0 3.65e+0
#> 7 AFG SP.POP… 2.01e7 2.10e7 2.20e7 2.31e7 2.41e+7 2.51e+7 2.59e+7 2.66e+7
#> 8 AFG SP.POP… 3.49e0 4.25e0 4.72e0 4.82e0 4.47e+0 3.87e+0 3.23e+0 2.76e+0
#> 9 AGO SP.URB… 8.23e6 8.71e6 9.22e6 9.77e6 1.03e+7 1.09e+7 1.15e+7 1.21e+7
#> 10 AGO SP.URB… 5.44e0 5.59e0 5.70e0 5.76e0 5.75e+0 5.69e+0 4.92e+0 4.89e+0
#> # … with 1,046 more rows, 10 more variables: `2008` <dbl>, `2009` <dbl>,
#> # `2010` <dbl>, `2011` <dbl>, `2012` <dbl>, `2013` <dbl>, `2014` <dbl>,
#> # `2015` <dbl>, `2016` <dbl>, `2017` <dbl>, and abbreviated variable name
#> # ¹indicator
私の目標は、各変数が1列になっている整頓されたデータセットを作成することである。どのような手順が必要かはまだ明らかではないが、最も明白な問題から始めよう:yearが複数の列にまたがっている。
<- world_bank_pop %>%
pop2 pivot_longer(`2000`:`2017`, names_to = "year", values_to = "value")
pop2#> # A tibble: 19,008 × 4
#> country indicator year value
#> <chr> <chr> <chr> <dbl>
#> 1 ABW SP.URB.TOTL 2000 42444
#> 2 ABW SP.URB.TOTL 2001 43048
#> 3 ABW SP.URB.TOTL 2002 43670
#> 4 ABW SP.URB.TOTL 2003 44246
#> 5 ABW SP.URB.TOTL 2004 44669
#> 6 ABW SP.URB.TOTL 2005 44889
#> 7 ABW SP.URB.TOTL 2006 44881
#> 8 ABW SP.URB.TOTL 2007 44686
#> 9 ABW SP.URB.TOTL 2008 44375
#> 10 ABW SP.URB.TOTL 2009 44052
#> # … with 18,998 more rows
次に。indicator
の変数について考える必要がある。
%>% count(indicator)
pop2 #> # A tibble: 4 × 2
#> indicator n
#> <chr> <int>
#> 1 SP.POP.GROW 4752
#> 2 SP.POP.TOTL 4752
#> 3 SP.URB.GROW 4752
#> 4 SP.URB.TOTL 4752
ここで。SP.POP.GROW
は人口増加。SP.POP.TOTL
は総人口。SP.URB.*
は同じであるが、都市部のみである。これを。area
(総人口または都市部)と実際の変数(人口または成長率)の2つに分けて考えてみよう。
<- pop2 %>%
pop3 separate(indicator, c(NA, "area", "variable"))
pop3#> # A tibble: 19,008 × 5
#> country area variable year value
#> <chr> <chr> <chr> <chr> <dbl>
#> 1 ABW URB TOTL 2000 42444
#> 2 ABW URB TOTL 2001 43048
#> 3 ABW URB TOTL 2002 43670
#> 4 ABW URB TOTL 2003 44246
#> 5 ABW URB TOTL 2004 44669
#> 6 ABW URB TOTL 2005 44889
#> 7 ABW URB TOTL 2006 44881
#> 8 ABW URB TOTL 2007 44686
#> 9 ABW URB TOTL 2008 44375
#> 10 ABW URB TOTL 2009 44052
#> # … with 18,998 more rows
あとは。variable
と value
をピボットして。TOTL
と GROW
の列を作れば、整理整頓は完了である。
%>%
pop3 pivot_wider(names_from = variable, values_from = value)
#> # A tibble: 9,504 × 5
#> country area year TOTL GROW
#> <chr> <chr> <chr> <dbl> <dbl>
#> 1 ABW URB 2000 42444 1.18
#> 2 ABW URB 2001 43048 1.41
#> 3 ABW URB 2002 43670 1.43
#> 4 ABW URB 2003 44246 1.31
#> 5 ABW URB 2004 44669 0.951
#> 6 ABW URB 2005 44889 0.491
#> 7 ABW URB 2006 44881 -0.0178
#> 8 ABW URB 2007 44686 -0.435
#> 9 ABW URB 2008 44375 -0.698
#> 10 ABW URB 2009 44052 -0.731
#> # … with 9,494 more rows
Maxime Wack, <https://github.com/tidyverse/tidyr/issues/384>;) の提案に基づき、最後の例では、多肢選択式データの一般的な記録方法に対処する方法を示す。しばしば、次のようなデータが得られる。
<- tribble(
multi ~id, ~choice1, ~choice2, ~choice3,
1, "A", "B", "C",
2, "C", "B", NA,
3, "D", NA, NA,
4, "B", "D", NA
)
しかし、実際の順序は重要ではなく、個々の質問を列で表示することを好むだろう。2つのステップで目的の変換を行うことができる。まず、データを長くして、明示的な NA
sを削除し、この選択肢が選ばれたことを示す列を追加する。
<- multi %>%
multi2 pivot_longer(!id, values_drop_na = TRUE) %>%
mutate(checked = TRUE)
multi2#> # A tibble: 8 × 4
#> id name value checked
#> <dbl> <chr> <chr> <lgl>
#> 1 1 choice1 A TRUE
#> 2 1 choice2 B TRUE
#> 3 1 choice3 C TRUE
#> 4 2 choice1 C TRUE
#> 5 2 choice2 B TRUE
#> 6 3 choice1 D TRUE
#> 7 4 choice1 B TRUE
#> 8 4 choice2 D TRUE
そして、データを広くして、欠落している観測値を FALSE
で埋める。
%>%
multi2 pivot_wider(
id_cols = id,
names_from = value,
values_from = checked,
values_fill = FALSE
)#> # A tibble: 4 × 5
#> id A B C D
#> <dbl> <lgl> <lgl> <lgl> <lgl>
#> 1 1 TRUE TRUE TRUE FALSE
#> 2 2 FALSE TRUE TRUE FALSE
#> 3 3 FALSE FALSE FALSE TRUE
#> 4 4 FALSE TRUE FALSE TRUE
pivot_longer()
と pivot_wider()
の引数を使うと、さまざまなデータセットをピボット化することができる。しかし、データ構造に適用される創造性は無限であるため。pivot_longer()
と pivot_wider()
を使用してどのように再構築するかすぐに分からないデータセットに遭遇する可能性は十分にある。ピボットの制御を強化するには、代わりに「spec」データフレームを作成して、列名に格納されたデータがどのように変数になるか(またはその逆)を正確に記述することができる。このセクションでは、spec データ構造を紹介し。pivot_longer()
や pivot_wider()
では不十分な場合に使用する方法を説明する。
この仕組みを理解するために。relig_income
のデータセットにピボットを適用する最も単純なケースに戻りよう。まず spec オブジェクトを作成し ( build_longer_spec()
を使用)、そのオブジェクトでピボット操作を記述する。
<- relig_income %>% build_longer_spec(
spec cols = !religion,
names_to = "income",
values_to = "count"
)pivot_longer_spec(relig_income, spec)
#> # A tibble: 180 × 3
#> religion income count
#> <chr> <chr> <dbl>
#> 1 Agnostic <$10k 27
#> 2 Agnostic $10-20k 34
#> 3 Agnostic $20-30k 60
#> 4 Agnostic $30-40k 81
#> 5 Agnostic $40-50k 76
#> 6 Agnostic $50-75k 137
#> 7 Agnostic $75-100k 122
#> 8 Agnostic $100-150k 109
#> 9 Agnostic >150k 84
#> 10 Agnostic Don't know/refused 96
#> # … with 170 more rows
(これは前と同じ結果をもたらするが、ただコードが増えただけである。ここで使う必要はない。これは spec
を使うための簡単な例として紹介されている)
spec
はどのようなものか? これは、ワイドフォーマット版のデータでロングフォーマットには存在しない各列に1行ずつと。.
で始まる2つの特別な列を持つデータフレームである。
.name
は列の名前を与える。.value
は、セル内の値が入る列の名前である。 に入る。また。spec
には、データのロングフォーマットに存在し、データのワイドフォーマットには存在しない列がそれぞれ1つずつある。これは。pivot_longer()
と build_longer_spec()
の names_to
引数。pivot_wider()
と build_wider_spec()
の names_from
引数に相当する。 この例では、所得列はピボットされる列の名前を文字ベクトルで表したものである。
spec#> # A tibble: 10 × 3
#> .name .value income
#> <chr> <chr> <chr>
#> 1 <$10k count <$10k
#> 2 $10-20k count $10-20k
#> 3 $20-30k count $20-30k
#> 4 $30-40k count $30-40k
#> 5 $40-50k count $40-50k
#> 6 $50-75k count $50-75k
#> 7 $75-100k count $75-100k
#> 8 $100-150k count $100-150k
#> 9 >150k count >150k
#> 10 Don't know/refused count Don't know/refused
以下では。us_rent_income
を pivot_wider()
で広げている。結果はオーケーであるが、もっと改善できると思われる。
%>%
us_rent_income pivot_wider(names_from = variable, values_from = c(estimate, moe))
#> # A tibble: 52 × 6
#> GEOID NAME estimate_income estimate_rent moe_income moe_rent
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 01 Alabama 24476 747 136 3
#> 2 02 Alaska 32940 1200 508 13
#> 3 04 Arizona 27517 972 148 4
#> 4 05 Arkansas 23789 709 165 5
#> 5 06 California 29454 1358 109 3
#> 6 08 Colorado 32401 1125 109 5
#> 7 09 Connecticut 35326 1123 195 5
#> 8 10 Delaware 31560 1076 247 10
#> 9 11 District of Columbia 43198 1424 681 17
#> 10 12 Florida 25952 1077 70 3
#> # … with 42 more rows
income
, rent
, income_moe
, rent_moe
という列があった方がいいと思うのであるが、これは手動仕様で実現できる。現在の仕様はこのような感じである。
<- us_rent_income %>%
spec1 build_wider_spec(names_from = variable, values_from = c(estimate, moe))
spec1#> # A tibble: 4 × 3
#> .name .value variable
#> <chr> <chr> <chr>
#> 1 estimate_income estimate income
#> 2 estimate_rent estimate rent
#> 3 moe_income moe income
#> 4 moe_rent moe rent
この場合。spec
を変異させ、列名を慎重に構築する。
<- spec1 %>%
spec2 mutate(.name = paste0(variable, ifelse(.value == "moe", "_moe", "")))
spec2#> # A tibble: 4 × 3
#> .name .value variable
#> <chr> <chr> <chr>
#> 1 income estimate income
#> 2 rent estimate rent
#> 3 income_moe moe income
#> 4 rent_moe moe rent
この仕様を pivot_wider()
に供給することで、求めている結果が得られる。
pivot_wider_spec(us_rent_income, spec2)
#> # A tibble: 52 × 6
#> GEOID NAME income rent income_moe rent_moe
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 01 Alabama 24476 747 136 3
#> 2 02 Alaska 32940 1200 508 13
#> 3 04 Arizona 27517 972 148 4
#> 4 05 Arkansas 23789 709 165 5
#> 5 06 California 29454 1358 109 3
#> 6 08 Colorado 32401 1125 109 5
#> 7 09 Connecticut 35326 1123 195 5
#> 8 10 Delaware 31560 1076 247 10
#> 9 11 District of Columbia 43198 1424 681 17
#> 10 12 Florida 25952 1077 70 3
#> # … with 42 more rows
スペックを計算できない(あるいは便利でない)場合もあり、その場合は「手書き」でスペックを作成した方が便利である。 “. For example, take this construction
data, which is lightly modified from Table 5” <https://www.census.gov/construction/nrc/index.html>; にある “completions” を参照してみよう。
construction#> # A tibble: 9 × 9
#> Year Month `1 unit` `2 to 4 units` 5 units …¹ North…² Midwest South West
#> <dbl> <chr> <dbl> <lgl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 2018 January 859 NA 348 114 169 596 339
#> 2 2018 February 882 NA 400 138 160 655 336
#> 3 2018 March 862 NA 356 150 154 595 330
#> 4 2018 April 797 NA 447 144 196 613 304
#> 5 2018 May 875 NA 364 90 169 673 319
#> 6 2018 June 867 NA 342 76 170 610 360
#> 7 2018 July 829 NA 360 108 183 594 310
#> 8 2018 August 939 NA 286 90 205 649 286
#> 9 2018 September 835 NA 304 117 175 560 296
#> # … with abbreviated variable names ¹`5 units or more`, ²Northeast
このようなデータは政府機関では珍しくない。列名は実際には異なる変数に属しており、ここではユニット数(1、2-4、5以上)と国の地域(NE、NW、midwest、S、W)の要約が示されている。それを最も簡単に表現できるのが、tibbleである。
<- tribble(
spec ~.name, ~.value, ~units, ~region,
"1 unit", "n", "1", NA,
"2 to 4 units", "n", "2-4", NA,
"5 units or more", "n", "5+", NA,
"Northeast", "n", NA, "Northeast",
"Midwest", "n", NA, "Midwest",
"South", "n", NA, "South",
"West", "n", NA, "West",
)
となり、次のような長い形式が得られる。
pivot_longer_spec(construction, spec)
#> # A tibble: 63 × 5
#> Year Month units region n
#> <dbl> <chr> <chr> <chr> <dbl>
#> 1 2018 January 1 <NA> 859
#> 2 2018 January 2-4 <NA> NA
#> 3 2018 January 5+ <NA> 348
#> 4 2018 January <NA> Northeast 114
#> 5 2018 January <NA> Midwest 169
#> 6 2018 January <NA> South 596
#> 7 2018 January <NA> West 339
#> 8 2018 February 1 <NA> 882
#> 9 2018 February 2-4 <NA> NA
#> 10 2018 February 5+ <NA> 400
#> # … with 53 more rows
units
と region
の変数には重複がないことに注意。この場合、データは2つの独立した表で記述されるのが最も自然であろう。
spec
の素晴らしい特性のひとつは。pivot_longer()
と pivot_wider()
で同じ仕様が必要なことである。これにより、2つの操作が対称的であることが非常に明確になる。
%>%
construction pivot_longer_spec(spec) %>%
pivot_wider_spec(spec)
#> # A tibble: 9 × 9
#> Year Month `1 unit` `2 to 4 units` 5 units …¹ North…² Midwest South West
#> <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 2018 January 859 NA 348 114 169 596 339
#> 2 2018 February 882 NA 400 138 160 655 336
#> 3 2018 March 862 NA 356 150 154 595 330
#> 4 2018 April 797 NA 447 144 196 613 304
#> 5 2018 May 875 NA 364 90 169 673 319
#> 6 2018 June 867 NA 342 76 170 610 360
#> 7 2018 July 829 NA 360 108 183 594 310
#> 8 2018 August 939 NA 286 90 205 649 286
#> 9 2018 September 835 NA 304 117 175 560 296
#> # … with abbreviated variable names ¹`5 units or more`, ²Northeast
ピボット仕様では。pivot_longer(df, spec = spec)
が df
の形状をどのように変化させるかをより正確に把握することができる。nrow(df) * nrow(spec)
の行と ncol(df) - nrow(spec) + ncol(spec) - 2
の列を持つことになる。