एक स्तंभ में अलग-अलग पंक्तियों में कॉमा-पृथक स्ट्रिंग्स को विभाजित करें


109

मेरे पास एक डेटा फ्रेम है, जैसे:

data.frame(director = c("Aaron Blaise,Bob Walker", "Akira Kurosawa", 
                        "Alan J. Pakula", "Alan Parker", "Alejandro Amenabar", "Alejandro Gonzalez Inarritu", 
                        "Alejandro Gonzalez Inarritu,Benicio Del Toro", "Alejandro González Iñárritu", 
                        "Alex Proyas", "Alexander Hall", "Alfonso Cuaron", "Alfred Hitchcock", 
                        "Anatole Litvak", "Andrew Adamson,Marilyn Fox", "Andrew Dominik", 
                        "Andrew Stanton", "Andrew Stanton,Lee Unkrich", "Angelina Jolie,John Stevenson", 
                        "Anne Fontaine", "Anthony Harvey"), AB = c('A', 'B', 'A', 'A', 'B', 'B', 'B', 'A', 'B', 'A', 'B', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B', 'A'))

जैसा कि आप देख सकते हैं, directorकॉलम में कुछ प्रविष्टियां कॉमा द्वारा अलग किए गए कई नाम हैं। मैं अन्य कॉलम के मूल्यों को बनाए रखते हुए इन प्रविष्टियों को अलग-अलग पंक्तियों में विभाजित करना चाहूंगा। एक उदाहरण के रूप में, ऊपर डेटा फ्रेम में पहली पंक्ति को दो पंक्तियों में विभाजित किया जाना चाहिए, जिसमें directorकॉलम में प्रत्येक नाम और कॉलम में 'ए' होगा AB


2
बस स्पष्ट पूछने के लिए: क्या यह डेटा आपको इंटरव्यू पर पोस्ट करना चाहिए?
रिकार्डो सपोर्ट

1
वे "सभी बी फिल्में नहीं थे"। काफी मासूम लगता है।
मैथ्यू लुंडबर्ग

24
ये सभी लोग एकेडमी अवार्ड के नामांकित व्यक्ति हैं, जो मुझे शायद ही लगता है कि एक रहस्य है =)
RoyalTS

जवाबों:


79

इस पुराने प्रश्न को अक्सर ठगी लक्ष्य (टैग के साथ r-faq) के रूप में उपयोग किया जा रहा है । आज तक, यह तीन बार 6 अलग-अलग तरीकों की पेशकश का जवाब दिया गया है, लेकिन एक बेंचमार्क की कमी है क्योंकि मार्गदर्शन में कौन सा दृष्टिकोण सबसे तेज 1 है

बेंचमार्क किए गए समाधानों में शामिल हैं

microbenchmarkपैकेज का उपयोग करके डेटा फ्रेम के 6 विभिन्न आकारों पर कुल मिलाकर 8 अलग-अलग तरीकों को बेंचमार्क किया गया था (नीचे कोड देखें)।

ओपी द्वारा दिए गए सैंपल डेटा में केवल 20 पंक्तियाँ होती हैं। बड़े डेटा फ्रेम बनाने के लिए, इन 20 पंक्तियों को केवल 1, 10, 100, 1000, 10000 और 100000 बार दोहराया जाता है जो 2 मिलियन पंक्तियों तक की समस्या का आकार देते हैं।

बेंचमार्क परिणाम

यहां छवि विवरण दर्ज करें

बेंचमार्क परिणाम बताते हैं कि पर्याप्त रूप से बड़े डेटा फ़्रेम के लिए सभी data.tableविधियाँ किसी अन्य विधि की तुलना में तेज़ हैं। लगभग 5000 से अधिक पंक्तियों वाले डेटा फ़्रेम के लिए, जैप की data.tableविधि 2 और संस्करण DT3सबसे तेज़ हैं, सबसे धीमी विधियों की तुलना में तेज़ी से परिमाण।

उल्लेखनीय रूप से, दो tidyverseतरीकों और splistackshapeसमाधान का समय इतना समान है कि चार्ट में घटता को भेदना मुश्किल है। वे सभी डेटा फ़्रेम आकारों में बेंचमार्क विधियों की सबसे धीमी हैं।

छोटे डेटा फ्रेम के लिए, मैट का आधार आर सॉल्यूशन और data.tableविधि 4 में अन्य तरीकों की तुलना में कम ओवरहेड लगता है।

कोड

director <- 
  c("Aaron Blaise,Bob Walker", "Akira Kurosawa", "Alan J. Pakula", 
    "Alan Parker", "Alejandro Amenabar", "Alejandro Gonzalez Inarritu", 
    "Alejandro Gonzalez Inarritu,Benicio Del Toro", "Alejandro González Iñárritu", 
    "Alex Proyas", "Alexander Hall", "Alfonso Cuaron", "Alfred Hitchcock", 
    "Anatole Litvak", "Andrew Adamson,Marilyn Fox", "Andrew Dominik", 
    "Andrew Stanton", "Andrew Stanton,Lee Unkrich", "Angelina Jolie,John Stevenson", 
    "Anne Fontaine", "Anthony Harvey")
AB <- c("A", "B", "A", "A", "B", "B", "B", "A", "B", "A", "B", "A", 
        "A", "B", "B", "B", "B", "B", "B", "A")

library(data.table)
library(magrittr)

समस्या आकार के बेंचमार्क रन के लिए फ़ंक्शन को परिभाषित करें n

run_mb <- function(n) {
  # compute number of benchmark runs depending on problem size `n`
  mb_times <- scales::squish(10000L / n , c(3L, 100L)) 
  cat(n, " ", mb_times, "\n")
  # create data
  DF <- data.frame(director = rep(director, n), AB = rep(AB, n))
  DT <- as.data.table(DF)
  # start benchmarks
  microbenchmark::microbenchmark(
    matt_mod = {
      s <- strsplit(as.character(DF$director), ',')
      data.frame(director=unlist(s), AB=rep(DF$AB, lengths(s)))},
    jaap_DT1 = {
      DT[, lapply(.SD, function(x) unlist(tstrsplit(x, ",", fixed=TRUE))), by = AB
         ][!is.na(director)]},
    jaap_DT2 = {
      DT[, strsplit(as.character(director), ",", fixed=TRUE), 
         by = .(AB, director)][,.(director = V1, AB)]},
    jaap_dplyr = {
      DF %>% 
        dplyr::mutate(director = strsplit(as.character(director), ",")) %>%
        tidyr::unnest(director)},
    jaap_tidyr = {
      tidyr::separate_rows(DF, director, sep = ",")},
    cSplit = {
      splitstackshape::cSplit(DF, "director", ",", direction = "long")},
    DT3 = {
      DT[, strsplit(as.character(director), ",", fixed=TRUE),
         by = .(AB, director)][, director := NULL][
           , setnames(.SD, "V1", "director")]},
    DT4 = {
      DT[, .(director = unlist(strsplit(as.character(director), ",", fixed = TRUE))), 
         by = .(AB)]},
    times = mb_times
  )
}

विभिन्न समस्या आकार के लिए बेंचमार्क चलाएँ

# define vector of problem sizes
n_rep <- 10L^(0:5)
# run benchmark for different problem sizes
mb <- lapply(n_rep, run_mb)

साजिश रचने के लिए डेटा तैयार करें

mbl <- rbindlist(mb, idcol = "N")
mbl[, n_row := NROW(director) * n_rep[N]]
mba <- mbl[, .(median_time = median(time), N = .N), by = .(n_row, expr)]
mba[, expr := forcats::fct_reorder(expr, -median_time)]

चार्ट बनाएं

library(ggplot2)
ggplot(mba, aes(n_row, median_time*1e-6, group = expr, colour = expr)) + 
  geom_point() + geom_smooth(se = FALSE) + 
  scale_x_log10(breaks = NROW(director) * n_rep) + scale_y_log10() + 
  xlab("number of rows") + ylab("median of execution time [ms]") +
  ggtitle("microbenchmark results") + theme_bw()

सत्र जानकारी और पैकेज संस्करण (अंश)

devtools::session_info()
#Session info
# version  R version 3.3.2 (2016-10-31)
# system   x86_64, mingw32
#Packages
# data.table      * 1.10.4  2017-02-01 CRAN (R 3.3.2)
# dplyr             0.5.0   2016-06-24 CRAN (R 3.3.1)
# forcats           0.2.0   2017-01-23 CRAN (R 3.3.2)
# ggplot2         * 2.2.1   2016-12-30 CRAN (R 3.3.2)
# magrittr        * 1.5     2014-11-22 CRAN (R 3.3.0)
# microbenchmark    1.4-2.1 2015-11-25 CRAN (R 3.3.3)
# scales            0.4.1   2016-11-09 CRAN (R 3.3.2)
# splitstackshape   1.4.2   2014-10-23 CRAN (R 3.3.3)
# tidyr             0.6.1   2017-01-10 CRAN (R 3.3.2)

1 मेरी जिज्ञासा को इस शानदार टिप्पणी से बहुत अच्छा लगा! तीव्रता के आदेश तेजी से! इस tidyverseप्रश्न के उत्तर के रूप में बंद किए गए एक प्रश्न के उत्तर के लिए ।


अच्छा! CSplit में सुधार के लिए कमरे की तरह दिखता है और अलग-अलग_रो (जो विशेष रूप से ऐसा करने के लिए डिज़ाइन किए गए हैं)। Btw, cSplit भी एक निश्चित = arg लेता है और एक data.table- आधारित पैकेज होता है, इसलिए हो सकता है कि यह DF के बजाय DT को दे। इसके अलावा fwiw, मुझे नहीं लगता कि कारक से चार में रूपांतरण बेंचमार्क में आता है (क्योंकि इसे शुरू करने के लिए चार होना चाहिए)। मैंने जाँच की और इनमें से कोई भी बदलाव गुणात्मक रूप से परिणामों के लिए कुछ भी नहीं करता है।
फ्रैंक

1
@Frank बेंचमार्क में सुधार लाने और परिणामों पर प्रभाव की जांच के लिए आपके सुझावों के लिए धन्यवाद। इस लेने जाएगा जब के अगले संस्करण के रिलीज के बाद एक अद्यतन कर data.table, dplyrआदि
उवे

मुझे लगता है कि दृष्टिकोण तुलनीय नहीं हैं, कम से कम सभी अवसरों में नहीं हैं, क्योंकि डेटा योग्य दृष्टिकोण केवल "चयनित" स्तंभों के साथ तालिकाओं का उत्पादन करते हैं, जबकि dplyr सभी स्तंभों के साथ एक परिणाम उत्पन्न करता है (विश्लेषण में शामिल नहीं होने वाले और बिना होने के बिना) समारोह में उनके नाम लिखने के लिए)।
फेरो

