여러 열 집합 수집


108

응답자가 질문을 1-3 번 반복하는 온라인 설문 조사의 데이터가 있습니다. 설문 조사 소프트웨어 (Qualtrics)는이 데이터를 여러 열에 기록합니다. 즉, 설문 조사의 Q3.2 Q3.2.1.에는 Q3.2.2., 및 Q3.2.3.다음 열이 있습니다 .

df <- data.frame(
  id = 1:10,
  time = as.Date('2009-01-01') + 0:9,
  Q3.2.1. = rnorm(10, 0, 1),
  Q3.2.2. = rnorm(10, 0, 1),
  Q3.2.3. = rnorm(10, 0, 1),
  Q3.3.1. = rnorm(10, 0, 1),
  Q3.3.2. = rnorm(10, 0, 1),
  Q3.3.3. = rnorm(10, 0, 1)
)

# Sample data

   id       time    Q3.2.1.     Q3.2.2.    Q3.2.3.     Q3.3.1.    Q3.3.2.     Q3.3.3.
1   1 2009-01-01 -0.2059165 -0.29177677 -0.7107192  1.52718069 -0.4484351 -1.21550600
2   2 2009-01-02 -0.1981136 -1.19813815  1.1750200 -0.40380049 -1.8376094  1.03588482
3   3 2009-01-03  0.3514795 -0.27425539  1.1171712 -1.02641801 -2.0646661 -0.35353058
...

모든 QN.N * 열을 깔끔한 개별 QN.N 열로 결합하여 궁극적으로 다음과 같은 결과를 얻고 싶습니다.

   id       time loop_number        Q3.2        Q3.3
1   1 2009-01-01           1 -0.20591649  1.52718069
2   2 2009-01-02           1 -0.19811357 -0.40380049
3   3 2009-01-03           1  0.35147949 -1.02641801
...
11  1 2009-01-01           2 -0.29177677  -0.4484351
12  2 2009-01-02           2 -1.19813815  -1.8376094
13  3 2009-01-03           2 -0.27425539  -2.0646661
...
21  1 2009-01-01           3 -0.71071921 -1.21550600
22  2 2009-01-02           3  1.17501999  1.03588482
23  3 2009-01-03           3  1.11717121 -0.35353058
...

tidyr라이브러리는이 gather()결합하는 좋은 작품 기능, 하나의 열 세트를 :

library(dplyr)
library(tidyr)
library(stringr)

df %>% gather(loop_number, Q3.2, starts_with("Q3.2")) %>% 
  mutate(loop_number = str_sub(loop_number,-2,-2)) %>%
  select(id, time, loop_number, Q3.2)


   id       time loop_number        Q3.2
1   1 2009-01-01           1 -0.20591649
2   2 2009-01-02           1 -0.19811357
3   3 2009-01-03           1  0.35147949
...
29  9 2009-01-09           3 -0.58581232
30 10 2009-01-10           3 -2.33393981

결과 데이터 프레임에는 예상대로 30 개의 행이 있습니다 (개인 10 명, 각각 3 개의 루프). 그러나 두 번째 열 집합을 수집하는 것은 올바르게 작동하지 않습니다. 두 개의 결합 된 열 Q3.2및을 성공적으로 만들지 Q3.3만 30 개가 아닌 90 개의 행으로 끝납니다 (모든 개인 조합 10 개, Q3.2의 3 개 루프, Q3의 3 개 루프). .3; 실제 데이터의 각 열 그룹에 대해 조합이 크게 증가합니다) :

df %>% gather(loop_number, Q3.2, starts_with("Q3.2")) %>% 
  gather(loop_number, Q3.3, starts_with("Q3.3")) %>%
  mutate(loop_number = str_sub(loop_number,-2,-2))


   id       time loop_number        Q3.2        Q3.3
