聰明不如鈍筆
총명불여둔필
assignment KindeR

파이선 'for' 코드를 R 'map' 코드로 '번역하기'

페이스북 '데이터 분석 커뮤니티'에서 재미있는 게시물을 발견했습니다.

 

파이썬을 주로 사용하는 유저입니다. R을 배우고 있는데 for loop가 좀 어렵네요.

 

파이썬을 이용해서 예시 데이터 세트를 만들어 보았습니다(이미지). 이것을 R로 코드를 짠다면 어떻게 하면 될까요...?

 

p.s.

R에서는 for loop를 잘 사용하지 않는다고도 들었습니다.  

 

for loop를 대체해서 apply 류 함수나 purrr 패키지의 map 류 함수를 이용해서 대체가 가능하다는 내용이었습니다.

 

이와 관련해서도 해당 예시에서도 적용 가능한지 고수님들의 의견을 듣고 싶습니다.

 

이 블로그에 'R에서 깔끔하게 반복 작업 처리하기(feat. purrr)' 포스트를 남긴 사람으로서 p. s.에 대한 답을 남겨 보려고 합니다.

 

일단 이미지로 나와 있는 파이선 코드를 텍스트로 바꾸면 이렇습니다.

import pandas as pd
import numpy as np

df_dict = dict()

for y in range(2002, 2006+1):
    df_dict[f'stnd_{y}'] = np.random.randint(3, size = 10)

df = pd.DataFrame(df_dict)

for y in range(2002, 2005+1):
    df.loc[df[f'stnd_{y}'] == df[f'stnd_{y + 1}'], f'age_{y}'] = df[f'stnd_{y}'] * 5 - 1

for y in range(2002, 2006+1):
    df.loc[df[f'stnd_{y}'] == 0, f'age_{y}'] = 0

print(df)
   stnd_2002  stnd_2003  stnd_2004  ...  age_2004  age_2005  age_2006
0          1          0          1  ...       4.0       NaN       0.0
1          2          2          0  ...       0.0       NaN       NaN
2          1          1          0  ...       0.0       9.0       NaN
3          1          0          2  ...       NaN       4.0       NaN
4          2          2          1  ...       NaN       0.0       NaN
5          2          0          0  ...       0.0       NaN       0.0
6          1          1          2  ...       NaN       NaN       NaN
7          0          0          2  ...       NaN       4.0       NaN
8          0          2          1  ...       4.0       NaN       NaN
9          2          1          0  ...       0.0       4.0       NaN

[10 rows x 10 columns]

 

이 코드는 stnd_2002~stnd_2006 열에 0~2를 랜덤으로  10개씩 만들어 넣는 걸로 시작합니다.

 

그리고 각 행별로 오른쪽 열이 왼쪽 열보다 1이 더 크면 '난수 × 5 - 1'을 age_* 열에 넣는 코드로 이어집니다.

 

예컨대 stnd_2004에 1, stnd_2004에 2가 있을 때 age_2004에 4(= 1 × 5 - 1)를 넣는 겁니다.

 

마지막 코드는 stnd_*가 0일 때는 age_*에도 0을 넣으라는 내용입니다.

 

R로는 이 내용을 어떻게 구현할 수 있을까요?

 

시작은 늘 그렇듯 tidyverse 패키지를 (설치하고) 불러오는 겁니다.

#install.packages('tidyverse')
library('tidyverse')
## -- Attaching packages --------------------------------------- tidyverse 1.3.1 --
## v ggplot2 3.3.6     v purrr   0.3.4
## v tibble  3.1.7     v dplyr   1.0.9
## v tidyr   1.2.0     v stringr 1.4.0
## v readr   2.1.2     v forcats 0.5.1
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()

 

그러고 나서 아래 코드를 실행하면 일단 파이선 코드와 같은 작업을 진행할 수 있습니다.

rerun(5, sample(0:2, 10, rep = TRUE)) %>% 
  as_tibble(.name_repair = ~str_c('stnd_', 2002:2006)) -> df
map2(df %>% select(1:4),
     df %>% select(2:5),
     ~if_else(.x + 1 == .y, .x * 5 - 1, NA_real_) %>% 
       if_else(.x == 0, 0, .)) -> df[str_c('age_', 2002:2005)]
