Соберите несколько наборов столбцов

У меня есть данные из онлайн-опроса, где респонденты проходят цикл вопросов 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() , которая отлично tidyr для объединения одного набора столбцов:

 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 , но заканчивается 90 строками вместо 30 (все комбинации из 10 человек, 3 петли Q3.2 , и 3 петли Q3.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(key, value, -id, -time) %>% extract(key, c("question", "loop_number"), "(Q.\\..)\\.(.)") %>% spread(question, value) 

    Сначала соберите все столбцы вопросов, используйте extract() чтобы разделить на question и loop_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 

    Это можно сделать, 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 

    С недавним обновлением до 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 

    Вы можете получить версию разработки отсюда .

    Это совсем не связано с «tidyr» и «dplyr», но вот еще один вариант: merged.stack из моего пакета «splitstackshape» , 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. 

    Если вы похожи на меня и не можете решить, как использовать «регулярное выражение с группами захвата» для 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) 

    Проблема здесь в том, что начальная assembly формирует ключевой столбец, который на самом деле представляет собой комбинацию из двух ключей. Я решил использовать mutate в своем исходном решении в комментариях, чтобы разбить этот столбец на два столбца с эквивалентной информацией, столбец loop_number столбец question_number . spread затем может использоваться для преобразования данных длинной формы, которые являются парами ключевых значений (question_number, value) для широкоформатных данных.

    Interesting Posts

    Как подключиться к локальной сети через VPN-маршрутизатор

    Монитор неправильно отображает поддерживаемое разрешение

    Как мне разрешить ввод номера в мое консольное приложение C #?

    Android AudioRecord class – быстро запускает живой микрофон, настраивает функцию обратного вызова

    Как получить размер экрана устройства?

    Почему Java неявно (без cast) конвертирует `long` в` float`?

    Передача параметров в рельсах redirect_to

    Тайм-аут брандмауэра

    Порядок инициализации статических полей в статическом classе

    NullPointerException в invokeLater при запуске через Java Webstart

    Java-цикл игры (живопись) замораживает мое окно

    Как я могу восстановить полные привилегии пользователя root root?

    Как добавить тень в FAB, предоставляемую библиотекой дизайна поддержки Android?

    Как изменить fragmentы с помощью Android-навигатора

    Почему не рекомендуется использовать attach () в R, и что я должен использовать вместо этого?

    Давайте будем гением компьютера.