[R] 4. select(), filter(), group_by(), summarise(), arrange(), rename()

반응형

0. 예시로 쓰일 데이터 예제

set.seed(2021)

# 임의로 데이터를 생성한다. (100명의 유저가 특정 곡을 스트리밍한 이력)
temp <- tibble(
  user_id = c(10000:10099),
  user_age = round(runif(n = 100, min = 18, max = 50), 0),
  song_id = sample(x = letters[1:15], size = 100, replace = TRUE),
  streaming_count = rpois(n = 100, lambda = 20)
)

# 임의로 유저의 연령값에 결측값을 섞는다.
temp[round(runif(n = 5, min = 0, max = 100)), "user_age"] <- NA

temp
## # A tibble: 100 x 4
##    user_id user_age song_id streaming_count
##      <int>    <dbl> <chr>             <int>
##  1   10000       32 f                    17
##  2   10001       43 l                    14
##  3   10002       41 f                    19
##  4   10003       30 f                    29
##  5   10004       38 h                    14
##  6   10005       40 k                    23
##  7   10006       38 l                    20
##  8   10007       27 m                    29
##  9   10008       44 a                    11
## 10   10009       49 n                    20
## # … with 90 more rows





1. select()

  • select() 함수를 통해 데이터에서 필요한 변수를 선택하여 조회할 수 있습니다.
# user_id와 user_age 컬럼만 조회
temp %>% 
  select(user_id, user_age)
## # A tibble: 100 x 2
##    user_id user_age
##      <int>    <dbl>
##  1   10000       32
##  2   10001       43
##  3   10002       41
##  4   10003       30
##  5   10004       38
##  6   10005       40
##  7   10006       38
##  8   10007       27
##  9   10008       44
## 10   10009       49
## # … with 90 more rows
  • 비슷한 방식으로 필요하지 않은 변수들을 직접 선택하여 -를 붙여 선택할 수도 있습니다.
# song_id와 streaming_count 컬럼은 조회에서 제외
temp %>% 
  select(-song_id, -streaming_count)
## # A tibble: 100 x 2
##    user_id user_age
##      <int>    <dbl>
##  1   10000       32
##  2   10001       43
##  3   10002       41
##  4   10003       30
##  5   10004       38
##  6   10005       40
##  7   10006       38
##  8   10007       27
##  9   10008       44
## 10   10009       49
## # … with 90 more rows



  • 데이터 분석을 위해 전처리를 하는 작업을 거치다보면서 변수명에 _id 와 같이 특정 변수명 규칙을 발견할 수 있습니다.
  • 이를 알면 데이터 전처리 또한 매우 쉽게 처리하실 수 있습니다.
  • ends_with() 함수는 특정 표현으로 끝나는 변수명을 선택할 수 있는 함수입니다.
# 컬럼이 _id로 끝나는 컬럼들 모두 조회
temp %>% 
  select(ends_with("_id"))
## # A tibble: 100 x 2
##    user_id song_id
##      <int> <chr>  
##  1   10000 f      
##  2   10001 l      
##  3   10002 f      
##  4   10003 f      
##  5   10004 h      
##  6   10005 k      
##  7   10006 l      
##  8   10007 m      
##  9   10008 a      
## 10   10009 n      
## # … with 90 more rows
  • 비슷한 방식으로 starts_with() 특정 표현으로 시작하는 변수명을 선택할 수도 있습니다.
# 컬럼이 user_로 시작하는 컬럼들 모두 조회
temp %>% 
  select(starts_with("user_"))
## # A tibble: 100 x 2
##    user_id user_age
##      <int>    <dbl>
##  1   10000       32
##  2   10001       43
##  3   10002       41
##  4   10003       30
##  5   10004       38
##  6   10005       40
##  7   10006       38
##  8   10007       27
##  9   10008       44
## 10   10009       49
## # … with 90 more rows
  • 특정 표현이 포함된 변수명을 선택하는 경우에는 contains() 함수를 활용할 수 있습니다.