df %>% 
  mutate(age_2006 = if_else(stnd_2006 == 0, 0, NA_real_))
## # A tibble: 10 x 10
##    stnd_2002 stnd_2003 stnd_2004 stnd_~1 stnd_~2 age_2~3 age_2~4 age_2~5 age_2~6
##        <int>     <int>     <int>   <int>   <int>   <dbl>   <dbl>   <dbl>   <dbl>
##  1         1         2         0       0       2       4      NA       0       0
##  2         0         0         0       0       0       0       0       0       0
##  3         0         0         1       1       0       0       0      NA      NA
##  4         2         0         1       2       2      NA       0       4      NA
##  5         2         2         2       2       0      NA      NA      NA      NA
##  6         2         1         1       2       1      NA      NA       4      NA
##  7         0         2         2       2       2       0      NA      NA      NA
##  8         0         2         2       2       2       0      NA      NA      NA
##  9         0         2         1       2       0       0      NA       4      NA
## 10         1         1         2       0       0      NA       4      NA       0
## # ... with 1 more variable: age_2006 <dbl>, and abbreviated variable names
## #   1: stnd_2005, 2: stnd_2006, 3: age_2002, 4: age_2003, 5: age_2004,
## #   6: age_2005
## # i Use `colnames()` to see all variable names

 

이 코드를 어떻게 쓴 건지 한 줄, 한 줄 정리해 보겠습니다.

 

일단 랜덤으로 0~2 사이 숫자 10개를 만듭니다.

 

이럴 때는 sample() 함수를 쓰면 됩니다. 

sample(0:2, 10, rep = TRUE)
##  [1] 2 2 1 0 0 1 1 1 1 1

 

여기서 'rep = TRUE'는 반복을 허락하라는 뜻입니다.

 

만약 이 옵션을 주지 않으면 0~2 사이 숫자는 3개뿐이라 10개를 만들 수가 없습니다.

 

우리는 stnd_2002부터 stnd_2006까지 5개 열에 걸쳐 이 작업을 반복해야 합니다.

 

purrr 패키지에 들어 있는 rerun() 함수를 쓰면 같은 작업을 반복할 수 있습니다.

rerun(5, sample(0:2, 10, rep = TRUE))
## [[1]]
##  [1] 2 1 0 2 1 2 1 0 1 0
## 
## [[2]]
##  [1] 0 2 1 1 2 2 1 0 1 0
## 
## [[3]]
##  [1] 1 2 1 2 2 0 0 1 0 0
## 
## [[4]]
##  [1] 1 0 0 2 2 0 2 2 1 2
## 
## [[5]]
##  [1] 0 0 1 2 2 1 1 1 2 0

 

이제 이 결과를 데이터 프레임 조금 더 정확하게는 tidyverse 생태계에서 쓰는 데이터 프레임인 tibble로 만듭니다.

 

리스트나 매트릭스를 tibble로 만들고 싶을 때는 as_tibble() 함수를 씁니다.

rerun(5, sample(0:2, 10, rep = TRUE)) %>% 
  as_tibble()
## Error:
## ! Columns 1, 2, 3, 4, and 5 must be named.
## Use .name_repair to specify repair.
## Caused by error in `repaired_names()`:
## ! Names can't be empty.
## x Empty names found at locations 1, 2, 3, 4, and 5.

 

원하는 결과 대신 에러 메시지가 나옵니다.

 

걱정하실 것 없습니다. 차근차근 읽어 보시면 어떻게 대처해야 하는지 나오니까요.

 

결국 '.name_repair' 옵션을 주라는 얘기입니다. 일단 'unique'를 넣어 봅니다.

rerun(5, sample(0:2, 10, rep = TRUE)) %>% 
  as_tibble(.name_repair = 'unique')
## New names:
## * `` -> `...1`
## * `` -> `...2`
## * `` -> `...3`
## * `` -> `...4`
## * `` -> `...5`
## # A tibble: 10 x 5
##     ...1  ...2  ...3  ...4  ...5
##    <int> <int> <int> <int> <int>
##  1     1     0     0     0     0
##  2     2     1     0     1     1
##  3     2     1     1     1     1
##  4     2     2     0     0     2
##  5     0     2     1     2     1
##  6     0     1     2     1     2
##  7     1     1     0     2     0
##  8     0     1     1     2     0
##  9     0     0     1     1     2
## 10     1     1     2     1     1

 

