10  tidyr 패키지로 데이터 정리

9장에서 Tidy Data의 개념에 대해서 설명했다. 여기서는 실제로 사용되는 함수에 관해 좀 더 자세히 설명한다.

10.1 피벗팅(Pivoting)

10.1.1 피벗팅의 기본

우선 tidyr 패키지 cheatsheet에 있는 예시를 보면서 기초를 익히자.

다음과 같은 Untidy data가 있다.

table4a
# A tibble: 3 × 3
  country     `1999` `2000`
  <chr>        <dbl>  <dbl>
1 Afghanistan    745   2666
2 Brazil       37737  80488
3 China       212258 213766

위 데이터셋은 각 연도별 케이스 수를 나타내는 데이터이다. 값이 되어야할 연도가 열의 이름으로 들어가 있어 이 데이터는 untidy data이다.

이 데이터를 tidy data로 변환하기 위해서는 연도를 값으로 만들어야 하므로, 이 열들 1999, 2000을 묶어서 녹인 다음, year라는 열과 cases라는 열로 재배열한다.

중요
  • pivot_longer() 함수
    • pivot_longer(df, cols, names_to = "새이름", values_to = "새이름")
    • 여러 열 –> (하나로 녹임) –> 펼쳐서 2개의 열로 재배열
    • 이 2개의 열에 이름을 부여한다.
      • 이름 열은 names_to 인자에, 값 열을 values_to 인자에 지정한다.
pivot_longer(table4a,
    cols = 2:3, names_to = "year",
    values_to = "cases"
)
# A tibble: 6 × 3
  country     year   cases
  <chr>       <chr>  <dbl>
1 Afghanistan 1999     745
2 Afghanistan 2000    2666
3 Brazil      1999   37737
4 Brazil      2000   80488
5 China       1999  212258
6 China       2000  213766

다음 table2는 각 나라의 인구와 케이스 수를 정리하는 데이터셋이다.

table2
# A tibble: 12 × 4
   country      year type            count
   <chr>       <dbl> <chr>           <dbl>
 1 Afghanistan  1999 cases             745
 2 Afghanistan  1999 population   19987071
 3 Afghanistan  2000 cases            2666
 4 Afghanistan  2000 population   20595360
 5 Brazil       1999 cases           37737
 6 Brazil       1999 population  172006362
 7 Brazil       2000 cases           80488
 8 Brazil       2000 population  174504898
 9 China        1999 cases          212258
10 China        1999 population 1272915272
11 China        2000 cases          213766
12 China        2000 population 1280428583

이 데이터셋은 변수로 사용되어야 것들이 값으로 들어가 있다. 이것을 정리하려면 pivot_wider() 함수를 사용한다.

중요
  • pivot_wider() 함수
    • pivot_wider(df, names_from = "이름열", values_from = "값열")
    • 2개의 열 –> (하나로 녹임) –> 펼쳐서 여러 열로 재배열
    • 이 여러 열에 이름을 부여한다.
      • 이름으로 사용되는 값이 있는 열를 names_from 인자에 지정
      • 값으로 사용되는 값이 있는 열을 values_from 인자에 지정
pivot_wider(table2,
    names_from = type,
    values_from = count
)
# A tibble: 6 × 4
  country      year  cases population
  <chr>       <dbl>  <dbl>      <dbl>
1 Afghanistan  1999    745   19987071
2 Afghanistan  2000   2666   20595360
3 Brazil       1999  37737  172006362
4 Brazil       2000  80488  174504898
5 China        1999 212258 1272915272
6 China        2000 213766 1280428583

10.1.2 복잡한 경우의 피벗팅: Longer 예시

이 절의 자료는 tidyr 패키지의 Pivoting 비니에트를 참고했다. 이 자료에는 여러 경우의 Untidy data들과 이것을 Tidy data로 변환하는 방법들이 설명되어 있다. 번역, 요약, 주석을 추가했다.

10.1.2.1 문자열 데이터가 열 이름으로 들어간 경우

다음 reglig_incom를 보자.

relig_income |>
    head()
