147  実践 pivot_longerで複数列を同時に作成する

library(tidyverse)

ここからは、pivot_longerの話です。1つ前の動画で作成したデータですが、

dat <- tibble(
  age = c("<20","20-30","30-40",">40","<20","20-30","30-40",">40"),
  grp = c("A","B","A","B","A","B","A","B"),
  sex = c("m","m","m","m","f","f","f","f"),
  val = c(1:8)
)

yoko <- dat %>% 
  pivot_wider(
    id_cols = grp,
    names_from = c(age, sex),
    values_from = val,
    values_fill = 0
  )

yoko
# A tibble: 2 × 9
  grp   `<20_m` `20-30_m` `30-40_m` `>40_m` `<20_f` `20-30_f` `30-40_f` `>40_f`
  <chr>   <int>     <int>     <int>   <int>   <int>     <int>     <int>   <int>
1 A           1         0         3       0       5         0         7       0
2 B           0         2         0       4       0         6         0       8

このデータを縦方向に並べてみましょう解説した方法だけだと、

tate1 <- yoko %>% 
  pivot_longer(
    cols = !grp,
    names_to = "name",
    values_to = "value"
  )

tate1
# A tibble: 16 × 3
   grp   name    value
   <chr> <chr>   <int>
 1 A     <20_m       1
 2 A     20-30_m     0
 3 A     30-40_m     3
 4 A     >40_m       0
 5 A     <20_f       5
 6 A     20-30_f     0
 7 A     30-40_f     7
 8 A     >40_f       0
 9 B     <20_m       0
10 B     20-30_m     2
11 B     30-40_m     0
12 B     >40_m       4
13 B     <20_f       0
14 B     20-30_f     6
15 B     30-40_f     0
16 B     >40_f       8

こんな形になるので、

tate1 %>% 
  separate(name,c("age","sex"), sep="_")
# A tibble: 16 × 4
   grp   age   sex   value
   <chr> <chr> <chr> <int>
 1 A     <20   m         1
 2 A     20-30 m         0
 3 A     30-40 m         3
 4 A     >40   m         0
 5 A     <20   f         5
 6 A     20-30 f         0
 7 A     30-40 f         7
 8 A     >40   f         0
 9 B     <20   m         0
10 B     20-30 m         2
11 B     30-40 m         0
12 B     >40   m         4
13 B     <20   f         0
14 B     20-30 f         6
15 B     30-40 f         0
16 B     >40   f         8

separate関数や、

tate1 %>% 
  extract(name,c("age","sex"), regex="(.+)_(.+)")
# A tibble: 16 × 4
   grp   age   sex   value
   <chr> <chr> <chr> <int>
 1 A     <20   m         1
 2 A     20-30 m         0
 3 A     30-40 m         3
 4 A     >40   m         0
 5 A     <20   f         5
 6 A     20-30 f         0
 7 A     30-40 f         7
 8 A     >40   f         0
 9 B     <20   m         0
10 B     20-30 m         2
11 B     30-40 m         0
12 B     >40   m         4
13 B     <20   f         0
14 B     20-30 f         6
15 B     30-40 f         0
16 B     >40   f         8

extract関数を利用してさらに分割してあげる必要があります

実は、pivot_longer関数に列の分割まで含めていっきにやってくれるargumentが備えられており、

yoko %>% 
  pivot_longer(
    cols = !grp,
    names_to = c("age","sex"),
    values_to = "value",
    names_sep = "_"
  )
# A tibble: 16 × 4
   grp   age   sex   value
   <chr> <chr> <chr> <int>
 1 A     <20   m         1
 2 A     20-30 m         0
 3 A     30-40 m         3
 4 A     >40   m         0
 5 A     <20   f         5
 6 A     20-30 f         0
 7 A     30-40 f         7
 8 A     >40   f         0
 9 B     <20   m         0
10 B     20-30 m         2
11 B     30-40 m         0
12 B     >40   m         4
13 B     <20   f         0
14 B     20-30 f         6
15 B     30-40 f         0
16 B     >40   f         8

names_toに最終的な変数の名前と、names_sepで分割するための正規表現を与えてあげることで、separate関数をnames_to列に実行したような結果を1つの関数で実施可能です。

extract関数と同じ働きも備えられており、

yoko %>% 
  pivot_longer(
    cols = !grp,
    names_to = c("age","sex"),
    values_to = "value",
    names_pattern = "(.+)_(.+)"
  )
# A tibble: 16 × 4
   grp   age   sex   value
   <chr> <chr> <chr> <int>
 1 A     <20   m         1
 2 A     20-30 m         0
 3 A     30-40 m         3
 4 A     >40   m         0
 5 A     <20   f         5
 6 A     20-30 f         0
 7 A     30-40 f         7
 8 A     >40   f         0
 9 B     <20   m         0
10 B     20-30 m         2
11 B     30-40 m         0
12 B     >40   m         4
13 B     <20   f         0
14 B     20-30 f         6
15 B     30-40 f         0
16 B     >40   f         8

names_toに目的とする最終的な列名、names_patternにextract関数でいうところのregrexを与えてあげると分割することができました。

この機能、複雑な列名がついた横持ちデータには非常に有効ですので、覚えておいてください(もちろん、pivot_longerとextract等を組み合わせるような使い方でも問題ありません!)