Pytanie Podział kolumny przy ograniczniku w ramce danych [duplikat]


To pytanie już zawiera odpowiedź:

Chciałbym podzielić jedną kolumnę na dwie w ramce danych na podstawie ogranicznika. Na przykład,

a|b
b|c

zostać

a    b
b    c

w ramach danych.

Dzięki!


76
2017-08-15 18:37


pochodzenie


Związane z: stackoverflow.com/questions/7033187/sets-in-r-dataframe/... - Chase


Odpowiedzi:


@Taesung Shin ma rację, ale po prostu trochę więcej magii, aby zrobić z niego data.frame. Dodałem linię "x | y", aby uniknąć niejasności:

df <- data.frame(ID=11:13, FOO=c('a|b','b|c','x|y'))
foo <- data.frame(do.call('rbind', strsplit(as.character(df$FOO),'|',fixed=TRUE)))

Lub, jeśli chcesz zastąpić kolumny w istniejącym data.frame:

within(df, FOO<-data.frame(do.call('rbind', strsplit(as.character(FOO), '|', fixed=TRUE))))

Który produkuje:

  ID FOO.X1 FOO.X2
1 11      a      b
2 12      b      c
3 13      x      y

74
2017-08-15 19:00



Co byś zrobił, gdyby była jedną kolumną w istniejącej dużej ramce danych z setkami kolumn? - Jeff Erickson
dobre pytanie. Zrobiłbym podział, przekształciłbym go w ramkę danych, zmieniam jej nazwę odpowiednio ( rename funkcja z reshape pakiet jest przydatny do robienia tego w locie), a następnie rbind to z istniejącą ramą danych - dodatkowy wysiłek, aby wstawić ją zamiast poprzedniej pojedynczej kolumny, a nie jako pierwszą lub ostatnią kolumnę ... - Ben Bolker
Moja zaktualizowana wersja obsługuje także kilka setek innych kolumn. - Tommy
+1 Jest to najbardziej ogólne rozwiązanie. Może nawet obsłużyć sytuacje, w których różne liczby są oddzielone przez "|". Oczywiście, mamy do czynienia z wynikiem rbind, który może być zawracany do tych wierszy z mniejszą ilością elementów. Ale inne rozwiązanie zawodzi w takim przypadku. - Yu Shen
Bądź ostrożny, length(strsplit('a|', '|', fixed=TRUE)) jest 1 -- tak jak doktorzy say: "... ale jeśli na końcu łańcucha występuje dopasowanie, dane wyjściowe są takie same jak przy usuniętym dopasowaniu." Jak mówi @YuShen, to rozwiązanie zostanie "odtworzone". Dla mnie chciałem tylko pustych przestrzeni, a nie recyklingu. - The Red Pea


Hadley ma bardzo eleganckie rozwiązanie, aby zrobić to w swoich ramkach danych reshape pakiet, za pomocą funkcji colsplit.

require(reshape)
> df <- data.frame(ID=11:13, FOO=c('a|b','b|c','x|y'))
> df
  ID FOO
1 11 a|b
2 12 b|c
3 13 x|y
> df = transform(df, FOO = colsplit(FOO, split = "\\|", names = c('a', 'b')))
> df
  ID FOO.a FOO.b
1 11     a     b
2 12     b     c
3 13     x     y

52
2017-08-15 19:52



Czy istnieje sposób na uzyskanie tego bez wynikowych nazw kolumn, takich jak "FOO.a" i "FOO.b", ale tak jak "a" i "b" (łatwe do zmiany, ale tylko zastanawiające ...)? - Amyunimus
Możesz wykonać następujące czynności with(df, cbind(ID, colsplit(df$FOO, pattern = "\\|", names = c('a', 'b')))). Zwróć uwagę, że parametr split zmienił nazwę na pattern w reshape2 która jest późniejszą wersją reshape pakiet. - Ramnath
Moja edycja jest w tej chwili zablokowana ... Po prostu zaktualizowałbym twoją odpowiedź, by wydrukować df po każdym przypisaniu ... wymagają (przekształcenia)> df <- data.frame (ID = 11: 13, FOO = c ('a | b ',' b | c ',' x | y '))> df ID FOO 1 11 a | b 2 12 b | c 3 13 x | y> df = transform (df, FOO = colsplit (FOO, split = "\\ |", imiona = c ("a", "b")))> df ID FOO.a FOO.b 1 11 ab 2 12 bc 3 13 xy - The Red Pea
Czy istnieje sposób, aby to zrobić, jeśli nie znasz liczby kolumn, które by spowodowały? - The Red Pea
Mały problem z tym rozwiązaniem. Dzieli kolumny na "zagnieżdżoną" ramkę danych, więc jeśli potrzebujesz użyć danych dla wykresu za pomocą ggplot2, nazwy kolumn nie są rozpoznawane. Znalazłem separate funkcja w tidyr bardziej przydatny do tego celu. Zobacz odpowiedź wg @Gregor - Alison Bennett


Nowo popularne tidyr pakiet robi to za pomocą separate. Używa wyrażeń regularnych, więc będziesz musiał uciec |