# A tibble: 6 × 11
  religion  `<$10k` `$10-20k` `$20-30k` `$30-40k` `$40-50k` `$50-75k` `$75-100k`
  <chr>       <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>      <dbl>
1 Agnostic       27        34        60        81        76       137        122
2 Atheist        12        27        37        52        35        70         73
3 Buddhist       27        21        30        34        33        58         62
4 Catholic      418       617       732       670       638      1116        949
5 Don’t kn…      15        14        15        11        10        35         21
6 Evangeli…     575       869      1064       982       881      1486        949
# ℹ 3 more variables: `$100-150k` <dbl>, `>150k` <dbl>,
#   `Don't know/refused` <dbl>

이 데이터셋은 각 종교별 소득 분포를 나타내는 데이터이다. 이 데이터셋은 열 이름에 수입 범위가 들어가 있어서 untidy data이다. 이것을 tidy data로 변환하자.

relig_income |>
    pivot_longer(
        cols = !religion, # 종교 이름이 들어가지 않는 열들을 모두 선택
        names_to = "income", # 수입 범위가 들어가는 열의 이름을 "income"으로 지정
        values_to = "count" # 수입 범위에 해당하는 값이 들어가는 열의 이름을 "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
# ℹ 170 more rows

10.1.2.2 숫자형 데이터가 열 이름으로 들어간 경우

다음 billboard 데이터셋을 보자.

billboard |>
    head()
# A tibble: 6 × 79
  artist      track date.entered   wk1   wk2   wk3   wk4   wk5   wk6   wk7   wk8
  <chr>       <chr> <date>       <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 2 Pac       Baby… 2000-02-26      87    82    72    77    87    94    99    NA
2 2Ge+her     The … 2000-09-02      91    87    92    NA    NA    NA    NA    NA
3 3 Doors Do… Kryp… 2000-04-08      81    70    68    67    66    57    54    53
4 3 Doors Do… Loser 2000-10-21      76    76    72    69    67    65    55    59
5 504 Boyz    Wobb… 2000-04-15      57    34    25    17    17    31    36    49
6 98^0        Give… 2000-08-19      51    39    34    26    26    19     2     2
# ℹ 68 more variables: wk9 <dbl>, 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>, …

이 데이터셋은 앨범에 들어 있는 곡의 빌보트 차트 순위를 나타내는 데이터이다. 이 데이터셋은 열 이름에 주간 순위가 들어가 있어서 untidy data이다. 이것을 tidy data로 변환하자.

billboard |>
    pivot_longer(
        cols = !c(artist, track, date.entered), # 아티스트, 곡, 진입일이 들어가지 않는 열들을 모두 선택
        names_to = "week", # 주간 순위가 들어가는 열의 이름을 "week"으로 지정
        values_to = "rank" # 주간 순위에 해당하는 값이 들어가는 열의 이름을 "rank"로 지정
    )
# A tibble: 24,092 × 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 2 Pac  Baby Don't Cry (Keep... 2000-02-26   wk8      NA
 9 2 Pac  Baby Don't Cry (Keep... 2000-02-26   wk9      NA
10 2 Pac  Baby Don't Cry (Keep... 2000-02-26   wk10     NA
# ℹ 24,082 more rows

이 데이터 변환 과정에서 어떤 곡이 어느 주에 빌보드 차트에서 빠진 경우에는 NA로 표시된다. 이렇게 의미있는 NA 값이 있는 경우에는 사용자가 제거할 수 있게 하는 옵션이 있다. values_drop_na 인자를 TRUE로 지정하면 된다.

billboard |>
    pivot_longer(
        cols = !c(artist, track, date.entered), # 아티스트, 곡, 진입일이 들어가지 않는 열들을 모두 선택
        names_to = "week", # 주간 순위가 들어가는 열의 이름을 "week"으로 지정
        values_to = "rank", # 주간 순위에 해당하는 값이 들어가는 열의 이름을 "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
# ℹ 5,297 more rows

위 결과를 보면 week 열에 wk1 등과 같이 문자열로 되어 있는데, 이것은 숫자형 데이터가 되어야 맞을 것이다. 그래야 어떤 곡이 몇 주 동안 빌보드에 남아있는지 등을 계산할 수 있다.

이런 경우에 wk1에서 wk를 제거할 수 있는 옵션이 있다. names_prefix 인자가 그것이다.

billboard |>
    pivot_longer(
        cols = !c(artist, track, date.entered), # 아티스트, 곡, 진입일이 들어가지 않는 열들을 모두 선택
        names_to = "week", # 주간 순위가 들어가는 열의 이름을 "week"으로 지정
        names_prefix = "wk", # "wk" 문자열을 제거
        values_to = "rank", # 주간 순위에 해당하는 값이 들어가는 열의 이름을 "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   1        87
 2 2 Pac   Baby Don't Cry (Keep... 2000-02-26   2        82
 3 2 Pac   Baby Don't Cry (Keep... 2000-02-26   3        72
 4 2 Pac   Baby Don't Cry (Keep... 2000-02-26   4        77
 5 2 Pac   Baby Don't Cry (Keep... 2000-02-26   5        87
 6 2 Pac   Baby Don't Cry (Keep... 2000-02-26   6        94
 7 2 Pac   Baby Don't Cry (Keep... 2000-02-26   7        99
 8 2Ge+her The Hardest Part Of ... 2000-09-02   1        91
 9 2Ge+her The Hardest Part Of ... 2000-09-02   2        87
10 2Ge+her The Hardest Part Of ... 2000-09-02   3        92
# ℹ 5,297 more rows

위 결과를 잘 보면 week 열이 아직도 문자열로 되어 있다. 이것을 숫자형 데이터로 변환할 수 있다.

billboard |>
    pivot_longer(
        cols = !c(artist, track, date.entered), # 아티스트, 곡, 진입일이 들어가지 않는 열들을 모두 선택
        names_to = "week", # 주간 순위가 들어가는 열의 이름을 "week"으로 지정
        names_prefix = "wk", # "wk" 문자열을 제거
        values_to = "rank", # 주간 순위에 해당하는 값이 들어가는 열의 이름을 "rank"로 지정
        values_drop_na = TRUE # 값이 없는 경우에는 제거
    ) |>
    mutate(week = as.numeric(week))
# A tibble: 5,307 × 5
   artist  track                   date.entered  week  rank
   <chr>   <chr>                   <date>       <dbl> <dbl>
 1 2 Pac   Baby Don't Cry (Keep... 2000-02-26       1    87
 2 2 Pac   Baby Don't Cry (Keep... 2000-02-26       2    82
 3 2 Pac   Baby Don't Cry (Keep... 2000-02-26       3    72
 4 2 Pac   Baby Don't Cry (Keep... 2000-02-26       4    77
 5 2 Pac   Baby Don't Cry (Keep... 2000-02-26       5    87
 6 2 Pac   Baby Don't Cry (Keep... 2000-02-26       6    94
 7 2 Pac   Baby Don't Cry (Keep... 2000-02-26       7    99
 8 2Ge+her The Hardest Part Of ... 2000-09-02       1    91
 9 2Ge+her The Hardest Part Of ... 2000-09-02       2    87
10 2Ge+her The Hardest Part Of ... 2000-09-02       3    92
# ℹ 5,297 more rows

그런데 pivot_longer() 함수에서 이 작업을 해주는 옵션이 있다. pivot_longer() 함수는 names_to에 의해서 생성되는 열들을 디폴트로 문자열 타입으로 만들고, values_to에 의해서 생성되는 열들의 타입은 입력되는 열에서 가장 흔한 타입이 되게 한다. 이 과정을 names_transform, values_transform 인자를 사용하여 변경할 수 있다.

billboard |>
    pivot_longer(
        cols = !c(artist, track, date.entered), # 아티스트, 곡, 진입일이 들어가지 않는 열들을 모두 선택
        names_to = "week", # 주간 순위가 들어가는 열의 이름을 "week"으로 지정
        values_to = "rank", # 주간 순위에 해당하는 값이 들어가는 열의 이름을 "rank"로 지정
        names_prefix = "wk", # "wk" 문자열을 제거
        names_transform = list(week = as.numeric), # 열의 이름을 지정할 때 사용되는 타입을 integer로 지정
    )
# A tibble: 24,092 × 5
   artist track                   date.entered  week  rank
   <chr>  <chr>                   <date>       <dbl> <dbl>
 1 2 Pac  Baby Don't Cry (Keep... 2000-02-26       1    87
 2 2 Pac  Baby Don't Cry (Keep... 2000-02-26       2    82
 3 2 Pac  Baby Don't Cry (Keep... 2000-02-26       3    72
 4 2 Pac  Baby Don't Cry (Keep... 2000-02-26       4    77
 5 2 Pac  Baby Don't Cry (Keep... 2000-02-26       5    87
 6 2 Pac  Baby Don't Cry (Keep... 2000-02-26       6    94
 7 2 Pac  Baby Don't Cry (Keep... 2000-02-26       7    99
 8 2 Pac  Baby Don't Cry (Keep... 2000-02-26       8    NA
 9 2 Pac  Baby Don't Cry (Keep... 2000-02-26       9    NA
10 2 Pac  Baby Don't Cry (Keep... 2000-02-26      10    NA
# ℹ 24,082 more rows

10.1.2.3 여러 개의 변수들이 열 이름에 포함된 경우

다음 who 데이터셋을 보자.

who |>
    head()
# A tibble: 6 × 60
  country   iso2  iso3   year new_sp_m014 new_sp_m1524 new_sp_m2534 new_sp_m3544
  <chr>     <chr> <chr> <dbl>       <dbl>        <dbl>        <dbl>        <dbl>
1 Afghanis… AF    AFG    1980          NA           NA           NA           NA
2 Afghanis… AF    AFG    1981          NA           NA           NA           NA
3 Afghanis… AF    AFG    1982          NA           NA           NA           NA
4 Afghanis… AF    AFG    1983          NA           NA           NA           NA
5 Afghanis… AF    AFG    1984          NA           NA           NA           NA
6 Afghanis… AF    AFG    1985          NA           NA           NA           NA
# ℹ 52 more variables: new_sp_m4554 <dbl>, new_sp_m5564 <dbl>,
#   new_sp_m65 <dbl>, new_sp_f014 <dbl>, new_sp_f1524 <dbl>,
#   new_sp_f2534 <dbl>, new_sp_f3544 <dbl>, new_sp_f4554 <dbl>,
#   new_sp_f5564 <dbl>, new_sp_f65 <dbl>, new_sn_m014 <dbl>,
#   new_sn_m1524 <dbl>, new_sn_m2534 <dbl>, new_sn_m3544 <dbl>,
#   new_sn_m4554 <dbl>, new_sn_m5564 <dbl>, new_sn_m65 <dbl>,
#   new_sn_f014 <dbl>, new_sn_f1524 <dbl>, new_sn_f2534 <dbl>, …
names(who)
 [1] "country"      "iso2"         "iso3"         "year"         "new_sp_m014" 
 [6] "new_sp_m1524" "new_sp_m2534" "new_sp_m3544" "new_sp_m4554" "new_sp_m5564"
[11] "new_sp_m65"   "new_sp_f014"  "new_sp_f1524" "new_sp_f2534" "new_sp_f3544"
[16] "new_sp_f4554" "new_sp_f5564" "new_sp_f65"   "new_sn_m014"  "new_sn_m1524"
[21] "new_sn_m2534" "new_sn_m3544" "new_sn_m4554" "new_sn_m5564" "new_sn_m65"  
[26] "new_sn_f014"  "new_sn_f1524" "new_sn_f2534" "new_sn_f3544" "new_sn_f4554"
[31] "new_sn_f5564" "new_sn_f65"   "new_ep_m014"  "new_ep_m1524" "new_ep_m2534"
[36] "new_ep_m3544" "new_ep_m4554" "new_ep_m5564" "new_ep_m65"   "new_ep_f014" 
[41] "new_ep_f1524" "new_ep_f2534" "new_ep_f3544" "new_ep_f4554" "new_ep_f5564"
[46] "new_ep_f65"   "newrel_m014"  "newrel_m1524" "newrel_m2534" "newrel_m3544"
[51] "newrel_m4554" "newrel_m5564" "newrel_m65"   "newrel_f014"  "newrel_f1524"
[56] "newrel_f2534" "newrel_f3544" "newrel_f4554" "newrel_f5564" "newrel_f65"  

이 데이터셋에서 new_ 또는 new는 새로운 케이이스를 의미한다. m rel, ep는 진단방법을 의미하고 m, f는 성별을 의미한다. 1524, 65 등은 나이 범위를 의미한다.

이 데이터셋을 pivot_longer() 함수를 사용하여 tidy data로 변환해 보자. 먼저 names_to에 의해서 생성되는 열들의 이름을 지정한다. 또 names_pattern 인자를 사용하여 열 이름에 포함된 문자열을 패턴으로 지정한다.

사실 이 기능을 사용하려면 R 언어의 정규 표현식을 알아야 한다. 정규 표현식은 문자열의 패턴을 지정하는 표현식이다. 관심이 있는 경우는 R for Data Science (2e) 15장 Regular Expressions 장을 참고하라.

힌트

names_pattern 인자에 사용되는 정규 표현식은 다음과 같다.

  • new_?(.*)_(.)(.*)
    • new_ 또는 new: _?라고 했고 ?는 앞의 문자가 0번 또는 1번 나오는 것을 의미한다.
    • (.*)는 하나의 정규 표현식의 그룹(group)인데, 그 안에 .*가 들어가 있어 다음 _ 문자가 나오기 전까지 모든 문자들을 의미한다.
    • (.)도 그룹이고, .은 하나의 alphanumeric 문자을 의미한다.
    • (.*)도 그룹으로, 모든 문자열을 의미한다.

여기서 사용된 그룹은 names_to에서 잡는다.

who |>
    pivot_longer(
        cols = !c(country, iso2, iso3, year), # 나라, 국제 표준 국가 코드, 국제 표준 국가 코드, 연도가 들어가지 않는 열들을 모두 선택
        names_to = c("diagnosis", "gender", "age"), # 진단방법, 성별, 나이 범위가 들어가는 열의 이름을 지정
        names_pattern = "new_?(.*)_(.)(.*)",
        values_to = "count" # 값이 들어가는 열의 이름을 "count"로 지정
    )
# A tibble: 405,440 × 8
   country     iso2  iso3   year diagnosis gender age   count
   <chr>       <chr> <chr> <dbl> <chr>     <chr>  <chr> <dbl>
 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
# ℹ 405,430 more rows

앞에서도 설명했지만 names_to로 생성되는 열의 데이터타입은 문자열이다. 이것을 names_transform 인자를 사용하여 팩터로 변환하였다.

who |>
    pivot_longer(
        cols = !c(country, iso2, iso3, year), # 나라, 국제 표준 국가 코드, 국제 표준 국가 코드, 연도가 들어가지 않는 열들을 모두 선택
        names_to = c("diagnosis", "gender", "age"), # 진단방법, 성별, 나이 범위가 들어가는 열의 이름을 지정
        names_pattern = "new_?(.*)_(.)(.*)",
        names_transform = list(
            diagnosis = as.factor,
            gender = as.factor,
            age = as.factor
        ),
        values_to = "count" # 값이 들어가는 열의 이름을 "count"로 지정
    ) |>
    rename(
        age_group = age
    ) |>
    tail()
# A tibble: 6 × 8
  country  iso2  iso3   year diagnosis gender age_group count
  <chr>    <chr> <chr> <dbl> <fct>     <fct>  <fct>     <dbl>
1 Zimbabwe ZW    ZWE    2013 rel       f      1524       2069
2 Zimbabwe ZW    ZWE    2013 rel       f      2534       4649
3 Zimbabwe ZW    ZWE    2013 rel       f      3544       3526
4 Zimbabwe ZW    ZWE    2013 rel       f      4554       1453
5 Zimbabwe ZW    ZWE    2013 rel       f      5564        811
6 Zimbabwe ZW    ZWE    2013 rel       f      65          725

10.1.2.4 하나의 행에 여러 관측이 들어간 경우

다음 household 데이터셋을 보자.

household
# A tibble: 5 × 5
  family dob_child1 dob_child2 name_child1 name_child2
   <int> <date>     <date>     <chr>       <chr>      
1      1 1998-11-26 2000-01-29 Susan       Jose       
2      2 1996-06-22 NA         Mark        <NA>       
3      3 2002-07-11 2004-04-05 Sam         Seth       
4      4 2004-10-10 2009-08-27 Craig       Khai       
5      5 2000-12-05 2005-02-28 Parker      Gracie     

이 데이터셋은 하나의 행에 자녀의 출생일과 그 이름을 정리할 것인데, 이것을 하나의 열로 모아서 표현한 것이다.

이 경우 names_sep 인자를 사용하여 열을 분리하고, names_to 인자에 .value라는 특수한 값을 지정하면 이 부분에 해당되는 부분을 열의 이름으로 지정한다. dob_child1, name_child2과 같은 열이 있을 때 names_sep = "_"로 지정했기 때문에 dobname.value에 들어가게 되고, child1, child2 부분이 child에 들어가게 된다.

household |>
    pivot_longer(
        cols = !family,
        names_to = c(".value", "child"),
        names_sep = "_",
        values_drop_na = TRUE
    )
# A tibble: 9 × 4
  family child  dob        name  
   <int> <chr>  <date>     <chr> 
1      1 child1 1998-11-26 Susan 
2      1 child2 2000-01-29 Jose  
3      2 child1 1996-06-22 Mark  
4      3 child1 2002-07-11 Sam   
5      3 child2 2004-04-05 Seth  
6      4 child1 2004-10-10 Craig 
7      4 child2 2009-08-27 Khai  
8      5 child1 2000-12-05 Parker
9      5 child2 2005-02-28 Gracie

10.1.3 복잡한 경우의 피벗팅: Wider 예시

다음 fish_encounters 데이터셋은 물고기에 태그를 붙이고, 여러 지점(station)에서 수중 센서를 사용해 해당 물고기의 이동이 감지되었는지를 기록한 데이터이다.

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
# ℹ 104 more rows

이 경우 어떤 물고기가 어떤 지점에서 감지되었는 정리해 보고 싶을 수 있다. 이 경우 pivot_wider() 함수를 사용할 수 있다. names_from에 열로 올려 놓을 이름을 지정하다.

fish_encounters |>
    pivot_wider(
        names_from = station,
        values_from = seen
    )
# 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
11 4855        1     1      1     1       1    NA    NA    NA    NA    NA    NA
12 4857        1     1      1     1       1     1     1     1     1    NA    NA
13 4858        1     1      1     1       1     1     1     1     1     1     1
14 4859        1     1      1     1       1    NA    NA    NA    NA    NA    NA
15 4861        1     1      1     1       1     1     1     1     1     1     1
16 4862        1     1      1     1       1     1     1     1     1    NA    NA
17 4863        1     1     NA    NA      NA    NA    NA    NA    NA    NA    NA
18 4864        1     1     NA    NA      NA    NA    NA    NA    NA    NA    NA
19 4865        1     1      1    NA      NA    NA    NA    NA    NA    NA    NA

이 경우 감지되지 않은 경우에는 NA로 표시되는데, 이것을 0으로 채워 넣고 싶을 수 있다. 이 경우 values_fill 인자를 사용하면 된다.

fish_encounters |>
    pivot_wider(
        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
11 4855        1     1      1     1       1     0     0     0     0     0     0
12 4857        1     1      1     1       1     1     1     1     1     0     0
13 4858        1     1      1     1       1     1     1     1     1     1     1
14 4859        1     1      1     1       1     0     0     0     0     0     0
15 4861        1     1      1     1       1     1     1     1     1     1     1
16 4862        1     1      1     1       1     1     1     1     1     0     0
17 4863        1     1      0     0       0     0     0     0     0     0     0
18 4864        1     1      0     0       0     0     0     0     0     0     0
19 4865        1     1      1     0       0     0     0     0     0     0     0

여러 조합으로 구성된 데이터가 있다고 해 보자. 다음 코드에서 .은 파이프 연산자를 %>%를 사용할 때 앞의 데이터프레임을 대신하는 표현식이다. 그래서 |> 연산자를 사용할 때는 작동하지 않는다.

production <-
    expand_grid(
        product = c("A", "B"),
        country = c("AI", "EI"),
        year = 2000:2014
    ) %>%
    as_tibble() %>%
    mutate(production = rnorm(nrow(.)))
production
# A tibble: 60 × 4
   product country  year production
   <chr>   <chr>   <int>      <dbl>
 1 A       AI       2000     0.226 
 2 A       AI       2001    -2.26  
 3 A       AI       2002     0.729 
 4 A       AI       2003     1.59  
 5 A       AI       2004     0.126 
 6 A       AI       2005     0.0442
 7 A       AI       2006    -0.849 
 8 A       AI       2007    -0.0277
 9 A       AI       2008     0.0537
10 A       AI       2009    -0.847 
# ℹ 50 more rows
glimpse(production)
Rows: 60
Columns: 4
$ product    <chr> "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A",…
$ country    <chr> "AI", "AI", "AI", "AI", "AI", "AI", "AI", "AI", "AI", "AI",…
$ year       <int> 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,…
$ production <dbl> 0.22620776, -2.26375359, 0.72890163, 1.59301748, 0.12635456…

이 데이터셋을 연도별로 정리해서 보여줄 필요가 있을 수 있고 다음과 같은 코드를 사용할 수 있다.

production |>
    pivot_wider(
        names_from = c(product, country),
        values_from = production
    )
# A tibble: 15 × 5
    year    A_AI    A_EI   B_AI    B_EI
   <int>   <dbl>   <dbl>  <dbl>   <dbl>
 1  2000  0.226  -1.36    0.251  0.176 
 2  2001 -2.26    0.634  -0.791 -0.936 
 3  2002  0.729  -1.09   -1.30  -0.294 
 4  2003  1.59    1.23   -1.55   0.546 
 5  2004  0.126  -1.08   -0.749  0.672 
 6  2005  0.0442  0.753  -1.02   0.643 
 7  2006 -0.849  -0.0387 -1.06  -0.0511
 8  2007 -0.0277 -0.430  -0.616  1.41  
 9  2008  0.0537 -0.179  -1.32   0.133 
10  2009 -0.847  -1.16   -0.613 -0.392 
11  2010 -1.28   -1.34    1.41  -1.00  
12  2011  0.0374 -0.931   0.170 -1.39  
13  2012 -1.02   -1.33   -0.631 -1.28  
14  2013  0.0155 -0.252   0.176  1.90  
15  2014  2.27    2.74    0.245  1.68  

이런 경우 이름을 _로 연결하여 만들고 있다. 이것을 바꾸려면 names_sep 인자를 사용한다. names_prefix 인자를 사용하면 접두사를 붙일 수 있다.

production |>
    pivot_wider(
        names_from = c(product, country),
        values_from = production,
        names_sep = ".",
        names_prefix = "production."
    )
# A tibble: 15 × 5
    year production.A.AI production.A.EI production.B.AI production.B.EI
   <int>           <dbl>           <dbl>           <dbl>           <dbl>
 1  2000          0.226          -1.36             0.251          0.176 
 2  2001         -2.26            0.634           -0.791         -0.936 
 3  2002          0.729          -1.09            -1.30          -0.294 
 4  2003          1.59            1.23            -1.55           0.546 
 5  2004          0.126          -1.08            -0.749          0.672 
 6  2005          0.0442          0.753           -1.02           0.643 
 7  2006         -0.849          -0.0387          -1.06          -0.0511
 8  2007         -0.0277         -0.430           -0.616          1.41  
 9  2008          0.0537         -0.179           -1.32           0.133 
10  2009         -0.847          -1.16            -0.613         -0.392 
11  2010         -1.28           -1.34             1.41          -1.00  
12  2011          0.0374         -0.931            0.170         -1.39  
13  2012         -1.02           -1.33            -0.631         -1.28  
14  2013          0.0155         -0.252            0.176          1.90  
15  2014          2.27            2.74             0.245          1.68  

10.2 정리

  • Pivoting 비니에트에 있는 사례들을 모두 정리한 것은 아니다. 더 많은 사례를 보려면 이 링크를 참고한다.