Tidyverse

R for Data Science (2e) by Wickham, Çetinkaya-Rundel & Grolemund

Published

September 4, 2026

데이터를 가공하고 요약하는 일은 tidyverse의 dplyr 패키지가 담당함.
아래 함수들(dplyr에서는 verb라고 부름)만 익히면 대부분의 데이터 가공이 가능함.

자세한 내용은 R for Data Science (2판) / Data transformation 참고
(1판의 한글 번역서가 있으나, 온라인 2판이 최신 문법을 반영하고 있음)

Inspecting data

함수들: print(), glimpse(), summary(), count()
() 안에 들어가는 것을 argument라고 부름

library(tidyverse)

cps <- as_tibble(mosaicData::CPS85) # mosaicData package의 CPS85 데이터셋
cps
# A tibble: 534 × 11
   wage  educ race  sex   hispanic south married exper union   age sector  
  <dbl> <int> <fct> <fct> <fct>    <fct> <fct>   <int> <fct> <int> <fct>   
1   9      10 W     M     NH       NS    Married    27 Not      43 const   
2   5.5    12 W     M     NH       NS    Married    20 Not      38 sales   
3   3.8    12 W     F     NH       NS    Single      4 Not      22 sales   
4  10.5    12 W     F     NH       NS    Married    29 Not      47 clerical
5  15      12 W     M     NH       NS    Married    40 Union    58 const   
6   9      16 W     F     NH       NS    Married    27 Not      49 clerical
# ℹ 528 more rows
데이터 프레임의 출력

콘솔에서 데이터셋의 이름만 입력하면 내용이 출력됨. print(cps)와 같은 의미이므로 print()는 생략함.
강의 노트에 보이는 출력도 콘솔에서 보는 것과 동일함.

print()를 명시하면 표시되는 방식을 조정해서 볼 수 있음

print(cps, n = 3) # 처음 3개 행
# A tibble: 534 × 11
   wage  educ race  sex   hispanic south married exper union   age sector
  <dbl> <int> <fct> <fct> <fct>    <fct> <fct>   <int> <fct> <int> <fct> 
1   9      10 W     M     NH       NS    Married    27 Not      43 const 
2   5.5    12 W     M     NH       NS    Married    20 Not      38 sales 
3   3.8    12 W     F     NH       NS    Single      4 Not      22 sales 
# ℹ 531 more rows

변수(열)가 많으면 화면 너비에 맞춰 일부가 생략되는데, width = Inf로 모두 볼 수 있음

print(cps, n = 3, width = Inf)
# A tibble: 534 × 11
   wage  educ race  sex   hispanic south married exper union   age sector
  <dbl> <int> <fct> <fct> <fct>    <fct> <fct>   <int> <fct> <int> <fct> 
1   9      10 W     M     NH       NS    Married    27 Not      43 const 
2   5.5    12 W     M     NH       NS    Married    20 Not      38 sales 
3   3.8    12 W     F     NH       NS    Single      4 Not      22 sales 
# ℹ 531 more rows

기본 셋팅을 변경하려면

options(tibble.print_min = 10, tibble.width = Inf) # 10개의 행과 모든 열

많은 변수들을 간략히 보는 방법으로는 glimpse()

glimpse(cps)
Rows: 534
Columns: 11
$ wage     <dbl> 9.00, 5.50, 3.80, 10.50, 15.00, 9.00, 9.57, 15.00, 11.00, 5.0…
$ educ     <int> 10, 12, 12, 12, 12, 16, 12, 14, 8, 12, 17, 17, 14, 14, 12, 14…
$ race     <fct> W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, NW, NW, W,…
$ sex      <fct> M, M, F, F, M, F, F, M, M, F, M, M, M, M, M, M, M, M, M, M, F…
$ hispanic <fct> NH, NH, NH, NH, NH, NH, NH, NH, NH, NH, Hisp, NH, Hisp, NH, N…
$ south    <fct> NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, N…
$ married  <fct> Married, Married, Single, Married, Married, Married, Married,…
$ exper    <int> 27, 20, 4, 29, 40, 27, 5, 22, 42, 14, 18, 3, 4, 14, 35, 0, 7,…
$ union    <fct> Not, Not, Not, Not, Union, Not, Union, Not, Not, Not, Not, No…
$ age      <int> 43, 38, 22, 47, 58, 49, 23, 42, 56, 32, 41, 26, 24, 34, 53, 2…
$ sector   <fct> const, sales, sales, clerical, const, clerical, service, sale…
Tip

엑셀 스프레드시트처럼 보는 방법은

  • Positron: 오른쪽 Variables 패널에서 cps를 클릭하면 Data Explorer로 열림 (열별 요약통계, 정렬, 필터 가능)
  • 콘솔에서는 View(cps)

