-
R과 데이터프레임(3)R 이모저모 2019. 3. 24. 21:41
R에서의 데이터프레임 기초와 활용방법(3)
저번 포스팅에선 R에서 dplyr패키지를 이용해서 데이터프레임을 다루는 방법들을 소개하고, 간단한 문제풀이 느낌의 리뷰도 해보았습니다. 이번 포스팅에선 dplyr 관련 포스팅을 마무리 지어보도록 하겠습니다.
1. dplyr과 데이터 병합
데이터 분석 업무를 하다 보면 여러 DB에 흩어져 있는 데이터들 중 쓸모 있는 것들을 선별해서 하나의 데이터 셋으로 병합해야 하는 경우가 많습니다. dplyr을 쓰지 않는다면 merge를 사용하는 경우가 많지만 merge는 데이터 크기가 크고 키 값이 복잡해지면 처리속도 때문에 마냥 기다려 주기가 버겁습니다. 이 점을 해결하기 위해 dplyr에선 데이터 병합을 위한 함수로 _join 시리즈를 제공하고 있습니다.
표의 설명들을 보면 굉장히 어렵고 복잡한 문법들을 쓰는 것 같지만, 예제를 통해 확인해보면 간단한 함수라는 걸 알 수 있습니다. 구글에 검색해보시면 이리저리 도식화해서 표현한 그림들이 보이지만 저는 바로 코드로 넘어가도록 하겠습니다.
이 두 개의 데이터 프레임으로 위의 함수들을 사용해보도록 하겠습니다. 보시면 a라는 공통된 key를 가지고 있고 df1은 1에서 10, df2 는 1,3…19로 서로 일치하는 값도 있고 그렇지 않은 부분도 있다는 걸 알 수 있습니다.
먼저 inner_join입니다. 위의 설명대로 inner join은 x에서 y와 일치하는 행들을 골라내고, 거기에 y의 나머지(by로 지정한 key값 제외) 열을 붙입니다. 모든 join 함수들은 (x, y, by) 순으로 인풋을 받는다는걸 명심하세요!
공통된 a값인 1,3,5,7,9가 선택되고 x가 df1인 경우에는 abc순으로, df2일때는 acb순으로 열들이 합쳐짐을 확인할 수 있습니다.
left_join은 말 그대로 왼쪽으로 들어오는 함수입니다. 왼쪽인 x를 우대해서 전부 보여주고, 그 조건에 맞는 y들만 옆에 따로 보여주는 것입니다. 그렇다면 right_join은 그 반대인 y를 기준으로 하겠죠?
첫 케이스에서 df2의 a에는 2,4,6,8,10이 없기에 join하는 c열의 해당 부분은 NA처리되있음을 볼 수 있습니다. 반대로 right_join을 하면 df1의 열인 b가 NA처리되어 있습니다.
Left와 right join은 서로 x나 y를 기준으로 남는 쪽을 필터링 하는 함수인데 반해, full_join은 통 크게 전부 다 담고 빈 값들은 NA처리하는 함수입니다. 반대로 semi_join은 조건에 맞는 x만 뱉어내고 끝인, 일종의 filter 함수의 join 버전입니다.
anti_join은 semi_join의 반대버전으로 키값이 맞는 행을 제외한 나머지들만 보여주는 함수입니다. duplicated()함수를 이용해서 중복을 제거하는 과정을 join으로 한다고 생각하시면 편할 것 같습니다.
다음으로 nest_join은 조금 난해한 형태를 가지고 있습니다. 이 함수는 x를 가져오고 y의 열을 붙이는 대신 y라는 list를 x의 옆에 붙입니다. 그리고 해당 list는 하나하나가 y로 사용된 데이터 중 key값이 맞는 row의 정보를 가지고 있습니다.
좀더 명확한 차이를 위해 df2에 d,e칼럼을 추가했습니다
위 그림으로는 어떤 구조인지 아직 명확하지 않아 보입니다. 그래서 해당 객체의 첫 행을 불러와보겠습니다.
첫 행의 3번째 열을 보니 c,d,e라는 df2의 칼럼 3개를 포함하고 있는 모습이 보입니다. 이러한 방식으로 nest_join은 키값이 맞으면 해당 데이터를, 맞지 않는다면 빈 리스트를 생성하게 됩니다. 이 nest join 객체는 tidyr의 unnest(list형식의 칼럼을 풀어주는 함수)를 활용하면 아래 그림처럼 inner_join을 한 결과로 바꿀 수 있습니다(결과가 없는 list는 drop되기 때문). 개인적으로 저는 분석을 하면서 nest_join을 쓴 적은 없었던 만큼 썩 효율적인 함수라고는 얘기할 수 없을 것 같습니다.
2. group_by와 데이터 요약
데이터 전처리에서는 적절한 필터링과 생성, 규칙에 따른 변형과 병합도 중요하지만 앞의 과정을 위해선 데이터를 파악하는 것이 선행되어야 합니다. Scatter plot을 그려 보거나 여러 통계치를 확인해보는 것이 그 방법 중 하나이며, 이때 group_by와 summarise 같은 함수들이 요긴하게 쓰입니다.
먼저 group_by가 무엇인지 알아봅시다. sql을 다루던 분이시라면 아마 익숙할 group_by는 특정 변수를 기준으로 데이터를 묶어주는 함수입니다. 예를 들어, datasets 패키지의 iris 데이터를 Species 변수 기준으로 묶으면 다음과 같이 출력이 됩니다.
tibble 형식 데이터도 중요한 이야깃거리지만 일단 넘어가겠습니다
위의 Groups : Species [3]는 해당 데이터가 Species란 변수로 묶였으며, 3개의 그룹으로 나뉘었다는 것을 보여줍니다. 이렇게 묶인 데이터는 dplyr의 여러 함수를 적용해 볼 수 있습니다. 만약 각 Species마다 샘플데이터를 2개씩 뽑아내고 싶다면 sample_n(2)를 사용하면 됩니다.
또 그룹마다 중복되지 않는 값만 찾아내고 싶다면 group_by 후 distinct를 적용하면 쉽게 할 수 있습니다.
그렇다면 데이터 요약을 볼 땐 어떻게 해야 할까요? summarise 함수는 여러 값들을 지정한 규칙에 맞게 요약해서 보여주는 기능을 가진 함수로, 단독으로도 유용하지만 group_by와 함께 사용하면 더 요긴하게 사용할 수 있습니다. Iris 데이터에서 Species별 Sepal.Length의 평균을 구해보겠습니다.
지정한 smean이란 변수명으로 새로운 요약 데이터가 생성됨을 볼 수 있습니다. Species를 제외한 다른 변수들이 전부 수치형 변수기에, summarise 함수의 확장형인 summarise_all을 활용해 평균을 구해보겠습니다.
그렇다면 summarise_all에서 여러 통계치, 예를 들어 평균과 분산을 동시에 계산하게 하고 싶다면 어떻게 해야 할까요? 이 경우 해당 함수의 .funs인풋에 list형식으로 여러 함수를 집어넣어주면 됩니다.
3. 마치며
이 포스팅에서 dplyr에 관한 기초 내용들을 마무리하며, R에서 데이터 프레임 형식의 데이터가 들어왔을 때 어떻게 다뤄볼지에 대한 부분을 마쳐볼까 합니다.
정리하자면,
1. 데이터 프레임은 행과 열로 이루어진 2차원 데이터 형식이다
2. 따라서 matrix와 동일한 대괄호 형태([,])로 indexing이 가능하며, 행과 열의 이름(rownames, colnames)로도 호출이 가능하다
3. rbind, cbind 또는 dplyr의 bind_rows, bind_cols 등을 통해서 행과 열을 붙이는 것이 가능하다. 단, 팩터 변수의 경우 변수의 level에 포함되지 않은 데이터를 넣으려 할 경우 오류가 발생한다
4. 기본함수로도 데이터프레임을 조작할 수 있으나, dplyr을 활용하면 데이터 변환, 추가, 병합 및 요약을 보다 쉽고 빠르게 처리할 수 있다
*다음 포스팅 주제에 관해서는 plot(+ggplot)이나 modeling관련(아마 xgboost) 등을 생각하고 있습니다.
[ 컨텐츠 코드 ]'R 이모저모' 카테고리의 다른 글
R과 병렬처리 (4) 2019.04.03 알고리즘 소개 : XGBoost (0) 2019.03.30 각양각색의 R 질문들과 풀이 (0) 2019.03.21 R과 데이터프레임(2) : dplyr (0) 2019.03.14 R과 데이터프레임 (1) (0) 2019.03.10