1   1 2009-01-01           1 -0.20591649  1.52718069
2   2 2009-01-02           1 -0.19811357 -0.40380049
3   3 2009-01-03           1  0.35147949 -1.02641801
...
89  9 2009-01-09           3 -0.58581232 -0.13187024
90 10 2009-01-10           3 -2.33393981 -0.48502131

gather()올바른 행 수를 유지하면서 이와 같은 열의 작은 하위 집합을 결합 하여 이와 같은 여러 호출을 사용하는 방법 이 있습니까?


무슨 문제df %>% gather(loop_number, Q3.2, starts_with("Q3."))
알렉스

60 개의 행이있는 하나의 통합 된 열을 얻습니다. seperate()Q3.3 (및 그 이상) 값을 자체 열로 나누기 위해 일종의 호출을 포함하면 작동 할 수 있다고 생각합니다 . 하지만 여전히 ... 정말 로터리 해키 솔루션처럼 보인다
앤드류

사용하십시오 spread난 이제 해결책을하고 있어요 : P
알렉스

이 시도! df %>% gather(question_number, Q3.2, starts_with("Q3.")) %>% mutate(loop_number = str_sub(question_number,-2,-2), question_number = str_sub(question_number,1,4)) %>% select(id, time, loop_number, question_number, Q3.2) %>% spread(key = question_number, value = Q3.2)
알렉스

오, 두 변수에 대해 정말 잘 작동합니다. 하지만 확장 가능한지 궁금합니다. 실제 데이터에는 Q3.2-Q3.30이 있으므로 spread(). 어쨌든 여러 번의 호출이 불가피 해 보이지만 generate()작동하는 것이 든 중첩 된 것이 든 spread()
Andrew

답변:


146

이 접근 방식은 나에게 매우 자연스러운 것 같습니다.

df %>%
  gather(key, value, -id, -time) %>%
  extract(key, c("question", "loop_number"), "(Q.\\..)\\.(.)") %>%
  spread(question, value)

우선 질문의 열을 수집, 사용 extract()으로 분리합니다 questionloop_number다음, spread()열로 질문을 다시합니다.

#>    id       time loop_number         Q3.2        Q3.3
#> 1   1 2009-01-01           1  0.142259203 -0.35842736
#> 2   1 2009-01-01           2  0.061034802  0.79354061
#> 3   1 2009-01-01           3 -0.525686204 -0.67456611
#> 4   2 2009-01-02           1 -1.044461185 -1.19662936
#> 5   2 2009-01-02           2  0.393808163  0.42384717

5
안녕하세요. age1, age2, weight1, weight2, blood1, blood2와 같이 이름이 1과 2로 끝나는 열이 많이 있습니다. 여기에 방법을 어떻게 적용할까요?
skan

4
이 부분은 무엇을 의미합니까 : "(Q. \\ ..) \\. (.)"거기에서 무슨 일이 일어나고 있는지 해독하려면 무엇을 검색해야합니까?
mob

3
정규 표현식 @mob
해들리

1
@mob "(Q. \\ ..) \\. (.)"는 "question"및 "loop_number"로 추출 할 정규식 그룹을 정의하는 괄호가있는 정규식입니다. 보다 구체적으로,이 예에서 "Q. \\ .."표현식이있는 키의 항목은 "질문"열 (예 : "Q3.2"및 "Q3.3")으로 이동 한 다음 다음 부분으로 이동합니다. 마침표는 "."로 표시되며 "loop_number"열에 들어갑니다.
LC-datascientist

31

이것은 reshape. dplyr그래도 가능합니다 .

  colnames(df) <- gsub("\\.(.{2})$", "_\\1", colnames(df))
  colnames(df)[2] <- "Date"
  res <- reshape(df, idvar=c("id", "Date"), varying=3:8, direction="long", sep="_")
  row.names(res) <- 1:nrow(res)

   head(res)
  #  id       Date time       Q3.2       Q3.3
  #1  1 2009-01-01    1  1.3709584  0.4554501
  #2  2 2009-01-02    1 -0.5646982  0.7048373
  #3  3 2009-01-03    1  0.3631284  1.0351035
  #4  4 2009-01-04    1  0.6328626 -0.6089264
  #5  5 2009-01-05    1  0.4042683  0.5049551
  #6  6 2009-01-06    1 -0.1061245 -1.7170087