일단 tibble로 만드는 데 성공했습니다.

 

기왕이면 질문자께서 제시하신 것처럼 stnd_2002~stnd_2006으로 이름을 붙이면 다 좋을 것 같습니다..

 

파이선 f-string처럼 R에서도 str_c() 함수를 쓰면 일부분만 달라지는 문자열을 어렵지 않게 만들 수 있습니다.

str_c('stnd_', 2002:2006)
## [1] "stnd_2002" "stnd_2003" "stnd_2004" "stnd_2005" "stnd_2006"

 

이렇게 만든 문자열을 활용해 .name_repair 옵션을 다시 줍니다.

 

이때는 이 작업을 반복해서 적용한다는 뜻으로 '~'를 앞에 붙입니다.

 

purrr에서 ~는 '여기부터는 함수야'라고 알려주는 구실을 합니다.

rerun(5, sample(0:2, 10, rep = TRUE)) %>% 
  as_tibble(.name_repair = ~str_c('stnd_', 2002:2006)) -> df

 

맨 끝에 있는 '-> df'에 당황한 분이 계실지도 모릅니다.

 

R에서 객체를 저장할 때는 흔히 '객체 <-' 형태로 쓰지만 '-> 객체'로 써도 (대부분) 같은 결과를 얻을 수 있습니다.

 

실제로 잘 들어갔는지 df 객체를 열어보겠습니다.

df
## # A tibble: 10 x 5
##    stnd_2002 stnd_2003 stnd_2004 stnd_2005 stnd_2006
##        <int>     <int>     <int>     <int>     <int>
##  1         0         1         0         0         1
##  2         0         0         0         2         1
##  3         2         1         1         2         2
##  4         2         1         2         2         0
##  5         1         0         1         1         1
##  6         2         2         0         1         0
##  7         2         1         1         0         0
##  8         0         2         1         1         2
##  9         0         0         1         0         1
## 10         2         2         2         2         2

 

예시 데이터를 만들었으니까 이제 각 열을 바로 뒤에 있는 열과 비교하는 작업을 해야 합니다.

 

이를 달리 말하면 첫 번재~네 번째 열(①)과 두 번째~다섯 번째 열(②)을 비교하라는 뜻이 됩니다.

 

select() 함수를 쓰면 어렵지 않게 ①, ②를 따로 따로 tibble로 만들 수 있습니다.

df %>% select(1:4)
## # A tibble: 10 x 4
##    stnd_2002 stnd_2003 stnd_2004 stnd_2005
##        <int>     <int>     <int>     <int>
##  1         0         1         0         0
##  2         0         0         0         2
##  3         2         1         1         2
##  4         2         1         2         2
##  5         1         0         1         1
##  6         2         2         0         1
##  7         2         1         1         0
##  8         0         2         1         1
##  9         0         0         1         0
## 10         2         2         2         2
df %>% select(2:5)
## # A tibble: 10 x 4
##    stnd_2003 stnd_2004 stnd_2005 stnd_2006
##        <int>     <int>     <int>     <int>
##  1         1         0         0         1
##  2         0         0         2         1
##  3         1         1         2         2
##  4         1         2         2         0
##  5         0         1         1         1
##  6         2         0         1         0
##  7         1         1         0         0
##  8         2         1         1         2
##  9         0         1         0         1
## 10         2         2         2         2

 

이렇게 데이터가 두 개로 작업을 반복하고 싶을 때는 map2_*() 함수를 쓰면 됩니다.

 

이때 첫 번째 데이터는 .x, 두 번째 데이터는 .y로 호출합니다.

 

두 데이터를 더하는 코드는 이렇게 간단하게 쓸 수 있습니다.

map2(df %>% select(1:4),
     df %>% select(2:5),
     ~.x + .y)
## $stnd_2002
##  [1] 1 0 3 3 1 4 3 2 0 4
## 
## $stnd_2003
##  [1] 1 0 2 3 1 2 2 3 1 4
## 
## $stnd_2004
##  [1] 0 2 3 4 2 1 1 2 1 4
## 
## $stnd_2005
##  [1] 1 3 4 2 2 1 0 3 1 4

 