# 컬럼에 id라는 문자열에 포함된 컬럼들 모두 조회
temp %>% 
  select(contains("id"))
## # A tibble: 100 x 2
##    user_id song_id
##      <int> <chr>  
##  1   10000 f      
##  2   10001 l      
##  3   10002 f      
##  4   10003 f      
##  5   10004 h      
##  6   10005 k      
##  7   10006 l      
##  8   10007 m      
##  9   10008 a      
## 10   10009 n      
## # … with 90 more rows



  • a:b 표현을 활용하여 a에서부터 b까지 변수명을 모두 출력할 수도 있습니다.
# user_id ~ song_id 까지 조회
temp %>% 
  select(user_id:song_id)
## # A tibble: 100 x 3
##    user_id user_age song_id
##      <int>    <dbl> <chr>  
##  1   10000       32 f      
##  2   10001       43 l      
##  3   10002       41 f      
##  4   10003       30 f      
##  5   10004       38 h      
##  6   10005       40 k      
##  7   10006       38 l      
##  8   10007       27 m      
##  9   10008       44 a      
## 10   10009       49 n      
## # … with 90 more rows
# 두 번째 컬럼부터 네 번째 컬럼까지 조회
temp %>% 
  select(2:4)
## # A tibble: 100 x 3
##    user_age song_id streaming_count
##       <dbl> <chr>             <int>
##  1       32 f                    17
##  2       43 l                    14
##  3       41 f                    19
##  4       30 f                    29
##  5       38 h                    14
##  6       40 k                    23
##  7       38 l                    20
##  8       27 m                    29
##  9       44 a                    11
## 10       49 n                    20
## # … with 90 more rows





2. filter()

  • SQL에서 SELECT 함수를 통해 변수를 선택하고 WHERE 절을 통해 데이터에서의 조건을 부여 합니다.
  • 마찬가지로 R에서 WHERE절의 역할을 하는 함수는 filter() 함수 입니다. 함수 이름 그대로 설정된 조건을 필터링 하는 기능을 합니다.
    • R에서 ’같다’의 의미는 ==, ’다르다’의 의미는 !=로 쓰입니다.
# a라는 곡을 들은 유저만 조회
temp %>% 
  filter(song_id == "a")
## # A tibble: 9 x 4
##   user_id user_age song_id streaming_count
##     <int>    <dbl> <chr>             <int>
## 1   10008       44 a                    11
## 2   10025       47 a                    16
## 3   10028       48 a                    18
## 4   10033       47 a                    17
## 5   10037       26 a                    32
## 6   10058       46 a                    28
## 7   10064       45 a                    26
## 8   10067       NA a                    20
## 9   10087       44 a                    20



  • ‘AND’ 조건은 &, ‘OR’ 조건은 | 표현을 사용합니다.
  • filter() 함수에 조건절을 컴마(,)로 구분하는 경우 ‘AND’ 조건으로 인식합니다.
# a라는 곡을 들었으면서 나이는 30세 이하인 유저
temp %>% 
  filter(song_id == "a" & user_age <= 30)
## # A tibble: 1 x 4
##   user_id user_age song_id streaming_count
##     <int>    <dbl> <chr>             <int>
## 1   10037       26 a                    32
# a라는 곡을 들은 유저들 또는 c라는 곡을 들은 유저들
temp %>% 
  filter(song_id == "a" | song_id == "c")