변수들에 대한 통계치 요약 summary()

summary(cps)
      wage             educ       race     sex     hispanic   south   
 Min.   : 1.000   Min.   : 2.00   NW: 67   F:245   Hisp: 27   NS:378  
 1st Qu.: 5.250   1st Qu.:12.00   W :467   M:289   NH  :507   S :156  
 Median : 7.780   Median :12.00                                       
 Mean   : 9.024   Mean   :13.02                                       
 3rd Qu.:11.250   3rd Qu.:15.00                                       
 Max.   :44.500   Max.   :18.00                                       
                                                                      
    married        exper         union          age             sector   
 Married:350   Min.   : 0.00   Not  :438   Min.   :18.00   prof    :105  
 Single :184   1st Qu.: 8.00   Union: 96   1st Qu.:28.00   clerical: 97  
               Median :15.00               Median :35.00   service : 83  
               Mean   :17.82               Mean   :36.83   manuf   : 68  
               3rd Qu.:26.00               3rd Qu.:44.00   other   : 68  
               Max.   :55.00               Max.   :64.00   manag   : 55  
                                                           (Other) : 58  

카테고리별 개수를 세주는 count()
Number(수)에 대해서도 적용 가능: ex. educ 수준 2, 3, … 18 각각에 대해서

cps |> # pipe operator: shift + command + M (ctrl + shift + M)
  count(sector)
# A tibble: 8 × 2
  sector       n
  <fct>    <int>
1 clerical    97
2 const       20
3 manag       55
4 manuf       68
5 other       68
6 prof       105
7 sales       38
8 service     83
cps |>
  count(sex, married)
# A tibble: 4 × 3
  sex   married     n
  <fct> <fct>   <int>
1 F     Married   162
2 F     Single     83
3 M     Married   188
4 M     Single    101
cps |>
  count(sector, sort = TRUE) |> # sort = TRUE: 개수가 많은 순으로 정렬
  head(3)
# A tibble: 3 × 2
  sector       n
  <fct>    <int>
1 prof       105
2 clerical    97
3 service     83

카테고리의 개수만 알고 싶다면 n_distinct()

n_distinct(cps$sector)
[1] 8
Pipe operator

|> 또는 %>% (’then’의 의미로…)

x |> f(y)        # f(x, y)
x |> f(y) |> g(z) # g(f(x, y), z)

summary(cps)는 다음과 같음

cps |>
  summary()

count(cps, sector)는 다음과 같음

cps |>
  count(sector)

|>는 R 4.1부터 R 자체에 내장된 native pipe이고, %>%는 magrittr 패키지의 것으로 tidyverse를 로드하면 사용 가능함. 새로 배우는 경우 |>를 권장 (설정: positron.r.pipe)

Rows

행에 적용되는 함수들
filter(), arrange(), distinct(), slice_*()

filter()

조건에 맞는 행을 선택

Conditional operators:
>, >=, <, <=,
== (equal to), != (not equal to)
& (and)
| (or)
! (not)
%in% (includes)

# 임금(wage)이 10 이상인 사람들
cps |>
  filter(wage >= 10)
# A tibble: 184 × 11
   wage  educ race  sex   hispanic south married exper union   age sector  
  <dbl> <int> <fct> <fct> <fct>    <fct> <fct>   <int> <fct> <int> <fct>   
1  10.5    12 W     F     NH       NS    Married    29 Not      47 clerical
2  15      12 W     M     NH       NS    Married    40 Union    58 const   
3  15      14 W     M     NH       NS    Single     22 Not      42 sales   
4  11       8 W     M     NH       NS    Married    42 Not      56 manuf   
5  25.0    17 W     M     Hisp     NS    Married    18 Not      41 prof    
6  20.4    17 W     M     NH       NS    Single      3 Not      26 prof    
# ℹ 178 more rows
# 임금(wage)이 10 이상이고 여성(F)들
cps |>
  filter(wage >= 10 & sex == "F")
# A tibble: 62 × 11
   wage  educ race  sex   hispanic south married exper union   age sector  
  <dbl> <int> <fct> <fct> <fct>    <fct> <fct>   <int> <fct> <int> <fct>   
1  10.5    12 W     F     NH       NS    Married    29 Not      47 clerical
2  11.2    17 NW    F     NH       NS    Married    32 Not      55 clerical
3  25.0    17 W     F     NH       NS    Single      5 Not      28 prof    
4  12.6    17 W     F     NH       NS    Married    13 Not      36 manag   
5  11.7    16 W     F     NH       NS    Single     42 Not      64 clerical
6  12.5    15 W     F     NH       NS    Married     6 Not      27 clerical
# ℹ 56 more rows
Tip

&로 연결하는 대신 comma로 나열해도 됨: filter(wage >= 10, sex == "F")