이제 질문자께서 제시했던 조건을 채워보겠습니다.

 

'이번 열보다 다음 열이 1이 더 크면…'은 조건문에 해당합니다.

 

if_else() 함수를 이용해 이를 구현하려면 그냥 '.x + 1 == y'라고 쓰면 그만입니다.

 

이 조건을 충족할 때는 '.x * 5 - 1' 아닐 때는 빈 값(NA)을 출력하면 됩니다.

 

기왕 쓰는 김에 이번 열이 0일 때는 그냥 0을 출력하는 내용까지 한 번에 가겠습니다.

map2(df %>% select(1:4),
     df %>% select(2:5),
     ~if_else(.x + 1 == .y, .x * 5 - 1, NA_real_) %>% 
       if_else(.x == 0, 0, .))
## $stnd_2002
##  [1]  0  0 NA NA NA NA NA  0  0 NA
## 
## $stnd_2003
##  [1] NA  0 NA  4  0 NA NA NA  0 NA
## 
## $stnd_2004
##  [1]  0  0  4 NA NA  0 NA NA NA NA
## 
## $stnd_2005
##  [1]  0 NA NA NA NA NA  0  4  0 NA

 

map2() 함수 대신 map2_df() 함수를 쓰면 결과를 리스트가 아니라 tibble로 받을 수 있습니다.

map2_df(df %>% select(1:4),
        df %>% select(2:5),
        ~if_else(.x + 1 == .y, .x * 5 - 1, NA_real_) %>%
          if_else(.x == 0, 0, .)) 
## # A tibble: 10 x 4
##    stnd_2002 stnd_2003 stnd_2004 stnd_2005
##        <dbl>     <dbl>     <dbl>     <dbl>
##  1         0        NA         0         0
##  2         0         0         0        NA
##  3        NA        NA         4        NA
##  4        NA         4        NA        NA
##  5        NA         0        NA        NA
##  6        NA        NA         0        NA
##  7        NA        NA        NA         0
##  8         0        NA        NA         4
##  9         0         0        NA         0
## 10        NA        NA        NA        NA

 

질문자께서는 age_*로 열 이름을 붙이셨는데 우리는 따로 지정한 적이 없어 stnd_* 형태가 그냥 남아 있습니다.

 

이럴 때는 rename_with() 함수로 이름을 한 번에 바꿀 수 있습니다.

 

일단 특정한 문자 패턴을 다른 패턴으로 바꾸고 싶을 때 쓰는 str_replace() 함수로 stnd를 age로 바꿔 줍니다.

 

그리고 everything() 함수로 모든 열을 선택해 주면 이름이 바뀝니다.

map2_df(df %>% select(1:4),
        df %>% select(2:5),
        ~if_else(.x + 1 == .y, .x * 5 - 1, NA_real_) %>%
         if_else(.x == 0, 0, .)) %>% 
  rename_with(~str_replace(.x, 'stnd', 'age'), everything())
## # A tibble: 10 x 4
##    age_2002 age_2003 age_2004 age_2005
##       <dbl>    <dbl>    <dbl>    <dbl>
##  1        0       NA        0        0
##  2        0        0        0       NA
##  3       NA       NA        4       NA
##  4       NA        4       NA       NA
##  5       NA        0       NA       NA
##  6       NA       NA        0       NA
##  7       NA       NA       NA        0
##  8        0       NA       NA        4
##  9        0        0       NA        0
## 10       NA       NA       NA       NA

 

이제 bind_cols() 함수로 원래 데이터(df)와 새로 만든 데이터를 묶어 주면 그만입니다.

bind_cols(
  df,
  map2_df(df %>% select(1:4),
          df %>% select(2:5),
          ~if_else(.x + 1 == .y, .x * 5 - 1, NA_real_) %>%
            if_else(.x == 0, 0, .)) %>% 
    rename_with(~str_replace(.x, 'stnd', 'age'), everything())
) -> df

 

bind_cols() 함수를 쓰는 대신에 대괄호([ ])를 써서 열을 추가하는 방법도 있습니다.

