- summarise()
summarise()는 data frame을 한 행으로 축소한다.
library(dplyr)
library(nycflights13)
summarise(flights, delay = mean(dep_delay, na.rm = TRUE))
- group_by()
summarise()함수는 group_by()와 함께 사용했을 때 유용하다. 그 사례를 보며 학습해보겠다.
by_day <- group_by(flights, year, month, day)
summarise(by_day, delay = mean(dep_delay, na.rm = TRUE))
코드 첫 줄에서 by_day라는 객체에 flights 데이터의 year, month, day를 그룹화했고, summarise에서 by_day를 기준으로 delay 변수에 대하여 dep_delay값을 결측치를 무시함(na.rm = TRUE)과 동시에 평균값을 구하도록 명령하였기 때문에, 연, 월, 일별 delay의 평균값을 반환 받을 수 있다.
class(flights)
class(by_day)
dim(flights)
dim(by_day)
flights dataset의 class를 보면 tibble data frame이고 flights dataset에 대해서 group_by한 객체는 tibble data frame이자 grouped_df라는 것을 확인할 수 있다. 하지만 dim()으로 shape을 파악했을 때 데이터에는 size에는 변형을 주지 않은 것을 확인할 수 있다.
즉, 이전에 우리가 group_by()함수로 특정 변수들을 묶어서 반환된 결과에 특정 변수만이 보인다고 하더라도 보이지 않을 뿐 데이터가 삭제됐거나 변형되지 않았다는 것을 의미한다.
각 위치별 average delay와 distance 간 관계를 파악해보자.
by_dest <- group_by(flights, dest)
delay <- summarise(by_dest,
count = n(),
dist = mean(distance, na.rm = TRUE),
delay = mean(arr_delay, na.rm = TRUE))
delay <- filter(delay, count > 20)
count가 너무 작은 비행은 이벤트성 비행이라고 생각하여 표본에서 제외하기로 하여 count > 20이라는 조건을 추가로 부여했다.
이제 앞에 배운 ggplot패키지를 사용해 시각화하여 결과를 인지해보자.
library(ggplot2)
ggplot(delay, aes(dist, delay)) +
geom_point(aes(size =count), alpha = 0.5) +
geom_smooth(se = FALSE)
위에서 보인 데이터 시각화까지의 3step을 요약하자면
1. flights 데이터를 destination을 기준으로 그룹핑한다.
2. distance, average delay, number of flights로 summarise()함수를 사용한다.
3. filter()함수를 사용하여 noisy point라고 생각되는 count < 20 데이터를 걸러낸다.
- %>%(pipe operation)
다음은 pipe 연산자를 사용하여 multiple operation을 조합해보자. 이게 뭔 말인지 모르겠다면 코드로 이해하면 좋을 것이다.
우리가 직전에 한땀 한땀 객체화해서 구현한 코드를 파이프연산을 사용하면 어떻게 효율적으로 보기 좋게 만들 수 있는지를 중점으로 이해본다면 도움이 될 것이다.
delays <- flights %>%
group_by(dest) %>%
summarise(
count = n(),
dist = mean(distance, na.rm = TRUE),
delay = mean(arr_delay, na.rm = TRUE)
) %>%
filter(count > 20, dest != 'HNL')
앞에서는 코드를 연결하기 위해 3번의 객체화가 있었지만 파이프 연산을 사용하면 객체화 한 번에 모든 코드를 연결하는 것이 가능해지고 이는 코드에 대한 가독성을 올려준다.
- na.rm =
이번 챕터에서 앞선 예제 코드에서 확인했던 na.rm 인자에 대해서 아직 모르는 사람이 있어서 한 번 짚고 넘어가도록 하겠다.
flights %>%
group_by(year, month, day) %>%
summarise(mean = mean(dep_delay))
na.rm arugument없이 summarise()를 했을 때, dep_delay와 같은 input값으로 들어가는 value중 하나라도 NA인 값이 있으면 summarise()함수는 기본적으로 NA연산의 결과인 NA값을 반환한다. 하지만 우리는 이런 결과를 원하지 않기 때문에, na.rm argument를 사용한다.
flights %>%
group_by(year, month, day) %>%
summarise(mean = mean(dep_delay, na.rm = TRUE))
모든 집계함수는 na.rm argument를 갖으며, missing values가 계산되기 전에 제거해주는 역할을 한다고 할 수 있겠다.
flights %>%
group_by(year, month, day) %>%
summarise(mean = mean(dep_delay))
이렇게 NA값으로 인해서 원하는 결과를 얻지 못할 때, 우리는 NA값을 제거함으로써 문제를 해결할 수 있다.
not_cancelled <- flights %>%
filter(!is.na(dep_delay), !is.na(arr_delay))
not_cancelled %>%
group_by(year, month, day) %>%
summarise(mean = mean(dep_delay))
- count()
우리는 많은 상황에서 다양한 집계를 필요한다. 이럴 때 추가로 n()함수를 이용한 count를 결과에 덧붙이는 것은 항상 좋은 영향을 가져올 수 있다. NA값이 아닌 value의 count를 집계하는 sum(!is.na()) 또한 기억해두면 좋을 것이다.
n()과 sum(!is.na())함수를 이용하여 결과를 체크하면 우리가 너무 적은 양의 데이터(NA값이 많은 경우)를 가지고 결론을 내린 것은 아닌가 확인이 가능하다.
delays <- not_cancelled %>%
group_by(tailnum) %>%
summarise(
delay = mean(arr_delay, na.rm = TRUE),
n= n()
)
ggplot(delays, aes(n, delay)) +
geom_point(alpha = 1/10)
위 코드의 결과인 다음의 point graph에서 우리는 개체수가 적은 data에 대해서 filter()함수를 사용함으로써 더 나은 시각화를 시도할 수 있다.
delays %>%
filter(n > 25) %>%
ggplot(aes(n, delay)) +
geom_point(alpha = 1/10)
filter를 사용한 plot에는 작은 수의 극단적인 값을 가진 데이터를 배제했기때문에, 패턴을 읽는데 더 용이하다고 할 수 있겠다.
때때로 집계함수를 사용할 때 logical subsetting은 중요하다. 어떤 경우에 사용되는지 예제로 공부해보자.
not_cancelled %>%
group_by(year, month, day) %>%
summarise(
avg_delay1 = mean(arr_delay),
avg_delay2 = mean(arr_delay[arr_delay > 0])
)
avg_delay2의 경우 arr_delay >0이라는 logical subsetting을 해주어 positive delay값에 대한 average를 구할 수 있도록 한다. delay가 negative를 기록했다면 early arrive를 뜻할텐데 이 데이터가 지각한 데이터의 평균을 구하는데 있어서 방해요소가 되는 경우가 때에 따라 존재할 수 있다.
- sd(), IQR(), mad()
spread를 측정하는 함수도 있다. 대표적으로 표준편차만 한 번 구해보겠다.
- sd() 표준편차
- IQR() 사분위수 범위
- mad() 중간 절대 편차
not_cancelled %>%
group_by(dest) %>%
summarise(distance_sd = sd(distance)) %>%
arrange(desc(distance_sd))
- min(), quantile(), max()
rank을 측정하는 다양한 함수도 있다.
- min()
- quantile(x, 0.25)
- max()
quantile()함수로 구할 수 있는 분위수는 중앙값의 일반화 개념이다. 예를 들어 quantile(x, 0.25)의 값은 x 데이터에서 하위25퍼센트 값을 반환한다.
not_cancelled %>%
group_by(year, month, day) %>%
summarise(
first = min(dep_time),
last = max(dep_time)
)
- first(), last(), nth()
위치를 포커스로 값을 반환하는 함수도 있다.
- first()
- last()
- nth()
first(x)함수는 x[1]
last(x)함수는 x[length(x)]
nth(x,n)함수는 x[n] 과 유사하게 작동한다.
- n(), sum(!is.na()), n_distinct()
앞서 n()으로 groupping한 data에 대한 size을 구할 수 있었다. 추가로 non-missing values의 개수를 반환하는 sum(!is.na())도 배웠다. 특정 변수가 갖고 있는 고유값. unique당 개수를 확인하기 위해서는 n_distinct() argument를 사용할 수 있다.
not_cancelled %>%
group_by(dest) %>%
summarise(carriers = n_distinct(carrier)) %>%
arrange(desc(carriers))
목적지를 ATL로 하는 carrier의 고유값 개수는 7개다. 즉, 7개의 항공사가 목적지를 ATL로 하는 flight 갖고 있다는 것으로 해석할 수 있다.
- sum(x > 10), mean(y == 0) 비율과 조건에 대한 Count
5시전에 이륙한 비행기의 개수는 어떻게 찾을 수 있을까?
head(flights$dep_time < 500)
dep_time < 500의 결과는 logical value를 반환했다는 것을 알고 있다.
not_cancelled %>%
group_by(year, month, day) %>%
summarise(n_early = sum(dep_time < 500))
때문에 위의 결과는 summarise()를 통해 n_early변수에 dep_time < 500이 TRUE인 값의 개수를 반환할 것이고,
우리는 몇 개의 항공편이 5시 이전에 이륙했다는 것을 확인할 수 있다.
not_cancelled %>%
group_by(year, month, day) %>%
summarise(hour_prop = mean(arr_delay > 60))
비슷한 개념으로 1으로 취급되는 TRUE와 0으로 취급되는 FALSE값으로 인해, 우리는 도착 지연이 60분보다 큰 비행의 비율 또한 확인할 수 있다.
group_by()와 summarise()의 인자함수를 사용하여 다양하게 응용이 가능하다. 예제로 몇 개를 건드려보자.
daily <- group_by(flights, year, month, day)
per_day <- summarise(daily, flights = n())
per_day
위의 코드로 일별 비행 횟수를 확인할 수 있을 것이다.
per_month <- summarise(per_day, flights = sum(flights))
나에겐 당연한 느낌이지만 summarise()의 결과에 대해서 상위 열로 집계함수를 사용하면 열축소와 동시에 해당 상위기준의 집계를 반환받을 수 있다. 한 번더 하면 요렇게 될 것이다.
per_year <- summarise(per_month, flights = sum(flights))
- remove grouping
이걸 사용할 일이 있을진 모르겠으나, 그룹핑한 것을 제거하는 것을 배워보자.
daily
기존에 daily는 flights 데이터셋에서 year month, day를 그룹핑한 것이다. 이 group data를 제거해보자.
daily %>%
ungroup() %>%
summarise(flights = n())
그룹화되었던 year, month, day에 대한 그룹핑이 해체되고, 해당 값을 통해서 summarise()함수에 n()인자를 사용했을 땐, 전체 데이터 flights의 개수를 반환한다.
'R > Exploratory data analysis' 카테고리의 다른 글
R for Data Science::tibbles and data import (0) | 2023.06.08 |
---|---|
R for Data Science::transformation 3 ; Exercise 5.7 (1) | 2023.06.08 |
R for Data Science::transformation (0) | 2023.06.06 |
R for Data Science::scales 2 (0) | 2023.05.24 |
ggplot2::Position scales and axes (0) | 2023.05.21 |