# 간부급(management)과 전문직(professional)에 종사하는 사람들
cps |>
  filter(sector == "manag" | sector == "prof")
# A tibble: 160 × 11
   wage  educ race  sex   hispanic south married exper union   age sector
  <dbl> <int> <fct> <fct> <fct>    <fct> <fct>   <int> <fct> <int> <fct> 
1  25.0    17 W     M     Hisp     NS    Married    18 Not      41 prof  
2  20.4    17 W     M     NH       NS    Single      3 Not      26 prof  
3  10      16 W     M     Hisp     NS    Married     7 Union    29 manag 
4  15      16 NW    M     NH       NS    Married    26 Union    48 manag 
5  25.0    17 W     F     NH       NS    Single      5 Not      28 prof  
6  10      14 W     M     NH       NS    Married    22 Not      42 prof  
# ℹ 154 more rows

다음과 같이 편리하게 %in%을 이용하여 여러 항목을 포함하는, 즉 |==를 합친 조건문을 생성
즉, include인지 판별

# A shorter way to select sectors for management or professional
cps |>
  filter(sector %in% c("manag", "prof"))
# A tibble: 160 × 11
   wage  educ race  sex   hispanic south married exper union   age sector
  <dbl> <int> <fct> <fct> <fct>    <fct> <fct>   <int> <fct> <int> <fct> 
1  25.0    17 W     M     Hisp     NS    Married    18 Not      41 prof  
2  20.4    17 W     M     NH       NS    Single      3 Not      26 prof  
3  10      16 W     M     Hisp     NS    Married     7 Union    29 manag 
4  15      16 NW    M     NH       NS    Married    26 Union    48 manag 
5  25.0    17 W     F     NH       NS    Single      5 Not      28 prof  
6  10      14 W     M     NH       NS    Married    22 Not      42 prof  
# ℹ 154 more rows
Important

filter()로 얻은 데이터 프레임은 원래 데이터 프레임을 수정하는 것이 아니므로 계속 사용하려면 저장해야 함
이후 모든 함수들에 대해서도 마찬가지

prestige <- cps |>
  filter(sector %in% c("manag", "prof"))

prestige
# A tibble: 160 × 11
   wage  educ race  sex   hispanic south married exper union   age sector
  <dbl> <int> <fct> <fct> <fct>    <fct> <fct>   <int> <fct> <int> <fct> 
1  25.0    17 W     M     Hisp     NS    Married    18 Not      41 prof  
2  20.4    17 W     M     NH       NS    Single      3 Not      26 prof  
3  10      16 W     M     Hisp     NS    Married     7 Union    29 manag 
4  15      16 NW    M     NH       NS    Married    26 Union    48 manag 
5  25.0    17 W     F     NH       NS    Single      5 Not      28 prof  
6  10      14 W     M     NH       NS    Married    22 Not      42 prof  
# ℹ 154 more rows
Tip

잦은 실수들

cps |>
  filter(sex = "F") # "==" vs. "="
cps |>
  filter(sector == "manag" | "prof") # | 전후 모두 완결된 조건문 필요

arrange()

Column의 값을 기준으로 row를 정렬

# 교육정도(educ)와 임금(wage)에 따라 오름차순으로 정렬
cps |>
  arrange(educ, wage) |>
  print(n = 10)
# A tibble: 534 × 11
    wage  educ race  sex   hispanic south married exper union   age sector 
   <dbl> <int> <fct> <fct> <fct>    <fct> <fct>   <int> <fct> <int> <fct>  
 1  3.75     2 W     M     Hisp     NS    Single     16 Not      24 service
 2  7        3 W     M     Hisp     S     Married    55 Not      64 manuf  
 3  6        4 W     M     NH       NS    Married    54 Not      64 service
 4 14        5 W     M     NH       S     Married    44 Not      55 const  
 5  3        6 W     F     Hisp     NS    Married    43 Union    55 manuf  
 6  4.62     6 NW    F     NH       S     Single     33 Not      45 manuf  
 7  5.75     6 W     M     NH       S     Married    45 Not      57 manuf  
 8  3.35     7 W     M     NH       S     Married    43 Not      56 manuf  
 9  4.5      7 W     M     Hisp     S     Married    14 Not      27 service
10  6        7 W     F     NH       S     Married    15 Not      28 manuf  
# ℹ 524 more rows

desc()을 이용하면 내림차순으로 정렬

# educ을 내림차순으로 정렬
cps |>
  arrange(desc(educ)) |>
  print(n = 10)
