2017-11-13 3 views
1

Das Dummy-Daten-Set ist:cumsum für einzigartigen Wert mit dplyr mutieren

data <- data.frame(
    id = c(1,1,2,2,3,4,5,6), 
    value = c(10,10,20,20,10,30,40,50), 
    other = c(1,2,3,4,5,6,7,8) 
) 

Die Daten waren Ausgang group_by(id) Betrieb in dplyr Rohr. Jede id ist mit höchstens einem Wert verknüpft und zwei verschiedene id können denselben Wert haben. Ich muss kumulative Summe über IDs finden, indem ich neue Spalte hinzufüge: cum_col = c(10,10,30,30,40,70,110,160) Die cumsum in mutate wird kumulative Summe über die gesamte Spalte der Werte finden und wählt nicht nur einen Wert pro Gruppe aus. summarise ist nicht nützlich, da es andere Spalten gibt, die ich intakt behalten muss.

Gibt es einen Ausweg, ohne summarise und dann join zu verwenden - rückwärts zu gehen? Oder bitte zeigen Sie mir, ob der Link bereits beantwortet wurde.

Edit: Nur für Informationen die tatsächlichen Daten hat ~ 2 Millionen Zeilen und 100 Spalten.

+0

Sie benötigen nur 'als auch dplyr' Antwort oder Sie sind offen für andere Optionen tun könnte? Außerdem hätte eine Gruppe immer nur einen einzigen "Wert"? –

+0

Nur 'dplyr' wie bin Rohr verwenden, um ein paar andere Mutationen und Operationen in einem Codeblock zu tun – Kaur

+0

Ja, Gruppe wird immer einen eindeutigen Wert haben – Kaur

Antwort

3

Eine Alternative Nest der Datenrahmen von id Spalte sein könnte, die kumulative Summe berechnen und dann UNNEST:

data %>% 
    group_by(id) %>% nest() %>% 
    mutate(cum_col = cumsum(sapply(data, function(dat) dat$value[1]))) %>% 
    unnest() 

# A tibble: 8 x 4 
#  id cum_col value other 
# <dbl> <dbl> <dbl> <dbl> 
#1  1  10 10  1 
#2  1  10 10  2 
#3  2  30 20  3 
#4  2  30 20  4 
#5  3  40 10  5 
#6  4  70 30  6 
#7  5  110 40  7 
#8  6  160 50  8 

vergleichen summarize und join:

summarise_f <- function(data) data %>% 
    group_by(id) %>% 
    summarise(val = first(value)) %>% 
    mutate(cum_col = cumsum(val)) %>% 
    select(-val) %>% 
    inner_join(data, by="id") 

nest_f <- function(data) data %>% 
    group_by(id) %>% nest() %>% 
    mutate(cum_col = cumsum(sapply(data, function(dat) dat$value[1]))) %>% 
    unnest() 

df <- bind_rows(rep(list(data), 100000)) 

microbenchmark::microbenchmark(summarise_f(df), nest_f(df)) 
#Unit: milliseconds 
#   expr  min  lq  mean median  uq  max neval 
# summarise_f(df) 79.78891 89.65753 117.8480 93.56766 99.97694 277.3773 100 
#  nest_f(df) 191.10597 208.07364 280.2466 225.65567 369.20202 524.5106 100 

Summarize und dann join ist eigentlich schneller.

Mit einem größeren Datensatz:

df <- bind_rows(rep(list(data), 1000000)) 
microbenchmark::microbenchmark(summarise_f(df), nest_f(df)) 
#Unit: milliseconds 
#   expr  min  lq  mean median  uq  max neval 
# summarise_f(df) 819.5588 905.2136 993.4916 961.1797 1040.947 1480.391 100 
#  nest_f(df) 1768.3060 1992.6753 2069.1454 2057.3091 2162.440 2501.715 100 
+1

Ich denke, Sie können ein bisschen mehr Overhead in der 'summarise' Option zu vermeiden -' Daten%>% distinct (ID, Wert)%>% muate (cum_col = Cumsum (Wert))%>% select (-Wert)%>% inner_join (Daten, durch = "ID") 'zum Beispiel. – thelatemail

+0

@Psidom @thelatemail Danke für Ihre Antwort. Der innere Join bedeutet, dass ich eine Kopie der Daten speichern muss, um später verbunden zu werden. aber diese Daten werden als Teil einer Pipe in dplyr erzeugt. Habe zwischendurch mal gewechselt. 'original_data%>% group_by (einige Spalten)%>% muate (Spalten hinzufügen)%>% filter (Bedingungen)%>% group_by (andere Spalten)%>% mutate (berechnet cumsum in einer der anderen Spalten)%>% weiter mit dem Rest der Operationen " ' nest', obwohl langsam kann die bessere Option sein, um die Notwendigkeit zu vermeiden, Daten zu speichern, um später zu verbinden – Kaur

+0

@thelatemail Gute Wahl. Es ist ungefähr so ​​schnell wie "zusammenfassen". – Psidom

3

Eine weitere Alternative ist, dass wir eine Dummy-Spalte (cols) zu erstellen, die nur erste hat value pro Gruppe und Rest durch 0 ersetzt werden, und dann nehmen wir cumsum über die gesamte Säule .

library(dplyr) 
data %>% 
    group_by(id) %>% 
    mutate(cols = c(value[1], rep(0, n() -1))) %>% 
    ungroup() %>% 
    mutate(cum_col = cumsum(cols)) %>% 
    select(-cols) 


# A tibble: 8 x 4 
#  id value other cum_col 
# <dbl> <dbl> <dbl> <dbl> 
#1  1 10  1  10 
#2  1 10  2  10 
#3  2 20  3  30 
#4  2 20  4  30 
#5  3 10  5  40 
#6  4 30  6  70 
#7  5 40  7  110 
#8  6 50  8  160 
+0

Danke, scheint einfach und clever. Aber nicht sicher über die Leistung. Werden sehen – Kaur

1

Wir arbeiten auch mit duplicated

library(dplyr) 
data %>% 
    mutate(cum_col = cumsum(value*!duplicated(id))) 
# id value other cum_col 
#1 1 10  1  10 
#2 1 10  2  10 
#3 2 20  3  30 
#4 2 20  4  30 
#5 3 10  5  40 
#6 4 30  6  70 
#7 5 40  7  110 
#8 6 50  8  160 
Verwandte Themen