डेटा कॉलम स्ट्रिंग कॉलम को कई कॉलम में विभाजित करें


246

मैं फॉर्म का डेटा लेना चाहता हूं

before = data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2'))
  attr          type
1    1   foo_and_bar
2   30 foo_and_bar_2
3    4   foo_and_bar
4    6 foo_and_bar_2

और इस तरह से कुछ पाने के लिए ऊपर से split()" type" कॉलम का उपयोग करें:

  attr type_1 type_2
1    1    foo    bar
2   30    foo  bar_2
3    4    foo    bar
4    6    foo  bar_2

मैं अविश्वसनीय रूप से कुछ जटिल के साथ आया था जिसमें applyउस काम के कुछ रूप शामिल थे , लेकिन मैं तब से गलत है। यह सबसे अच्छा तरीका होने के लिए बहुत जटिल लग रहा था। मैं strsplitनीचे के रूप में उपयोग कर सकता हूं , लेकिन फिर यह स्पष्ट नहीं कर सकता कि डेटा फ्रेम में उस 2 कॉलम में वापस कैसे लाया जाए।

> strsplit(as.character(before$type),'_and_')
[[1]]
[1] "foo" "bar"

[[2]]
[1] "foo"   "bar_2"

[[3]]
[1] "foo" "bar"

[[4]]
[1] "foo"   "bar_2"

किसी भी संकेत के लिए धन्यवाद। मैंने अभी तक R सूचियों को बिलकुल नहीं बनाया है।

जवाबों:


280

उपयोग stringr::str_split_fixed

library(stringr)
str_split_fixed(before$type, "_and_", 2)

2
यह मेरी समस्या के लिए आज भी ठीक काम किया है .. लेकिन यह प्रत्येक पंक्ति की शुरुआत में एक 'सी' जोड़ रहा था। किसी भी विचार क्यों है ??? left_right <- str_split_fixed(as.character(split_df),'\">',2)
LearneR

मैं एक पैटर्न के साथ विभाजित करना चाहूंगा जिसमें "..." है, जब मैं उस फ़ंक्शन को लागू करता हूं, तो यह कुछ भी नहीं देता है। क्या समस्या हो सकती है। मेरा प्रकार कुछ "टेस्ट ... स्कोर"
जैसा है

2
@ user3841581 - आप की पुरानी जानकारी मुझे पता है, लेकिन यह प्रलेखन में शामिल है - तर्क में "एक निश्चित स्ट्रिंग से मिलान" के str_split_fixed("aaa...bbb", fixed("..."), 2)साथ ठीक काम करता है । रेगेक्स में 'किसी भी वर्ण' का अर्थ है। fixed()pattern=.
Thelatemail

थैंक्स हैडली, बहुत ही कॉन्फिडेंट मेथड, लेकिन एक चीज और बेहतर हो सकती है, अगर ओरिजिनल कॉलम में NA है, तो अलग होने के बाद यह रिजल्ट कॉलम में सेवरल खाली स्ट्रिंग बन जाएगा, जो अवांछित है, मैं NA को अभी भी NA रखना चाहता हूं। जुदाई
बादलों की भीड़

अच्छी तरह से काम करता है यानी यदि विभाजक गायब है! अगर मेरे पास एक वेक्टर 'ए <-c ("1 एन", "2 एन") है, तो मैं कॉलम' 1,1, "एन", "एन" "एन '' मैं 'को' str_split_fixed (s, ') में अलग करना चाहूंगा। ", 2) '। मुझे यकीन नहीं है कि इस दृष्टिकोण में अपने नए कॉलम को कैसे नाम दिया जाए, 'col1 <-c (1,1)' और 'col2 <-c ("N", "N")
maycca

174

एक अन्य विकल्प नए टिडियर पैकेज का उपयोग करना है।

library(dplyr)
library(tidyr)

before <- data.frame(
  attr = c(1, 30 ,4 ,6 ), 
  type = c('foo_and_bar', 'foo_and_bar_2')
)

before %>%
  separate(type, c("foo", "bar"), "_and_")

##   attr foo   bar
## 1    1 foo   bar
## 2   30 foo bar_2
## 3    4 foo   bar
## 4    6 foo bar_2

अलग-अलग विभाजन की संख्या को सीमित करने का एक तरीका है? मान लें कि मैं '_' पर केवल एक बार विभाजन करना चाहता हूं (या str_split_fixedमौजूदा डेटाफ़्रेम में कॉलम जोड़ना और करना )?
जेलेनाउक्लिना