# A tibble: 534 × 11
    wage  educ race  sex   hispanic south married exper union   age sector
   <dbl> <int> <fct> <fct> <fct>    <fct> <fct>   <int> <fct> <int> <fct> 
 1 15       18 W     M     NH       NS    Married    12 Not      36 prof  
 2 14.0     18 W     F     NH       NS    Married    14 Not      38 manag 
 3 13.5     18 W     M     NH       NS    Married    14 Union    38 prof  
 4 20       18 W     F     NH       NS    Married    19 Not      43 manag 
 5  7       18 W     M     NH       NS    Married    33 Not      57 prof  
 6 11.2     18 W     M     NH       NS    Married    19 Not      43 prof  
 7  5.71    18 W     M     NH       NS    Married     3 Not      27 prof  
 8 18       18 W     M     NH       NS    Married    15 Not      39 prof  
 9 19       18 W     M     NH       NS    Single     13 Not      37 manag 
10 22.8     18 W     F     NH       NS    Single     37 Not      61 prof  
# ℹ 524 more rows

arrange()filter()를 함께 사용하여 좀 더 복잡한 문제를 해결할 수 있음

# 높은 지위의 섹터에서 일하는 사람들 중 임금이 상위에 있는 사람들
cps |>
  filter(sector %in% c("manag", "prof")) |>
  arrange(desc(wage))
# A tibble: 160 × 11
   wage  educ race  sex   hispanic south married exper union   age sector
  <dbl> <int> <fct> <fct> <fct>    <fct> <fct>   <int> <fct> <int> <fct> 
1  44.5    14 W     F     NH       NS    Single      1 Not      21 manag 
2  26.3    17 W     M     NH       S     Married    32 Not      55 manag 
3  25.0    17 W     M     Hisp     NS    Married    18 Not      41 prof  
4  25.0    17 W     F     NH       NS    Single      5 Not      28 prof  
5  25.0    16 W     M     NH       NS    Married    18 Not      40 manag 
6  25.0    17 W     M     NH       NS    Married    31 Not      54 prof  
# ℹ 154 more rows

slice_*()

상위/하위 몇 개의 행을 바로 뽑을 때는 arrange() 대신 사용

# 임금 상위 5명
cps |>
  slice_max(wage, n = 5)
# A tibble: 10 × 11
    wage  educ race  sex   hispanic south married exper union   age sector 
   <dbl> <int> <fct> <fct> <fct>    <fct> <fct>   <int> <fct> <int> <fct>  
 1  44.5    14 W     F     NH       NS    Single      1 Not      21 manag  
 2  26.3    17 W     M     NH       S     Married    32 Not      55 manag  
 3  26      14 W     M     NH       NS    Married    21 Union    41 other  
 4  25      14 W     M     Hisp     NS    Single      4 Union    24 service
 5  25.0    17 W     M     Hisp     NS    Married    18 Not      41 prof   
 6  25.0    17 W     F     NH       NS    Single      5 Not      28 prof   
 7  25.0    16 W     M     NH       NS    Married    18 Not      40 manag  
 8  25.0    17 W     M     NH       NS    Married    31 Not      54 prof   
 9  25.0    18 W     M     NH       NS    Married    29 Not      53 manag  
10  25.0    16 W     F     NH       S     Single      5 Not      27 prof   
  • slice_min(), slice_max(): 값이 가장 작은/큰 행
  • slice_head(), slice_tail(): 처음/마지막 행
  • slice_sample(n = 10): 무작위 추출

distinct()

유니크한 조합들을 리스트

cps |>
  distinct(sector, sex)
# A tibble: 15 × 2
   sector   sex  
   <fct>    <fct>
 1 const    M    
 2 sales    M    
 3 sales    F    
 4 clerical F    
 5 service  F    
 6 manuf    M    
 7 prof     M    
 8 service  M    
 9 other    M    
10 clerical M    
11 manag    M    
12 prof     F    
13 manag    F    
14 manuf    F    
15 other    F    
  • 변수를 지정하지 않으면 (distinct()) 중복된 행 전체를 제거함
  • .keep_all = TRUE를 주면 나머지 열도 함께 남김

Columns

열에 적용되는 함수들
mutate(), select(), relocate(), rename()

여기서는 penguins 데이터셋을 이용함: 남극 Palmer Station 부근 세 섬에서 관측된 펭귄 344마리의 측정치

Note

penguins는 R 4.5부터 base R(datasets 패키지)에 포함되어 별도의 설치 없이 사용 가능함.
이전 버전의 R에서는 palmerpenguins::penguins를 이용 (열 이름이 bill_length_mm 등으로 더 김)

penguins <- as_tibble(penguins)
penguins
# A tibble: 344 × 8
  species island    bill_len bill_dep flipper_len body_mass sex     year
  <fct>   <fct>        <dbl>    <dbl>       <int>     <int> <fct>  <int>
