Improving Creating and Modifying Variables: Mutate, Select, and Filter

{dplyr}

데이터를 R로 가져오려고 할 때, 데이터 시각화 및 데이터 모델링에 필요한, 원하는 형태의 데이터를 쉽게 다운받을 수 있는 경우는 드뭅니다. {dplyr} 패키지는 데이터를 필요한 형태로 변환(transform)하거나 조작(manipulation)할 수 있는 일련의 함수 집합을 제공합니다.

“dplyr is a grammar of data manipulation, providing a consistent set of verbs that help you solve the most common data manipulation challenges:

  • mutate() adds new variables that are functions of existing variables

  • select() picks variables based on their names.

  • filter() picks cases based on their values.

  • summarise() reduces multiple values down to a single summary.

  • arrange() changes the ordering of the rows.

Tibbles

Base R에서는 데이터를 저장하기 위해 데이터 프레임을 사용하는 경우가 많습니다. 티블은 Base R에서 데이터 프레임의 {tidyverse} 버전입니다. {tidyverse} 함수는 데이터 프레임과 티블을 모두 받습니다. 종종 as_tibble()을 사용하여 데이터 프레임을 티블로 변환하고 싶을 수 있습니다:

# Base R에 내장된 데이터 프레임인 iris를 살펴보자
as_tibble(iris)
# A tibble: 150 × 5
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
          <dbl>       <dbl>        <dbl>       <dbl> <fct>  
 1          5.1         3.5          1.4         0.2 setosa 
 2          4.9         3            1.4         0.2 setosa 
 3          4.7         3.2          1.3         0.2 setosa 
 4          4.6         3.1          1.5         0.2 setosa 
 5          5           3.6          1.4         0.2 setosa 
 6          5.4         3.9          1.7         0.4 setosa 
 7          4.6         3.4          1.4         0.3 setosa 
 8          5           3.4          1.5         0.2 setosa 
 9          4.4         2.9          1.4         0.2 setosa 
10          4.9         3.1          1.5         0.1 setosa 
# ℹ 140 more rows

티블 데이터 유형에 대한 더 자세한 내용을 알고 싶다면 여기의 링크를 확인해 보세요.

Select Columns with select()

select() 함수는 데이터셋의 열을 특정하여 선택합니다. select()와 같은 경우 다른 패키지에서도 사용할 수 있는 보편적이고 흔한 함수명이기 때문에, 충돌이 발생하는 것을 방지하기 위하여 dplyr 패키지에 속한 select() 함수를 가져오라고 특정해줄 필요가 있습니다. 패키지를 특정하는 것은 ::를 이용해 가능합니다. 즉, dplyr::select()select() 함수를 사용하되 {dplyr}에 속한 select() 함수를 가져오라는 의미입니다.

# nycflight13 패키지에서 flights 데이터를 사용해봅시다.
library(nycflights13)

# names() 함수는 데이터셋의 열 이름을 나타냅니다.
names(flights)
 [1] "year"           "month"          "day"            "dep_time"      
 [5] "sched_dep_time" "dep_delay"      "arr_time"       "sched_arr_time"
 [9] "arr_delay"      "carrier"        "flight"         "tailnum"       
[13] "origin"         "dest"           "air_time"       "distance"      
[17] "hour"           "minute"         "time_hour"     
# 열 이름을 사용하여 데이터셋에서 필요한 열 들을 선택합시다.
flights |> select(year, month, day)
# A tibble: 336,776 × 3
    year month   day
   <int> <int> <int>
 1  2013     1     1
 2  2013     1     1
 3  2013     1     1
 4  2013     1     1
 5  2013     1     1
 6  2013     1     1
 7  2013     1     1
 8  2013     1     1
 9  2013     1     1
10  2013     1     1
# ℹ 336,766 more rows
  • {tidyverse} 패키지의 select() 함수는 변수를 선택하는 여러 가지 방식을 제공합니다. 더 자세한 내용을 알고 싶다면 여기의 링크를 확인해보세요.

  • :는 일련의 연속적인 범주의 변수들을 선택할 수 있도록 돕습니다.

# year와 day 사이의 모든 열을 선택해봅시다
flights |> select(year:day)
# A tibble: 336,776 × 3
    year month   day
   <int> <int> <int>
 1  2013     1     1
 2  2013     1     1
 3  2013     1     1
 4  2013     1     1
 5  2013     1     1
 6  2013     1     1
 7  2013     1     1
 8  2013     1     1
 9  2013     1     1
10  2013     1     1
# ℹ 336,766 more rows
  • !는 일련의 변수의 여집합(complement)을 의미합니다.