67

5 साल बाद अनिवार्य data.tableसमाधान जोड़ना

library(data.table) ## v 1.9.6+ 
setDT(before)[, paste0("type", 1:2) := tstrsplit(type, "_and_")]
before
#    attr          type type1 type2
# 1:    1   foo_and_bar   foo   bar
# 2:   30 foo_and_bar_2   foo bar_2
# 3:    4   foo_and_bar   foo   bar
# 4:    6 foo_and_bar_2   foo bar_2

हम दोनों यह भी सुनिश्चित कर सकते हैं कि परिणामी कॉलम में सही प्रकार होंगे और जोड़ने type.convertऔर fixedतर्क द्वारा प्रदर्शन में सुधार होगा (क्योंकि "_and_"वास्तव में एक रेगीक्स नहीं है)

setDT(before)[, paste0("type", 1:2) := tstrsplit(type, "_and_", type.convert = TRUE, fixed = TRUE)]

यदि आपके '_and_'पैटर्न की संख्या अलग-अलग है, तो आप max(lengths(strsplit(before$type, '_and_')))
बजे के

यह मेरा पसंदीदा उत्तर है, बहुत अच्छा काम करता है! क्या आप बता सकते हैं कि यह कैसे काम करता है। क्यों पक्षांतरित (strsplit (...)) और तार श्रृंखलाबद्ध के लिए paste0 नहीं है - बंटवारे उन्हें नहीं ...
छिपकली

1
@ जीको मुझे यकीन नहीं है कि सवाल क्या है। यदि आप इसका उपयोग करते हैं तो strsplitयह प्रत्येक स्लॉट में 2 मानों के साथ एक एकल वेक्टर बनाता है, इसलिए प्रत्येक में एक मान के साथ tstrsplitइसे 2 वैक्टर में स्थानांतरित करता है। paste0इसका उपयोग केवल स्तंभ नामों को बनाने के लिए किया जाता है, इसका उपयोग मूल्यों पर नहीं किया जाता है। समीकरण के LHS पर कॉलम के नाम हैं, RHS पर कॉलम पर विभाजन + ट्रांसपोज़ ऑपरेशन है। :=" जगह में असाइन करें " के लिए खड़ा है , इसलिए आप <-वहां असाइनमेंट ऑपरेटर नहीं देखते हैं ।
डेविड ऐरनबर्ग

58

अभी तक एक और दृष्टिकोण: rbindपर उपयोग out:

before <- data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2'))  
out <- strsplit(as.character(before$type),'_and_') 
do.call(rbind, out)

     [,1]  [,2]   
[1,] "foo" "bar"  
[2,] "foo" "bar_2"
[3,] "foo" "bar"  
[4,] "foo" "bar_2"

और गठबंधन करने के लिए:

data.frame(before$attr, do.call(rbind, out))

4
नए आर संस्करणों पर एक और विकल्प हैstrcapture("(.*)_and_(.*)", as.character(before$type), data.frame(type_1 = "", type_2 = ""))
एलेक्सिस_लाज

37

ध्यान दें कि "[" के साथ नीलम का उपयोग उन सूचियों में पहले या दूसरे आइटम को निकालने के लिए किया जा सकता है:

before$type_1 <- sapply(strsplit(as.character(before$type),'_and_'), "[", 1)
before$type_2 <- sapply(strsplit(as.character(before$type),'_and_'), "[", 2)
before$type <- NULL

और यहाँ एक gsub विधि है:

before$type_1 <- gsub("_and_.+$", "", before$type)
before$type_2 <- gsub("^.+_and_", "", before$type)
before$type <- NULL

32

यहाँ एक लाइनर के साथ एक लाइन के रूप में है aiko समाधान, लेकिन हैडली स्ट्रिंग पैकेज का उपयोग कर:

do.call(rbind, str_split(before$type, '_and_'))

1
अच्छी पकड़, मेरे लिए सबसे अच्छा समाधान। हालांकि stringrपैकेज की तुलना में थोड़ा धीमा ।
मेलका

20

विकल्पों में जोड़ने के लिए, आप splitstackshape::cSplitइस तरह से मेरे फ़ंक्शन का उपयोग कर सकते हैं :

library(splitstackshape)
cSplit(before, "type", "_and_")
#    attr type_1 type_2
# 1:    1    foo    bar
# 2:   30    foo  bar_2
# 3:    4    foo    bar
# 4:    6    foo  bar_2