또는 사용 dplyr

  library(tidyr)
  library(dplyr)
  colnames(df) <- gsub("\\.(.{2})$", "_\\1", colnames(df))

  df %>%
     gather(loop_number, "Q3", starts_with("Q3")) %>% 
     separate(loop_number,c("L1", "L2"), sep="_") %>% 
     spread(L1, Q3) %>%
     select(-L2) %>%
     head()
  #  id       time       Q3.2       Q3.3
  #1  1 2009-01-01  1.3709584  0.4554501
  #2  1 2009-01-01  1.3048697  0.2059986
  #3  1 2009-01-01 -0.3066386  0.3219253
  #4  2 2009-01-02 -0.5646982  0.7048373
  #5  2 2009-01-02  2.2866454 -0.3610573
  #6  2 2009-01-02 -1.7813084 -0.7838389

최신 정보

를 사용하면 여러 열의 모양을 변경하는 데 tidyr_0.8.3.9000사용할 수 있습니다 pivot_longer. ( gsub위에서 변경된 열 이름 사용 )

library(dplyr)
library(tidyr)
df %>% 
    pivot_longer(cols = starts_with("Q3"), 
          names_to = c(".value", "Q3"), names_sep = "_") %>% 
    select(-Q3)
# A tibble: 30 x 4
#      id time         Q3.2    Q3.3
#   <int> <date>      <dbl>   <dbl>
# 1     1 2009-01-01  0.974  1.47  
# 2     1 2009-01-01 -0.849 -0.513 
# 3     1 2009-01-01  0.894  0.0442
# 4     2 2009-01-02  2.04  -0.553 
# 5     2 2009-01-02  0.694  0.0972
# 6     2 2009-01-02 -1.11   1.85  
# 7     3 2009-01-03  0.413  0.733 
# 8     3 2009-01-03 -0.896 -0.271 
#9     3 2009-01-03  0.509 -0.0512
#10     4 2009-01-04  1.81   0.668 
# … with 20 more rows

참고 : 입력 데이터 세트를 생성 할 때 설정된 시드가 없기 때문에 값이 다릅니다.


워, 완벽하게 작동합니다. tidyr은 외관상으로는 모양 변경을위한 교체 / 업그레이드입니다. @hadley가 dplyr 또는 tidyr로 이와 동일한 작업을 수행하는 방법을 알고 있는지 궁금합니다.
Andrew

그것은 순수한 마법입니다. 내가 추가 한 유일한 것은 mutate(loop_number = as.numeric(L2))드롭하기 전이고 L2완벽합니다.
앤드류

1
@Andrew 저는 개인적 reshape으로 압축 코드에 대한 방법을 선호 하지만 dplyr큰 데이터 세트의 경우 더 빠를 수 있습니다.
akrun 2014 년

1
나는 reshape()기능 을 이해할 수 없었고 , 내가보기에 꽤 깔끔한 구현으로 보이는 내 솔루션을 참조하십시오.
hadley

22

에 대한 최근 업데이트를 통해 melt.data.table이제 여러 기둥을 녹일 수 있습니다. 이를 통해 다음을 수행 할 수 있습니다.

require(data.table) ## 1.9.5
melt(setDT(df), id=1:2, measure=patterns("^Q3.2", "^Q3.3"), 
     value.name=c("Q3.2", "Q3.3"), variable.name="loop_number")
 #    id       time loop_number         Q3.2        Q3.3
 # 1:  1 2009-01-01           1 -0.433978480  0.41227209
 # 2:  2 2009-01-02           1 -0.567995351  0.30701144
 # 3:  3 2009-01-03           1 -0.092041353 -0.96024077
 # 4:  4 2009-01-04           1  1.137433487  0.60603396
 # 5:  5 2009-01-05           1 -1.071498263 -0.01655584
 # 6:  6 2009-01-06           1 -0.048376809  0.55889996
 # 7:  7 2009-01-07           1 -0.007312176  0.69872938