df <- data.frame(ID=11:13, FOO=c('a|b', 'b|c', 'x|y'))
separate(data = df, col = FOO, into = c("left", "right"), sep = "\\|")

  ID left right
1 11    a     b
2 12    b     c
3 13    x     y

choć w tym przypadku wartości domyślne są wystarczająco inteligentne, aby działały (szukają znaków niealfanumerycznych do podziału).

separate(data = df, col = FOO, into = c("left", "right"))

45
2018-02-19 17:28



To rozwiązanie jest niesamowicie proste. Dzięki!! - Francisco QV


Właśnie natknąłem się na to pytanie, ponieważ było ono połączone ostatnie pytanie na temat SO.

Bezwstydna wtyczka odpowiedzi: Użyj cSplit z mojego pakietu "splitstackshape":

df <- data.frame(ID=11:13, FOO=c('a|b','b|c','x|y'))
library(splitstackshape)
cSplit(df, "FOO", "|")
#   ID FOO_1 FOO_2
# 1 11     a     b
# 2 12     b     c
# 3 13     x     y

Ta konkretna funkcja obsługuje również dzielenie wielu kolumn, nawet jeśli każda kolumna ma inne ograniczenie:

df <- data.frame(ID=11:13, 
                 FOO=c('a|b','b|c','x|y'), 
                 BAR = c("A*B", "B*C", "C*D"))
cSplit(df, c("FOO", "BAR"), c("|", "*"))
#   ID FOO_1 FOO_2 BAR_1 BAR_2
# 1 11     a     b     A     B
# 2 12     b     c     B     C
# 3 13     x     y     C     D

Zasadniczo jest to wygodne opakowanie do wygodnego używania read.table(text = some_character_vector, sep = some_sep) i powiązanie tego wyjścia z oryginałem data.frame. Innymi słowy, inny Podstawowym podejściem R może być:

df <- data.frame(ID=11:13, FOO=c('a|b','b|c','x|y'))
cbind(df, read.table(text = as.character(df$FOO), sep = "|"))
  ID FOO V1 V2
1 11 a|b  a  b
2 12 b|c  b  c
3 13 x|y  x  y

30
2017-12-05 16:12



Szczególnie podoba mi się to, że nie musisz definiować nazw kolumn, do których przechodzą dane - tospig
Zgadzam się z Tospig. Uwielbiam tę natywną metodę read.table(c('a|b','c|d'), '|') może zrobić lewę, jeśli tworzymy nowy plik data.frame, mimo że SO mówił o "w [istniejącej] ramce danych". - The Red Pea
Wadą pliku read.table, sep może być tylko jeden bajt, a separator mieszający jest interpretowany jako komentarz? read.table(text=c('a#b'), sep='#') produkuje tylko jedną kolumnę - spodziewałem się dwóch kolumn. - The Red Pea
@ TheRedPea, teraz w telefonie, ale nie możesz uzyskać pożądanego rezultatu, określając także znak komentarza w read.table tak jak ""? - A5C1D2H2I1M1N2O1R2T1
@AnandaMahto jesteś absolutnie poprawny; w ten sposób powstają 2 kolumny: read.table(text=c('a#b'), sep='#', comment.char = '') a jeśli chodzi o ogranicznik pojedynczego znaku, jeśli przekażę c ("a ~~ b") do gsub, mogę uzyskać separator jednobajtowy, którego potrzebuję: gsub('~~','~', c('a~~b')) - The Red Pea


strsplit(c('a|b','b|c'),'|',fixed=TRUE)

6
2017-08-15 18:52





Łączenie odpowiedzi @Ramnath i @ Tommy pozwoliło mi znaleźć podejście, które działa w bazie R dla jednej lub więcej kolumn.

Podstawowe użycie:

> df = data.frame(
+   id=1:3, foo=c('a|b','b|c','c|d'), 
+   bar=c('p|q', 'r|s', 's|t'), stringsAsFactors=F)
> transform(df, test=do.call(rbind, strsplit(foo, '|', fixed=TRUE)), stringsAsFactors=F)
  id foo bar test.1 test.2
1  1 a|b p|q      a      b
2  2 b|c r|s      b      c
3  3 c|d s|t      c      d

Wiele kolumn:

> transform(df, lapply(list(foo,bar),
+ function(x)do.call(rbind, strsplit(x, '|', fixed=TRUE))), stringsAsFactors=F)
  id foo bar X1 X2 X1.1 X2.1
1  1 a|b p|q  a  b    p    q
2  2 b|c r|s  b  c    r    s
3  3 c|d s|t  c  d    s    t

Lepsze nazewnictwo wielu kolumn podzielonych:

> transform(df, lapply({l<-list(foo,bar);names(l)=c('foo','bar');l}, 
+                          function(x)do.call(rbind, strsplit(x, '|', fixed=TRUE))), stringsAsFactors=F)
  id foo bar foo.1 foo.2 bar.1 bar.2
1  1 a|b p|q     a     b     p     q
2  2 b|c r|s     b     c     r     s
3  3 c|d s|t     c     d     s     t

4
2018-02-19 17:21