## # A tibble: 17 x 4
##    user_id user_age song_id streaming_count
##      <int>    <dbl> <chr>             <int>
##  1   10008       44 a                    11
##  2   10011       45 c                    10
##  3   10025       47 a                    16
##  4   10026       48 c                    17
##  5   10028       48 a                    18
##  6   10032       48 c                    26
##  7   10033       47 a                    17
##  8   10037       26 a                    32
##  9   10044       NA c                    18
## 10   10053       35 c                    18
## 11   10058       46 a                    28
## 12   10064       45 a                    26
## 13   10065       22 c                    21
## 14   10067       NA a                    20
## 15   10084       30 c                    19
## 16   10085       25 c                    17
## 17   10087       44 a                    20



  • 결측값을 여부를 TRUE 또는 FALSE로 표현해주는 함수는 is.na() 함수가 있습니다.
    (TRUE == 1, FALSE == 0으로 인식)
x <- c(NA, 3, 5, NA, 10)

# is.na() 함수를 이용하여 결측값에 해당하는 부분에는 TRUE를 결측값이 아니면 FALSE를 할당
is.na(x)
## [1]  TRUE FALSE FALSE  TRUE FALSE
# TRUE는 숫자로 1을, FALSE는 0에 해당하는 값임
sum(is.na(x))
## [1] 2
  • filter() 함수와 is.na() 함수를 활용하여 데이터 결측 여부를 확인할 수 있습니다.
# user_age 컬럼에 결측이 있는 값들만 조회
temp %>% 
  filter(is.na(user_age) == TRUE)
## # A tibble: 5 x 4
##   user_id user_age song_id streaming_count
##     <int>    <dbl> <chr>             <int>
## 1   10044       NA c                    18
## 2   10051       NA g                    17
## 3   10067       NA a                    20
## 4   10093       NA k                    15
## 5   10097       NA m                    22
# drop_na() 함수를 이용하여 결측값이 존재하지 않는 관측값만을 추출
temp %>% 
  drop_na()
## # A tibble: 95 x 4
##    user_id user_age song_id streaming_count
##      <int>    <dbl> <chr>             <int>
##  1   10000       32 f                    17
##  2   10001       43 l                    14
##  3   10002       41 f                    19
##  4   10003       30 f                    29
##  5   10004       38 h                    14
##  6   10005       40 k                    23
##  7   10006       38 l                    20
##  8   10007       27 m                    29
##  9   10008       44 a                    11
## 10   10009       49 n                    20
## # … with 85 more rows





3. group_by()

  • 위에서 filter() 함수를 이용하여 특정한 값을 갖는 관측값들을 선별하는 사례를 살펴보았다면, 데이터 전처리 과정에서 특정한 조건에 따라 구분해야 하는 경우도 발생할 수 있습니다.
  • 이 떄 이용하는 함수는 group_by() 함수로 SQL에서 GROUP BY와 같은 역할을 수행합니다.
  • 아래 코드는 group_by() 함수를 이용하여 변수 수준 단위로 그룹화하는 작업입니다.
# 곡 별 기준으로 그룹화
temp %>% 
  select(user_id, song_id, streaming_count) %>% 
  group_by(song_id)
## # A tibble: 100 x 3
## # Groups:   song_id [15]
##    user_id song_id streaming_count
##      <int> <chr>             <int>
##  1   10000 f                    17
##  2   10001 l                    14
##  3   10002 f                    19
##  4   10003 f                    29
##  5   10004 h                    14
##  6   10005 k                    23
##  7   10006 l                    20
##  8   10007 m                    29
##  9   10008 a                    11
## 10   10009 n                    20
## # … with 90 more rows
  • song_id 변수를 기준으로 묵였다고 표시되는 것(“Groups: song_id”)을 확인할 수 있습니다.
  • 그룹화는 한 개 이상 변수를 그룹화 시킬 수 있습니다.



  • 이렇게 그룹화 된 데이터를 summarise() 함수를 이용하여 연산을 수행할 수 있습니다.
    • 합계 sum(), 평균 mean(), 표준편차 sd(), 카운트 n(), 유니크 카운트 n_distinct() 등이 있습니다.
temp %>% 
  drop_na() %>% 
  group_by(song_id) %>% 
  summarise(
    total_streaming_count = sum(streaming_count),
    avg_user_age = mean(user_age),
    unique_user_count = n_distinct(user_id)
  )