map2(df %>% select(1:4),
     df %>% select(2:5),
     ~if_else(.x + 1 == .y, .x * 5 - 1, NA_real_) %>% 
       if_else(.x == 0, 0, .)) -> df[str_c('age_', 2002:2005)]

 

지금까지 한 번도 age_2006을 만들어 준 적이 없습니다.

 

stnd_2006은 따로 비교할 열이 없으니까 논리적으로 이 열이 존재할 수가 없는 것.

 

그래도 질문자께서 요구하신 조건을 충족시키는 차원에서 붙여 줍니다.

df %>% 
  mutate(age_2006 = if_else(stnd_2006 == 0, 0, NA_real_))
## # A tibble: 10 x 10
##    stnd_2002 stnd_2003 stnd_2004 stnd_~1 stnd_~2 age_2~3 age_2~4 age_2~5 age_2~6
##        <int>     <int>     <int>   <int>   <int>   <dbl>   <dbl>   <dbl>   <dbl>
##  1         0         1         0       0       1       0      NA       0       0
##  2         0         0         0       2       1       0       0       0      NA
##  3         2         1         1       2       2      NA      NA       4      NA
##  4         2         1         2       2       0      NA       4      NA      NA
##  5         1         0         1       1       1      NA       0      NA      NA
##  6         2         2         0       1       0      NA      NA       0      NA
##  7         2         1         1       0       0      NA      NA      NA       0
##  8         0         2         1       1       2       0      NA      NA       4
##  9         0         0         1       0       1       0       0      NA       0
## 10         2         2         2       2       2      NA      NA      NA      NA
## # ... with 1 more variable: age_2006 <dbl>, and abbreviated variable names
## #   1: stnd_2005, 2: stnd_2006, 3: age_2002, 4: age_2003, 5: age_2004,
## #   6: age_2005
## # i Use `colnames()` to see all variable names

 

물론 이런 작업을 진행하는 코드가 이 방식 하나만 있는 건 아닙니다.

 

모든 열에 함수를 적용하는 across() 함수로는 이렇게 작업을 진행할 수 있습니다.

rerun(5, sample(0:2, 10, rep = TRUE)) %>% 
  as_tibble(.name_repair = ~str_c('stnd_', 2002:2006)) %>% 
  mutate(
    across(
      stnd_2002:stnd_2005,
      ~(if_else(.x == 0, 
                0,
                if_else(.x + 1 == get((str_c('stnd_', parse_number(cur_column()) + 1))),
                        .x * 5 - 1, 
                        NA_real_))),
      .names = 'age_{col}'
    )) %>% 
  rename_with(~str_replace(.x, 'stnd_', ''), starts_with('age')) %>% 
  mutate(age_2006 = if_else(stnd_2006 == 0, 0, NA_real_))
## # A tibble: 10 x 10
##    stnd_2002 stnd_2003 stnd_2004 stnd_~1 stnd_~2 age_2~3 age_2~4 age_2~5 age_2~6
##        <int>     <int>     <int>   <int>   <int>   <dbl>   <dbl>   <dbl>   <dbl>
##  1         0         0         0       0       2       0       0       0       0
##  2         0         0         1       0       0       0       0      NA       0
##  3         1         1         1       2       1      NA      NA       4      NA
##  4         2         1         1       2       2      NA      NA       4      NA
##  5         2         2         0       0       2      NA      NA       0       0
##  6         1         1         0       0       2      NA      NA       0       0
##  7         2         2         1       1       2      NA      NA      NA       4
##  8         1         1         0       1       1      NA      NA       0      NA
##  9         2         1         1       0       1      NA      NA      NA       0
## 10         2         1         1       2       2      NA      NA       4      NA
## # ... with 1 more variable: age_2006 <dbl>, and abbreviated variable names
## #   1: stnd_2005, 2: stnd_2006, 3: age_2002, 4: age_2003, 5: age_2004,
## #   6: age_2005
## # i Use `colnames()` to see all variable names

 

그리고 사실 오른쪽-아래쪽보다 위-아래가 '깔끔하게' 작업하기에 더 편합니다.

 

제가 시뮬레이션에 자주 쓰는 crossing() 함수를 쓰면 일단 예제 tibble은 이렇게 만들 수 있습니다.

