2015-02-19 11 views
7

mam matrycę (1000 x 2830), jak to:podzielone elementy kolumna łańcuch w rzędzie wewnątrz dataframe

 9178 3574 3547 
160  B_B  B_B  A_A 
301  B_B  A_B  A_B 
303  B_B  B_B  A_A 
311  A_B  A_B  A_A 
312  B_B  A_B  A_A 
314  B_B  A_B  A_A 

i chcą uzyskać następujące (kopiowe colnames i rozbicie każdy element każdej kolumny) :

 9178 9178 3574 3574 3547 3547 
160  B  B  B  B  A  A 
301  B  B  A  B  A  B 
303  B  B  B  B  A  A 
311  A  B  A  B  A  A 
312  B  B  A  B  A  A 
314  B  B  A  B  A  A 

próbowałem za pomocą strsplit ale mam komunikaty o błędach, ponieważ jest to macierz, a nie ciąg. Czy możesz podać kilka pomysłów na rozwiązanie tego problemu?

+0

Żaden z nich nie wygląda jak matryca w tej chwili. Czy możesz spróbować sformatować to trochę inaczej? – LauriK

+1

Rozważ użycie prefiksu lub sufiksu w nazwach kolumn, takich jak 9178_1 i 9178_2, do _avoid_ zduplikowanych nazw kolumn (co znacznie utrudnia wybór późniejszych kolumn). –

Odpowiedz

7

Oto opcja korzystania dplyr (dla bind_cols) i tidyr (dla separate_) wraz z lapply od podstawy R. Zakłada ona, że ​​dane jest data.frame (czyli może być konieczne, aby przekształcić go data.frame pierwszy) :

library(dplyr) 
library(tidyr) 

lapply(names(df), function(x) separate_(df[x], x, paste0(x,"_",1:2), sep = "_")) %>% 
    bind_cols 
# X9178_1 X9178_2 X3574_1 X3574_2 X3547_1 X3547_2 
#1  B  B  B  B  A  A 
#2  B  B  A  B  A  B 
#3  B  B  B  B  A  A 
#4  A  B  A  B  A  A 
#5  B  B  A  B  A  A 
#6  B  B  A  B  A  A 
+1

Dziękuję bardzo, ta opcja działa idealnie !!!!! – July

+0

Wydaje się jednak, że brakuje rupii - potrzebna jest drobna modyfikacja. – A5C1D2H2I1M1N2O1R2T1

+1

@July, czy interesują Cię oryginalne nazwy wierszy? Czy zawierają one wszelkie potrzebne informacje? –

4

coś można zrobić, choć wydaje się nieco "skręcony" (yourmat jest swoim matrycy) ...:

inter<-data.frame(t(sapply(as.vector(yourmat), function(x) { 
               strsplit(x, "_")[[1]] 
              })), 
        row.names=paste0(rep(colnames(yourmat), e=nrow(yourmat)), 1:nrow(yourmat)), 
        stringsAsFactors=F) 
res<-do.call("cbind", 
       split(inter, factor(substr(row.names(inter), 1, 4), level = colnames(yourmat)))) 
res 
#  9178.X1 9178.X2 3574.X1 3574.X2 3547.X1 3547.X2 
# 91781  B  B  B  B  A  A 
# 91782  B  B  A  B  A  B 
# 91783  B  B  B  B  A  A 
# 91784  A  B  A  B  A  A 
# 91785  B  B  A  B  A  A 
# 91786  B  B  A  B  A  A 

Edycja
Jeśli chcesz row.names z res być taka sama, jak w yourmat, można zrobić:

row.names(res)<-row.names(yourmat) 

NB: Jeśli yourmat jest data.frame zamiast matrix funkcja w pierwszym as.vector linia musi zostać zmieniona na unlist.

+1

Wielkie dzięki, próbowałem również tej opcji i chociaż jest nieco bardziej skomplikowana niż odpowiedź na to pytanie, zadziałało. Dzięki jeszcze raz! – July

+0

Nie rozumiem, dlaczego przenieśliście 'colnames' na' rownames' tutaj ... – A5C1D2H2I1M1N2O1R2T1

+0

@AnandaMahto, to znaczy dla 'inter' data.frame? to jest tak, że mogę później podzielić dane.frame według poprzednich kolumn (ponieważ jest to macierz, tracę nazwy przy użyciu 'as.vector') – Cath

6

Jestem stronniczy, ale polecam używanie cSplit z mojego pakietu "splitstackshape". Ponieważ wydaje się, że masz rownames w wejściu, należy as.data.table(., keep.rownames = TRUE):

library(splitstackshape) 
cSplit(as.data.table(mydf, keep.rownames = TRUE), names(mydf), "_") 
#  rn X9178_1 X9178_2 X3574_1 X3574_2 X3547_1 X3547_2 
# 1: 160  B  B  B  B  A  A 
# 2: 301  B  B  A  B  A  B 
# 3: 303  B  B  B  B  A  A 
# 4: 311  A  B  A  B  A  A 
# 5: 312  B  B  A  B  A  A 
# 6: 314  B  B  A  B  A  A 

Mniej czytelne niż cSplit (ale obecnie prawdopodobny być szybciej) byłoby użyć stri_split_fixed z „stringi”, tak:

library(stringi) 
`dimnames<-`(do.call(cbind, 
        lapply(mydf, stri_split_fixed, "_", simplify = TRUE)), 
      list(rownames(mydf), rep(colnames(mydf), each = 2))) 
