본문 바로가기
  • plotly로 바로쓰는 동적시각화 in R & 파이썬
데이터 전처리

데이터 프레임 피봇(pivot) in R - pivot_longer, pivot_wider

by 아참형인간 2022. 4. 30.
reshape.knit

사용데이터 : https://2stndard.tistory.com/68

데이터 구조 변환(reshape)

tidyverse 생태계에서 가장 기본적인 전제는 데이터가 간결히 정제(tidy)되어야 한다는 것이다. 이 간결히 정제된 데이터는 원본 데이터(raw data)에서는 적용이 가능하다. 하지만 전처리가 시작되면 간결히 정제된 데이터로 유지하기 어려울 경우도 있고 경우에 따라서는 일부러 정제되지 않은 데이터로 변환해야 할 경우가 있다. 정제되지 않은 데이터는 긴(long) 형태의 데이터와 넓은(wide) 형태의 데이터 두가지 종류로 구분된다.

긴 형태의 데이터와 넓은 형태의 데이터

긴 형태의 데이터는 열로 표현된 변수들을 행 방향으로 풀어 넣음으로써 열의 개수는 줄고 행의 개수는 늘어나는 형태의 데이터이다. 긴 형태의 데이터는 이 책에서 중점적으로 설명할 데이터 시각화에 매우 적합한 형태이다.

긴 형태의 데이터는 R에서 데이터 핸들링부터 머신러닝 알고리즘을 사용한 데이터 분석 및 예측까지에서 가장 많이 사용되는 Hadley Wickam의 ‘tidyverse’ 생태계에서 정의하는 ’tidy’한 데이터의 전형이다. 이 tidy한 데이터는 각 열은 자체 변수이고, 각 행은 하나의 관찰이며 각 셀은 하나의 값으로 표현된다.

긴 형태의 데이터와 팩터를 잘 활용하면 짧고 읽기 쉬운 코드로 데이터 시각화가 가능하다는 장점이 있다. 특히 긴 형태의 데이터에서는 NA를 최소화할 수 있다는 장점을 지닌다. 위의 그림과 같이 여러 열로 표현된 넓은 형태의 데이터는 모든 열에 해당한 데이터가 존재하지 않는다면 이들을 NA로 처리해야하기 떄문에 당연히 NA가 늘어나게 되어 이를 처리하는데 추가적인 작업이 필요할 수 있다.

하지만 긴 형태의 데이터는 사람이 데이터를 읽고 판단하기에 매우 부적합하는 단점이 있다.

반면 넓은 형태의 데이터는 행로 표현된 변수들을 열 방향으로 풀어 넣음으로써 행의 개수는 줄고 열의 개수는 늘어나는 형태의 데이터이다. 위의 그림에서와 같이 넓은 형태의 데이터는 사용자가 직관적으로 데이터의 전반적 분포를 살펴보기가 좋지만 긴 형태의 데이터는 사용자가 전체적인 데이터를 살펴보기가 어렵다. 또 넓은 형식을 사용하면 필드에 데이터를 쉽게 입력할 수 있기 때문에 데이터를 넓은 형식으로 시작하는 것이 일반적이다.

반면 앞에서 지적한 바와 같이 데이터에 NA가 다소 많아지게 되고 열의 수가 많아지기 때문에 빅데이터 연산에 많이 사용되는 벡터 연산에 적절하지 않다. 벡터 연산을 적용하기 위해서 대부분 각 열을 벡터화하여 이 벡터 단위로 연산하는 경우가 많은데 이렇게 벡터의 개수가 많으면 벡터 연산을 여러번 실행하여야 한다는 단점이 존재한다.

긴 형태의 데이터 변환

긴 형태의 데이터를 만들기 위해서는 tidyr 패키지에서 제공하는 gather() 또는 pivot_longer()를 사용한다. tidyr의 공식 매뉴얼에 의하면 gather()의 개발은 완료되었고 향후 pivot_longer()로 전환되어 사용을 권고한다고 표기되어 있다. 따라서 pivot_longer()를 위주로 설명하겠다.

pivot_longer(data, cols, names_to = “name”, names_prefix = NULL, names_sep = NULL, names_pattern = NULL, names_ptypes = list(), names_transform = list(), names_repair = “check_unique”, values_to = “value”, values_drop_na = FALSE, values_ptypes = list(), values_transform = list(), …) - data : 긴 형태로 만들 데이터프레임 - cols : 긴 형태로 만들 열 이름 벡터, 열 번호, 열 번호 범위 등 - names_to : cols에서 지정한 열 이름으로 구성될 열의 이름 - values_to : 각 셀의 값을 저장할 열 이름

