Ich versuche aus Geschwindigkeitsgründen, meinen Datenmanipulationscode von dplyr in data.table umzuwandeln. Ich bin fast da, aber ich verpasse den letzten Schritt.

Ich habe einige Beispieldaten, um mein Problem zu replizieren.

c_dt = data.table(u_id=rep(c("u1", "u2"),each=5),
                  p_id=c("p1", "p1", "p1", "p2","p2", "p1", "p2", "p2", "p2", "p2" ), 
                  c_dt=c("2015-12-01", "2015-12-02", "2015-12-03", "2015-12-02",
                         "2015-12-05", "2015-12-02", "2015-12-03", "2015-12-04", 
                         "2015-12-05", "2015-12-06"))

Ich möchte die Zeilen identifizieren, in denen u_id und p_id dupliziert werden. und behalten Sie nur die Zeile mit dem Minimum c_dt bei (behalten Sie im Wesentlichen die erste Instanz bei). Ich verwende dafür den folgenden dplyr Code:

c_df <- as.data.frame(c_dt)
cdedup_df <- c_df %>% group_by(p_id, u_id) %>% filter(c_dt == min(c_dt))

Welche geben die folgende Ausgabe

> cdedup_df
Source: local data frame [4 x 3]
Groups: p_id, u_id

  u_id p_id       c_dt
1   u1   p1 2015-12-01
2   u1   p2 2015-12-02
3   u2   p1 2015-12-02
4   u2   p2 2015-12-03

Ich habe den folgenden data.table Code, der die erforderlichen Zeilen korrekt identifiziert, aber ich kann nicht herausfinden, wie nur die Zeile gefiltert werden soll.

cdedup_dt <- c_dt[,c_dt == min(c_dt),by = list(u_id, p_id)]
cdedup_dt
    u_id p_id    V1
 1:   u1   p1  TRUE
 2:   u1   p1 FALSE
 3:   u1   p1 FALSE
 4:   u1   p2  TRUE
 5:   u1   p2 FALSE
 6:   u2   p1  TRUE
 7:   u2   p2  TRUE
 8:   u2   p2 FALSE
 9:   u2   p2 FALSE
10:   u2   p2 FALSE
3
grammar 1 Jän. 2016 im 19:38

3 Antworten

Beste Antwort

Unter meinem Ansatz. Ich würde erwarten, dass es für große Datenmengen besser skaliert, da es keine min von group gibt, sondern nur eine einzige Sortierung, die data.table sehr effizient macht, und dann zuerst nach Gruppen unterteilt.

setorderv(c_dt, "c_dt")[, .SD[1L], .(u_id, p_id)]
# in data.table 1.9.7+ you can also use `head`
setorderv(c_dt, "c_dt")[, head(.SD, 1L), .(u_id, p_id)]

Der folgende Code enthält die Validierung der aktuellen anderen Antworten.
Wenn OP einen großen Datensatz bereitstellt, kann ich Benchmarks hinzufügen.

library(data.table)
c_dt = data.table(u_id=rep(c("u1", "u2"),each=5), p_id=c("p1", "p1", "p1", "p2","p2", "p1", "p2", "p2", "p2", "p2" ), c_dt=c("2015-12-01", "2015-12-02", "2015-12-03", "2015-12-02", "2015-12-05", "2015-12-02", "2015-12-03", "2015-12-04", "2015-12-05", "2015-12-06"))

zero = c_dt[, list(c_dt=min(c_dt)), by=list(u_id, p_id)]
ananda = c_dt[, list(c_dt = c_dt[c_dt == min(c_dt)]), by = .(u_id, p_id)]
tal = c_dt[, .SD[rank(c_dt, ties.method = c("first")) == 1],by = .(u_id, p_id)]
all.equal(zero, ananda)
#[1] TRUE
all.equal(ananda, tal)
#[1] TRUE

jan = setorderv(c_dt, "c_dt")[, .SD[1L], .(u_id, p_id)]
all.equal(tal, jan)
#[1] TRUE
3
jangorecki 1 Jän. 2016 im 21:37

So etwas sollte den Trick machen:

c_dt[, list(c_dt=min(c_dt)), by=list(u_id, p_id)]
##    u_id p_id       c_dt
## 1:   u1   p1 2015-12-01
## 2:   u1   p2 2015-12-02
## 3:   u2   p1 2015-12-02
## 4:   u2   p2 2015-12-03
3
zero323 1 Jän. 2016 im 16:43

Sie stehen sich also sehr nahe. Sie haben lediglich .SD in der Spalte j übergeben. Mal sehen, wie es funktioniert:

library(data.table)
c_dt = data.table(u_id=rep(c("u1", "u2"),each=5),  
                  p_id=c("p1", "p1", "p1", "p2","p2", "p1", "p2", "p2", "p2", "p2" ), 
                  c_dt=c("2015-12-01", "2015-12-02",  
                   "2015-12-03", "2015-12-02", "2015-12-05", 
                   "2015-12-02", "2015-12-03", "2015-12-04", 
                   "2015-12-05", "2015-12-06"))
c_dt
    u_id p_id       c_dt
 1:   u1   p1 2015-12-01
 2:   u1   p1 2015-12-02
 3:   u1   p1 2015-12-03
 4:   u1   p2 2015-12-02
 5:   u1   p2 2015-12-05
 6:   u2   p1 2015-12-02
 7:   u2   p2 2015-12-03
 8:   u2   p2 2015-12-04
 9:   u2   p2 2015-12-05
10:   u2   p2 2015-12-06

Jetzt gruppieren wir nach u_id und p_id und filtern nach dem Mindestwert von c_df :

cdedup_dt <- c_dt[ , .SD[c_dt == min(c_dt)], by = .(u_id, p_id)]
cdedup_dt
   u_id p_id       c_dt
1:   u1   p1 2015-12-01
2:   u1   p2 2015-12-02
3:   u2   p1 2015-12-02
4:   u2   p2 2015-12-03

Beachten Sie, dass .(u_id, p_id) gleich list(u_id, p_id) ist und .SD sich auf die Teilmenge der Datentabelle für jede Gruppe bezieht. Alles, was Sie vermisst haben, ist das .SD.
Wie von @ zero323 erwähnt, behält min Duplikate bei (was im Grunde bedeutet, dass wir in unserem Beispiel einige doppelte Zeilen haben). Wenn Sie nur einen Datensatz für jede Gruppe führen möchten, ist es sicherer, die Rangfunktion zu verwenden:

cdedup_dt <- c_dt[, .SD[rank(c_dt, ties.method = c("first")) == 1],by = .(u_id, p_id)]

cdedup_dt
   u_id p_id       c_dt
1:   u1   p1 2015-12-01
2:   u1   p2 2015-12-02
3:   u2   p1 2015-12-02
4:   u2   p2 2015-12-03
2
Tal J. Levy 1 Jän. 2016 im 18:08