R 데이터 핸들링 dplyr

R – dplyr 설치

R 데이터 핸들링 관련 가장 많이 사용하는 dplyr 라이브러리를 중심으로 이야기하고자 한다.

우선 dplyr 라이브러리가 R에 미리 설치가 되어있어야 한다.

따라서, library(dplyr) 명령어가 에러 발생한다면 install.packages(“dplyr”) 명령어를 통하여 라이브러리 설치를 하도록 하자. 추가로, 데이터 핸들링만 하고 그래프까지는 필요없다면 ggplot2 라이브러리는 로딩할 필요없지만, 본 실습에서 예제로 사용할 mpg 데이터셋이 ggplot2 라이브러리에 기본 내장된 데이터이므로 특별히 ggplot2 라이브러리도 같이 로딩하도록 하겠다.

View(mpg)


R – dplyr 기능

dplyr 라이브러리를 통하여 사용할 기능은 크게 5가지다.

1. 조건에 맞는 행 선택 filter()

2. 조건에 맞는 열 선택 select()

3. 정렬 arrange()

4. 새로운 열 추가 생성 mutate()

5. 그룹화하여 요약 summarise() 또는 summarize()


filter() 함수

먼저, 행(관측치)을 추출하는 filter() 함수에 대해 알아보자.

mpg 데이터에서 제조사가 현다이(hyundai) 아니, 현대자동차인 것만 찾는다고 가정해보자.

(참고로, 아래 두 구문은 같은 결과를 도출한다)


파이프라인

여기서 잠깐! 굳이 두 구문을 (각 결과가 같음에도) 병기한 것은, 파이프라인 용법을 알려주고자 함이다.

파이프라인(%>%)은 그 뉘앙스가 알려주듯이 파이프처럼 %>%를 매개로 계속 이어져 연결되어 있다는 뜻인데, 가장 앞에 mpg 데이터셋이 위치해있는 것에서 힌트를 얻을 수 있다. 즉, %>% 뒤에서부터 차례로 나오는 구문들은 모두 mpg 데이터셋을 기점으로 차례대로 수행된다는 뜻이다.

코딩을 한 줄만 해서 큰 차이가 없어보일 수 있다. 그래서 이번에는 조건을 2개로 늘려 예시해 보겠다. 현대자동차의 차종이 크게 소나타(sonata), 티뷰론(tiburon) 두 종류가 있는데, 이 중 model == “tiburon” 을 넣어 티뷰론만 재추출해 보겠다. 참고로, 아래 두 구문의 실행 결과는 서로 같다.

결과는 같지만, 구문 형태가 서로 사뭇 다르다. 그렇다. 파이프라인(%>%)을 사용하지 않을 경우, 전자와 같이 첫 번째 조건을 둔 채로 양쪽에 구문을 추가해야 한다. 즉, 왼쪽에 filter( 를 추가하였고, 오른쪽에 model == “tiburon”)을 추가하였다. 반면에, 후자의 경우 기존 조건 오른쪽에만 파이프라인(%>%)을 추가하고는 별도의 filter 구문을 추가하였다. 언뜻 보기에도 후자의 경우가 워크플로(workflow) 흐름을 이해하기 빠르고 작성하기도 쉬워보인다. 이러한 차이는 구문이 추가되면 될수록 더 커질 것이다. 따라서 이후부터는 파이프라인(%>%)을 적극 활용해 전개하고자 한다.


비교연산자

여기서 또 하나 짚고가야 할 것은, 바로 비교 연산자(Comparison Operator)이다.

상기 filter() 함수 내에 작성한 조건식에서 등호를 ‘==’로 표현한 것은 오타가 아니다. 즉, ‘==’가 아닌 ‘=’로 표현하면 다음 같은 에러가 발생한다.

친절하게도 R에서 혹시 ‘==’을 원하는데 ‘=’으로 잘못 입력한 것 아니냐는 예리한 질문을 한다. 뜨끔하다.

참고로, 각 비교연산자의 뜻은 다음과 같다.

== 같다

!= 다르다

> 크다

>= 크거나 같다

< 작다

<= 작거나 같다


논리연산자

기왕 하는 거 다 알아보자. 논리연산자(불 연산자)는 그리고(and)가 & 이고, 또는(or)이 | 이다. 산술 연산자의 경우는 사칙연산(+, -, *, /)은 아실테고, 특이한 산술연산자는 다음과 같다.

%/% 몫 (ex. 9%/%5 의 결과는 1)

%% 나머지 (ex. 9%%5 의 결과는 4)

^ 또는 ** 제곱 (ex. 9^2 또는 9**2의 결과는 81)

