Split-kolom van het gegevensframe in meerdere kolommen

Ik zou graag gegevens van het formulier

willen nemen

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

Gebruik split()op de kolom “type” van boven om zoiets te krijgen:

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

Ik kwam met iets ongelofelijk complex met een bepaalde vorm van applydie werkte, maar ik heb sindsdien dat gedaan. Het leek veel te gecompliceerd om de beste manier te zijn. Ik kan strsplitzoals hieronder gebruiken, maar onduidelijk hoe u dat terug kunt krijgen in 2 kolommen in het gegevensframe.

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

Bedankt voor alle aanwijzingen. Ik heb nog niet helemaal geroosterd R-lijsten.


1, Autoriteit 100%

Gebruik stringr::str_split_fixed

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

2, Autoriteit 66%

Een andere optie is om het nieuwe Tatyr-pakket te gebruiken.

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

Antwoord 3, autoriteit 25%

5 jaar later de verplichte data.table-oplossing toegevoegd

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

We kunnen er ook voor zorgen dat de resulterende kolommen de juiste typen hebben ende prestaties verbeteren door type.converten fixedargumenten toe te voegen ( aangezien "_and_"niet echt een regex is)

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

Antwoord 4, autoriteit 20%

Nog een andere benadering: gebruik rbindop 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"

En om te combineren:

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

Antwoord 5, autoriteit 13%

Merk op dat sapply met “[” kan worden gebruikt om het eerste of tweede item in die lijsten te extraheren, dus:

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

en hier is een GSUB-methode:

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

6, Autoriteit 10%

Hier is een een voering in dezelfde lijnen als de oplossing van Aniko, maar met behulp van Hadley’s StringR-pakket:

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

7, Autoriteit 8%

Om toe te voegen aan de opties, kunt u ook mijn splitstackshape::cSplitFunctie zoals deze gebruiken:

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

8, Autoriteit 5%

Een eenvoudige manier is om sapply()en de [-functie:

te gebruiken

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_')

Bijvoorbeeld:

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

sapply()‘S-resultaat is een matrix en moet worden omgezet en teruggeeft naar een gegevensframe. Het is dan enkele eenvoudige manipulaties die het resultaat opleveren dat u wilde:

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

Op dit moment is afterwat je wilde

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

Antwoord 9, autoriteit 5%

Het onderwerp is bijnauitgeput, ik zou echter graag een oplossing willen bieden voor een iets algemenere versie waar je a priori niet het aantal uitvoerkolommen weet. Dus je hebt bijvoorbeeld

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

We kunnen dplyr separate()niet gebruiken omdat we het aantal resultaatkolommen voor de splitsing niet weten, dus heb ik een functie gemaakt die gebruikmaakt van stringrom een kolom te splitsen, gegeven het patroon en een naamprefix voor de gegenereerde kolommen. Ik hoop dat de gebruikte coderingspatronen correct zijn.

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)
}

We kunnen dan split_into_multiplein een DLEYR-pijp als volgt gebruiken:

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>

En dan kunnen we gatherom op te ruimen …

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

10, Autoriteit 3%

Hier is een basis r één voering die een aantal eerdere oplossingen overlapt, maar retourneert een gegevens. Frame met de juiste namen.

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

Het gebruikt strsplitom de variabele en data.framemet do.call/ rbindom de gegevens weer in een gegevens te plaatsen. Frame. De aanvullende incrementele verbetering is het gebruik van setNamesom variabele namen toe te voegen aan de gegevens. Frame.


11, Autoriteit 2%

Deze vraag is vrij oud, maar ik zal de oplossing toevoegen die ik op dit moment de eenvoudigste vond.

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

Antwoord 12, autoriteit 2%

Sinds R-versie 3.4.0 kunt u strcapture()gebruiken uit het utils-pakket (meegeleverd met basis R-installaties), waarbij de uitvoer aan de andere kolom(men) wordt gebonden ).

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

Antwoord 13, autoriteit 2%

basis maar waarschijnlijk traag:

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

Antwoord 14

Een andere benadering als je bij strsplit()wilt blijven, is door de opdracht unlist()te gebruiken. Hier is een oplossing in die zin.

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")

Other episodes