web-dev-qa-db-fra.com

Concaténer les lignes d'un cadre de données

Je voudrais prendre un cadre 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 en tant qu'élément unique dans un vecteur. Par exemple, je crée un bloc de données de lettres et de chiffres, puis j'aimerais concaténer la première ligne via la fonction Coller et, si tout va bien, renvoyer 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 est en train de convertir chaque élément de la ligne en un entier qui correspond à "l'index du niveau correspondant" comme s'il s'agissait d'un facteur, et le conserve comme vecteur de longueur deux. (Je sais/crois que les facteurs qui sont forcés à être des caractères se comportent de cette façon, mais comme R ne stocke pas df [1,] en tant que facteur (testé par is.factor (), je ne peux pas le vérifier. 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, il est donc logique qu’il se comporte bizarrement, mais je ne peux pas le contraindre à devenir un vecteur.

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

Utiliser as.character n'a pas semblé aider dans mes tentatives

Quelqu'un peut-il expliquer ce comportement?

25
Sam

Tandis que d'autres se sont concentrés sur les raisons pour lesquelles votre code ne fonctionnait pas et sur la façon de l'améliorer, je vais essayer de me concentrer davantage sur l'obtention du résultat souhaité. De votre description, il semble que vous pouvez facilement réaliser ce que vous voulez en utilisant la pâte:

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 modifier df$letters en caractère à l'aide de df$letters <- as.character(df$letters) si vous ne souhaitez 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 voulez les coller toutes ensemble. Nous pouvons également le faire avec votre exemple minimal:

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

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

EDIT: Méthode alternative et explication:

Je me suis rendu compte 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 l'a repris). 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 l'argument collapse. 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 as.list ne fonctionne-t-il pas?

as.list convertit un objet en liste. Donc ça marche. Il convertira votre cadre de données en liste et ignorera ensuite l'argument sep="". c combine des objets ensemble. Techniquement, un cadre de données est simplement une liste dans laquelle chaque colonne est un élément et tous les éléments doivent avoir la même longueur. Ainsi, lorsque je le combine avec sep="", il devient simplement 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 en utilisant une liste nommée comme arguments. Vous ne pouvez pas simplement jeter la liste directement dans paste, car elle n'aime pas les images. Il est conçu pour concaténer des vecteurs. Alors rappelez-vous que dfargs est une liste contenant un vecteur de lettres, un vecteur de nombres et sep qui est un vecteur de longueur 1 contenant uniquement "". Lorsque j'utilise do.call, la fonction de collage résultante est essentiellement paste(letters, numbers, sep).
Mais que se passe-t-il si ma base de données d'origine contenait des colonnes "letters", "numbers", "squigs", "blargs" après lesquelles j'ai ajouté le séparateur comme auparavant? Ensuite, la fonction coller à travers do.call ressemblerait à ceci:

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

Donc, vous voyez que cela fonctionne pour un nombre quelconque de colonnes.

50
sebastian-c

C'est en effet un peu bizarre, mais c'est aussi ce qui est censé se passer. Lorsque vous créez le data.frame comme vous l'avez fait, la colonne letters est stockée sous la forme factor. Naturellement, les facteurs n’ont pas d’ordre, donc, lorsque as.numeric() est appliqué à un facteur, il retourne l’ordre de celui-ci. 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]; par conséquent, A est converti en la valeur 1, lorsque as.numeric est appliqué. C'est ce qui se passe lorsque vous appelez paste(df[1, ]). Comme les colonnes 1 et 2 appartiennent à des classes différentes, l'option coller transforme d'abord les deux éléments de la ligne 1 en valeurs numériques, puis en caractères. 

Lorsque vous souhaitez concaténer les deux colonnes, vous devez d’abord transformer la première ligne en caractère: 

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

Comme @ sebastian-c l'a fait remarquer, vous pouvez également utiliser stringsAsFactors = FALSE dans la création de data.frame, puis vous pouvez omettre l'étape as.character().

4
adibender

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

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

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

3
Shirley

si tu veux commencer avec

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

.. alors 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 les fonctions de modélisation, le caractère pour certains et le nombre entier pour d'autres. Même la même fonction, telle que coller, peut l'interpréter différemment, selon l'utilisation que vous en faites:

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 éléments internes de chaque fonction.

Les facteurs semblent être convertis en nombres 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, de sorte que la première ligne d'un cadre de données est également une liste. un vecteur, quelque chose comme ça arrive :)

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

Je ne sais pas comment apply réalise ce qu’elle fait (c’est-à-dire que les facteurs sont représentés par des valeurs de caractère) - si cela vous intéresse, regardez son code source. Il peut être utile de savoir, cependant, que vous pouvez faire confiance (dans ce sens spécifique) apply (à cette occasion spécifique). Plus généralement, il est utile de stocker chaque élément de données dans un format logique, ce qui inclut le stockage de chaînes sous forme de chaînes, c'est-à-dire à l'aide de stringsAsFactors=FALSE

Au fait, 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 très douce) au zen de la pêcherie de données avec R, the stringsAsFactors = FALSE way».

0
lebatsnok