pivot_longer()를 사용하기 위해서 반드시 data, cols의 두가지 매개변수가 필수적으로 필요하고 보통 names_to, values_to까지 네 가지 매개변수를 설정한다. data는 긴 형태로 변환할 데이터 프레임을 지정하는데 pipe(|>%>%)를 사용하면 생략될 수 있다. cols는 긴 형태로 변환할 때 하나의 열로 합쳐질 열을 벡터로 설정한다. cols는 하나 이상의 열을 지정해야하기 때문에 열 이름 벡터, 열 번호, 열 번호 시퀀스 등으로 설정할 수 있다. names_tocols로 합쳐질 열의 이름을 설정하고 values_to는 합쳐질 열의 데이터가 저장될 열의 이름을 설정한다. names_tovalues_to를 설정하지 않으면 각각 ‘name’, ’value’로 자동 설정된다.

pivot_longer()를 실습하기 위해서 본 포스트에서 사용하는 데이터의 df_covid19_100를 먼저 넓은 형태의 데이터로 다음과 같이 전환해보겠다.

df_wider <- df_covid19_100 |> 
  select(date, location, total_cases, new_cases, total_deaths, new_deaths)

df_wider
## # A tibble: 707 x 6
##    date       location   total_cases new_cases total_deaths new_deaths
##    <date>     <fct>            <dbl>     <dbl>        <dbl>      <dbl>
##  1 2022-01-18 아프리카      10446204     26951       234504        317
##  2 2022-01-18 아시아        91658433    572629      1278156       1287
##  3 2022-01-18 유럽         107865438   1520977      1582414       3851
##  4 2022-01-18 북미          79545882   1185873      1257391       2936
##  5 2022-01-18 오세아니아     2128565     85220         5163         70
##  6 2022-01-18 남미          43721308    369086      1199893        828
##  7 2022-01-18 한국            705902      5800         6452         74
##  8 2022-01-19 아프리카      10481433     35229       234734        230
##  9 2022-01-19 아시아        92507804    849371      1279360       1204
## 10 2022-01-19 유럽         109463174   1597736      1585693       3279
## # ... with 697 more rows

이 넓은 형태의 데이터를 긴 형태의 데이터로 바꾸는 방법은 다음과 같이 세개의 방법을 사용할 수 있다. 첫 번째는 열 이름 벡터로, 두 번째 코드는 열 번호 벡터로, 세번째 코드는 시퀀스를 이용한 열 번호 벡터를 사용하는 방법이다.

df_wider |> 
  pivot_longer(cols = c('total_cases', 'new_cases', 'total_deaths', 'new_deaths'))
## # A tibble: 2,828 x 4
##    date       location name             value
##    <date>     <fct>    <chr>            <dbl>
##  1 2022-01-18 아프리카 total_cases   10446204
##  2 2022-01-18 아프리카 new_cases        26951
##  3 2022-01-18 아프리카 total_deaths    234504
##  4 2022-01-18 아프리카 new_deaths         317
##  5 2022-01-18 아시아   total_cases   91658433
##  6 2022-01-18 아시아   new_cases       572629
##  7 2022-01-18 아시아   total_deaths   1278156
##  8 2022-01-18 아시아   new_deaths        1287
##  9 2022-01-18 유럽     total_cases  107865438
## 10 2022-01-18 유럽     new_cases      1520977
## # ... with 2,818 more rows
df_wider |> pivot_longer(c(3, 4, 5, 6), names_to = '구분', values_to = 'persons')
## # A tibble: 2,828 x 4
##    date       location 구분           persons
##    <date>     <fct>    <chr>            <dbl>
##  1 2022-01-18 아프리카 total_cases   10446204
##  2 2022-01-18 아프리카 new_cases        26951
##  3 2022-01-18 아프리카 total_deaths    234504
##  4 2022-01-18 아프리카 new_deaths         317
##  5 2022-01-18 아시아   total_cases   91658433
##  6 2022-01-18 아시아   new_cases       572629
##  7 2022-01-18 아시아   total_deaths   1278156
##  8 2022-01-18 아시아   new_deaths        1287
##  9 2022-01-18 유럽     total_cases  107865438
## 10 2022-01-18 유럽     new_cases      1520977
## # ... with 2,818 more rows
df_wider |> pivot_longer(3:6, names_to = '구분', values_to = 'persons')
## # A tibble: 2,828 x 4
##    date       location 구분           persons
##    <date>     <fct>    <chr>            <dbl>
##  1 2022-01-18 아프리카 total_cases   10446204
##  2 2022-01-18 아프리카 new_cases        26951
##  3 2022-01-18 아프리카 total_deaths    234504
##  4 2022-01-18 아프리카 new_deaths         317
##  5 2022-01-18 아시아   total_cases   91658433
##  6 2022-01-18 아시아   new_cases       572629
##  7 2022-01-18 아시아   total_deaths   1278156
##  8 2022-01-18 아시아   new_deaths        1287
##  9 2022-01-18 유럽     total_cases  107865438
## 10 2022-01-18 유럽     new_cases      1520977
## # ... with 2,818 more rows