그렇다면 의문이 들 수도 있다. 이전에 filter() 함수를 두 번 사용하여 제조사가 ‘현대자동차’인 것을 추출한 다음, 모델이 ‘티뷰론’인 것을 찾았는데, 논리연산자를 사용해서 filter() 함수를 한 번만 써 찾을 수도 있지 않을까? 물론 가능하다. 결국 아래 3개의 구문은 같은 결과를 도출한다.

다만, 지금껏 별도 데이터셋 저장없이 하다보니 가공된 데이터가 화면 표시만 되고 휘발성 있는 것처럼 바로 사라지고 만다. 따라서 이후부터는 test 라는 데이터셋으로 별도 저장해가며 비교해보고자 한다.


select() 함수

위와 같이, test 데이터셋은 현대자동차 중 티뷰론 모델만을 가지고 있는 자료이다. 그런데 보아하니, class가 모두 subcompact(준중형)로 되어 있어 그다지 유용하지 않아보인다. 이번에는 select() 함수를 통해 열(변수)를 취사 선택하는 방법을 배워보자. select() 함수를 통해 원하는 열만 추출할 수도, 뺄 수도 있다. 빼는 방식을 써 보자.

빠졌다. 무엇이? 바로 class가. 즉, select() 함수에서 변수명 앞 마이너스(-)를 붙이면 해당 열만 빼고, 나머지 열은 그대로 가져간다. 반대로 select() 함수에 마이너스(-) 없이 변수명만 나열하면, 나열된 변수명만 (나열한 순서대로) 가져가고 나열되지 않은 변수명(열)은 제거된다.

(참고로, cty는 도시 연비drv는 구동방식( f – 전륜 구동, r – 후륜 구동, 4 – 4륜 구동)을 각각 의미한다)


arrange() 함수

이번엔 arrange() 함수를 사용하여, 이걸 다시 정렬해보자.

arrange(cty) 즉, cty를 기준으로 ‘오름차순’ 정렬하여 저장하라고 했더니, 그렇게 된 것이다. 이번엔 desc 구문을 이용해 ‘내림차순’ 정렬을 해 보자.

사실, arrange() 함수에는 조건을 여러 개 넣을 수도 있다. 자료를 ‘모든 현대자동차’로 다시 확대한 다음, year는 내림차순, cty는 오름차순으로 순서대로 넣어보자. 이 경우, 첫 번째 year를 기준으로 우선 정렬하되, year값이 같은 행은, 두 번째 cty를 기준으로 재정렬함을 알 수 있다.


mutate() 함수

이제 추가로 열(변수)를 생성할 수 있는 mutate() 함수에 대해 알아보자.

mutate() 함수는 임의값을 부여하거나, 기존 변수(열) 토대로 연산한 결과를 부여할 수 있는 등 자유로이 활용할 수 있다. 올해가 2021년이니까, now라는 열(변수)로 2021이라는 상수를 똑같이 부여해보자.

(참고로, 아래 두 구문은 같은 결과를 도출한다)

그럼 이번에는 각 차량의 연도(year)가 올해(now) 기준 얼마나 지났는지 즉, now – year 값을 계산하여 추가해보자.

age 열이 하나 더 추가되었음을 알 수 있다. 이번엔 좀 더 복잡한 연산을 해 보자. mean() 함수 통해 cty의 평균값을 구해 열을 추가해보자.

전체 cty 평균은 18.64286이라는 것을 알 수 있다. 그런데 연도(year)별로 cty를 따로 구하면 유의적 차이가 있지 않을까? 이 때 쓰는 것이 바로 그룹화 함수 group_by() 이다. 전에 dplyr의 파이프라인(%>%)은 차례대로 수행이 된다고 하였다. 이 말은즉슨, 앞서 산출된 결과가 다음 파이프라인(%>%) 함수로 전달되어 또 계산된다는 뜻이다. 따라서 그룹화하여 각 평균을 구하고자 한다면, 그 앞에 group_by() 함수를 통해 분류를 먼저 해 주어야 할 것이다.

보면, avg_cty가 일률적으로 18.64286인 것과 달리, year가 2008년인 경우 18.87500, year가 1999년인 경우 18.33333으로서 각각 그룹화되어 산출되었음을 알 수 있다.


group_by() 함수

이번에는 그룹별로 카운트를 해서 열을 추가해보자.

즉, year_c 열을 추가, 2008년 차량은 총 8대, 1999년 차량은 총 6대가 존재함을 알 수 있다. 이 외에도 여러가지 계산 관련 함수가 있는데, 주로 쓰이는 것을 열거하면 다음과 같다.

mean() 평균

median() 중앙값

n() 빈도

min() 최소값

max() 최대값

