कॉलम के कई सेट इकट्ठा करें


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मैं अब एक समाधान पर काम कर रहा हूँ: पी
एलेक्स

इसे इस्तेमाल करे! 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()एस ...
एंड्रयू

जवाबों:


146

यह दृष्टिकोण मुझे बहुत स्वाभाविक लगता है:

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

5
हैलो। मेरे पास 1 और 2 में समाप्त होने वाले नामों के साथ कई कॉलम हैं, जैसे age1, age2, weight1, weight2, blood1, blood2 .... मैं यहां आपकी विधि कैसे लागू करूंगा?
स्कैन

4
इस भाग का क्या अर्थ है: "(Q। \\ ..) \\। (?)" मैं क्या खोज रहा हूं कि वहां क्या घट रहा है?
भीड़

3
@ मम नियमित अभिव्यक्ति
हडले

1
@mob "(Q। \\ ..) \\। (।)" कोष्ठक के साथ एक नियमित अभिव्यक्ति है जो "प्रश्न" और "लूप_नंबर" निकालने के लिए नियमित अभिव्यक्ति के समूहों को परिभाषित करता है। विशेष रूप से, इस उदाहरण में, "Q। \\ .." अभिव्यक्ति के साथ कुंजी में आइटम "प्रश्न" कॉलम (यानी, "Q3.2" और "Q3.3") में जाते हैं, फिर अगले भाग के बाद अवधि, "।" के रूप में व्यक्त, "लूप_नंबर" कॉलम में जाता है।
एलसी-डेट्राइटिसिस्ट

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 मूल रूप से reshape के लिए एक प्रतिस्थापन / उन्नयन है - मुझे आश्चर्य है कि अगर @hadley को duspr या tidyr के साथ ऐसा ही करने का तरीका पता है ...
एंड्रयू

वह शुद्ध जादू है। केवल एक चीज जो मैंने जोड़ी थी वह mutate(loop_number = as.numeric(L2))छोड़ने से पहले थी L2, और यह एकदम सही है।
एंड्रयू

1
@ और मुझे व्यक्तिगत रूप से reshapeइसके कॉम्पैक्ट कोड के लिए विधि पसंद है , हालांकि dplyrबड़े डेटासेट के लिए तेज़ हो सकता है।
एकरून

1
मैं कभी भी reshape()फ़ंक्शन को समझने में सक्षम नहीं हुआ , मेरे समाधान को देखें जो मुझे लगता है कि मुझे एक बहुत अच्छा टिडियर कार्यान्वयन है।
हैडली

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

आप यहां से विकास संस्करण प्राप्त कर सकते हैं


हैलो। मेरे पास 1 और 2 में समाप्त होने वाले नामों के साथ कई कॉलम हैं, जैसे age1, age2, weight1, weight2, blood1, blood2 .... मैं यहां आपकी विधि कैसे लागू करूंगा?
स्कैन

Skan, जाँच देगी शब्दचित्र । सौभाग्य!
अरुण

मैंने किया लेकिन मुझे नहीं पता कि कॉलम नामों को विभाजित करने और इसे पिघलाने के लिए नियमित अभिव्यक्तियों को ठीक से कैसे एम्बेड किया जाए। पैटर्न के साथ केवल एक उदाहरण है, और यह बहुत सरल है। मेरे मामले में मुझे पैटर्न के अंदर कई कॉलम नाम शामिल करने की आवश्यकता होगी ()
स्कैन

कल्पना कीजिए कि आपके पास ये कॉलम हैं: पेस्ट0 (प्रतिनिधि (प्रत्येक, 3 = 1), 1: 3) और आप एक पत्र और एक नंबर द्वारा परिभाषित लंबी तालिका प्राप्त करना चाहते हैं
स्कैन

यह सबसे सफल और व्याख्या करने में आसान है।
माइकल बेलहाउस 18

10

यह "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.

1
हैलो। मेरे पास 1 और 2 में समाप्त होने वाले नामों के साथ कई कॉलम हैं, जैसे age1, age2, weight1, weight2, blood1, blood2 .... मैं यहां आपकी विधि कैसे लागू करूंगा?
स्कैन

6

यदि आप मेरे जैसे हैं, और वर्कआउट समूहों के साथ "नियमित अभिव्यक्ति का उपयोग करने के तरीके" का उपयोग नहीं कर सकते हैं extract, तो निम्नलिखित कोड extract(...)हेडलीज़ के उत्तर में लाइन को दोहराता है :

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)व्यापक फॉर्म डेटा के लिए महत्वपूर्ण मूल्य जोड़े हैं ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.