## # A tibble: 15 x 4
##    song_id total_streaming_count avg_user_age unique_user_count
##    <chr>                   <int>        <dbl>             <int>
##  1 a                         168         43.4                 8
##  2 b                          46         35                   2
##  3 c                         128         36.1                 7
##  4 d                          77         27.5                 4
##  5 e                         224         35.3                12
##  6 f                         167         32.5                 8
##  7 g                         150         36.9                 7
##  8 h                          63         42.2                 4
##  9 i                          89         38                   5
## 10 j                          68         32.7                 3
## 11 k                         187         35.1                 9
## 12 l                          73         42                   4
## 13 m                         203         34.2                11
## 14 n                         171         30                   9
## 15 o                          41         39                   2



  • 만일 먼저 데이터를 집단의 수준별로 구분한 후 특정 변수와 특정 변수(들)의 관계를 살펴보는 경우에는 R에서 기본적으로 제공하는 split() 함수와 purrr 라이브러리의 map() 함수를 같이 이용하면 매우 유용합니다.
  • split() 함수는 tidyverse 라이브러리의 group_by() 함수와 차이점이 있다면 괄호 안의 변수 수준을 구분할 때 사용되는 변수를 투입하는 방식에서 차이가 있습니다. 아래 코드에서 보듯 split() 함수의 괄호 안에 투입되는 변수는 .$변수 형태로 마침표로 시작하게 됩니다.
  • 여기서 마침표는 코드 앞에서 제시된 데이터를 의미하며 따라서 .$변수는 앞서 제시된 데이터에 포함된 변수를 의미하게 됩니다.
# song_id별 데이터를 리스트 형태로 분리 (song_id는 a, c만 대상)
temp %>% 
  drop_na() %>% 
  filter(song_id %in% c("a", "c")) %>% 
  split(.$song_id)
## $a
## # A tibble: 8 x 4
##   user_id user_age song_id streaming_count
##     <int>    <dbl> <chr>             <int>
## 1   10008       44 a                    11
## 2   10025       47 a                    16
## 3   10028       48 a                    18
## 4   10033       47 a                    17
## 5   10037       26 a                    32
## 6   10058       46 a                    28
## 7   10064       45 a                    26
## 8   10087       44 a                    20
## 
## $c
## # A tibble: 7 x 4
##   user_id user_age song_id streaming_count
##     <int>    <dbl> <chr>             <int>
## 1   10011       45 c                    10
## 2   10026       48 c                    17
## 3   10032       48 c                    26
## 4   10053       35 c                    18
## 5   10065       22 c                    21
## 6   10084       30 c                    19
## 7   10085       25 c                    17
  • 위의 결과에서 보면 각 song_id별로 streaming_count를 보실 수 있습니다.
  • 이를 가지고 각 song_id별 streaming_count와 user_age간 상관계수를 확인하고 싶다면 아래와 같이 magrittr 라이브러리를 불러온 후 map() 함수를 이용하시면 됩니다.
  • map() 함수는 벡터의 각 요인별로 특정 함수를 적용하는 함수로 map(~ fun, data = .x)의 형태로 코딩하시면 됩니다.
library(magrittr)

# song_id별 데이터를 리스트로 분리 후 map() 함수를 적용. streaming_count, user_age간의 상관분석
temp %>% 
  drop_na() %>% 
  filter(song_id %in% c("a", "c")) %>% 
  split(.$song_id) %>% 
  map(~ cor.test(~ streaming_count + user_age, data = .x))
## $a
## 
##  Pearson's product-moment correlation
## 
## data:  streaming_count and user_age
## t = -1.9245, df = 6, p-value = 0.1026
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  -0.9213593  0.1538638
## sample estimates:
##        cor 
## -0.6177925 
## 
## 
## $c
## 
##  Pearson's product-moment correlation
## 
## data:  streaming_count and user_age
## t = -0.17867, df = 5, p-value = 0.8652
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  -0.7855868  0.7163783
## sample estimates:
##         cor 
## -0.07964767
  • 만일 위와 같은 결과에서 상관계수만 추정하여 저장하고 싶다면? map("estimate") 함수를 이용하여 저장할 수 있습니다.
