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.

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))

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

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"

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.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.