여기 에서 개발 버전을 얻을 수 있습니다 .


안녕하세요. age1, age2, weight1, weight2, blood1, blood2와 같이 이름이 1과 2로 끝나는 열이 많이 있습니다. 여기에 방법을 어떻게 적용할까요?
skan

skan, 모양 변경 비 네트를 확인하십시오 . 행운을 빕니다!
Arun

나는했지만 정규식을 올바르게 포함하여 열 이름을 분할하고 녹이기 위해 전달하는 방법을 모르겠습니다. 패턴이있는 예는 하나 뿐이며 너무 간단합니다. 제 경우에는 pattern () 안에 많은 열 이름을 포함해야합니다
skan

다음과 같은 열이 있다고 가정합니다. paste0 (rep (LETTERS, each = 3), 1 : 3) 문자와 숫자로 정의 된 긴 테이블을 가져
오려고합니다

이것은 가장 간결하고 해석하기 쉽습니다.
Michael Bellhouse

10

"tidyr"및 "dplyr"와는 전혀 관련이 없지만 고려해야 할 또 다른 옵션이 있습니다. merged.stackfrom my "splitstackshape"package , V1.4.0 이상.

library(splitstackshape)
merged.stack(df, id.vars = c("id", "time"), 
             var.stubs = c("Q3.2.", "Q3.3."),
             sep = "var.stubs")
#     id       time .time_1       Q3.2.       Q3.3.
#  1:  1 2009-01-01      1. -0.62645381  1.35867955
#  2:  1 2009-01-01      2.  1.51178117 -0.16452360
#  3:  1 2009-01-01      3.  0.91897737  0.39810588
#  4:  2 2009-01-02      1.  0.18364332 -0.10278773
#  5:  2 2009-01-02      2.  0.38984324 -0.25336168
#  6:  2 2009-01-02      3.  0.78213630 -0.61202639
#  7:  3 2009-01-03      1. -0.83562861  0.38767161
# <<:::SNIP:::>>
# 24:  8 2009-01-08      3. -1.47075238 -1.04413463
# 25:  9 2009-01-09      1.  0.57578135  1.10002537
# 26:  9 2009-01-09      2.  0.82122120 -0.11234621
# 27:  9 2009-01-09      3. -0.47815006  0.56971963
# 28: 10 2009-01-10      1. -0.30538839  0.76317575
# 29: 10 2009-01-10      2.  0.59390132  0.88110773
# 30: 10 2009-01-10      3.  0.41794156 -0.13505460
#     id       time .time_1       Q3.2.       Q3.3.

1
안녕하세요. age1, age2, weight1, weight2, blood1, blood2와 같이 이름이 1과 2로 끝나는 열이 많이 있습니다. 여기에 방법을 어떻게 적용할까요?
skan

6

당신이 나와 같고 "그룹을 캡처하는 정규식"을 사용하는 방법을 알아낼 수없는 extract경우 다음 코드 extract(...)는 Hadleys의 대답에 있는 줄을 복제합니다 .

df %>% 
    gather(question_number, value, starts_with("Q3.")) %>%
    mutate(loop_number = str_sub(question_number,-2,-2), question_number = str_sub(question_number,1,4)) %>%
    select(id, time, loop_number, question_number, value) %>% 
    spread(key = question_number, value = value)

여기서 문제는 초기 수집이 실제로 두 키의 조합 인 키 열을 형성한다는 것입니다. mutate이 열을 동일한 정보, loop_number열 및 열로 두 개의 열로 분할하기 위해 주석의 원래 솔루션에서 사용하기로 결정했습니다 question_number. spread그런 다음 키 값 쌍인 긴 형식 데이터를 변환하는 데 사용할 수 있습니다.(question_number, value) 를 넓은 형식 데이터 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.