#  X9178 X9178 X3574 X3574 X3547 X3547 
# 160 "B" "B" "B" "B" "A" "A" 
# 301 "B" "B" "A" "B" "A" "B" 
# 303 "B" "B" "B" "B" "A" "A" 
# 311 "A" "B" "A" "B" "A" "A" 
# 312 "B" "B" "A" "B" "A" "A" 
# 314 "B" "B" "A" "B" "A" "A" 

Jeśli prędkość jest najważniejszy, proponuję sprawdzić na "iotools" package, zwłaszcza funkcję mstrsplit. Podejście byłoby podobne do podejścia „stringi”:

library(iotools) 
`dimnames<-`(do.call(cbind, 
       lapply(mydf, mstrsplit, "_", ncol = 2, type = "character")), 
      list(rownames(mydf), rep(colnames(mydf), each = 2))) 

może trzeba dodać lapply(mydf, as character) tam jeśli zapomniałeś użyć stringsAsFactors = FALSE podczas konwersji z matrix do data.frame, ale powinna jeszcze pokonać nawet Podejście stri_split.

+1

Drugie rozwiązanie jest niezwykle szybkie (patrz benchmark poniżej) –

2

roztwór zasady R bez użycia ramek danych:

# split 
z <- unlist(strsplit(m,'_')) 
M <- matrix(c(z[c(T,F)],z[c(F,T)]),nrow=nrow(m)) 

# properly order columns 
i <- 1:ncol(M) 
M <- M[,order(c(i[c(T,F)],i[c(F,T)]))] 

# set dimnames 
rownames(M) <- rownames(m) 
colnames(M) <- rep(colnames(m),each=2) 

# 9178 9178 3574 3574 3547 3547 
# 160 "B" "B" "A" "B" "B" "A" 
# 301 "B" "A" "A" "B" "B" "B" 
# 303 "B" "B" "A" "B" "B" "A" 
# 311 "A" "A" "A" "B" "B" "A" 
# 312 "B" "A" "A" "B" "B" "A" 
# 314 "B" "A" "A" "B" "B" "A" 

[Update] Oto mały badaniach porównawczych z proponowanych rozwiązań (nie zawierać roztwór cSplit ponieważ zbyt wolno) :

Setup:

m <- matrix('A_B',nrow=1000,ncol=2830) 
d <- as.data.frame(m, stringsAsFactors = FALSE) 

##### 
f.mtrx <- function(m) { 
    z <- unlist(strsplit(m,'_')) 
    M <- matrix(c(z[c(T,F)],z[c(F,T)]),nrow=nrow(m)) 

    # properly order columns 
    i <- 1:ncol(M) 
    M <- M[,order(c(i[c(T,F)],i[c(F,T)]))] 

    # set dimnames 
    rownames(M) <- rownames(m) 
    colnames(M) <- rep(colnames(m),each=2) 
    M 
} 

library(stringi) 
f.mtrx2 <- function(m) { 
    z <- unlist(stri_split_fixed(m,'_')) 
    M <- matrix(c(z[c(T,F)],z[c(F,T)]),nrow=nrow(m)) 

    # properly order columns 
    i <- 1:ncol(M) 
    M <- M[,order(c(i[c(T,F)],i[c(F,T)]))] 

    # set dimnames 
    rownames(M) <- rownames(m) 
    colnames(M) <- rep(colnames(m),each=2) 
    M 
} 

##### 
library(splitstackshape) 
f.cSplit <- function(mydf) cSplit(as.data.table(mydf, keep.rownames = TRUE), names(mydf), "_") 

##### 
library(stringi) 
f.stringi <- function(mydf) `dimnames<-`(do.call(cbind, 
        lapply(mydf, stri_split_fixed, "_", simplify = TRUE)), 
      list(rownames(mydf), rep(colnames(mydf), each = 2))) 

##### 
library(dplyr) 
library(tidyr) 

f.dplyr <- function(df) lapply(names(df), function(x) separate_(df[x], x, paste0(x,"_",1:2), sep = "_")) %>% 
    bind_cols 

##### 
library(iotools) 
f.mstrsplit <- function(mydf) `dimnames<-`(do.call(cbind, 
        lapply(mydf, mstrsplit, "_", ncol = 2, type = "character")), 
      list(rownames(mydf), rep(colnames(mydf), each = 2))) 



##### 
library(rbenchmark) 

benchmark(f.mtrx(m), f.mtrx2(m), f.dplyr(d), f.stringi(d), f.mstrsplit(d), replications = 10) 

Wyniki:

 test replications elapsed relative user.self sys.self user.child sys.child 
3  f.dplyr(d)   10 27.722 10.162 27.360 0.269   0   0 
5 f.mstrsplit(d)   10 2.728 1.000  2.607 0.098   0   0 
1  f.mtrx(m)   10 37.943 13.909 34.885 0.799   0   0 
2  f.mtrx2(m)   10 15.176 5.563 13.936 0.802   0   0 
4 f.stringi(d)   10 8.107 2.972  7.815 0.247   0   0 

W uaktualnionej odniesienia, zwycięzcą jest f.mstrsplit.

+0

Wydaje mi się, że 'f.stringi' jest najszybszym więc nie jest jasne, jak spotkała się data.table hod wygrać tutaj. – akrun

+0

Jeśli naprawdę chcesz wygrać, sprawdź aktualizację u dołu mojej odpowiedzi. – A5C1D2H2I1M1N2O1R2T1

+0

@AnandaMahto, OP wspomniana matryca o rozmiarze 1000x2830, więc myślę, że wydajność jest tutaj kluczowa. Twoje najnowsze rozwiązanie, 'f.mstrsplit()', jest zwycięzcą na dużych macierzach. –