1 Adelie  Torgersen     39.1     18.7         181      3750 male    2007
2 Adelie  Torgersen     39.5     17.4         186      3800 female  2007
3 Adelie  Torgersen     40.3     18           195      3250 female  2007
4 Adelie  Torgersen     NA       NA            NA        NA <NA>    2007
5 Adelie  Torgersen     36.7     19.3         193      3450 female  2007
6 Adelie  Torgersen     39.3     20.6         190      3650 male    2007
# ℹ 338 more rows
변수 의미
species, island 펭귄의 종, 관측된 섬
bill_len, bill_dep 부리의 길이, 두께 (mm)
flipper_len 지느러미발 길이 (mm)
body_mass 몸무게 (g)
sex, year 성별, 관측 연도

mutate()

Columns/변수들로부터 값을 계산하여 새로운 변수를 만듦

penguins |>
  mutate(
    body_mass_kg = body_mass / 1000,
    bill_ratio = bill_len / bill_dep
  )
# A tibble: 344 × 10
  species island    bill_len bill_dep flipper_len body_mass sex     year
  <fct>   <fct>        <dbl>    <dbl>       <int>     <int> <fct>  <int>
1 Adelie  Torgersen     39.1     18.7         181      3750 male    2007
2 Adelie  Torgersen     39.5     17.4         186      3800 female  2007
3 Adelie  Torgersen     40.3     18           195      3250 female  2007
4 Adelie  Torgersen     NA       NA            NA        NA <NA>    2007
5 Adelie  Torgersen     36.7     19.3         193      3450 female  2007
6 Adelie  Torgersen     39.3     20.6         190      3650 male    2007
# ℹ 338 more rows
# ℹ 2 more variables: body_mass_kg <dbl>, bill_ratio <dbl>

새로 만든 변수는 기본적으로 맨 뒤에 추가되는데, 위치를 지정할 수 있음

penguins |>
  mutate(
    body_mass_kg = body_mass / 1000,
    .after = species # .before = species: 앞에
  )
# A tibble: 344 × 9
  species body_mass_kg island    bill_len bill_dep flipper_len body_mass sex   
  <fct>          <dbl> <fct>        <dbl>    <dbl>       <int>     <int> <fct> 
1 Adelie          3.75 Torgersen     39.1     18.7         181      3750 male  
2 Adelie          3.8  Torgersen     39.5     17.4         186      3800 female
3 Adelie          3.25 Torgersen     40.3     18           195      3250 female
4 Adelie         NA    Torgersen     NA       NA            NA        NA <NA>  
5 Adelie          3.45 Torgersen     36.7     19.3         193      3450 female
6 Adelie          3.65 Torgersen     39.3     20.6         190      3650 male  
# ℹ 338 more rows
# ℹ 1 more variable: year <int>

.keep = "used"로 계산에 사용된 변수와 새 변수만 남길 수 있음

penguins |>
  mutate(
    bill_ratio = bill_len / bill_dep,
    .keep = "used"
  )
# A tibble: 344 × 3
  bill_len bill_dep bill_ratio
     <dbl>    <dbl>      <dbl>
1     39.1     18.7       2.09
2     39.5     17.4       2.27
3     40.3     18         2.24
4     NA       NA        NA   
5     36.7     19.3       1.90
6     39.3     20.6       1.91
# ℹ 338 more rows

if_else(): 두 가지 경우, case_when(): 여러 경우

penguins |>
  mutate(
    size = if_else(body_mass >= 4200, "large", "small"),
    size2 = case_when(
      body_mass < 3550 ~ "small",
      body_mass < 4750 ~ "medium",
      body_mass >= 4750 ~ "large",
      .default = NA # 어디에도 해당하지 않는 경우 (여기서는 결측치)
    ),
    .keep = "used"
  )
# A tibble: 344 × 3
  body_mass size  size2 
      <int> <chr> <chr> 
1      3750 small medium
2      3800 small medium
3      3250 small small 
4        NA <NA>  <NA>  
5      3450 small small 
6      3650 small medium
# ℹ 338 more rows

select()

Columns/변수를 선택

penguins |>
  select(species, bill_len, bill_dep, body_mass)
# A tibble: 344 × 4
  species bill_len bill_dep body_mass
  <fct>      <dbl>    <dbl>     <int>
1 Adelie      39.1     18.7      3750
2 Adelie      39.5     17.4      3800
3 Adelie      40.3     18        3250
4 Adelie      NA       NA          NA
5 Adelie      36.7     19.3      3450
6 Adelie      39.3     20.6      3650
# ℹ 338 more rows
# bill_len에서 body_mass까지, 그리고 sex column 선택
penguins |>
  select(bill_len:body_mass, sex) # select(3:6, 7)처럼 number로 선택 가능
# A tibble: 344 × 5
  bill_len bill_dep flipper_len body_mass sex   
     <dbl>    <dbl>       <int>     <int> <fct> 
