4 votes

Réorganiser le cadre de données - R

J'ai un cadre de données qui ressemble à ceci :

       a        b       c         d
ab    0        0        1         0
cd   -0.415    1.415    0         0
ef    0        0        0.0811    0.918

Existe-t-il un moyen simple de transformer ce tableau en :

       a        b       c         d
ab    0        0        1         0
cd   -0.415    0        0         0
cd    0    1.415        0         0
ef    0        0        0.0811    0
ef    0        0        0         0.918

S'il y a deux nombres ou plus dans le tableau original, je veux le transformer en nombres de lignes correspondants. Je n'ai pas la moindre idée de la manière de le faire et toute aide serait la bienvenue.

4voto

flodel Points 41487

Voici une façon de le faire, en utilisant l'indexation matricielle. (Les données sont transformées en matrice, il est donc préférable que vos données soient d'un seul type, comme cela semble être le cas dans votre exemple).

reformat.dat <- function(dat) {
  tdat <- t(dat)
  nz <- tdat != 0
  i <- col(tdat)[nz]
  j <- row(tdat)[nz]
  out <- matrix(0, sum(nz), ncol(dat))
  out[cbind(seq_len(sum(nz)), j)] <- tdat[nz]
  rownames(out) <- rownames(dat)[i]
  colnames(out) <- colnames(dat)
  out
}

reformat.dat(dat)
#         a     b      c     d
# ab  0.000 0.000 1.0000 0.000
# cd -0.415 0.000 0.0000 0.000
# cd  0.000 1.415 0.0000 0.000
# ef  0.000 0.000 0.0811 0.000
# ef  0.000 0.000 0.0000 0.918

4voto

Jack Ryan Points 862

J'en emprunte à @AnandaMahto et je les fais fondre selon votre demande. Veuillez considérer : toute combinaison unique que vous souhaitez examiner va sur le côté gauche~ les valeurs de la variable vont sur le côté droit. Dans ce cas, les noms des variables sont devenus des valeurs.

library(reshape2)
mydf <- structure(list(a = c(0, -0.415, 0), b = c(0, 1.415, 0), 
                       c = c(1, 0, 0.0811), d = c(0, 0, 0.918)), 
                  .Names = c("a", "b", "c", "d"), 
                  class = "data.frame", row.names = c("ab", "cd", "ef"))
mydf$rows<- rownames(mydf)
m1<- melt(mydf, id="rows", measured= names(mydf))
m2<- dcast(m1, rows+value~..., fill=0)
m2<- m2[m2$value!=0, ]
m2$value <- NULL    

#rows      a     b      c     d
#2   ab  0.000 0.000 1.0000 0.000
#3   cd -0.415 0.000 0.0000 0.000
#5   cd  0.000 1.415 0.0000 0.000
#7   ef  0.000 0.000 0.0811 0.000
#8   ef  0.000 0.000 0.0000 0.918

2voto

Arun Points 41689

Voici une solution simple qui utilise diag :

o <- apply(df, 1, function(x) {
    t <- diag(x)
    colnames(t) <- names(x)
    t <- t[rowSums(t == 0) != length(x), ,drop = FALSE]
    t
})
ids <- rep(names(o), sapply(o, nrow))
o <- do.call(rbind, o)
row.names(o) <- ids

#         a     b      c     d
# ab  0.000 0.000 1.0000 0.000
# cd -0.415 0.000 0.0000 0.000
# cd  0.000 1.415 0.0000 0.000
# ef  0.000 0.000 0.0811 0.000
# ef  0.000 0.000 0.0000 0.918

Il s'agit d'un matrix . Utilisez as.data.frame(.) si vous avez besoin d'un data.frame .

1voto

Ananda Mahto Points 67213

Voici une approche, mais vous devrez ensuite procéder à quelques modifications cosmétiques pour fixer les noms des lignes.

Vos données sous une forme reproductible :

mydf <- structure(list(a = c(0, -0.415, 0), b = c(0, 1.415, 0), 
                       c = c(1, 0, 0.0811), d = c(0, 0, 0.918)), 
                  .Names = c("a", "b", "c", "d"), 
                  class = "data.frame", row.names = c("ab", "cd", "ef"))

Remplacer les zéros par NA s :

mydf[mydf == 0] <- NA

stack votre data.frame pour en faire un "long" data.frame :

mydf1 <- data.frame(Rows = rownames(mydf), stack(mydf))

Générer des valeurs uniques pour les "Rows"

mydf1$Rows <- make.unique(as.character(mydf1$Rows))
# Let's see what we have so far....
mydf1
#    Rows  values ind
# 1    ab      NA   a
# 2    cd -0.4150   a
# 3    ef      NA   a
# 4  ab.1      NA   b
# 5  cd.1  1.4150   b
# 6  ef.1      NA   b
# 7  ab.2  1.0000   c
# 8  cd.2      NA   c
# 9  ef.2  0.0811   c
# 10 ab.3      NA   d
# 11 cd.3      NA   d
# 12 ef.3  0.9180   d

Maintenant, il suffit d'utiliser xtabs pour obtenir le résultat que vous recherchez. Enveloppez-le dans as.data.frame.matrix si vous voulez un data.frame et nettoyez les noms de lignes si nécessaire.

as.data.frame.matrix(xtabs(values ~ Rows + ind, mydf1))
#           a     b      c     d
# ab.2  0.000 0.000 1.0000 0.000
# cd   -0.415 0.000 0.0000 0.000
# cd.1  0.000 1.415 0.0000 0.000
# ef.2  0.000 0.000 0.0811 0.000
# ef.3  0.000 0.000 0.0000 0.918

-1voto

themel Points 5718

Je ne pense pas qu'il y ait une version élégante de ce que vous demandez précisément, mais vous pouvez peut-être utiliser melt de reshape2 à la place ? Cela vous donnera une ligne par paire ligne/colonne :

> library(reshape2) 
> # add row names as column
> df <- cbind(df, names=rownames(df))
> df <- melt(df,id.var="names")
Using  as id variables
> df[df$value != 0,]
   names variable   value
2     cd        a -0.4150
5     cd        b  1.4150
7     ab        c  1.0000
9     ef        c  0.0811
12    ef        d  0.9180

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X