temp %>% 
  drop_na() %>% 
  filter(song_id %in% c("a", "c")) %>% 
  split(.$song_id) %>% 
  map(~ cor.test(~ streaming_count + user_age, data = .x)) %>% 
  map("estimate") %>% 
  as_tibble()
## # A tibble: 1 x 2
##        a       c
##    <dbl>   <dbl>
## 1 -0.618 -0.0796





4. arrange()

  • arrange() 함수는 SQL에서 ORDER BY와 같은 역할을 하는 함수로 데이터를 오름차순 또는 내림차순 정렬할 때 쓰입니다.
  • 디폴트는 오름차순이며, 내림차순은 desc() 함수를 이용합니다.
# streaming_count 기준 오름차순
temp %>% 
  arrange(streaming_count) %>% 
  head()
## # A tibble: 6 x 4
##   user_id user_age song_id streaming_count
##     <int>    <dbl> <chr>             <int>
## 1   10011       45 c                    10
## 2   10019       35 f                    10
## 3   10082       49 h                    10
## 4   10008       44 a                    11
## 5   10047       19 m                    12
## 6   10010       19 e                    13
# streaming_count 기준 내림차순
temp %>% 
  arrange(desc(streaming_count)) %>% 
  head()
## # A tibble: 6 x 4
##   user_id user_age song_id streaming_count
##     <int>    <dbl> <chr>             <int>
## 1   10037       26 a                    32
## 2   10015       26 k                    30
## 3   10003       30 f                    29
## 4   10007       27 m                    29
## 5   10048       20 g                    29
## 6   10058       46 a                    28
  • group_by() 함수와 마찬가지로 두 가지 이상의 변수를 정렬할 수 있습니다.
temp %>% 
  arrange(desc(streaming_count), user_age) %>% 
  head()
## # A tibble: 6 x 4
##   user_id user_age song_id streaming_count
##     <int>    <dbl> <chr>             <int>
## 1   10037       26 a                    32
## 2   10015       26 k                    30
## 3   10048       20 g                    29
## 4   10007       27 m                    29
## 5   10003       30 f                    29
## 6   10098       42 f                    28
  • 그룹으로 구분된 데이터를 정렬하는 경우라면 arrange() 함수 내에 .by_group = TRUE 옵션을 추가하시면 됩니다.
# POC코드별 스트리밍 횟수 내림차순 정렬
temp %>% 
  group_by(song_id) %>% 
  arrange(desc(streaming_count), .by_group = TRUE)
## # A tibble: 100 x 4
## # Groups:   song_id [15]
##    user_id user_age song_id streaming_count
##      <int>    <dbl> <chr>             <int>
##  1   10037       26 a                    32
##  2   10058       46 a                    28
##  3   10064       45 a                    26
##  4   10067       NA a                    20
##  5   10087       44 a                    20
##  6   10028       48 a                    18
##  7   10033       47 a                    17
##  8   10025       47 a                    16
##  9   10008       44 a                    11
## 10   10089       31 b                    25
## # … with 90 more rows





5. rename()

  • 변수의 이름을 분석가가 원하는 이름으로 바꾸고자 할 떄 방법은 크게 두 가지가 있습니다.
  • 첫 번째로 tidyverse 패키지에서 제공하는 rename() 함수를 이용하는 것이고,
    두 번째로 티블 데이터에 names() 함수를 적용한 결과를 텍스트 형태의 범주형 변수로 취급하여 원하는 표현을 일괄적으로 바꾸는 방법 입니다.
  • 두 방법 모두 직관적이며 매우 간단하고 이해하기 쉽습니다. 아래 예제 코드를 통해 살펴보겠습니다.