5
@ फ़िरोज़ा गलत है, डेटाटेबल्स दृष्टिकोण "तालिका" को संशोधित करते हैं, सभी कॉलम रखे जाते हैं, निश्चित रूप से यदि आप उस जगह को संशोधित नहीं करते हैं, तो आपको केवल वही फ़िल्टर की गई कॉपी मिलती है जो आपने मांगी है। संक्षिप्त डेटा में। डेटा अप करने के लिए डेटासेटेबल उत्पादन नहीं होता है, बल्कि डेटासेट को अपडेट करने के लिए डेटाटेबल और डुप्लीयर के बीच वास्तविक अंतर होता है।
तेंसिबाई

1
वास्तव में अच्छी तुलना! हो सकता है आप में जोड़ सकते हैं matt_mod और jaap_dplyr जब कर रही है, strsplit fixed=TRUE। जैसा कि दूसरे के पास है और इससे समय पर प्रभाव पड़ेगा। R 4.0.0 के बाद से , डिफॉल्ट, जब क्रिएट होता data.frameहै stringsAsFactors = FALSE, तो as.characterइसे हटाया जा सकता है।
जीके

94

कई विकल्प:

1) दो तरीकों से :

library(data.table)
# method 1 (preferred)
setDT(v)[, lapply(.SD, function(x) unlist(tstrsplit(x, ",", fixed=TRUE))), by = AB
         ][!is.na(director)]
