2011-06-20 5 views
6

Je dois coller, élément par élément, le contenu de deux trames de données pour les entrer dans un autre programme. J'ai une trame de données de moyennes et une trame de données d'erreurs standard de la moyenne.Coller ensemble deux trames de données élément par élément dans R

J'ai essayé d'utiliser la fonction R paste(), mais elle ne semble pas pouvoir gérer les trames de données. Lorsque vous utilisez un vecteur, il semble concaténer tous les éléments du premier vecteur dans une chaîne et tous les éléments de la seconde dans une chaîne séparée. Au lieu de cela, j'ai besoin que chaque élément réciproque dans les deux trames de données soit concaténé ensemble.

Des suggestions sur la façon d'aborder cette question? J'ai inclus des données d'entrée fictives (datMean et datSE) et ma sortie désirée (datNew). Mes trames de données réelles ont environ 10 lignes sur 150 colonnes.

# means and SEM 
datMean <- data.frame(a=rnorm(10, 3), b=rnorm(10, 3), d=rnorm(10, 3)) 
datSE <- data.frame(a=rnorm(10, 3)/100, b=rnorm(10, 3)/100, d=rnorm(10, 3)/100) 

# what the output should look like 
# i've chosen some arbitrary values here, and show only the first row. 
datNew <- data.frame(a="2.889-2.926", b="1.342-1.389", d="2.569-2.576") 

L'idée est que chaque élément de datNew être une gamme comprenant des « signifie - se » et « moyenne + SE », séparés par un tiret « - ». La fonction paste() peut le faire pour un élément, comment faire cela sur l'ensemble de l'image?

paste(datMean[1,1] - datSE[1,1], datMean[1,1] + datSE[1,1], sep="-") 

EDIT 1: regardant quelques-unes des réponses que je réalise que je quittai sur un peu d'information importante dans la question. Chaque rangée des trames de données d'origine est nommée, et j'ai besoin de reconstituer la trame de données finale avec ces noms. Par exemple:

rownames(datMean) <- LETTERS[1:10] 
rownames(datSE) <- LETTERS[1:10] 

J'ai besoin de datNew pour éventuellement avoir à nouveau ces 10 noms de base. Cela pourrait être problématique avec certaines des solutions utilisant melt().

Répondre

9

Si vous convertissez d'abord en matrices, vous pouvez le faire sans aucune application ou sans aucune boucle. Vous pouvez également envisager formatC pour une meilleure mise en forme.

lo <- formatC(MdatMean - MdatSE, format="f", digits=3) 
hi <- formatC(MdatMean + MdatSE, format="f", digits=3) 
matrix(paste(lo, hi, sep="-"), 
     nrow=nrow(MdatMean), dimnames=dimnames(MdatMean)) 

Si vous voulez un data.frame à la fin juste envelopper la dernière ligne as.data.frame.

+0

merci beaucoup pour cela - j'ai décidé d'accepter cela comme la meilleure réponse car elle ne repose pas sur des paquets externes et est plus rapide que les alternatives , puisqu'il ne dépend pas de apply ou de boucles. + 1 pour for formatC(), je n'avais pas l'air d'avant et le formatage est bien meilleur. – Steve

2

Vous pouvez le faire sur chaque ligne à la fois, mais vous appliquez des colonnes appariées entre deux noms de données. Puisque vous avez un travail spécifique de la pâte à faire à chaque fois, définir la fonction:

pfun <- function(x, y) paste(x - y, x + y, sep = "-") 

puis construire la nouvelle data.frame avec la fonction:

datNew <- data.frame(a = pfun(datMean$a, datSE$a), b = pfun(datMean$b, datSE$b), d = pfun(datMean$d, datSE$d)) 

Il y aurait des moyens d'appliquer cette TERSER , mais peut-être que cela vous aide à mieux comprendre. Vous pouvez passer des colonnes entières à coller, mais pas des fichiers de données complets.

Utilisez une boucle pour faire correspondre toutes les colonnes du résultat sans les spécifier individuellement.

D'abord créer une liste pour stocker toutes les colonnes, nous allons convertir en data.frame avec les noms de colonnes de droite.

datNew <- vector("list", ncol(datMean)) 

La dénomination ne suppose que nombre de colonnes, les noms et l'ordre sont une correspondance exacte entre les deux data.frames entrée.

names(datNew) <- names(datMean) 

for (i in 1:ncol(datMean)) { 
    datNew[[i]] <- pfun(datMean[[i]], datSE[[i]]) 
} 

Convertir data.frame:

datNew <- as.data.frame(datNew) 
+0

cela fonctionne très bien, merci. Pour un bloc de données de plus de 150 colonnes, cela va être laborieux. Je me demande s'il existe un moyen de l'automatiser davantage ... – Steve

+0

mis à jour pour s'adapter à n'importe quel nombre de colonnes, fwiw – mdsumner

2

Voici comment je comprends votre problème. J'ai fondu les données pour les moyennes et SE de plusieurs colonnes à une colonne en utilisant reshape2::melt.

library(reshape2) 
datMean <- melt(datMean)$value 
datSE <- melt(datSE)$value 
dat <- cbind(datMean, datSE) 

apply(X = dat, MARGIN = 1, FUN = function(x) { 
      paste(x[1] - x[2], x[1] + x[2], sep = " - ") 
     }) 

Et le résultat

[1] "3.03886802467251 - 3.08551547263516" 
[2] "3.01803172559258 - 3.05247871975711" 
[3] "3.4609230722069 - 3.56097173966387" 
[4] "1.35368243309618 - 1.45548512578821" 
[5] "2.39936853846605 - 2.47570756724791" 
[6] "3.21849170272184 - 3.29653660329785" 

EDIT

Cette solution respecte vos dimensions de données d'origine. Ce que je fais est de faire un tableau 3D et de travailler sur chaque cellule à la fois en maintenant la troisième dimension ([x,y, 1:2]) constante.

dat <- array(c(datMean, datSE), dim = c(10, 3, 2)) 

datNEW <- matrix(rep(NA, nrow(dat)*ncol(dat)), ncol = ncol(dat)) 

for (column in seq(ncol(dat))) { 
    cls <- rep(NA, nrow(dat)) 
    for (rows in seq(nrow(dat))) { 
     tmp <- dat[rows, column, 1:2] 
     cls[rows] <- paste(tmp[1] - tmp[2], tmp[1] + tmp[2], sep = " - ") 
    } 
    datNEW[, column] <- cls 
} 
+0

Cela fonctionne bien, mais j'ai oublié de mentionner que j'ai besoin de datNew pour avoir la même structure que les autres trames de données et colnames - voir mon EDIT1). Il semble difficile de le faire avec la fonte et la fonte. – Steve

+0

En effet, @Steve. Voir ma modification qui va suivre dans quelques secondes. –

+0

... aussi, vous pourriez scinder ma première solution pour qu'elle corresponde à vos dimensions. –

6

Voici un moyen de le faire sans spécifier manuellement chaque colonne. D'abord, nous faisons les données et les mettre dans un tableau en utilisant le paquet abind, arrondi à 3 parce que l'air mieux:

datMean <- data.frame(a=rnorm(10, 3), b=rnorm(10, 3), d=rnorm(10, 3)) 
datSE <- data.frame(a=rnorm(10, 3)/100, b=rnorm(10, 3)/100, d=rnorm(10, 3)/100) 

library(abind) 

datArray <- round(abind(datMean,datSE,along=3),3) 

Ensuite, nous pouvons appliquer la fonction paste à chaque élément et la colonne de ce tableau:

apply(datArray,1:2,function(x)paste(x[1]-x[2],"-",x[1]+x[2])) 

     a    b    d    
[1,] "3.537 - 3.581" "3.358 - 3.436" "3.282 - 3.312" 
[2,] "2.452 - 2.516" "1.372 - 1.44" "3.041 - 3.127" 
[3,] "3.017 - 3.101" "3.14 - 3.228" "5.238 - 5.258" 
[4,] "3.397 - 3.451" "2.783 - 2.839" "3.381 - 3.405" 
[5,] "1.918 - 1.988" "2.978 - 3.02" "3.44 - 3.504" 
[6,] "4.01 - 4.078" "3.014 - 3.068" "1.914 - 1.954" 
[7,] "3.475 - 3.517" "2.117 - 2.159" "1.871 - 1.929" 
[8,] "2.551 - 2.619" "3.907 - 3.975" "1.588 - 1.614" 
[9,] "1.707 - 1.765" "2.63 - 2.678" "1.316 - 1.348" 
[10,] "4.051 - 4.103" "3.532 - 3.628" "3.235 - 3.287" 
+0

Merci beaucoup! Cela fonctionne vraiment bien, et est capable de retenir les noms de famille (voir mon edit). Une question: existe-t-il un moyen d'omettre l'espace avant et après le tiret? – Steve

+0

oui, utilisez l'argument 'sep =" "' dans 'paste()' –

Questions connexes