web-dev-qa-db-fra.com

Utiliser cbind sur une liste arbitraire longue d'objets

Je souhaite trouver un moyen de créer une donnée de données. à l'aide de cbind() pour rejoindre de nombreux objets distincts. Par exemple, si A, B, C & D, tous les vecteurs de la même longueur, on peut créer data.frame ABCD avec

ABCD <- cbind(A,B,C,D)

Cependant, lorsque le nombre d'objets à combiner devient grand, il devient fastidieux de taper tous leurs noms. En outre, existe-t-il un moyen d'appeler cbind() sur un vecteur de noms d'objet, par exemple.

objs <- c("A", "B", "C", "D")
ABCD <- cbind(objs)

ou sur une liste contenant tous les objets à combiner, par exemple.

obj.list <- list(A,B,C,D)
ABCD <- cbind(obj.list)

Actuellement, la seule solution de contournement que je peux penser est d'utiliser paste(), cat(), write.table() et source() pour construire les arguments à cbind(), écrivez-le comme un script et source. Cela ressemble à un très mauvais kludge. En outre, j'ai examiné do.call() mais ne semble pas sembler trouver un moyen d'accomplir ce que je veux avec elle.

28
rtyro

Les do.call La fonction est très utile ici:

A <- 1:10
B <- 11:20
C <- 20:11

> do.call(cbind, list(A,B,C))
      [,1] [,2] [,3]
 [1,]    1   11   20
 [2,]    2   12   19
 [3,]    3   13   18
 [4,]    4   14   17
 [5,]    5   15   16
 [6,]    6   16   15
 [7,]    7   17   14
 [8,]    8   18   13
 [9,]    9   19   12
[10,]   10   20   11
31
Prasad Chalasani

Vous devez d'abord get les objets que vous souhaitez et les stocker en tant que liste; Si vous pouvez construire leurs noms comme chaînes, vous utilisez la fonction get. Ici, je crée deux variables, A et B:

> A <- 1:4
> B <- rep(LETTERS[1:2],2)

Je construis ensuite un vecteur de caractère contenant leurs noms (stockés comme ns) et get ces variables utilisant lapply. J'ai ensuite défini les noms de la liste pour être les mêmes que leurs noms d'origine.

> (ns <- LETTERS[1:2])
[1] "A" "B"
> obj.list <- lapply(ns, get)
> names(obj.list) <- ns
> obj.list
$A
[1] 1 2 3 4

$B
[1] "A" "B" "A" "B"

Ensuite, vous pouvez utiliser do.call; Le premier argument est la fonction que vous souhaitez et la seconde est une liste avec les arguments que vous souhaitez transmettre.

> do.call(cbind, obj.list)
     A   B  
[1,] "1" "A"
[2,] "2" "B"
[3,] "3" "A"
[4,] "4" "B"

Cependant, comme al3xa note correctement, cela fait une matrice, pas une image de données, ce qui peut ne pas être ce que vous voulez si les variables sont différentes classes; Ici, mon A a été contraint à un vecteur de caractère au lieu d'un vecteur numérique. Pour créer une trame de données d'une liste, vous appelez simplement data.frame dessus; Ensuite, les classes des variables sont conservées.

> (AB <- data.frame(obj.list))
  A B
1 1 A
2 2 B
3 3 A
4 4 B
> sapply(AB, class)
        A         B 
"integer"  "factor" 
> str(AB)
'data.frame':   4 obs. of  2 variables:
 $ A: int  1 2 3 4
 $ B: Factor w/ 2 levels "A","B": 1 2 1 2
8
Aaron

Cependant, vous devriez nuire à cet esprit que cbind renvoie un vecteur atomique (matrice) lorsqu'il est appliqué uniquement sur des vecteurs atomiques (double dans ce cas). Comme vous pouvez le constater dans les réponses de @ Prasad et @ Aaron, l'objet résultant est une matrice. Si vous spécifiez d'autres vecteurs atomiques (entier, double, logique, complexe) ainsi que le vecteur de caractère, ils seront forcés à caractère. Et puis vous avez un problème - vous devez les convertir aux cours souhaités. Donc,

si A, B, C & D, les vecteurs sont tous des vecteurs de la même longueur, on peut créer des données.frame ABCD avec

ABCD <- data.frame(A, B, C, D)

Peut-être devriez-vous demander "Comment puis-je collecter facilement divers vecteurs de longueur égale et les mettre dans un data.frame "?cbind est génial, mais parfois ce n'est pas ce que vous cherchez ...

3
aL3xa

Vous pouvez mettre tous les vecteurs dans l'environnement dans la liste en utilisant ESAPPLY.

obj.list <- eapply(.GlobalEnv,function(x) if(is.vector(x)) x)
obj.list <- obj.list[names(obj.list) %in% LETTERS]
1
Wojciech Sobala