# method 2
setDT(v)[, strsplit(as.character(director), ",", fixed=TRUE), by = .(AB, director)
         ][,.(director = V1, AB)]

2) ए / मेल:

library(dplyr)
library(tidyr)
v %>% 
  mutate(director = strsplit(as.character(director), ",")) %>%
  unnest(director)

3) के साथ केवल: के साथ tidyr 0.5.0(और बाद में), आप भी बस का उपयोग कर सकते separate_rows:

separate_rows(v, director, sep = ",")

आप convert = TRUEसंख्याओं को स्वचालित रूप से संख्यात्मक कॉलम में बदलने के लिए पैरामीटर का उपयोग कर सकते हैं।

4) आधार के साथ आर:

# if 'director' is a character-column:
stack(setNames(strsplit(df$director,','), df$AB))

# if 'director' is a factor-column:
stack(setNames(strsplit(as.character(df$director),','), df$AB))

क्या एक साथ कई कॉलम के लिए ऐसा करने का कोई तरीका है? उदाहरण के लिए 3 कॉलम जिन्हें प्रत्येक के द्वारा अलग किया गया है ";" प्रत्येक स्तंभ के साथ समान संख्या में तार होते हैं। यानी data.table(id= "X21", a = "chr1;chr1;chr1", b="123;133;134",c="234;254;268")बन रहा है data.table(id = c("X21","X21",X21"), a=c("chr1","chr1","chr1"), b=c("123","133","134"), c=c("234","254","268"))?
रिलस्टीन

1
वाह बस एहसास हुआ कि यह पहले से ही एक साथ कई कॉलम के लिए काम करता है - यह आश्चर्यजनक है!
रिलस्टीन

@ रेलस्टीन क्या आप साझा कर सकते हैं कि आपने इसे कई कॉलमों के लिए कैसे अनुकूलित किया? मेरे पास एक ही उपयोग का मामला है, लेकिन अनिश्चित है कि इसके बारे में कैसे जाना जाए।
चंद्रमा_वर्कर

1
@Moon_Watcher विधि 1 से ऊपर के उत्तर में पहले से ही कई कॉलमों के लिए काम करता है, जो कि मुझे लगा कि यह आश्चर्यजनक था। setDT(dt)[,lapply(.SD, function(x) unlist(tstrsplit(x, ";",fixed=TRUE))), by = ID]मेरे लिए काम किया है।
रिलस्टीन

51

अपने मूल डेटा का नामकरण। फ़्रेम v, हमारे पास यह है:

> s <- strsplit(as.character(v$director), ',')
> data.frame(director=unlist(s), AB=rep(v$AB, sapply(s, FUN=length)))
                      director AB
1                 Aaron Blaise  A
2                   Bob Walker  A
3               Akira Kurosawa  B
4               Alan J. Pakula  A
5                  Alan Parker  A
6           Alejandro Amenabar  B
7  Alejandro Gonzalez Inarritu  B
8  Alejandro Gonzalez Inarritu  B
9             Benicio Del Toro  B
10 Alejandro González Iñárritu  A
11                 Alex Proyas  B
12              Alexander Hall  A
13              Alfonso Cuaron  B
14            Alfred Hitchcock  A
15              Anatole Litvak  A
16              Andrew Adamson  B
17                 Marilyn Fox  B
18              Andrew Dominik  B
19              Andrew Stanton  B
20              Andrew Stanton  B
21                 Lee Unkrich  B
22              Angelina Jolie  B
23              John Stevenson  B
24               Anne Fontaine  B
25              Anthony Harvey  A

repनए AB कॉलम बनाने के उपयोग पर ध्यान दें । यहां, sapplyप्रत्येक मूल पंक्तियों में नामों की संख्या लौटाता है।


1
मैं सोच रहा था कि क्या `एबी = प्रतिनिधि (वी $ एबी, अनलिस्ट (नीलम, एसयूएन, एफयूएन = लंबाई)))` और अधिक अस्पष्ट से समझ आसान हो सकता है vapply? क्या ऐसा कुछ है जो vapplyयहां अधिक उपयुक्त है?
IRTFM

7
आजकल के sapply(s, length)साथ प्रतिस्थापित किया जा सकता है lengths(s)
रिच स्क्रिप्‍ट

31

पार्टी के लिए देर हो चुकी है, लेकिन एक और सामान्यीकृत विकल्प cSplitमेरे "स्प्लिटस्टैक्सैप" पैकेज से उपयोग करना है जिसमें एक directionतर्क है। "long"आपके द्वारा निर्दिष्ट परिणाम प्राप्त करने के लिए इसे सेट करें:

library(splitstackshape)
head(cSplit(mydf, "director", ",", direction = "long"))
#              director AB
# 1:       Aaron Blaise  A
# 2:         Bob Walker  A
# 3:     Akira Kurosawa  B
# 4:     Alan J. Pakula  A
# 5:        Alan Parker  A
# 6: Alejandro Amenabar  B

2
devtools::install_github("yikeshu0611/onetree")

library(onetree)

dd=spread_byonecolumn(data=mydata,bycolumn="director",joint=",")

head(dd)
            director AB
1       Aaron Blaise  A
2         Bob Walker  A
3     Akira Kurosawa  B
4     Alan J. Pakula  A
5        Alan Parker  A
6 Alejandro Amenabar  B

0

एक अन्य बेंचमार्क जिसके परिणामस्वरूप आधार का उपयोग strsplitकिया जा सकता है, वर्तमान में एक स्तंभ में अलग-अलग पंक्तियों में अल्पविराम से अलग तारों को विभाजित करने की सिफारिश की जा सकती है , क्योंकि यह आकारों की एक विस्तृत श्रृंखला में सबसे तेज़ था:

s <- strsplit(v$director, ",", fixed=TRUE)
s <- data.frame(director=unlist(s), AB=rep(v$AB, lengths(s)))

ध्यान दें कि उपयोग करने fixed=TRUEका समय पर महत्वपूर्ण प्रभाव पड़ता है।

पंक्तियों की संख्या से अधिक गणना समय दिखाते हुए वक्र

तुलना के तरीके:

met <- alist(base = {s <- strsplit(v$director, ",") #Matthew Lundberg
   s <- data.frame(director=unlist(s), AB=rep(v$AB, sapply(s, FUN=length)))}
 , baseLength = {s <- strsplit(v$director, ",") #Rich Scriven
   s <- data.frame(director=unlist(s), AB=rep(v$AB, lengths(s)))}
 , baseLeFix = {s <- strsplit(v$director, ",", fixed=TRUE)
   s <- data.frame(director=unlist(s), AB=rep(v$AB, lengths(s)))}
 , cSplit = s <- cSplit(v, "director", ",", direction = "long") #A5C1D2H2I1M1N2O1R2T1
 , dt = s <- setDT(v)[, lapply(.SD, function(x) unlist(tstrsplit(x, "," #Jaap
   , fixed=TRUE))), by = AB][!is.na(director)]
#, dt2 = s <- setDT(v)[, strsplit(director, "," #Jaap #Only Unique
#  , fixed=TRUE), by = .(AB, director)][,.(director = V1, AB)]
 , dplyr = {s <- v %>%  #Jaap
    mutate(director = strsplit(director, ",", fixed=TRUE)) %>%
    unnest(director)}
 , tidyr = s <- separate_rows(v, director, sep = ",") #Jaap
 , stack = s <- stack(setNames(strsplit(v$director, ",", fixed=TRUE), v$AB)) #Jaap
#, dt3 = {s <- setDT(v)[, strsplit(director, ",", fixed=TRUE), #Uwe #Only Unique
#  by = .(AB, director)][, director := NULL][, setnames(.SD, "V1", "director")]}
 , dt4 = {s <- setDT(v)[, .(director = unlist(strsplit(director, "," #Uwe
   , fixed = TRUE))), by = .(AB)]}
 , dt5 = {s <- vT[, .(director = unlist(strsplit(director, "," #Uwe
   , fixed = TRUE))), by = .(AB)]}
   )

पुस्तकालय:

library(microbenchmark)
library(splitstackshape) #cSplit
library(data.table) #dt, dt2, dt3, dt4
#setDTthreads(1) #Looks like it has here minor effect
library(dplyr) #dplyr
library(tidyr) #dplyr, tidyr

डेटा:

v0 <- data.frame(director = c("Aaron Blaise,Bob Walker", "Akira Kurosawa", 
                        "Alan J. Pakula", "Alan Parker", "Alejandro Amenabar", "Alejandro Gonzalez Inarritu", 
                        "Alejandro Gonzalez Inarritu,Benicio Del Toro", "Alejandro González Iñárritu", 
                        "Alex Proyas", "Alexander Hall", "Alfonso Cuaron", "Alfred Hitchcock", 
                        "Anatole Litvak", "Andrew Adamson,Marilyn Fox", "Andrew Dominik", 
                        "Andrew Stanton", "Andrew Stanton,Lee Unkrich", "Angelina Jolie,John Stevenson", 
                        "Anne Fontaine", "Anthony Harvey"), AB = c('A', 'B', 'A', 'A', 'B', 'B', 'B', 'A', 'B', 'A', 'B', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B', 'A'))

गणना और समय परिणाम:

n <- 10^(0:5)
x <- lapply(n, function(n) {v <- v0[rep(seq_len(nrow(v0)), n),]
  vT <- setDT(v)
  ti <- min(100, max(3, 1e4/n))
  microbenchmark(list = met, times = ti, control=list(order="block"))})

y <- do.call(cbind, lapply(x, function(y) aggregate(time ~ expr, y, median)))
y <- cbind(y[1], y[-1][c(TRUE, FALSE)])
y[-1] <- y[-1] / 1e6 #ms
names(y)[-1] <- paste("n:", n * nrow(v0))
y #Time in ms
#         expr     n: 20    n: 200    n: 2000   n: 20000   n: 2e+05   n: 2e+06
#1        base 0.2989945 0.6002820  4.8751170  46.270246  455.89578  4508.1646
#2  baseLength 0.2754675 0.5278900  3.8066300  37.131410  442.96475  3066.8275
#3   baseLeFix 0.2160340 0.2424550  0.6674545   4.745179   52.11997   555.8610
#4      cSplit 1.7350820 2.5329525 11.6978975  99.060448 1053.53698 11338.9942
#5          dt 0.7777790 0.8420540  1.6112620   8.724586  114.22840  1037.9405
#6       dplyr 6.2425970 7.9942780 35.1920280 334.924354 4589.99796 38187.5967
#7       tidyr 4.0323765 4.5933730 14.7568235 119.790239 1294.26959 11764.1592
#8       stack 0.2931135 0.4672095  2.2264155  22.426373  289.44488  2145.8174
#9         dt4 0.5822910 0.6414900  1.2214470   6.816942   70.20041   787.9639
#10        dt5 0.5015235 0.5621240  1.1329110   6.625901   82.80803   636.1899

नोट, तरीके जैसे

(v <- rbind(v0[1:2,], v0[1,]))
#                 director AB
#1 Aaron Blaise,Bob Walker  A
#2          Akira Kurosawa  B
#3 Aaron Blaise,Bob Walker  A

setDT(v)[, strsplit(director, "," #Jaap #Only Unique
  , fixed=TRUE), by = .(AB, director)][,.(director = V1, AB)]
#         director AB
#1:   Aaron Blaise  A
#2:     Bob Walker  A
#3: Akira Kurosawa  B

निर्देशक केstrsplit लिए वापसी और इसके साथ तुलना की जा सकती हैunique

tmp <- unique(v)
s <- strsplit(tmp$director, ",", fixed=TRUE)
s <- data.frame(director=unlist(s), AB=rep(tmp$AB, lengths(s)))

लेकिन मेरी समझ में, यह नहीं पूछा गया था।

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