Concaténer des lignes d'une trame de données

Je voudrais prendre une trame de données avec des caractères et des nombres, et concaténer tous les éléments de chaque ligne en une seule chaîne, qui serait stockée comme un seul élément dans un vecteur. À titre d'exemple, je fais un cadre de données de lettres et de chiffres, puis je voudrais concaténer la première ligne via la fonction paste, et j'espère retourner la valeur " A1 "

df <- data.frame(letters = LETTERS[1:5], numbers = 1:5)
df

##   letters numbers
## 1       A       1
## 2       B       2
## 3       C       3
## 4       D       4
## 5       E       5

paste(df[1,], sep =".")
## [1] "1" "1"

Donc, paste convertit chaque élément de la ligne en un entier qui correspond à l'index du niveau correspondant ' comme si elle était un facteur, et il garde un vecteur de longueur deux. (Je sais / crois que les facteurs qui sont contraints d'être des caractères se comportent de cette manière, mais comme R ne stocke pas DF [1,] en tant que facteur (testé par is.factor (), Je ne peux pas vérifier que c'est en fait un index pour un niveau)

is.factor(df[1,])
## [1] FALSE
is.vector(df[1,])
## [1] FALSE

Donc, si ce n'est pas un vecteur, alors il est logique qu'elle se comporte bizarrement, mais je ne peux pas le forcer dans un vecteur

> is.vector(as.vector(df[1,]))
[1] FALSE

En utilisant as.character n'a pas semblé aider dans mon tentatives

Quelqu'un peut-il expliquer ce comportement?

25
demandé sur zx8754 2012-12-19 05:07:34

4 réponses

Alors que d'autres se sont concentrés sur la raison pour laquelle votre code ne fonctionne pas et comment l'améliorer, je vais essayer de me concentrer davantage sur l'obtention du résultat souhaité. D'après votre description, il semble que vous pouvez facilement réaliser ce que vous voulez en utilisant paste:

df <- data.frame(letters = LETTERS[1:5], numbers = 1:5, stringsAsFactors=FALSE)
paste(df$letters, df$numbers, sep=""))

## [1] "A1" "B2" "C3" "D4" "E5"

Vous pouvez changer df$letters en caractère en utilisant df$letters <- as.character(df$letters) Si vous ne voulez pas utiliser l'argument stringsAsFactors.

Mais supposons que ce n'est pas ce que vous voulez. Supposons que vous avez des centaines de colonnes et que vous souhaitez coller tous ensemble. Nous pouvons le faire cela avec votre exemple minimal aussi:

df_args <- c(df, sep="")
do.call(paste, df_args)

## [1] "A1" "B2" "C3" "D4" "E5"

EDIT: méthode Alternative et explication:

J'ai réalisé que le problème que vous rencontrez est une combinaison du fait que vous utilisez un facteur et que vous utilisez l'argument sep au lieu de collapse (comme @adibender a ramassé). La différence est que sep donne le séparateur entre deux vecteurs distincts et collapse donne des séparateurs dans un vecteur. Lorsque vous utilisez df[1,], vous fournissez un seul vecteur à paste et vous devez donc utiliser le collapse argument. En utilisant votre idée d'obtenir chaque ligne et de les concaténer, la ligne de code suivante fera exactement ce que vous voulez:

apply(df, 1, paste, collapse="")

Ok, maintenant, pour les explications:

Pourquoi ça ne marche pas?

as.list convertit un objet en liste. Donc, il ne le travail. Il convertira votre dataframe en une liste et ignorera ensuite l'argument sep="". c combine des objets ensemble. Techniquement, un dataframe est juste une liste où chaque colonne est un élément et tous les éléments doivent avoir la même longueur. Donc, quand je le combine avec sep="", il devient juste une liste régulière avec les colonnes du dataframe en tant qu'éléments.

Pourquoi utiliser do.call?