# year와 day 사이의 열을 제외한 모든 변수를 선택하라
flights |> select(!(year:day))
# A tibble: 336,776 × 16
   dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier
      <int>          <int>     <dbl>    <int>          <int>     <dbl> <chr>  
 1      517            515         2      830            819        11 UA     
 2      533            529         4      850            830        20 UA     
 3      542            540         2      923            850        33 AA     
 4      544            545        -1     1004           1022       -18 B6     
 5      554            600        -6      812            837       -25 DL     
 6      554            558        -4      740            728        12 UA     
 7      555            600        -5      913            854        19 B6     
 8      557            600        -3      709            723       -14 EV     
 9      557            600        -3      838            846        -8 B6     
10      558            600        -2      753            745         8 AA     
# ℹ 336,766 more rows
# ℹ 9 more variables: flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>

선택 도우미 함수(selection helper functions)는 이름에 있는 패턴을 확인하여 그에 일치하는 변수를 선택합니다:

  • starts_with(): 특정 문자로 시작하는 열을 모두 선택합니다.

  • ends_with(): 특정 문자로 끝나는 열을 모두 선택합니다.

  • contains(): 특정 문자열을 포함하는 열을 모두 선택합니다.

  • matches(): 특정 정규표현식의 조건에 일치하는 열을 모두 선택합니다.

  • num_range(): x01, x02, x03과 같은 숫자 범위가 일치하는 열을 모두 선택합니다.

# "dep"로 시작하는 모든 열을 선택합니다
flights |> select(starts_with("dep"))
# A tibble: 336,776 × 2
   dep_time dep_delay
      <int>     <dbl>
 1      517         2
 2      533         4
 3      542         2
 4      544        -1
 5      554        -6
 6      554        -4
 7      555        -5
 8      557        -3
 9      557        -3
10      558        -2
# ℹ 336,766 more rows
# "time"으로 끝나는 모든 열을 선택합니다
flights |> select(ends_with("time"))
# A tibble: 336,776 × 5
   dep_time sched_dep_time arr_time sched_arr_time air_time
      <int>          <int>    <int>          <int>    <dbl>
 1      517            515      830            819      227
 2      533            529      850            830      227
 3      542            540      923            850      160
 4      544            545     1004           1022      183
 5      554            600      812            837      116
 6      554            558      740            728      150
 7      555            600      913            854      158
 8      557            600      709            723       53
 9      557            600      838            846      140
10      558            600      753            745      138
# ℹ 336,766 more rows
# "time"을 포함하는 모든 열을 선택합니다
flights |> select(contains("time"))
# A tibble: 336,776 × 6
   dep_time sched_dep_time arr_time sched_arr_time air_time time_hour          
      <int>          <int>    <int>          <int>    <dbl> <dttm>             
 1      517            515      830            819      227 2013-01-01 05:00:00
 2      533            529      850            830      227 2013-01-01 05:00:00
 3      542            540      923            850      160 2013-01-01 05:00:00
 4      544            545     1004           1022      183 2013-01-01 05:00:00
 5      554            600      812            837      116 2013-01-01 06:00:00
 6      554            558      740            728      150 2013-01-01 05:00:00
 7      555            600      913            854      158 2013-01-01 06:00:00
 8      557            600      709            723       53 2013-01-01 06:00:00
 9      557            600      838            846      140 2013-01-01 06:00:00
10      558            600      753            745      138 2013-01-01 06:00:00
# ℹ 336,766 more rows
# "dep"으로 시작하면서 "time"을 포함하는 year와 day 사이의 모든 열을 선택합니다.
flights |> select(starts_with("dep"), contains("time"), year:day)
# A tibble: 336,776 × 10
   dep_time dep_delay sched_dep_time arr_time sched_arr_time air_time
      <int>     <dbl>          <int>    <int>          <int>    <dbl>
 1      517         2            515      830            819      227
 2      533         4            529      850            830      227
 3      542         2            540      923            850      160
 4      544        -1            545     1004           1022      183
 5      554        -6            600      812            837      116
 6      554        -4            558      740            728      150
 7      555        -5            600      913            854      158
 8      557        -3            600      709            723       53
 9      557        -3            600      838            846      140
10      558        -2            600      753            745      138
# ℹ 336,766 more rows
# ℹ 4 more variables: time_hour <dttm>, year <int>, month <int>, day <int>
# 1번째, 3번째, 5번째 열을 선택합니다
flights |> select(c(1,3,5))
# A tibble: 336,776 × 3
    year   day sched_dep_time
   <int> <int>          <int>
 1  2013     1            515
 2  2013     1            529
 3  2013     1            540
 4  2013     1            545
 5  2013     1            600
 6  2013     1            558
 7  2013     1            600
 8  2013     1            600
 9  2013     1            600
