dplyr, 데이터프레임 손쉽게 다루기

{dplyr}패키지는 정말 완소 패키지입니다. 데이터프레임의 슬라이싱이나 select 함수 등이 좋은 결과를 보여주지만 직관적이지 않고 복잡합니다. {plyr}이 대안일 수 있습니다만 느립니다. 속 터지지요. C++를 기반으로 작성된 {dplyr}은 상당히 괜찮은 data handling 기능을 제공합니다. 반드시 알아야 하겠습니다.

설치를 하기 위해

> install.packages("dplyr")

이제 패키지를 부릅니다.

> library(dplyr)

이번 포스트는 hflights 데이터를 사용하겠습니다.

> library(hflights)
> dim(hflights)
[1] 227496     21

21개의 변수에 22만개의 행 데이터가 있습니다.

다루기 편하도록 data.frame객체를 {dplyr}의 tbl_df 객체로 변환하겠습니다.

> hflights_df <- tbl_df(hflights)
> hflights_df
Source: local data frame [227,496 x 21]
....

tbl_df 객체는 출력에서 이점이 있습니다. 22만개를 다 보여주는 대신 각종 요약 정보와 데이터의 일부를 간편하게 출력합니다. 직접 해 보세요.

데이터의 일부를 추출


행 데이터 중 일부를 추출하는 전통적인 방법은

> df[condition,]
이렇게 하여 condition의 TRUE, FALSE 벡터를 이용하는 것입니다. 편리하지만 참 이해하기 어려운 표현이기도 합니다.

{dplyr}의 filter()함수와 chaining 연산자(%>%)를 이용해서 간단하게 처리합니다.


> hflights_df %>% filter(Month==1,DayofMonth==1)
Source: local data frame [552 x 21]

   Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum TailNum ActualElapsedTime AirTime ArrDelay DepDelay Origin Dest Distance
1  2011     1          1         6    1400    1500            AA       428  N576AA                60      40      -10        0    IAH  DFW      224
2  2011     1          1         6     728     840            AA       460  N520AA                72      41        5        8    IAH  DFW      224
3  2011     1          1         6    1631    1736            AA      1121  N4WVAA                65      37       -9        1    IAH  DFW      224
4  2011     1          1         6    1756    2112            AA      1294  N3DGAA               136     113       -3        1    IAH  MIA      964

위의 예는 hflights_df를 chaining으로 걸어 filter()함수로 데이터를 넘겨주고 있습니다.
이를 받아 filter는 두 개의 조건을 &(혹은 ,)로 엮어 해당되는 행 데이터를 뽑습니다.
예에서 조건은 Month == 1 & DayofMonth == 1입니다.

조건은 or(즉 |)도 가능하고 not(!=)도 가능합니다.

> hflights_df %>% filter(Month == 1 | Month == 2)
Source: local data frame [36,038 x 21]

   Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum TailNum ActualElapsedTime AirTime ArrDelay DepDelay Origin Dest Distance
1  2011     1          1         6    1400    1500            AA       428  N576AA                60      40      -10        0    IAH  DFW      224
2  2011     1          2         7    1401    1501            AA       428  N557AA                60      45       -9        1    IAH  DFW      224
3  2011     1          3         1    1352    1502            AA       428  N541AA                70      48       -8       -8    IAH  DFW      224


Chaining 연산자를 쓰지 않으면
> filter.result <- filter(hflights_df, Month==1 | Month==2)
혹은 바로 결과를 보고 싶다면
(> filter.result <- filter(hflights_df, Month==1 | Month==2))


정렬

데이터를 R의 기본 함수만으로 정렬(ordering)하려면 꽤나 번거롭습니다. {dplyr}은 arrange()로 간편하게 데이터프레임을 정렬합니다.


> hflights_df %>% arrange(DayofMonth)
Source: local data frame [227,496 x 21]

   Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum TailNum ActualElapsedTime AirTime ArrDelay DepDelay Origin Dest Distance
1  2011     1          1         6    1400    1500            AA       428  N576AA                60      40      -10        0    IAH  DFW      224
2  2011     1          1         6     728     840            AA       460  N520AA                72      41        5        8    IAH  DFW      224
3  2011     1          1         6    1631    1736            AA      1121  N4WVAA                65      37       -9        1    IAH  DFW      224
4  2011     1          1         6    1756    2112            AA      1294  N3DGAA               136     113       -3        1    IAH  MIA      964

기본은 내림차순이지만 desc()함수로 오름차순 정리도 할 수 있습니다.

> hflights_df %>% arrange(desc(DayofMonth))
Source: local data frame [227,496 x 21]

   Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum TailNum ActualElapsedTime AirTime ArrDelay DepDelay Origin Dest Distance
1  2011     1         31         1    1441    1553            AA       428  N505AA                72      39       43       41    IAH  DFW      224
2  2011     1         31         1     718     816            AA       460  N493AA                58      40      -19       -2    IAH  DFW      224
3  2011     1         31         1    1954    2105            AA       533  N477AA                71      38      -15      -11    IAH  DFW      224
4  2011     1         31         1    1656    1758            AA      1121  N455AA                62      41       13       26    IAH  DFW      224

만약 여러 값들이 주어지면 먼저 주어진 것부터 정렬합니다.

arrange(Year,DayofMonth) 이면 Year로 정렬된 결과에 대해 DayofMonth가 적용됩니다.

부분 데이터프레임 추출

데이터에서 부분값을 선택하는 함수는 select()입니다.

> hflights_df %>% select(Year,Month,DayOfWeek)
Source: local data frame [227,496 x 3]

     Year Month DayOfWeek
5424 2011     1         6
5425 2011     1         7
5426 2011     1         1
5427 2011     1         2
5428 2011     1         3
5429 2011     1         4