1     39.1     18.7         181      3750 male  
2     39.5     17.4         186      3800 female
3     40.3     18           195      3250 female
4     NA       NA            NA        NA <NA>  
5     36.7     19.3         193      3450 female
6     39.3     20.6         190      3650 male  
# ℹ 338 more rows
# island에서 flipper_len까지 columns은 제외하고
penguins |>
  select(!island:flipper_len) # !: not
# A tibble: 344 × 4
  species body_mass sex     year
  <fct>       <int> <fct>  <int>
1 Adelie       3750 male    2007
2 Adelie       3800 female  2007
3 Adelie       3250 female  2007
4 Adelie         NA <NA>    2007
5 Adelie       3450 female  2007
6 Adelie       3650 male    2007
# ℹ 338 more rows
# factor 타입의 변수들만 선택: 함수를 이용
penguins |>
  select(where(is.factor)) # 다른 함수들: is.numeric, is.character
# A tibble: 344 × 3
  species island    sex   
  <fct>   <fct>     <fct> 
1 Adelie  Torgersen male  
2 Adelie  Torgersen female
3 Adelie  Torgersen female
4 Adelie  Torgersen <NA>  
5 Adelie  Torgersen female
6 Adelie  Torgersen male  
# ℹ 338 more rows

다양한 select()의 선택방법은 ?select로 help 참고

penguins |>
  select(starts_with("bill")) # bill로 시작하는 이름의 열들
# A tibble: 344 × 2
  bill_len bill_dep
     <dbl>    <dbl>
1     39.1     18.7
2     39.5     17.4
3     40.3     18  
4     NA       NA  
5     36.7     19.3
6     39.3     20.6
# ℹ 338 more rows
  • starts_with("abc"), ends_with("mm"), contains("len")
  • all_of(vars), any_of(vars): 열 이름을 담은 문자열 벡터로 선택
  • everything(): 나머지 전부
Note

Base R에서 행과 열의 선택과 비교하면,

cps[2:5, c("wage", "married")] # 2~5행과 wage, married열
# A tibble: 4 × 2
   wage married
  <dbl> <fct>  
1   5.5 Married
2   3.8 Single 
3  10.5 Married
4  15   Married
cps |>
  select(wage, married) |>
  slice(2:5) # 행을 선택
# A tibble: 4 × 2
   wage married
  <dbl> <fct>  
1   5.5 Married
2   3.8 Single 
3  10.5 Married
4  15   Married

relocate()

Columns의 순서를 변경

penguins |>
  relocate(sex, year) |> # sex, year를 맨 앞으로 이동
  print(n = 2)
# A tibble: 344 × 8
  sex     year species island    bill_len bill_dep flipper_len body_mass
  <fct>  <int> <fct>   <fct>        <dbl>    <dbl>       <int>     <int>
1 male    2007 Adelie  Torgersen     39.1     18.7         181      3750
2 female  2007 Adelie  Torgersen     39.5     17.4         186      3800
# ℹ 342 more rows
penguins |>
  relocate(bill_len:body_mass, .after = last_col()) |> # .before: 앞에, .after: 뒤에
  print(n = 2)
# A tibble: 344 × 8
  species island    sex     year bill_len bill_dep flipper_len body_mass
  <fct>   <fct>     <fct>  <int>    <dbl>    <dbl>       <int>     <int>
1 Adelie  Torgersen male    2007     39.1     18.7         181      3750
2 Adelie  Torgersen female  2007     39.5     17.4         186      3800
# ℹ 342 more rows

rename()

Columns의 이름을 변경

cps |>
  rename(education = educ, marital = married) # new = old
# A tibble: 534 × 11
   wage education race  sex   hispanic south marital exper union   age sector  
  <dbl>     <int> <fct> <fct> <fct>    <fct> <fct>   <int> <fct> <int> <fct>   
1   9          10 W     M     NH       NS    Married    27 Not      43 const   
2   5.5        12 W     M     NH       NS    Married    20 Not      38 sales   
3   3.8        12 W     F     NH       NS    Single      4 Not      22 sales   
4  10.5        12 W     F     NH       NS    Married    29 Not      47 clerical
5  15          12 W     M     NH       NS    Married    40 Union    58 const   
6   9          16 W     F     NH       NS    Married    27 Not      49 clerical
# ℹ 528 more rows

변수를 select할 때 동시에 이름도 바꿀 수 있음

cps |>
  select(education = educ, marital = married) # new = old
# A tibble: 534 × 2
  education marital
      <int> <fct>  
1        10 Married
2        12 Married
3        12 Single 
4        12 Married
5        12 Married
6        16 Married
# ℹ 528 more rows

Groups

분석에서는 자주 카테고리별로 데이터를 나누어 통계치를 계산하곤 하는데,
group_by()summarise()의 두 함수를 함께 사용하여 가장 자주 사용하게 됨

group_by()