10  2013     1            600
# ℹ 336,766 more rows

이번에는 {tidyverse} 내의 billboard 데이터셋을 사용해보겠습니다. billboard 데이터셋은 빌보드 차트의 2000곡 중에서 상위 100곡에 대한 랭킹 정보를 제공합니다.

billboard
# A tibble: 317 × 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 D… Kryp… 2000-04-08      81    70    68    67    66    57    54    53
 4 3 Doors D… 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
 7 A*Teens    Danc… 2000-07-08      97    97    96    95   100    NA    NA    NA
 8 Aaliyah    I Do… 2000-01-29      84    62    51    41    38    35    35    38
 9 Aaliyah    Try … 2000-03-18      59    53    38    28    21    18    16    14
10 Adams, Yo… Open… 2000-08-26      76    76    74    69    68    67    61    58
# ℹ 307 more rows
# ℹ 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>, …
# names()로 데이터셋의 열들의 이름을 반환합니다
names(billboard)
 [1] "artist"       "track"        "date.entered" "wk1"          "wk2"         
 [6] "wk3"          "wk4"          "wk5"          "wk6"          "wk7"         
[11] "wk8"          "wk9"          "wk10"         "wk11"         "wk12"        
[16] "wk13"         "wk14"         "wk15"         "wk16"         "wk17"        
[21] "wk18"         "wk19"         "wk20"         "wk21"         "wk22"        
[26] "wk23"         "wk24"         "wk25"         "wk26"         "wk27"        
[31] "wk28"         "wk29"         "wk30"         "wk31"         "wk32"        
[36] "wk33"         "wk34"         "wk35"         "wk36"         "wk37"        
[41] "wk38"         "wk39"         "wk40"         "wk41"         "wk42"        
[46] "wk43"         "wk44"         "wk45"         "wk46"         "wk47"        
[51] "wk48"         "wk49"         "wk50"         "wk51"         "wk52"        
[56] "wk53"         "wk54"         "wk55"         "wk56"         "wk57"        
[61] "wk58"         "wk59"         "wk60"         "wk61"         "wk62"        
[66] "wk63"         "wk64"         "wk65"         "wk66"         "wk67"        
[71] "wk68"         "wk69"         "wk70"         "wk71"         "wk72"        
[76] "wk73"         "wk74"         "wk75"         "wk76"        
billboard |> select(num_range("wk", 20:23))
# A tibble: 317 × 4
    wk20  wk21  wk22  wk23
   <dbl> <dbl> <dbl> <dbl>
 1    NA    NA    NA    NA
 2    NA    NA    NA    NA
 3    14    12     7     6
 4    70    NA    NA    NA
 5    NA    NA    NA    NA
 6    94    NA    NA    NA
 7    NA    NA    NA    NA
 8    86    NA    NA    NA
 9     4     5     5     6
10    89    NA    NA    NA
# ℹ 307 more rows

Filter Rows with filter()

filter()는 값에 따라 관측치들을 필터링합니다. 필터링할 여러 인수는 논리 연산자를 사용하여 결합됩니다.

  • & AND

  • | OR

  • ! NOT

기본적으로 여러 조건은 &와 결합됩니다.

# 1월에 출발한 항공편으로 필터링해보겠습니다.
# month == 1 & day == 1    
flights |> dplyr::filter(month == 1 & day == 1) 
# A tibble: 842 × 19
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1     1      517            515         2      830            819
 2  2013     1     1      533            529         4      850            830
 3  2013     1     1      542            540         2      923            850
 4  2013     1     1      544            545        -1     1004           1022
 5  2013     1     1      554            600        -6      812            837
 6  2013     1     1      554            558        -4      740            728
 7  2013     1     1      555            600        -5      913            854
 8  2013     1     1      557            600        -3      709            723
 9  2013     1     1      557            600        -3      838            846
10  2013     1     1      558            600        -2      753            745
# ℹ 832 more rows
# ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
#   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
#   hour <dbl>, minute <dbl>, time_hour <dttm>
# 11월 또는 12월에 출발하는 비행편으로 필터링해봅시다
flights |> dplyr::filter(month == 11 | month == 12) 
# A tibble: 55,403 × 19
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013    11     1        5           2359         6      352            345
 2  2013    11     1       35           2250       105      123           2356
 3  2013    11     1      455            500        -5      641            651
 4  2013    11     1      539            545        -6      856            827
 5  2013    11     1      542            545        -3      831            855
 6  2013    11     1      549            600       -11      912            923
 7  2013    11     1      550            600       -10      705            659
 8  2013    11     1      554            600        -6      659            701
 9  2013    11     1      554            600        -6      826            827
