Data.table: Keyby / indem keine eindeutigen Gruppen mit Teilmenge zurückgegeben werden

Erstellt am 31. März 2018  ·  4Kommentare  ·  Quelle: Rdatatable/data.table

Im Folgenden finden Sie ein einfaches Beispiel, bei dem keyby (auch by) keine eindeutigen Gruppen mit Teilmenge zurückgibt.
Sobald jedoch die Teilmenge entfernt wurde, funktioniert keyby ordnungsgemäß.

library(data.table)
# data.table 1.10.5 IN DEVELOPMENT built 2018-03-21 23:49:00 UTC; travis
#  The fastest way to learn (by data.table authors): https://www.datacamp.com/courses/data-analysis-the-data-table-way
#  Documentation: ?data.table, example(data.table) and browseVignettes("data.table")
#  Release notes, videos and slides: http://r-datatable.com

# small dataset
dat <- data.table(Group = rep(c("All", "Not All"), times = 4), count = 1:8, ID = rep(1:2, each = 4))

# keyby returning non unique IDs with subset
dat[Group == "All" ,lapply(.SD, function(x) sum(x, na.rm = TRUE)), .SDcols= c("count"), keyby = ID, verbose = TRUE]
# Creating new index 'Group'
# Creating index Group done in ... 0.001sec 
# Optimized subsetting with index 'Group'
# on= matches existing index, using index
# Starting bmerge ...done in 0.000sec 
# i clause present and columns used in by detected, only these subset: ID 
# Finding groups using forderv ... 0.000sec 
# Finding group sizes from the positions (can be avoided to save RAM) ... 0.000sec 
# lapply optimization changed j from 'lapply(.SD, function(x) sum(x, na.rm = TRUE))' to 'list(..FUN1(count))'
# GForce is on, left j unchanged
# Old mean optimization is on, left j unchanged.
# Making each group and running j (GForce FALSE) ... 
#   collecting discontiguous groups took 0.000s for 2 groups
#   eval(j) took 0.000s for 2 calls
# 0.000sec 
#    ID count
# 1:  1     4
# 2:  1    12

# keyby working fine without subset
dat[,lapply(.SD, function(x) sum(x, na.rm = TRUE)), .SDcols= c("count"), keyby = ID] 
# Finding groups using forderv ... 0.000sec 
# Finding group sizes from the positions (can be avoided to save RAM) ... 0.000sec 
# lapply optimization changed j from 'lapply(.SD, function(x) sum(x, na.rm = TRUE))' to 'list(..FUN1(count))'
# GForce is on, left j unchanged
# Old mean optimization is on, left j unchanged.
# Making each group and running j (GForce FALSE) ... 
#   memcpy contiguous groups took 0.000s for 2 groups
#   eval(j) took 0.000s for 2 calls
# 0.000sec 
#    ID count
# 1:  1    10
# 2:  2    26

sessionInfo()
R version 3.4.4 (2018-03-15)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 9 (stretch)
Matrix products: default
BLAS: /usr/lib/openblas-base/libblas.so.3
LAPACK: /usr/lib/libopenblasp-r0.2.19.so
locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=C             
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     
other attached packages:
[1] data.table_1.10.5
loaded via a namespace (and not attached):
[1] compiler_3.4.4
bug dev

Hilfreichster Kommentar

Ich bin damit einverstanden, dass es ein Fehler ist.

Für die Aufzeichnung lautet der empfohlene Code in diesem Fall:

dat[Group == "All", lapply(.SD, sum, na.rm = TRUE), .SDcols= c("count"), keyby = ID]

Dies gibt die richtige Antwort, da diese Version GForce aktiviert und der Fehler in diesem Fall nicht vorhanden ist.

Dies ist natürlich keine Hilfe, wenn Ihr tatsächlicher Code nicht so geklärt werden kann.

Interessanterweise funktioniert der Code, wenn wir die Teilmengenzeilen direkt übergeben:

dat[c(1, 3, 5, 7),
    lapply(.SD, function(x) sum(x, na.rm = TRUE)),
    .SDcols= "count", keyby = ID, verbose = TRUE]
# i clause present and columns used in by detected, only these subset: ID 
# Finding groups using forderv ... 0.000sec 
# Finding group sizes from the positions (can be avoided to save RAM) ... 0.000sec 
# lapply optimization changed j from 'lapply(.SD, function(x) sum(x, na.rm = TRUE))' to 'list(..FUN1(count))'
# GForce is on, left j unchanged
# Old mean optimization is on, left j unchanged.
# Making each group and running j (GForce FALSE) ... 
#   collecting discontiguous groups took 0.000s for 2 groups
#   eval(j) took 0.000s for 2 calls
# 0.000sec 
#    ID count
# 1:  1     4
# 2:  2    12

Ich sehe den folgenden Unterschied in der Ausgabe von verbose :

Optimierte Teilmenge mit Index 'Gruppe'

Dies führte mich zur Installation von CRAN; Der Code läuft fehlerfrei auf 1.10.4-3 .

Ich denke, das ist etwas aus @ MarkusBonschs Arbeit zur Optimierung von Teilmengen?

Ich sehe den gleichen Fehler auch, wenn wir den Join explizit machen:

dat[.('All'), on = 'Group',
    lapply(.SD, function(x) sum(x, na.rm = TRUE)),
    .SDcols= "count", keyby = ID]
#    ID count
# 1:  1     4
# 2:  1    12

Aber die verschlüsselte Version ist in Ordnung:

setkey(dat, Group)
dat[.('All'), 
    lapply(.SD, function(x) sum(x, na.rm = TRUE)),
    .SDcols= "count", keyby = ID]#    ID count
# 1:  1     4
# 2:  2    12

Alle 4 Kommentare

Ich bin damit einverstanden, dass es ein Fehler ist.

Für die Aufzeichnung lautet der empfohlene Code in diesem Fall:

dat[Group == "All", lapply(.SD, sum, na.rm = TRUE), .SDcols= c("count"), keyby = ID]

Dies gibt die richtige Antwort, da diese Version GForce aktiviert und der Fehler in diesem Fall nicht vorhanden ist.

Dies ist natürlich keine Hilfe, wenn Ihr tatsächlicher Code nicht so geklärt werden kann.

Interessanterweise funktioniert der Code, wenn wir die Teilmengenzeilen direkt übergeben:

dat[c(1, 3, 5, 7),
    lapply(.SD, function(x) sum(x, na.rm = TRUE)),
    .SDcols= "count", keyby = ID, verbose = TRUE]
# i clause present and columns used in by detected, only these subset: ID 
# Finding groups using forderv ... 0.000sec 
# Finding group sizes from the positions (can be avoided to save RAM) ... 0.000sec 
# lapply optimization changed j from 'lapply(.SD, function(x) sum(x, na.rm = TRUE))' to 'list(..FUN1(count))'
# GForce is on, left j unchanged
# Old mean optimization is on, left j unchanged.
# Making each group and running j (GForce FALSE) ... 
#   collecting discontiguous groups took 0.000s for 2 groups
#   eval(j) took 0.000s for 2 calls
# 0.000sec 
#    ID count
# 1:  1     4
# 2:  2    12

Ich sehe den folgenden Unterschied in der Ausgabe von verbose :

Optimierte Teilmenge mit Index 'Gruppe'

Dies führte mich zur Installation von CRAN; Der Code läuft fehlerfrei auf 1.10.4-3 .

Ich denke, das ist etwas aus @ MarkusBonschs Arbeit zur Optimierung von Teilmengen?

Ich sehe den gleichen Fehler auch, wenn wir den Join explizit machen:

dat[.('All'), on = 'Group',
    lapply(.SD, function(x) sum(x, na.rm = TRUE)),
    .SDcols= "count", keyby = ID]
#    ID count
# 1:  1     4
# 2:  1    12

Aber die verschlüsselte Version ist in Ordnung:

setkey(dat, Group)
dat[.('All'), 
    lapply(.SD, function(x) sum(x, na.rm = TRUE)),
    .SDcols= "count", keyby = ID]#    ID count
# 1:  1     4
# 2:  2    12

Vielen Dank an @cathine für die Berichterstattung und an @MichaelChirico für die Untersuchung.
Die Hauptursache ist das fehlerhafte Verhalten der Join-Version, auf das Michael hingewiesen hat:
dat[.('All'), on = 'Group', lapply(.SD, function(x) sum(x, na.rm = TRUE)), .SDcols= "count", keyby = ID]

Wird wahrscheinlich gelöst, wenn dieses Problem # 2591 gelöst ist.
Bei der neuen Teilmengenoptimierung werden Teilmengen in den Join-Teil von data.table umgeleitet, sodass dieser Fehler jetzt sowohl Teilmengen als auch Joins betrifft. Ich werde versuchen, so schnell wie möglich zu untersuchen, ob ich das Problem lösen kann.
Bis dahin können Sie darauf zurückgreifen
dat[Group == "All"][ ,lapply(.SD, function(x) sum(x, na.rm = TRUE)), .SDcols= c("count"), keyby = ID, verbose = TRUE] zum Beispiel.
Entschuldigen Sie die Unannehmlichkeiten.

Danke @cathine! Bestätigt, dass dies nur für Entwickler gilt und mit options(datatable.optimize=2) behoben werden kann, da das Problem in der Optimierung der Stufe 3 zu liegen scheint. Ich frage mich, wie das durch die Tests gerutscht ist!
Noch einfachere Beispiele von einem anderen Kontakt, der ebenfalls berichtete:

> DT = data.table(
    id = c("a","a","a","b","b","c","c","d","d"),
    group = c(1,1,1,1,1,2,2,2,2),
    num = 1)
> DT[, uniqueN(id), by=group]          # ok 
   group    V1
   <num> <int>
1:     1     2
2:     2     2
> DT[num==1, uniqueN(id), by=group]    # group column wrong
   group    V1
   <num> <int>
1:     1     2
2:     1     2
> options(datatable.optimize=2)
> DT[num==1, uniqueN(id), by=group]    # ok
   group    V1
   <num> <int>
1:     1     2
2:     2     2
> options(datatable.optimize=3)        # not ok
> DT[num==1, uniqueN(id), by=group]
   group    V1
   <num> <int>
1:     1     2
2:     1     2
> DT[num==1, sum(num), by=group]       # ok
   group    V1
   <num> <num>
1:     1     7
2:     2     4
> DT[num==1, length(num), by=group]    # not ok
   group    V1
   <num> <int>
1:     1     7
2:     1     4
> options(datatable.optimize=2)        # ok
> DT[num==1, length(num), by=group]
   group    V1
   <num> <int>
1:     1     7
2:     2     4
> 

Warum ist es durch Tests gerutscht? Weil es nur auftritt, wenn die Gruppierungsspalte sortiert ist (siehe Code unten)! Ich habe die Gruppierung nach sortierten Spalten nicht speziell überprüft.

library(data.table)
DT = data.table(
  id = c("a","a","a","b","b","c","c","d","d"),
  group = c(1,1,1,1,1,2,2,2,2),
  group2 = c(1,1,1,1,1,2,2,2,1),
  num = 1)
DT[, uniqueN(id), by=group]          # ok 
# group    V1
# <num> <int>
# 1:     1     2
# 2:     2     2
DT[num==1, uniqueN(id), by=group]    # group column wrong
# group    V1
# <num> <int>
# 1:     1     2
# 2:     1     2
DT[num==1, uniqueN(id), by=group2]    # ok with other group column that is not sorted
# group2 V1
# 1:      1  3
# 2:      2  2

setkey(DT, group2)
DT[num==1, uniqueN(id), by=group2]    # not ok anymore since the group column is sorted now
# group2 V1
# 1:      1  3
# 2:      1  2
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen