Reduzieren / Verketten / Aggregieren einer Spalte zu einer einzelnen durch Kommas getrennten Zeichenfolge innerhalb jeder Gruppe

75

Ich möchte eine Spalte in einem Datenrahmen nach zwei Gruppierungsvariablen aggregieren und die einzelnen Werte durch ein Komma trennen.

Hier sind einige Daten:

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" und "B" sind Gruppierungsvariablen, und "C" ist die Variable, die ich in eine durch Kommas getrennte characterZeichenfolge reduzieren möchte . Ich habe versucht:

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

Aber als ich versuchte, die Testspalte in eine solche zu konvertieren, sieht characteres so aus:

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)

Wie kann ich das characterFormat beibehalten und durch Komma trennen? Zum Beispiel sollte Zeile 1 nur "5,7"und nicht c (5,7) sein.

linp
quelle

Antworten:

88

Hier sind einige Optionen toString, die eine Funktion verwenden, die einen Vektor von Zeichenfolgen mit Komma und Leerzeichen verkettet, um Komponenten zu trennen. Wenn Sie keine Kommas möchten, können Sie stattdessen paste()das collapseArgument verwenden.

Datentabelle

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

Aggregat Hierbei werden keine Pakete verwendet:

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

sqldf

Und hier ist eine Alternative, die die SQL-Funktion group_concatmit dem sqldf-Paket verwendet :

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

dplyr Eine dplyrAlternative:

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

plyr

# plyr
library(plyr)
ddply(data, .(A,B), summarize, C = toString(C))
G. Grothendieck
quelle
Nur eindeutige Werte beibehalten: as.data.table (data) [, toString (unique (C)), by = list (A, B)]
ddunn801
18

Hier ist die stringr/ tidyverseLösung:

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 test 
  <dbl> <int> <chr>
1   111     1 5, 7 
2   111     2 6    
3   222     1 9    
4   222     2 8, 10
Ben G.
quelle
1
Man kann auch Ersatz stringr::str_cfür pastevon der Basis R.
Rich - Pauloo
13

Ändern Sie, wo Sie setzen 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

Beachten Sie jedoch, dass jedes Element immer noch ein separates Zeichen und keine einzelne Zeichenfolge ist. Das heißt, dies ist keine tatsächliche Zeichenfolge, die wie "5, 7" aussieht, sondern zwei Zeichen, "5" und "7", die R mit einem Komma dazwischen anzeigt.

Vergleichen Sie mit folgendem:

> 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

Die vergleichbare Lösung in Base R ist natürlich 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"
A5C1D2H2I1M1N2O1R2T1
quelle
2

Hier gibt es eine kleine Verbesserung, um Duplikate zu vermeiden

# 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

Hoffe es kann nützlich sein.

Andrii
quelle