quantile() 분위값(1사분위, 2사분위, 3사분위)

sum() 합계

rank() 순위

range() 범위


summarise() 함수

마지막으로 summarise() 함수에 대해 알아보자. 지금까지 실습하는 과정에서 그다지 필요없는 열(변수)이 상당히 늘어났는데, 가끔은 간결하게 알고 싶은 함수만 나타났으면 하는 바램이 들 때가 있다. 이 때에 사용하는 것이 summarise() 함수이고, 동의어로서 summarize()로 철자를 s와 z를 바꿔써도 인식한다. 단어 뜻 그대로 필요한 변수만 따 와서 해당 변수로만 출력한다고 보면 된다. cty 별로 카운트한 열 cty_c가 mutate() 함수로 계산할 때와 summarise() 함수로 계산할 때가 어떻게 다른지는, 아래 결과를 비교해보면 알 수 있다.

즉, mutate() 함수로 cty_c를 생성하면 기존대로 마지막 열에 추가로 생성되지만, summarise() 함수로 생성할 경우에는 그룹화 기준 열 cty와 계산값 열 cty_c만 결과로 내놓음을 알 수 있다.

이로써 R의 대표적인 데이터 핸들링 라이브러리 dplyr에 대해 알아보았다. 사실 dplyr 말고도 비슷한 기능을 수행하는 라이브러리가 많이 있지만, 가장 널리 쓰이는 라이브러리이기 때문에 이를 기준으로 알아보았다. 이 정도라면 웬만한 데이터 가공은 충분히 할 수 있다고 생각하고, 더 고급 테크닉은 이를 기반으로 하여 응용하고 복합적으로 사용하는 정도라 보면 되겠다.


번외

위에서 filter() 함수 관련하여 여러 개의 조건을 거는 경우에 대해서 알아본 바 있다. 즉, mpg 데이터에서 제조사 여러 개를 지정해서 확인할 경우, 다음과 같은 방법으로 알 수 있다.

먼저, 제조사가 몇 개 있는지 카운트해 보겠다.

count() 함수를 이용하니 제조사가 총 15개인 것을 알 수 있다. 여기에서 jeep, land rover, lincoln 3개 제조사만 추출해 test 데이터셋을 만들려면 어떻게 해야 할까?

레퍼런스를 열심히 공부한 사람이라면 아래와 같이 작성할 것이다.

원하는대로 jeep, land rover, lincoln 3개의 제조사 데이터만 추출되었다. 하지만, 조건이 하나씩 늘어날 때마다 조건 전체를 일일이 타이핑하는 것은 코딩할 때뿐만 아니라, 나중에 유지보수할 때도 여간 번거로운 일이 아니다. 따라서 아래의 더 쉬운 방법을 추천해주고자 한다.

즉, %in% 구문을 사용할 경우, 이어지는 벡터 안의 조건들 각각이 또는( | )으로서 작용한다. 코딩하기도 편하고 눈에도 잘 들어온다. 하지만 코딩 전문이 길어지면 해당 조건을 수정할 경우, filter() 구문의 위치를 일일이 찾아야 하는 등 번거로운 과정이 따를 수 있다. 즉, 자주 수정/실행할 예정이라면 아예 별도 벡터를 눈에 잘 띄는 곳에서 (예를 들면, 최상단) 먼저 지정하고 이를 호출하는 것이 훨씬 편할 수 있음을 알아두자.

즉, 위와 같이 별도 임의 벡터(key)를 눈에 잘 띄는 곳에 따로 배치하여 이 부분만 수정한다면, 해당 filter() 구문이 어디에 있는지 스크롤해가며 찾는 수고를 덜 수 있다. 물론, 자주 수정하는 조건일 경우에 해당되고, 수정할 필요가 잘 없는 조건이라면 굳이 그럴 필요는 없을 것이다.

한편, select() 함수에 있어서도 좀 더 간편하게 열을 지정할 수 있는 방법이 있다.

starts_with(” “) ” “로 시작하는 열 일괄 선택

ends_with(” “) ” “로 끝나는 열 일괄 선택

num_range(” “, 1:9) ” “와 1:9 순차 선택 (ex. “a”, 1:9 인 경우, a1, a2, …  ,a9 선택)

everything() 따로 언급하지 않은 기존 열을 (기존 순서대로) 일괄 선택

contains(” “) ” ” 문구가 들어간 열 일괄 선택

matches(” “) [정규표현식]을 사용해 열 선택

예를 들어, 다음 같은 mpg 데이터셋에서 “m”으로 시작하는 열만 선택하고자 한다면 아래 같이 하면 된다.

이번엔 순서를 약식으로 좀 바꿔보자.


error: Content is protected !!