데이터셋을 분석을 위해 의미있는 그룹으로 나눔

다음은 성별로 데이터셋을 나눈 것인데, 실제 데이터를 수정하는 것은 아니고, 내부적으로 grouping되어 있음.
맨 위 줄에 보면 Groups: sex [2]로 표시되어 grouped data frame임을 명시함

cps |>
  group_by(sex)
# A tibble: 534 × 11
# Groups:   sex [2]
   wage  educ race  sex   hispanic south married exper union   age sector  
  <dbl> <int> <fct> <fct> <fct>    <fct> <fct>   <int> <fct> <int> <fct>   
1   9      10 W     M     NH       NS    Married    27 Not      43 const   
2   5.5    12 W     M     NH       NS    Married    20 Not      38 sales   
3   3.8    12 W     F     NH       NS    Single      4 Not      22 sales   
4  10.5    12 W     F     NH       NS    Married    29 Not      47 clerical
5  15      12 W     M     NH       NS    Married    40 Union    58 const   
6   9      16 W     F     NH       NS    Married    27 Not      49 clerical
# ℹ 528 more rows

summarise()

summarize()와 동일
group별로 통계치를 구해 하나의 행으로 산출

# 남녀별로 임금의 평균을 구함
cps |>
  group_by(sex) |>
  summarise(
    avg_wage = mean(wage, na.rm = TRUE), # mean(): 평균, na.rm: NA를 remove할 것인가
    n = n() # n(): 개수
  )
# A tibble: 2 × 3
  sex   avg_wage     n
  <fct>    <dbl> <int>
1 F         7.88   245
2 M         9.99   289

2개 이상의 변수들로 grouping할 수 있음

cps |>
  group_by(sex, married) |>
  summarise(
    avg_wage = mean(wage),
    sd_wage = sd(wage)
  )
# A tibble: 4 × 4
# Groups:   sex [2]
  sex   married avg_wage sd_wage
  <fct> <fct>      <dbl>   <dbl>
1 F     Married     7.68    3.73
2 F     Single      8.26    6.23
3 M     Married    10.9     5.35
4 M     Single      8.35    4.78

이때, 결과 데이터 프레임은 여전히 sex로 grouping되어 있음 (위의 message와 Groups: sex [2])
grouping을 해제하려면 ungroup()이 필요하고, 아예 남기지 않으려면 .groups = "drop"

cps |>
  group_by(sex, married) |>
  summarise(
    avg_wage = mean(wage),
    sd_wage = sd(wage),
    .groups = "drop"
  )
# A tibble: 4 × 4
  sex   married avg_wage sd_wage
  <fct> <fct>      <dbl>   <dbl>
1 F     Married     7.68    3.73
2 F     Single      8.26    6.23
3 M     Married    10.9     5.35
4 M     Single      8.35    4.78

.by argument

dplyr 1.1부터는 group_by() 없이 .by로 그룹을 지정할 수 있음.
grouping이 그 함수 안에서만 적용되고 결과에 남지 않아 (ungroup() 불필요) 더 간결함

cps |>
  summarise(
    avg_wage = mean(wage),
    sd_wage = sd(wage),
    n = n(),
    .by = c(sex, married)
  )
# A tibble: 4 × 5
  sex   married avg_wage sd_wage     n
  <fct> <fct>      <dbl>   <dbl> <int>
1 M     Married    10.9     5.35   188
2 F     Single      8.26    6.23    83
3 F     Married     7.68    3.73   162
4 M     Single      8.35    4.78   101

mutate(), filter(), slice_*()에서도 동일하게 쓸 수 있음

# 섹터별로 임금이 가장 높은 사람
cps |>
  slice_max(wage, n = 1, by = sector) |> # slice_*()에서는 점 없이 by
  select(sector, wage, educ, sex)
# A tibble: 12 × 4
   sector    wage  educ sex  
   <fct>    <dbl> <int> <fct>
 1 const     15      12 M    
 2 const     15      12 M    
 3 sales     20.0    14 M    
 4 clerical  15.0    12 F    
 5 service   25      14 M    
 6 manuf     22.2    12 M    
 7 prof      25.0    17 M    
 8 prof      25.0    17 F    
 9 prof      25.0    17 M    
10 prof      25.0    16 F    
11 other     26      14 M    
12 manag     44.5    14 F    
# 각자의 임금이 자신이 속한 섹터 평균의 몇 배인지
cps |>
  mutate(
    wage_ratio = wage / mean(wage),
    .by = sector,
    .keep = "used"
  )
# A tibble: 534 × 3
   wage sector   wage_ratio
  <dbl> <fct>         <dbl>
1   9   const         0.947
2   5.5 sales         0.724
3   3.8 sales         0.500
4  10.5 clerical      1.41 
5  15   const         1.58 
6   9   clerical      1.21 
# ℹ 528 more rows