넓은 형태의 데이터

넓은 형태의 데이터를 만들기 위해서는 tidyr 패키지에서 제공하는 spread() 또는 pivot_wider()를 사용한다. gather()와 마찬가지로 tidyr의 공식 매뉴얼에 의하면 spread()의 개발은 완료되었고 향후 pivot_wider()로 전환되어 사용을 권고한다고 표기되어 있다. 따라서 pivot_wider()를 위주로 설명하겠다.

pivot_wider(data, id_cols = NULL, names_from = name, names_prefix = ““, names_sep =”_“, names_glue = NULL, names_sort = FALSE, names_repair =”check_unique”, values_from = value, values_fill = NULL, values_fn = NULL, …) - data : 긴 형태로 만들 데이터프레임 - names_from : 열 이름으로 구성될 열의 이름 - values_from : 각 셀의 값으로 구성될 열 이름

pivot_wider()를 사용하기 위해서 반드시 data, names_from, values_from의 세 가지 매개변수가 필수적으로 필요하다. data는 넓은 형태로 변환할 데이터 프레임을 지정하는데 pipe(|>%>%)를 사용하면 생략될 수 있다. names_from은 넓은 형태로 변환할 때 열 이름으로 펼쳐질 데이터가 저장된 열을 설정한다. values_fromnames_from으로 펼쳐진 열에 저장될 데이터가 저장된 열을 설정한다.

pivot_wider()를 실습하기 위해서 본 포스트에서 사용하는 데이터의 df_covid19_100를 먼저 긴 형태의 데이터로 다음과 같이 전환해보겠다.

df_longer <- df_covid19_100 |> 
  select(date, location, total_cases)

df_longer
## # A tibble: 707 x 3
##    date       location   total_cases
##    <date>     <fct>            <dbl>
##  1 2022-01-18 아프리카      10446204
##  2 2022-01-18 아시아        91658433
##  3 2022-01-18 유럽         107865438
##  4 2022-01-18 북미          79545882
##  5 2022-01-18 오세아니아     2128565
##  6 2022-01-18 남미          43721308
##  7 2022-01-18 한국            705902
##  8 2022-01-19 아프리카      10481433
##  9 2022-01-19 아시아        92507804
## 10 2022-01-19 유럽         109463174
## # ... with 697 more rows

이렇게 길게 생성된 데이터를 넓은 형태로 변환하기 위해서는 우선 열로 사용할 변수를 설정하여야 하는데 여기서는 각 대륙별로 열을 펼치고 각각의 대륙에 ‘total_cases’ 데이터를 저장하는 코드는 다음과 같다.

df_longer |> 
  pivot_wider(names_from = location, values_from = total_cases) 
## # A tibble: 101 x 8
##    date       아프리카   아시아      유럽     북미 오세아니아     남미   한국
##    <date>        <dbl>    <dbl>     <dbl>    <dbl>      <dbl>    <dbl>  <dbl>
##  1 2022-01-18 10446204 91658433 107865438 79545882    2128565 43721308 705902
##  2 2022-01-19 10481433 92507804 109463174 80623224    2197384 44181939 712503
##  3 2022-01-20 10530788 93222825 111090014 81419617    2261818 44635486 719269
##  4 2022-01-21 10568210 93942144 112691756 82422002    2306022 45066178 726274
##  5 2022-01-22 10609278 94623085 113970416 82756200    2373663 45452780 733902
##  6 2022-01-23 10625760 95222958 115002903 83227036    2406091 45757226 741413
##  7 2022-01-24 10652684 95977454 116321732 84197652    2466338 46048605 749979
##  8 2022-01-25 10708754 96687023 118127733 84805519    2520592 46465897 762983
##  9 2022-01-26 10742258 97439805 119899381 85479827    2572899 46843672 777497
## 10 2022-01-27 10779762 98141834 121749700 86093967    2640481 47328453 793582
## # ... with 91 more rows

댓글