3 साल बाद - यह विकल्प मेरे पास एक समान समस्या के लिए सबसे अच्छा काम कर रहा है - हालांकि मैं जिस डेटाफ्रेम के साथ काम कर रहा हूं उसमें 54 कॉलम हैं और मुझे उन सभी को दो में विभाजित करने की आवश्यकता है। क्या इस विधि का उपयोग करने का एक तरीका है - ऊपर दिए गए कमांड को 54 बार टाइप करने से कम? बहुत धन्यवाद, निकी।
निकि

@ नीकी, क्या आपने कॉलम नाम या कॉलम पदों की एक वेक्टर प्रदान करने की कोशिश की है? यह करना चाहिए ....
A5C1D2H2I1M1N2O1R2T1

यह केवल स्तंभों का नामकरण नहीं कर रहा था - मुझे अपने df में कॉलमों की संख्या को प्रभावी ढंग से दोगुना करने के लिए स्तंभों को शाब्दिक रूप से विभाजित करने की आवश्यकता थी। नीचे जो मैं अंत में इस्तेमाल किया गया था: df2 <- cSplit (df1, विभाजितकॉल = 1:54, "/")
Nicki

14

एक आसान तरीका उपयोग sapply()और [समारोह है:

before <- data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2'))
out <- strsplit(as.character(before$type),'_and_')

उदाहरण के लिए:

> data.frame(t(sapply(out, `[`)))
   X1    X2
1 foo   bar
2 foo bar_2
3 foo   bar
4 foo bar_2

sapply()परिणाम एक मैट्रिक्स है और एक डेटा फ्रेम में वापस ट्रांसप्स्टिंग और कास्टिंग की आवश्यकता होती है। यह तब कुछ सरल जोड़तोड़ है जो आपके द्वारा वांछित परिणाम प्राप्त करता है:

after <- with(before, data.frame(attr = attr))
after <- cbind(after, data.frame(t(sapply(out, `[`))))
names(after)[2:3] <- paste("type", 1:2, sep = "_")

इस बिंदु पर, afterआप क्या चाहते थे

> after
  attr type_1 type_2
1    1    foo    bar
2   30    foo  bar_2
3    4    foo    bar
4    6    foo  bar_2

12

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

before = data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2', 'foo_and_bar_2_and_bar_3', 'foo_and_bar'))
  attr                    type
1    1             foo_and_bar
2   30           foo_and_bar_2
3    4 foo_and_bar_2_and_bar_3
4    6             foo_and_bar

हम dplyr का उपयोग नहीं कर सकते separate()क्योंकि हम विभाजन से पहले परिणाम कॉलम की संख्या नहीं जानते हैं, इसलिए मैंने तब एक फ़ंक्शन बनाया है stringrजो स्तंभ को विभाजित करने के लिए उपयोग करता है, जो कि दिए गए कॉलम के लिए पैटर्न और एक नाम उपसर्ग दिया गया है। मुझे आशा है कि उपयोग किए गए कोडिंग पैटर्न सही हैं।

split_into_multiple <- function(column, pattern = ", ", into_prefix){
  cols <- str_split_fixed(column, pattern, n = Inf)
  # Sub out the ""'s returned by filling the matrix to the right, with NAs which are useful
  cols[which(cols == "")] <- NA
  cols <- as.tibble(cols)
  # name the 'cols' tibble as 'into_prefix_1', 'into_prefix_2', ..., 'into_prefix_m' 
  # where m = # columns of 'cols'
  m <- dim(cols)[2]

  names(cols) <- paste(into_prefix, 1:m, sep = "_")
  return(cols)
}

हम फिर split_into_multipleएक dplyr पाइप में निम्नानुसार उपयोग कर सकते हैं:

after <- before %>% 
  bind_cols(split_into_multiple(.$type, "_and_", "type")) %>% 
  # selecting those that start with 'type_' will remove the original 'type' column
  select(attr, starts_with("type_"))

>after
  attr type_1 type_2 type_3
1    1    foo    bar   <NA>
2   30    foo  bar_2   <NA>
3    4    foo  bar_2  bar_3
4    6    foo    bar   <NA>

और फिर हम gatherसाफ करने के लिए उपयोग कर सकते हैं ...

after %>% 
  gather(key, val, -attr, na.rm = T)

   attr    key   val
1     1 type_1   foo
2    30 type_1   foo
3     4 type_1   foo
4     6 type_1   foo
5     1 type_2   bar
6    30 type_2 bar_2
7     4 type_2 bar_2
8     6 type_2   bar
11    4 type_3 bar_3

चीयर्स, मुझे लगता है कि यह बेहद उपयोगी है।
त्जेबो

8

यहां एक बेस आर वन लाइनर है जो पिछले कई समाधानों को ओवरलैप करता है, लेकिन उचित नामों के साथ एक डेटा.फ्रेम लौटाता है।

out <- setNames(data.frame(before$attr,
                  do.call(rbind, strsplit(as.character(before$type),
                                          split="_and_"))),
                  c("attr", paste0("type_", 1:2)))
out
  attr type_1 type_2
1    1    foo    bar
2   30    foo  bar_2
3    4    foo    bar
4    6    foo  bar_2

यह strsplitचर को तोड़ने के लिए और डेटा को डेटा में वापस लाने के लिए / के data.frameसाथ उपयोग करता है । अतिरिक्त वृद्धिशील सुधार data.frame में चर नामों को जोड़ने का उपयोग है ।do.callrbindsetNames


6

यह प्रश्न बहुत पुराना है, लेकिन मैं इस समाधान को जोड़ दूंगा जो मुझे वर्तमान में सबसे सरल लगता है।

library(reshape2)
before = data.frame(attr = c(1,30,4,6), type=c('foo_and_bar','foo_and_bar_2'))
newColNames <- c("type1", "type2")
newCols <- colsplit(before$type, "_and_", newColNames)
after <- cbind(before, newCols)
after$type <- NULL
after

यह अब तक का सबसे आसान है जब यह df वैक्टर के प्रबंध की बात आती है
Apricot

5

R संस्करण 3.4.0 के बाद से आप अन्य पैकेज (ओं) पर आउटपुट को बाइंड करते strcapture()हुए, बर्तन पैकेज (बेस आर इंस्टॉल के साथ शामिल) से उपयोग कर सकते हैं ।

out <- strcapture(
    "(.*)_and_(.*)",
    as.character(before$type),
    data.frame(type_1 = character(), type_2 = character())
)

cbind(before["attr"], out)
#   attr type_1 type_2
# 1    1    foo    bar
# 2   30    foo  bar_2
# 3    4    foo    bar
# 4    6    foo  bar_2

4

एक अन्य दृष्टिकोण यदि आप साथ रहना चाहते strsplit()हैं तो unlist()कमांड का उपयोग करना है । यहाँ उन लाइनों के साथ एक समाधान है।

tmp <- matrix(unlist(strsplit(as.character(before$type), '_and_')), ncol=2,
   byrow=TRUE)
after <- cbind(before$attr, as.data.frame(tmp))
names(after) <- c("attr", "type_1", "type_2")

4

आधार लेकिन शायद धीमा:

n <- 1
for(i in strsplit(as.character(before$type),'_and_')){
     before[n, 'type_1'] <- i[[1]]
     before[n, 'type_2'] <- i[[2]]
     n <- n + 1
}

##   attr          type type_1 type_2
## 1    1   foo_and_bar    foo    bar
## 2   30 foo_and_bar_2    foo  bar_2
## 3    4   foo_and_bar    foo    bar
## 4    6 foo_and_bar_2    foo  bar_2

1

यहां एक और आधार आर समाधान है। हम उपयोग कर सकते हैं read.tableलेकिन चूंकि यह केवल एक-बाइट sepतर्क को स्वीकार करता है और यहां हमारे पास मल्टी-बाइट विभाजक है हम gsubमल्टीबाइट विभाजक को किसी एक-बाइट विभाजक को बदलने के लिए उपयोग कर सकते हैं और उस sepतर्क के रूप में उपयोग कर सकते हैंread.table

cbind(before[1], read.table(text = gsub('_and_', '\t', before$type), 
                 sep = "\t", col.names = paste0("type_", 1:2)))

#  attr type_1 type_2
#1    1    foo    bar
#2   30    foo  bar_2
#3    4    foo    bar
#4    6    foo  bar_2

इस मामले में, हम इसे डिफ़ॉल्ट sepतर्क के साथ बदलकर छोटा भी कर सकते हैं, इसलिए हमें इसका स्पष्ट रूप से उल्लेख नहीं करना होगा

cbind(before[1], read.table(text = gsub('_and_', ' ', before$type), 
                 col.names = paste0("type_", 1:2)))
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.