Converti un dataframe in una matrice di assenza di presenza

Ho una tabella che ha un numero diverso di elementi in formato stringa

File1 ABC File2 ABD File3 EF 

Voglio convertire in un formato come segue

  ABCDEF File1 1 1 1 0 0 0 FIle2 1 1 0 1 0 0 File3 0 0 0 0 1 1 

Ho provato a farlo usando reshape2 ma non ha avuto successo.

Dati di esempio:

 mydata <- structure(list(V1 = c("File1", "File2", "File3"), V2 = c("A", "A", "E"), V3 = c("B", "B", "F"), V4 = c("C", "D", "")), .Names = c("V1", "V2", "V3", "V4"), class = "data.frame", row.names = c(NA, -3L)) 

    Una possibilità:

     library(reshape2) df2 <- melt(df, id.var = "V1") with(df2, table(V1, value)) # value # V1 ABCDEF # File1 1 1 1 0 0 0 # File2 1 1 0 1 0 0 # File3 0 0 0 0 1 1 

    Un approccio ragionevolmente efficiente consiste nell’utilizzare la funzione charMat (attualmente) non esportata dal mio pacchetto “splitstackshape”. Dal momento che non è esportato, dovrai usare ::: per accedervi.

     library(splitstackshape) cbind(mydata[1], splitstackshape:::charMat( split.default(mydata[-1], sequence(ncol(mydata)-1)), fill=0)) # V1 V1 ABCDEF # 1 File1 0 1 1 1 0 0 0 # 2 File2 0 1 1 0 1 0 0 # 3 File3 1 0 0 0 0 1 1 

    Sotto il cofano, charMat fa uso dell’indicizzazione della matrice per elaborare tutto in modo abbastanza efficiente. Passo dopo passo, questo è ciò che fa charMat .

     X <- split.default(mydata[-1], sequence(ncol(mydata)-1)) len <- length(X) vec <- unlist(X, use.names=FALSE) lvl <- sort(unique(vec)) out <- matrix(0L, nrow = len, ncol = length(lvl), dimnames = list(NULL, lvl)) i.idx <- rep(seq.int(len), vapply(X, length, integer(1L))) j.idx <- match(vec, lvl) out[cbind(i.idx, j.idx)] <- 1 out # ABCDEF # [1,] 0 1 1 1 0 0 0 # [2,] 0 1 1 0 1 0 0 # [3,] 1 0 0 0 0 1 1 

    Sembra un boccone, ma in realtà è un'operazione abbastanza veloce, resa più veloce utilizzando la funzione charMat 🙂


    Aggiornamento: Benchmark

    I seguenti benchmark testano la risposta di Henrik con la mia risposta charMat , e adattano la risposta di Henrik all'uso "data.table", invece, per una migliore efficienza.

    Sono stati eseguiti due test. Il primo è su un set di dati simile con righe 90K e il secondo su uno con righe 900K.

    Ecco i dati di esempio:

     biggerdata <- do.call(rbind, replicate(30000, mydata, simplify = FALSE)) biggerdata$V1 <- make.unique(biggerdata$V1) dim(biggerdata) # [1] 90000 4 evenBigger <- do.call(rbind, replicate(10, biggerdata, simplify = FALSE)) evenBigger$V1 <- make.unique(evenBigger$V1) dim(evenBigger) # [1] 900000 4 

    Ecco le funzioni per il benchmark:

     fun1 <- function(indf) { cbind(indf[1], splitstackshape:::charMat( split.default(indf[-1], sequence(ncol(indf)-1)), fill=0)) } library(reshape2) fun2 <- function(indf) { df2 <- melt(indf, id.var = "V1") with(df2, table(V1, value)) } library(data.table) library(reshape2) DT <- data.table(biggerdata) DT2 <- data.table(evenBigger) fun3 <- function(inDT) { DTL <- melt(inDT, id.vars="V1") dcast.data.table(DTL, V1 ~ value, fun.aggregate=length) } 

    E i risultati del benchmarking.

     library(microbenchmark) microbenchmark(fun1(biggerdata), fun2(biggerdata), fun3(DT), times = 20) # Unit: milliseconds # expr min lq median uq max neval # fun1(biggerdata) 185.3652 199.8725 289.0206 308.5826 327.4185 20 # fun2(biggerdata) 1453.8791 1605.6053 1639.8567 1758.3984 1797.2229 20 # suppressMessages(fun3(DT)) 469.8979 570.4664 586.4715 598.6229 675.2961 20 microbenchmark(fun1(evenBigger), fun2(evenBigger), fun3(DT2), times = 5) # Unit: seconds # expr min lq median uq max neval # fun1(evenBigger) 1.871611 1.896351 2.071355 2.140580 2.464569 5 # fun2(evenBigger) 26.911523 27.212910 27.363442 27.469812 27.938178 5 # fun3(DT2) 7.103615 7.131603 7.141908 7.205006 7.218321 5