R - dplyr 을 통한 데이터 변형과 장점
본 포스팅에서는 tidyverse의 핵심 구성원이라고 할 수 있는 dplyr 을 통해 데이터를 변형하는 기본적인 방법을 basic R과 비교하여 설명하겠습니다
dplyr 의 5가지 핵심 함수
- -값을 기준으로 선택하라 (filter)
- -행을 재정렬하라 (arrange)
- -이름으로 변수를 선택하라 (select)
- -기존 변수들의 함수로 새로운 변수를 생성하라 (mutate)
- -많은 값을 하나의 요약값으로 합쳐라 (summarize)
사용할 데이터셋은 뉴욕시에서 2013년에 출발한 336,776 개의 모든 항공편이 포함된 데이터 (nycflights13 패키지의 flights 데이터셋)
library(nycflights13)
library(tidyverse)
nrow(flights)
#
Filter
jan <- flights[flights$month == 1 & flights$day == 1, ]
nrow(jan)
#
jan <- filter(flights, month == 1, day == 1)
nrow(jan)
#
- -출발 지연 시간이 120 미만인 항공편 고르기
- -여기서 basic R과 dplyr 의 차이가 드러나는데, basic R 의 경우 arr_delay 변수가 NA 인 경우, 모든 column이 NA인 row를 포함한 dataframe을 반환한다.
- -basic R의 경우 filter 를 구현할 때, NA 인 경우도 고려해야하므로, dplyr이 더 간단하게 원하는 목적을 달성할 수 있다고 볼 수 있다.
'under_two <- flights[(flights$arr_delay < 120), ]
nrow(under_two)
#
sum(is.na(under_two$arr_delay))
#
sum(is.na(under_two$dep_delay))
#
under_two <- filter(flights, arr_delay < 120)
nrow(under_two)
#
sum(is.na(under_two$arr_delay))
#
sum(is.na(under_two$dep_delay))
#
under_two <- flights[(flights$arr_delay < 120) & !is.na(flights$arr_delay), ]
nrow(under_two)
#
sum(is.na(under_two$arr_delay))
#
sum(is.na(under_two$dep_delay))
#
- -출발 혹은 도착에서 2시간 이상 지연되지 않은 항공편을 모두 찾기
- -이 때, 두 조건절이 모두 NA 인 경우, basic R 에서는 모든 컬럼이 NA 인 행을 dataframe에 추가하여 반환한다.
under_two <- flights[!((flights$arr_delay > 120) | (flights$dep_delay > 120)), ]
nrow(under_two)
#
sum(is.na(under_two$arr_delay))
#
sum(is.na(under_two$dep_delay))
#
under_two <- filter(flights, !(arr_delay > 120 | dep_delay > 120))
nrow(under_two)
#
sum(is.na(under_two$arr_delay))
#
sum(is.na(under_two$dep_delay))
#
- -출발 시간, 도착 시간 둘 중 하나가 2시간 이상 지연된 항공편을 모두 찾기
over_two <- flights[((flights$arr_delay > 120) | (flights$dep_delay > 120)), ]
nrow(over_two)
#
sum(is.na(over_two$arr_delay))
#
sum(is.na(over_two$dep_delay))
#
over_two <- filter(flights, ((arr_delay > 120) | (dep_delay > 120)))
nrow(over_two)
#
sum(is.na(over_two$arr_delay))
#
sum(is.na(over_two$dep_delay))
#
- -일반 데이터 프레임의 문제점. 조건이 NA인 경우 모든 컬럼이 NA인 행이 출력된다.
- -dplyr 의 filter 는 이러한 문제 없이 원하는 결과를 출력해준다.
flights[NA,]
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
Arrange
- -날짜별로 오름차순 정렬하기
- -정렬의 경우, 마찬가지로 dplyr 이 훨씬 직관적이다.
arranged <- flights[order(flights$year, flights$month, flights$day),]
head(arranged)
## # A tibble: 6 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 1 517 515 2 830
## 2 2013 1 1 533 529 4 850
## 3 2013 1 1 542 540 2 923
## 4 2013 1 1 544 545 -1 1004
## 5 2013 1 1 554 600 -6 812
## 6 2013 1 1 554 558 -4 740
## # ... with 12 more variables: sched_arr_time <int>, 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>
arranged <- arrange(flights, year, month, day)
head(arranged)
## # A tibble: 6 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 1 517 515 2 830
## 2 2013 1 1 533 529 4 850
## 3 2013 1 1 542 540 2 923
## 4 2013 1 1 544 545 -1 1004
## 5 2013 1 1 554 600 -6 812
## 6 2013 1 1 554 558 -4 740
## # ... with 12 more variables: sched_arr_time <int>, 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>
desc <- flights[order(-flights$arr_delay),]
head(desc)
## # A tibble: 6 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 9 641 900 1301 1242
## 2 2013 6 15 1432 1935 1137 1607
## 3 2013 1 10 1121 1635 1126 1239
## 4 2013 9 20 1139 1845 1014 1457
## 5 2013 7 22 845 1600 1005 1044
## 6 2013 4 10 1100 1900 960 1342
## # ... with 12 more variables: sched_arr_time <int>, 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>
desc <- arrange(flights, desc(arr_delay))
head(desc)
## # A tibble: 6 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 9 641 900 1301 1242
## 2 2013 6 15 1432 1935 1137 1607
## 3 2013 1 10 1121 1635 1126 1239
## 4 2013 9 20 1139 1845 1014 1457
## 5 2013 7 22 845 1600 1005 1044
## 6 2013 4 10 1100 1900 960 1342
## # ... with 12 more variables: sched_arr_time <int>, 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>
Select
- -select 함수는 열을 선택하는 함수이다.
selected <- flights[, c("year", "month", "day")]
head(selected)
#
#
#
#
#
#
#
#
#
selected <- select(flights, year, month, day)
head(selected)
#
#
#
#
#
#
#
#
#
Mutate
- -mutate는 새로운 열을 추가하는 함수이다.
- -dplyr은 select를 할 때 end_with function 을 통해 해당 문자로 끝나는 컬럼을 선택할 수도 있고, : 를 통해 두 문자의 사이에 있는 컬럼을 선택할 수도 있다.
- -따라서 열 선택에 있어 더욱 많은 기능을 짧은 코드로 구현할 수 있으며, 새로운 변수를 정의할 때도, 한 문장에 정의할 수 있어 코드의 길이가 짧아지고 가독성이 좋아진다.
flights_sml <- select(flights, year:day, ends_with("delay"), distance, air_time)
flights_sml_new <- mutate(flights_sml, gain=arr_delay - dep_delay, speed = distance / air_time * 60)
nrow(flights_sml_new)
#
- -새 변수만을 남기고 싶다면 transmute를 사용한다.
flights_transmute <- transmute(flights, gain = arr_delay - dep_delay,
hours = air_time / 60,
gain_per_hour = gain / hours)
nrow(flights_transmute)
#
Summarize
summarize(flights, delay = mean(dep_delay, na.rm = TRUE), delay_sd = sd(dep_delay, na.rm=TRUE))
#
#
#
#
- -summarize 함수는 group_by (dplyr 에서 제공하는 함수) 와 보통 함께 사용하는 경우가 많다. 이것은 dplyr 의 이전 버전이라고 할 수 있는 plyr에서도 잘 구현되어 있었는데, dplyr 이 되면서 조금 더 이해하기 쉽게 문법이 바뀌었다.
by_day <- group_by(flights, year, month, day)
summarize(by_day, delay = mean(dep_delay, na.rm = TRUE))
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
파이프로 여러 작업 결합하기
- -파이프 명령어인 %>% 를 통해 dplyr 을 통한 데이터 변형 작업을 더욱 직관적이고, 가독성있게 수행할 수 있다.
- -각 위치에 대해 거리와 평균 지연 사이의 관계를 탐색하고 싶은 경우, 파이프를 사용하지 않는 방법은 아래와 같다.
by_dest <- group_by(flights, dest)
delay <- summarize(by_dest, count = n(),
dist = mean(distance, na.rm = TRUE),
delay = mean(arr_delay, na.rm = TRUE))
delay <- filter(delay, count > 20, dest != "HNL")
ggplot(data = delay, mapping = aes(x = dist, y = delay)) +
geom_point(aes(size = count), alpha = 1/3) +
geom_smooth(se=FALSE)
#

- -파이프를 사용하면?
- -아래와 같이 임시 변수를 따로 놓지 않으면서 단 한 번의 logic 으로 똑같은 목적을 달성할 수 있다!
- -이러한 파이프 방법은 일련의 데이터 변형 작업을 의미 단위로 묶어 코드가 모듈화가 되기 때문에 유저가 코드를 쉽게 구현하고 이해할 수 있도록 도와준다.
- -다만 파이프가 너무 길어지면 유용한 임시변수를 두는 방법이 효율적일 수 있다.
delay <- flights %>%
group_by(dest) %>%
summarize(count = n(),
dist = mean(distance, na.rm=TRUE),
delay = mean(arr_delay, na.rm=TRUE)
) %>%
filter(count > 20, dest != "HNL")
ggplot(data = delay, mapping = aes(x = dist, y = delay)) +
geom_point(aes(size = count), alpha = 1/3) +
geom_smooth(se=FALSE)
#