crossing(
  group = 1:10,
  year = 2002:2006
) %>% 
  mutate(stnd = sample(0:2, 50, replace = TRUE))
## # A tibble: 50 x 3
##    group  year  stnd
##    <int> <int> <int>
##  1     1  2002     1
##  2     1  2003     2
##  3     1  2004     1
##  4     1  2005     0
##  5     1  2006     0
##  6     2  2002     0
##  7     2  2003     1
##  8     2  2004     1
##  9     2  2005     1
## 10     2  2006     1
## # ... with 40 more rows
## # i Use `print(n = ...)` to see more rows

 

이렇게 세로로 작업할 때는 현재 행과 바로 다른 행을 비교하면 됩니다.

 

이럴 때 쓰는 함수는 lead()입니다.

crossing(
  group = 1:10,
  year = 2002:2006
) %>% 
  mutate(stnd = sample(0:2, 50, replace = TRUE)) %>% 
  mutate(lead = lead(stnd))
## # A tibble: 50 x 4
##    group  year  stnd  lead
##    <int> <int> <int> <int>
##  1     1  2002     0     0
##  2     1  2003     0     2
##  3     1  2004     2     2
##  4     1  2005     2     2
##  5     1  2006     2     2
##  6     2  2002     2     0
##  7     2  2003     0     1
##  8     2  2004     1     0
##  9     2  2005     0     2
## 10     2  2006     2     0
## # ... with 40 more rows
## # i Use `print(n = ...)` to see more rows

 

여기서 신경 써야 할 건 group이 바뀌면 다음 행에서 데이터를 가져오면 안 된다는 점입니다.

 

그냥 group_by() 함수를 한 번 쓰면 문제를 해결할 수 있습니다.

crossing(
  group = 1:10,
  year = 2002:2006
) %>% 
  mutate(stnd = sample(0:2, 50, replace = TRUE)) %>% 
  group_by(group) %>% 
  mutate(lead = lead(stnd))
## # A tibble: 50 x 4
## # Groups:   group [10]
##    group  year  stnd  lead
##    <int> <int> <int> <int>
##  1     1  2002     2     1
##  2     1  2003     1     2
##  3     1  2004     2     2
##  4     1  2005     2     2
##  5     1  2006     2    NA
##  6     2  2002     2     1
##  7     2  2003     1     0
##  8     2  2004     0     1
##  9     2  2005     1     2
## 10     2  2006     2    NA
## # ... with 40 more rows
## # i Use `print(n = ...)` to see more rows

 

준비를 마쳤으니 질문자께서 원하신 조건에 따라 작동하도록 if_else() 함수를 채워 넣습니다.

 

그룹 작업을 마쳤으니 ungroup() 함수도 써줍니다.

crossing(
  group = 1:10,
  year = 2002:2006
) %>% 
  mutate(stnd = sample(0:2, 50, replace = TRUE)) %>% 
  group_by(group) %>% 
  mutate(age = if_else(stnd + 1 == lead(stnd), stnd * 5 - 1, NA_real_)) %>% 
  mutate(age = if_else(stnd == 0, 0, age)) %>% 
  ungroup()
## # A tibble: 50 x 4
##    group  year  stnd   age
##    <int> <int> <int> <dbl>
##  1     1  2002     1    NA
##  2     1  2003     0     0
##  3     1  2004     1    NA
##  4     1  2005     0     0
##  5     1  2006     0     0
##  6     2  2002     0     0
##  7     2  2003     2    NA
##  8     2  2004     2    NA
##  9     2  2005     1     4
## 10     2  2006     2    NA
## # ... with 40 more rows
## # i Use `print(n = ...)` to see more rows

 

여기까지만 해도 사실 작업은 끝입니다.

 

질문자께서 제시하신 데이터와 같은 모양으로 보이도록 tibble을 펼쳐 줍니다.

 

pivot_wider() 함수를 쓰면 됩니다.

crossing(
  group = 1:10,
  year = 2002:2006
) %>% 
  mutate(stnd = sample(0:2, 50, replace = TRUE)) %>% 
  group_by(group) %>% 
  mutate(age = if_else(stnd + 1 == lead(stnd), stnd * 5 - 1, NA_real_)) %>% 
  mutate(age = if_else(stnd == 0, 0, age)) %>% 
  ungroup() %>% 
  pivot_wider(names_from = year, values_from = c(stnd, age)) 
