2014-11-17 15 views
5

Ho un frame dati che è il risultato di un altro comando. Questo frame di dati ha solo una riga con circa 40000 voci. Il mio problema è che 3 colonne sono un insieme di dati connessi. Pertanto voglio dividere la riga dopo ogni terza colonna e trasportarla come una nuova riga. Esempio:Dividere una riga dopo ogni 3a colonna e trasportare quelle 3 colonne come una nuova riga in r

creare una cornice di dati di test:

df=as.data.frame(matrix(seq(1:12), ncol=12, nrow=1)) 

Ora ho un frame di dati che assomiglia a questo.

V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 
1 2 3 4 5 6 7 8 9 10 11 12 

ma ho bisogno in questo modo:

V1 V2 V3 
1 2 3 
4 5 6 
7 8 9 
10 11 12 

Come posso realizzare questo?

+1

Sembra a me come si dovrebbe risolvere il comando che ha creato il problema! Puoi spiegare come hai finito con un frame di dati super-wide per cominciare? – A5C1D2H2I1M1N2O1R2T1

risposta

4

Prova

as.data.frame(matrix(unlist(df, use.names=FALSE),ncol=3, byrow=TRUE)) 
# V1 V2 V3 
#1 1 2 3 
#2 4 5 6 
#3 7 8 9 
#4 10 11 12 

Oppure si potrebbe usare direttamente matrix sul df

as.data.frame(matrix(df, ncol=3, byrow=TRUE)) 
+0

Non sono sicuro di quale fosse il valore aggiuntivo della terza opzione (o la prima) :) –

+0

@David Arenburg La terza opzione dovrebbe essere ovviamente lenta perché stiamo prendendo il 't'. Ho pubblicato la prima opzione e ho capito che si poteva fare senza usare 'unlist'. – akrun

2

potrebbe anche provare a utilizzare dim<- (solo per conoscenza generale)

as.data.frame(t(`dim<-`(unlist(df), c(3, 4)))) 
# V1 V2 V3 
# 1 1 2 3 
# 2 4 5 6 
# 3 7 8 9 
# 4 10 11 12 
1

Questa si è rivelata più veloce di quanto mi aspettassi (anche se stil Non sono veloce come l'approccio ovvio che ha preso @akrun), quindi pubblicherò questo (come David) "solo per conoscenza generale". (. Inoltre, "data.table" tutte le cose) :-)

Crea una data.table con tre colonne:

  1. I valori non quotati del vostro singola riga.
  2. Una variabile di raggruppamento per indicare a quale riga deve essere assegnato il valore nell'output finale.
  3. Una variabile di raggruppamento per indicare a quale colonna deve essere assegnato il valore nell'output finale.

Una volta che hai, puoi usare dcast.data.table per ottenere l'output che hai menzionato (più una colonna bonus).

Per il punto numero 2 di cui sopra, si può facilmente definire una funzione come la seguente per rendere il processo di creazione di gruppi facile:

groupMaker <- function(vecLen, perGroup) { 
    (0:(vecLen-1) %/% perGroup) + 1 
} 

Quindi possiamo usare come segue:

dcast.data.table(
    data.table(value = unlist(df, use.names = FALSE), 
      row = groupMaker(ncol(df), 3), 
      col = 1:3), 
    row ~ col) 
# row 1 2 3 
# 1: 1 1 2 3 
# 2: 2 4 5 6 
# 3: 3 7 8 9 
# 4: 4 10 11 12 

Ora, lei dice che si sta effettivamente occupando di una colonna a riga singola ~ 40K data.frame (presumo che sia 39.999 colonne poiché è ben divisibile per 3 e non voglio ak le altre risposte).

Tenendo questo a mente, ecco alcuni (inutili) benchmark (inutile perché stiamo parlando di millisecondi qui, davvero).

set.seed(1) 
S <- sample(20, 39999, TRUE) 
S <- data.frame(t(S)) 

funAM <- function(indf) { 
    dcast.data.table(
    data.table(value = unlist(indf, use.names = FALSE), 
       row = groupMaker(ncol(indf), 3), 
       col = 1:3), 
    row ~ col) 
} 

funDA <- function(indf) { 
    as.data.frame(t(`dim<-`(unlist(indf), c(3, ncol(indf)/3)))) 
} 

funAK <- function(indf) as.data.frame(matrix(indf, ncol=3, byrow=TRUE)) 

library(microbenchmark) 
microbenchmark(funAM(S), funDA(S), funAK(S)) 
# Unit: milliseconds 
#  expr  min  lq  mean median  uq  max neval 
# funAM(S) 18.487001 18.813297 22.105766 18.999891 19.455812 50.25876 100 
# funDA(S) 37.187177 37.450893 40.393893 37.870683 38.869726 94.20128 100 
# funAK(S) 5.018571 5.149758 5.929944 5.271679 5.536449 26.93281 100 

Dove questo potrebbe essere utile sarebbe nel caso in cui il numero di colonne desiderate e il numero di colonne di input non sono ben divisibile per l'altro.

Ad esempio, provare i seguenti dati di esempio:

set.seed(1) 
S2 <- sample(20, 40000, TRUE) 
S2 <- data.frame(t(S)) 

Con questi dati di esempio:

  • funAM darebbe una warning, ma sarebbe corretto dare le ultime due colonne della ultima riga come NA.
  • funAK fornirebbe un warning ma (presumibilmente) ricicla correttamente i valori nell'ultima riga.
  • funDA darebbe solo un error.

penso ancora che si dovrebbe solo risolvere il problema alla fonte se :-)