10  2013    11     1      554            600        -6      749            751
# ℹ 55,393 more rows
# ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
#   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
#   hour <dbl>, minute <dbl>, time_hour <dttm>
# IAH로 도착하는 항공편을 모두 필터링해봅시다
flights |> dplyr::filter(dest == "IAH")
# A tibble: 7,198 × 19
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1     1      517            515         2      830            819
 2  2013     1     1      533            529         4      850            830
 3  2013     1     1      623            627        -4      933            932
 4  2013     1     1      728            732        -4     1041           1038
 5  2013     1     1      739            739         0     1104           1038
 6  2013     1     1      908            908         0     1228           1219
 7  2013     1     1     1028           1026         2     1350           1339
 8  2013     1     1     1044           1045        -1     1352           1351
 9  2013     1     1     1114            900       134     1447           1222
10  2013     1     1     1205           1200         5     1503           1505
# ℹ 7,188 more rows
# ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
#   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
#   hour <dbl>, minute <dbl>, time_hour <dttm>

Add New Variables with mutate()

mutate() 는 새로운 변수를 만드는 함수입니다. mutate()는 변환을 반복적으로 실행하여 이후 변환이 이전 변환에서 생성한 열을 사용할 수 있도록 합니다. 즉, 여기서는 gain, hoursmutate() 내에서 먼저 만들었기 때문에 그 만들어진 변수들을 이용하여 gain_per_hour 변수를 생성할 수 있는 것입니다.

flights |> 
  mutate(gain = arr_delay - dep_delay, 
         hours = air_time / 60, 
         gain_per_hour = gain / hours)
# A tibble: 336,776 × 22
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
 1  2013     1     1      517            515         2      830            819
 2  2013     1     1      533            529         4      850            830
 3  2013     1     1      542            540         2      923            850
 4  2013     1     1      544            545        -1     1004           1022
 5  2013     1     1      554            600        -6      812            837
 6  2013     1     1      554            558        -4      740            728
 7  2013     1     1      555            600        -5      913            854
 8  2013     1     1      557            600        -3      709            723
 9  2013     1     1      557            600        -3      838            846
10  2013     1     1      558            600        -2      753            745
# ℹ 336,766 more rows
# ℹ 14 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
#   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
#   hour <dbl>, minute <dbl>, time_hour <dttm>, gain <dbl>, hours <dbl>,
#   gain_per_hour <dbl>

mutate()의 범위가 지정된 변형 함수는 여러 변수에 동일한 변형을 적용할 수 있도록 도와줍니다.

  • mutate_all()은 모든 변수에 영향을 줍니다.

  • mutate_at()은 문자 벡터로 선택한 변수에 영향을 줍니다.

  • mutate_if()는 술어 함수(true 또는 false를 반환하는 함수)로 선택한 변수에 영향을 줍니다.

head(iris)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa
# Sepal이라는 문자열을 포함한 모든 열에 log() 함수를 적용해봅시다
mutated <- iris |> mutate_at(vars(contains("Sepal")), log)
head(mutated) 
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1     1.629241    1.252763          1.4         0.2  setosa
2     1.589235    1.098612          1.4         0.2  setosa
3     1.547563    1.163151          1.3         0.2  setosa
4     1.526056    1.131402          1.5         0.2  setosa
5     1.609438    1.280934          1.4         0.2  setosa
6     1.686399    1.360977          1.7         0.4  setosa
# 숫자형 변수에만 log()를 적용해봅시다
mutated <- iris |> mutate_if(is.numeric, log)
head(mutated)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1     1.629241    1.252763    0.3364722  -1.6094379  setosa
2     1.589235    1.098612    0.3364722  -1.6094379  setosa
3     1.547563    1.163151    0.2623643  -1.6094379  setosa
4     1.526056    1.131402    0.4054651  -1.6094379  setosa
5     1.609438    1.280934    0.3364722  -1.6094379  setosa
6     1.686399    1.360977    0.5306283  -0.9162907  setosa
  • mutate() 의 범위가 지정된 변형에 대한 자세한 내용은 여기에서 확인할 수 있습니다.

  • {tidyverse}는 빠르게 변화합니다. 패키지(또는 함수)의 라이프사이클을 나타내기 위해 {tidyverse}라이프사이클 배지를 사용합니다.

  • 이제 across()summarise_at(), summarise_if(), summarise_all()과 같은 “범위가 지정된 변형” 제품군을 대체합니다. 즉, 앞으로 _at(), _if(), _all() 함수가 줄어들고 across() 함수가 더 많이 사용될 것입니다.