## # A tibble: 10 x 11
##    group stnd_~1 stnd_~2 stnd_~3 stnd_~4 stnd_~5 age_2~6 age_2~7 age_2~8 age_2~9
##    <int>   <int>   <int>   <int>   <int>   <int>   <dbl>   <dbl>   <dbl>   <dbl>
##  1     1       0       2       2       2       0       0      NA      NA      NA
##  2     2       1       0       0       2       2      NA       0       0      NA
##  3     3       0       1       2       0       0       0       4      NA       0
##  4     4       2       2       1       1       2      NA      NA      NA       4
##  5     5       0       1       2       0       0       0       4      NA       0
##  6     6       2       2       2       0       0      NA      NA      NA       0
##  7     7       0       0       1       2       0       0       0       4      NA
##  8     8       2       2       0       2       1      NA      NA       0      NA
##  9     9       1       2       0       0       1       4      NA       0       0
## 10    10       2       1       2       0       1      NA       4      NA       0
## # ... with 1 more variable: age_2006 <dbl>, and abbreviated variable names
## #   1: stnd_2002, 2: stnd_2003, 3: stnd_2004, 4: stnd_2005, 5: stnd_2006,
## #   6: age_2002, 7: age_2003, 8: age_2004, 9: age_2005
## # i Use `colnames()` to see all variable names

 

맨 앞에 있는 group 열은 작업에만 필요할 뿐 실제 결과를 나타날 때는 도움이 되지 않습니다.

 

필요 없다고 생각하시면 그냥 select() 함수로 빼면 그만입니다.

crossing(
  group = 1:10,
  year = 2002:2006
) %>% 
  mutate(stnd = sample(0:2, 50, replace = TRUE)) %>% 
  group_by(group) %>% 
  mutate(age = if_else(stnd + 1 == lead(stnd), stnd * 5 - 1, NA_real_)) %>% 
  mutate(age = if_else(stnd == 0, 0, age)) %>% 
  ungroup() %>% 
  pivot_wider(names_from = year, values_from = c(stnd, age)) %>% 
  select(-1)
## # A tibble: 10 x 10
##    stnd_2002 stnd_2003 stnd_2004 stnd_~1 stnd_~2 age_2~3 age_2~4 age_2~5 age_2~6
##        <int>     <int>     <int>   <int>   <int>   <dbl>   <dbl>   <dbl>   <dbl>
##  1         2         0         1       0       1      NA       0      NA       0
##  2         2         0         1       0       2      NA       0      NA       0
##  3         2         0         1       0       0      NA       0      NA       0
##  4         0         1         2       0       1       0       4      NA       0
##  5         0         0         1       2       0       0       0       4      NA
##  6         0         1         2       1       0       0       4      NA      NA
##  7         1         2         0       2       2       4      NA       0      NA
##  8         1         1         0       2       2      NA      NA       0      NA
##  9         1         1         2       0       2      NA       4      NA       0
## 10         1         2         0       1       2       4      NA       0       4
## # ... with 1 more variable: age_2006 <dbl>, and abbreviated variable names
## #   1: stnd_2005, 2: stnd_2006, 3: age_2002, 4: age_2003, 5: age_2004,
## #   6: age_2005
## # i Use `colnames()` to see all variable names

 

여기까지 읽으시면서 이해가 잘 가지 않으시는 게 있다면

이런 포스트가 도움이 될 수 있습니다.

 

그리고 아래 책을 참고하시면 더욱 확실하게 도움을 받으실 수 있으리라고 감히 말씀드립니다.

 

데이터 과학 입문서 '친절한 R with 스포츠 데이터'를 썼습니다

어쩌다 보니 '한국어 사용자에게 도움이 될지도 모르는 R 언어 기초 회화 교재'를 세상에 내놓게 됐습니다. 책 앞 부분은 '한국어 tidyverse 사투리' 번역, 뒷부분은 'tidymodels 억양 따라하기'에 초점

kuduz.tistory.com

 

댓글,

KindeR | 카테고리 다른 글 더 보기