어떤 값에서 어떤 값까지 포함된 것들을 다 뽑으려면 : 연산자를 씁니다.


> hflights_df %>% select(Year:DayOfWeek)
Source: local data frame [227,496 x 4]

     Year Month DayofMonth DayOfWeek
5424 2011     1          1         6
5425 2011     1          2         7
5426 2011     1          3         1
5427 2011     1          4         2
5428 2011     1          5         3

제외하려면 - 연산자를 씁니다. 아래에서 Year부터 DayOfWeek까지가 제외됩니다.

> hflights_df %>% select(-Year:DayOfWeek)
Source: local data frame [227,496 x 3]

     Month DayofMonth DayOfWeek
5424     1          1         6
5425     1          2         7
5426     1          3         1
5427     1          4         2
5428     1          5         3
5429     1          6         4

새로운 데이터 추가


데이터프레임의 어떤 데이터를 바탕으로 새로운 데이터를 만드려면 두 단계를 거칩니다.
1) 계산
2) cbind()

{dplyr}은 이것을 한 단계로 줄이고 속도를 높입니다. 함수 mutate()를 써서 해결합니다. 출력 결과물 중 ArrDelay, DepDelay, gain만 필요하다고 가정해서 두번의 chaining을 해봅시다. 이처럼 {dplyr}은 연속으로 chaining할 수 있습니다.

> hflights_df %>% mutate(gain=ArrDelay-DepDelay) %>% select(ArrDelay,DepDelay,gain)
Source: local data frame [227,496 x 3]

   ArrDelay DepDelay gain
1       -10        0  -10
2        -9        1  -10
3        -8       -8    0
4         3        3    0
5        -3        5   -8
6        -7       -1   -6

그룹연산

{dplyr}의 가치를 크게 높이는 기능은 바로 그룹 연산입니다. group_by()로 tbl_df 객체에 그룹 인덱싱을 합니다. 전통적인 R 교과서를 보면 split과 do.call 함수로 비슷한 일을 합니다만 전혀 도움이 안됩니다. 데이터가 작을 때나 쓸 수 있지 조금만 사이즈가 커도 컴퓨터가 뻗기 쉽습니다. {plyr}도 비슷한 기능을 제공합니다만 {dplyr}과는 비교도 안될 정도로 번거롭습니다.

그룹 지정을 위해 group_by() 객체를 만듭니다.

> planes <- group_by(hflights_df,TailNum)
> planes
Source: local data frame [227,496 x 21]
Groups: TailNum

     Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum TailNum ActualElapsedTime AirTime ArrDelay DepDelay Origin Dest
5424 2011     1          1         6    1400    1500            AA       428  N576AA                60      40      -10        0    IAH  DFW
5425 2011     1          2         7    1401    1501            AA       428  N557AA                60      45       -9        1    IAH  DFW

위 결과를 보면 Groups라는 항목이 새로 추가된 것을 확인합니다.

각 그룹에 대한 요약값 출력을 수행하기 위해 summarise()함수를 사용합시다.

> delay <- summarise(planes,
+                    count = n(),
+                    dist = mean(Distance, na.rm = TRUE),
+                    delay = mean(ArrDelay, na.rm = TRUE))
> delay
Source: local data frame [3,320 x 4]

   TailNum count      dist     delay
1            795  938.7157       NaN
2   N0EGMQ    40 1095.2500  1.918919
3   N10156   317  801.7192  8.199357
4   N10575    94  631.5319 18.148936
5   N11106   308  774.9805 10.101639
6   N11107   345  768.1130  8.052786
7   N11109   331  772.4532 10.280000

물론 chaining으로 이를 표현하면

delay <- planes %>% summarise(count=n(), ... ) 이렇게 쓸 수 있겠지요.

함수 n()은 그룹내 데이터의 개수를 구하는 C++ 함수입니다.

> destinations <- group_by(hflights_df, Dest)
> summarise(destinations,
+           planes = n_distinct(TailNum),
+           flights = n()
+ )
Source: local data frame [116 x 3]

   Dest planes flights
1   ABQ    716    2812
2   AEX    215     724
3   AGS      1       1
4   AMA    158    1297

n_distinct()는 unique value의 개수를 구합니다.

복수의 기준을 적용하여 그룹을 하는 이유는 주로 롤업(roll-up) 계산을 위해서입니다.

> daily <- group_by(hflights_df, Year, Month, DayofMonth)
> (per_day   <- summarise(daily, flights = n()))
Source: local data frame [365 x 4]
Groups: Year, Month

   Year Month DayofMonth flights
1  2011     1          1     552
2  2011     1          2     678
3  2011     1          3     702
4  2011     1          4     583
5  2011     1          5     590

> (per_month <- summarise(per_day, flights = sum(flights)))
Source: local data frame [12 x 3]
Groups: Year

   Year Month flights
1  2011     1   18910
2  2011     2   17128
3  2011     3   19470
4  2011     4   18593
5  2011     5   19172

> (per_year  <- summarise(per_month, flights = sum(flights)))
Source: local data frame [1 x 2]

  Year flights
1 2011  227496


댓글

  1. 교수님 항상 좋은 강의 및 자료 감사드립니다!

    포스팅 해 두신 자료들 유용하게 보고 있습니다. 특히 {dplyr}은 익혀두니 정말 유용하네요 ㅎㅎ

    앞으로 올라올 자료들도 기대하고 있겠습니다!!

    답글삭제
    답글
    1. 강충한씨. 유용하다니 다행입니다.

      삭제

댓글 쓰기

이 블로그의 인기 게시물

Bradley-Terry Model: paired comparison models

xlwings tutorial - 데이터 계산하여 붙여 넣기

R에서 csv 파일 읽는 법