# 영어로 된 변수명을 rename() 함수를 적용하여 한글로 변경
temp %>% 
  rename(
    `유저 아이디` = user_id,
    `유저 나이` = user_age,
    `곡 아이디` = song_id,
    `스트리밍 횟수` = streaming_count
  )
## # A tibble: 100 x 4
##    `유저 아이디` `유저 나이` `곡 아이디` `스트리밍 횟수`
##            <int>       <dbl> <chr>                 <int>
##  1         10000          32 f                        17
##  2         10001          43 l                        14
##  3         10002          41 f                        19
##  4         10003          30 f                        29
##  5         10004          38 h                        14
##  6         10005          40 k                        23
##  7         10006          38 l                        20
##  8         10007          27 m                        29
##  9         10008          44 a                        11
## 10         10009          49 n                        20
## # … with 90 more rows
# names() 함수를 이용하여 데이터의 변수명을 직접 변경
var_name = c("유저 아이디", "유저 나이", "곡 아이디", "스트리밍 횟수")
temp2 <- temp

names(temp2) = var_name
temp2
## # A tibble: 100 x 4
##    `유저 아이디` `유저 나이` `곡 아이디` `스트리밍 횟수`
##            <int>       <dbl> <chr>                 <int>
##  1         10000          32 f                        17
##  2         10001          43 l                        14
##  3         10002          41 f                        19
##  4         10003          30 f                        29
##  5         10004          38 h                        14
##  6         10005          40 k                        23
##  7         10006          38 l                        20
##  8         10007          27 m                        29
##  9         10008          44 a                        11
## 10         10009          49 n                        20
## # … with 90 more rows
  • names() 함수를 이용하여 일괄적 변경하는 방법은 조금 더 체계적으로 함수를 적용할 수 있습니다.
  • 예를 들면 user라는 이름이 들어가는 것을 유저라는 표현으로 바꾸는 경우 str_replace() 함수를 이용할 수 있고, 코드는 아래와 같습니다.
# str_replace() 함수를 이용하여 특정 조건에 해당하는 부분을 변경하여 변수명 수정
temp3 <- temp
names(temp3) = str_replace(names(temp3), "user", "유저_")
temp3
## # A tibble: 100 x 4
##    유저__id 유저__age song_id streaming_count
##       <int>     <dbl> <chr>             <int>
##  1    10000        32 f                    17
##  2    10001        43 l                    14
##  3    10002        41 f                    19
##  4    10003        30 f                    29
##  5    10004        38 h                    14
##  6    10005        40 k                    23
##  7    10006        38 l                    20
##  8    10007        27 m                    29
##  9    10008        44 a                    11
## 10    10009        49 n                    20
## # … with 90 more rows
# 특정 위치의 변수이름만 변경
names(temp3)[3] = "곡 아이디"
temp3
## # A tibble: 100 x 4
##    유저__id 유저__age `곡 아이디` streaming_count
##       <int>     <dbl> <chr>                 <int>
##  1    10000        32 f                        17
##  2    10001        43 l                        14
##  3    10002        41 f                        19
##  4    10003        30 f                        29
##  5    10004        38 h                        14
##  6    10005        40 k                        23
##  7    10006        38 l                        20
##  8    10007        27 m                        29
##  9    10008        44 a                        11
## 10    10009        49 n                        20
## # … with 90 more rows
반응형

'tidyverse' 카테고리의 다른 글

[R] 6. 날짜 및 시간 변수 (lubridate)  (3) 2021.07.05
[R] 5. mutate()  (0) 2021.07.05
[R] 3. 파이프 오퍼레이터 (%>%)  (0) 2021.07.05
[R] 2. tibble 데이터  (0) 2021.07.05
[R] 1. tidyverse 라이브러리와 tidy data  (0) 2021.07.05
TAGS.

Comments