99 votes

Réduire / concaténer / agréger une colonne à une seule chaîne séparée par des virgules dans chaque groupe.

Je veux agréger une colonne dans un cadre de données en fonction de deux variables de regroupement, et séparer les valeurs individuelles par une virgule.

Voici quelques données :

data <- data.frame(A = c(rep(111, 3), rep(222, 3)), B = rep(1:2, 3), C = c(5:10))
data
#     A B  C
# 1 111 1  5
# 2 111 2  6
# 3 111 1  7
# 4 222 2  8
# 5 222 1  9
# 6 222 2 10    

"A" et "B" sont des variables de regroupement, et "C" est la variable que je veux réduire en une variable séparée par une virgule. character ficelle. J'ai essayé :

library(plyr)
ddply(data, .(A,B), summarise, test = list(C))

    A B  test
1 111 1  5, 7
2 111 2     6
3 222 1     9
4 222 2 8, 10

mais lorsque j'ai essayé de convertir la colonne de test en character ça devient comme ça :

ddply(data, .(A,B), summarise, test = as.character(list(C)))
#     A B     test
# 1 111 1  c(5, 7)
# 2 111 2        6
# 3 222 1        9
# 4 222 2 c(8, 10)

Comment puis-je garder le character et les séparer par une virgule ? Par exemple, la ligne 1 doit être uniquement "5,7" et non comme c(5,7).

109voto

G. Grothendieck Points 40825

Voici quelques options utilisant toString une fonction qui concatène un vecteur de chaînes de caractères en utilisant des virgules et des espaces pour séparer les composants. Si vous ne voulez pas de virgules, vous pouvez utiliser la fonction paste() avec le collapse à la place.

table.de.données

# alternative using data.table
library(data.table)
as.data.table(data)[, toString(C), by = list(A, B)]

agrégat Cela n'utilise aucun paquet :

# alternative using aggregate from the stats package in the core of R
aggregate(C ~., data, toString)

sqldf

Et voici une alternative utilisant la fonction SQL group_concat en utilisant le paquet sqldf :

library(sqldf)
sqldf("select A, B, group_concat(C) C from data group by A, B", method = "raw")

dplyr A dplyr alternative :

library(dplyr)
data %>%
  group_by(A, B) %>%
  summarise(test = toString(C)) %>%
  ungroup()

plyr

# plyr
library(plyr)
ddply(data, .(A,B), summarize, C = toString(C))

34voto

Ben G Points 705

Voici le stringr / tidyverse solution :

library(tidyverse)
library(stringr)

data <- data.frame(A = c(rep(111, 3), rep(222, 3)), B = rep(1:2, 3), C = c(5:10))

data %>%
 group_by(A, B) %>%
 summarize(text = str_c(C, collapse = ", "))

# A tibble: 4 x 3
# Groups:   A [2]
      A     B text 
  <dbl> <int> <chr>
1   111     1 5, 7 
2   111     2 6    
3   222     1 9    
4   222     2 8, 10

15voto

Ananda Mahto Points 67213

Changez l'endroit où vous mettez as.character :

> out <- ddply(data, .(A, B), summarise, test = list(as.character(C)))
> str(out)
'data.frame':   4 obs. of  3 variables:
 $ A   : num  111 111 222 222
 $ B   : int  1 2 1 2
 $ test:List of 4
  ..$ : chr  "5" "7"
  ..$ : chr "6"
  ..$ : chr "9"
  ..$ : chr  "8" "10"
> out
    A B  test
1 111 1  5, 7
2 111 2     6
3 222 1     9
4 222 2 8, 10

Notez dans ce cas que chaque élément est toujours un caractère distinct, et non une chaîne de caractères unique. En d'autres termes, il ne s'agit pas d'une chaîne réelle ressemblant à "5, 7", mais plutôt de deux caractères, "5" et "7", que R affiche en les séparant par une virgule.

Comparez avec ce qui suit :

> out2 <- ddply(data, .(A, B), summarise, test = paste(C, collapse = ", "))
> str(out2)
'data.frame':   4 obs. of  3 variables:
 $ A   : num  111 111 222 222
 $ B   : int  1 2 1 2
 $ test: chr  "5, 7" "6" "9" "8, 10"
> out
    A B  test
1 111 1  5, 7
2 111 2     6
3 222 1     9
4 222 2 8, 10

La solution comparable en base R est, bien sûr, aggregate :

> A1 <- aggregate(C ~ A + B, data, function(x) c(as.character(x)))
> str(A1)
'data.frame':   4 obs. of  3 variables:
 $ A: num  111 222 111 222
 $ B: int  1 1 2 2
 $ C:List of 4
  ..$ 0: chr  "5" "7"
  ..$ 1: chr "9"
  ..$ 2: chr "6"
  ..$ 3: chr  "8" "10"
> A2 <- aggregate(C ~ A + B, data, paste, collapse = ", ")
> str(A2)
'data.frame':   4 obs. of  3 variables:
 $ A: num  111 222 111 222
 $ B: int  1 1 2 2
 $ C: chr  "5, 7" "9" "6" "8, 10"

4voto

andrii Points 813

Il y a une petite amélioration ici pour éviter les doublons

# 1. Original data set
data <- data.frame(
  A = c(rep(111, 3), rep(222, 3)), 
  B = rep(1:2, 3), 
  C = c(5:10))

# 2. Add duplicate row
data <- rbind(data, data.table(
  A = 111, B = 1, C = 5
))

# 3. Solution with duplicates
data %>%
  group_by(A, B) %>%
  summarise(test = toString(C)) %>%
  ungroup()

#      A     B test   
#   <dbl> <dbl> <chr>  
# 1   111     1 5, 7, 5
# 2   111     2 6      
# 3   222     1 9      
# 4   222     2 8, 10

# 4. Solution without duplicates
data %>%
  select(A, B, C) %>% unique() %>% 
  group_by(A, B) %>%
  summarise(test = toString(C)) %>%
  ungroup()

#    A     B test 
#   <dbl> <dbl> <chr>
# 1   111     1 5, 7 
# 2   111     2 6    
# 3   222     1 9    
# 4   222     2 8, 10

J'espère que cela peut être utile.

1voto

akrun Points 148302

Utilisation de collap de collapse

library(collapse)
collap(data, ~ A + B, toString)
    A B     C
1 111 1  5, 7
2 111 2     6
3 222 1     9
4 222 2 8, 10

données

data <- data.frame(A = c(rep(111, 3), rep(222, 3)), B = rep(1:2, 3), C = c(5:10))

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