Useful summary functions
자세한 사항은 R for Data Science (2판) / Data transformation

  • Measures of location: mean(), median()
  • Measures of spread: sd(), IQR(), mad()
  • Measures of rank: min(), max(), quantile(x, 0.25)
  • Measures of position: min_rank(), first(), nth(x, 2), last()
  • Measures of count: n(), n_distinct()

Missing

R에서 missing values (결측치)는 NA로 표시
NaN (not a number)는 주로 계산 결과로 나오는데, 예를 들어 0을 0으로 나눌 때처럼, R에서는 NA로 취급되니 크게 신경쓰지 않아도 됨.
자세한 사항은 R for Data Science (2판) / Missing values 참고

NA는 다음과 같은 성질을 지님

NA > 5
#> [1] NA
10 == NA
#> [1] NA
NA + 10
#> [1] NA
NA / 2
#> [1] NA
NA == NA
#> [1] NA

x <- NA
is.na(x)
#> [1] TRUE

즉, “모르는 값”이므로 비교의 결과도 “모름”임. 따라서 x == NA가 아니라 is.na(x)로 판별해야 함

NA는 filter()안의 조건문의 참거짓에 상관없이 모두 제외함

  • 실제로 조건문의 결과는 TRUE, FALSE로 이루어짐
df <- tibble(
  one = c(1, NA, 3, 4, 2, NA),
  two = c(2, 5, 3, NA, 10, NA),
  three = c("a", "a", "a", "a", "b", "b")
)
df
# A tibble: 6 × 3
    one   two three
  <dbl> <dbl> <chr>
1     1     2 a    
2    NA     5 a    
3     3     3 a    
4     4    NA a    
5     2    10 b    
6    NA    NA b    
df |> filter(one > 1)
# A tibble: 3 × 3
    one   two three
  <dbl> <dbl> <chr>
1     3     3 a    
2     4    NA a    
3     2    10 b    
# NA를 포함하고자 할 때
df |> filter(one > 1 | is.na(one))
# A tibble: 5 × 3
    one   two three
  <dbl> <dbl> <chr>
1    NA     5 a    
2     3     3 a    
3     4    NA a    
4     2    10 b    
5    NA    NA b    
# NA를 포함하지 않은 행들만
df |> filter(!is.na(one))
# A tibble: 4 × 3
    one   two three
  <dbl> <dbl> <chr>
1     1     2 a    
2     3     3 a    
3     4    NA a    
4     2    10 b    
df |> filter(!is.na(one) & !is.na(two)) # one, two 열에 모두 NA가 없는 행들만
# A tibble: 3 × 3
    one   two three
  <dbl> <dbl> <chr>
1     1     2 a    
2     3     3 a    
3     2    10 b    
# NA가 하나라도 있는 행은 모두 제거, 보통 결측치를 조심스럽게 대체한 후 사용
df |> drop_na() # base R의 na.omit(df)와 동일
# A tibble: 3 × 3
    one   two three
  <dbl> <dbl> <chr>
1     1     2 a    
2     3     3 a    
3     2    10 b    

함수 중에 NA를 직접 처리하는 경우들이 많음

mean(df$one)
[1] NA
mean(df$one, na.rm = TRUE) # NA removed
[1] 2.5

na.rm = TRUE로 얻은 계산값에서 몇 개의 데이터로 계산되었는지 알기 위해서는

df |>
  summarise(
    avg = mean(two, na.rm = TRUE),
    n = n(),
    n_notna = sum(!is.na(two)), # TRUE는 1로, FALSE는 0으로 계산됨
    .by = three
  )
# A tibble: 2 × 4
  three   avg     n n_notna
  <chr> <dbl> <int>   <int>
1 a      3.33     4       3
2 b     10        2       1
Tip

어느 변수에 결측치가 얼마나 있는지 한 번에 확인하려면

penguins |>
  summarise(across(everything(), \(x) sum(is.na(x))))
# A tibble: 1 × 8
  species island bill_len bill_dep flipper_len body_mass   sex  year
    <int>  <int>    <int>    <int>       <int>     <int> <int> <int>
1       0      0        2        2           2         2    11     0

across()는 여러 열에 같은 계산을 반복 적용함. \(x) ...는 함수를 즉석에서 정의하는 방법 (R 4.1부터)

Summary

다음 dplyr 패키지의 기본 verb 함수들로 데이터를 가공하면서 필요한 통계치를 구함

  • 조건에 맞는 행들(관측치)만 필터링: filter()
  • 행을 재정렬: arrange()
  • 변수들의 선택: select()
  • 변수들과 함수들을 이용하여 새로운 변수를 생성: mutate()
  • 원하는 요약 통계치를 간추림: summarise() (+ group_by() 또는 .by)