do.call vous permet d'appeler une fonction à l'aide d'une liste nommée comme arguments. Vous ne pouvez pas simplement lancer la liste directement dans paste, car elle n'aime pas les dataframes. Il est conçu pour concaténer des vecteurs. Rappelez-vous donc que dfargs est une liste contenant un vecteur de lettres, un vecteur de nombres et sep qui est un vecteur de longueur 1 contenant seulement "". Lorsque j'utilise do.call, la pâte obtenue fonction est essentiellement paste(letters, numbers, sep).
Mais que se passe-t-il si mon dataframe original avait des colonnes "letters", "numbers", "squigs", "blargs" après quoi j'ai ajouté le séparateur comme je l'ai fait auparavant? Ensuite, la fonction coller à travers do.call ressemblerait à:

paste(letters, numbers, squigs, blargs, sep)

Donc, vous voyez que cela fonctionne pour n'importe quel nombre de colonnes.

49
répondu sebastian-c 2012-12-20 02:31:51

C'est en effet un peu bizarre, mais c'est aussi ce qui est censé se produire. Lorsque vous créez le data.frame comme vous l'avez fait, la colonne letters est stockée comme factor. Naturellement, les facteurs n'ont pas d'ordre, donc lorsque as.numeric() est appliqué à un facteur, il renvoie l'ordre du facteur. Par exemple:

> df[, 1]
[1] A B C D E
Levels: A B C D E
> as.numeric(df[, 1])
[1] 1 2 3 4 5

A est le premier niveau du facteur df[, 1] donc A est converti à la valeur 1 lorsque as.numeric est appliquée. C'est ce qui se passe lorsque vous appelez paste(df[1, ]). Depuis les colonnes 1 et 2 sont de classe différente, coller d'abord transforme les deux éléments de la ligne 1 en numérique puis en caractères.

Lorsque vous voulez concaténer les deux colonnes, vous devez d'abord transformer la première ligne de caractères:

df[, 1] <- as.character(df[, 1])
paste(df[1,], collapse = "")

, Comme @sébastien-c souligné, vous pouvez également utiliser stringsAsFactors = FALSE dans la création des données.frame, alors vous pouvez omettre l'étape as.character().

3
répondu adibender 2012-12-19 02:05:45

Pour ceux qui utilisent la bibliothèque (tidyverse), vous pouvez simplement utiliser la fonction unite.

 new.df<-df%>%
 unite(together, letters, numbers, sep="")

Cela vous donnera une nouvelle colonne intitulée "ensemble" avec A1, B2, etc

3
répondu Shirley 2017-05-11 19:04:28

Si vous voulez commencer par

df <- data.frame(letters = LETTERS[1:5], numbers = 1:5, stringsAsFactors=TRUE)

.. ensuite, il n'y a pas de règle générale sur la façon dont df$letters sera interprété par une fonction donnée. C'est un facteur pour modéliser les fonctions, le caractère pour certains et l'entier pour d'autres. Même la même fonction telle que paste peut l'interpréter différemment, selon la façon dont vous l'utilisez:

paste(df[1,], collapse="") # "11"
apply(df, 1, paste, collapse="") # "A1" "B2" "C3" "D4" "E5"

Aucune logique sauf que cela aura probablement un sens une fois que vous connaîtrez les internes de chaque fonction.

Les facteurs semblent être convertis en entiers lorsqu'un argument est converti en vecteur (comme vous le savez, les trames de données sont des listes de vecteurs de longueur égale, donc la première ligne d'une trame de données est aussi une liste, et quand elle est forcée d'être un vecteur, quelque chose comme ça se produit:)

df[1,]
#    letters numbers
# 1       A       1
unlist(df[1,])
# letters numbers 
#  1       1 

Je ne sais pas comment apply réalise ce qu'il fait (c'est-à-dire que les facteurs sont représentés par des valeurs de caractères) - si vous êtes intéressé, regardez son code source. Il peut être utile de savoir, cependant, que vous pouvez faire confiance (dans ce sens) apply (dans ce occasion). Plus généralement, il est utile de stocker chaque donnée dans un format sensible, qui inclut le stockage de chaînes en tant que chaînes, c'est-à-dire en utilisant stringsAsFactors=FALSE.

Btw, chaque Livre d'introduction R devrait avoir cette idée dans un sous-titre. Par exemple, mon plan pour la retraite est d'écrire "une introduction (pas si) douce au ZEN de la pêche de données avec R, le stringsAsFactors=FALSE way".

0
répondu lebatsnok 2018-01-19 09:36:48