Tidyr: Feature Request: Machen Sie ordentlichere Funktionen mit data.table

Erstellt am 4. März 2016  ·  3Kommentare  ·  Quelle: tidyverse/tidyr

Die Aufräumfunktionen scheinen nicht mit der data.table-Funktionalität zu funktionieren. Zum Beispiel möchte ich fehlende Werte einer großen data.table wie folgt auffüllen:

#install.packages("data.table")
#install.packages("tidyr")
library(data.table)
library(tidyr)
dt <- data.table(
  year = c(2015, NA, NA, NA), 
  trt = c("A", NA, "B", NA)
)

dt[, year := fill(year)]
dt[, c("year", "trt") := .(fill(year), fill(trt))]

aber der obige Code wirft den folgenden Fehler aus:

# > dt[, year := fill(year)]
# Error in UseMethod("fill_") : 
#  no applicable method for 'fill_' applied to an object of class "c('double', 'numeric')"
# > dt[, c("year", "trt") := .(fill(year), fill(trt))]
# Error in UseMethod("fill_") : 
#  no applicable method for 'fill_' applied to an object of class "c('double', 'numeric')"

Da das sequentielle Anwenden von fill() auf jede Spalte keine Änderung an der Stelle bewirkt, ist die folgende Methode für große Datentabellen kostspielig.

rm(dt)
dt <- data.table(
  year = c(2015, NA, NA, NA), 
  trt = c("A", NA, "B", NA)
)
tracemem(dt)
dt <- fill(dt, year)
dt <- fill(dt, trt)

# > rm(dt)
# > dt <- data.table(
# +     year = c(2015, NA, NA, NA), 
# +     trt = c("A", NA, "B", NA)
# + )
# > tracemem(dt)
# [1] "<00000000088F6118>"
# > dt <- fill(dt, year)
# tracemem[0x00000000088f6118 -> 0x00000000092d8c50]: fill_.data.frame fill_ fill 
# tracemem[0x00000000092d8c50 -> 0x00000000092d8b00]: [[<-.data.frame [[<- fill_.data.frame  fill_ fill 
# tracemem[0x00000000092d8b00 -> 0x00000000092d8ac8]: [[<-.data.frame [[<- fill_.data.frame  fill_ fill 
# > dt <- fill(dt, trt)
# tracemem[0x00000000092d8ac8 -> 0x000000000951f1c0]: fill_.data.frame fill_ fill 
# tracemem[0x000000000951f1c0 -> 0x000000000951f0a8]: [[<-.data.frame [[<- fill_.data.frame fill_  fill 
# tracemem[0x000000000951f0a8 -> 0x000000000951f070]: [[<-.data.frame [[<- fill_.data.frame fill_   fill 
# > 

Danke fürs reinschauen!

Hilfreichster Kommentar

@mindymallory , Was Sie beschreiben, ist kein Problem mit der Funktion tidyr oder einer neuen zu implementierenden Funktion. Es kommt vielmehr darauf an, wie Sie tidyr Funktionen mit data.table .

Bei den Tests, die Sie mit dt[, year := fill(year)] durchgeführt haben, sind nur wenige Fehler

  • Zuerst nimmt fill 2 Argumente, zuerst ein data (data.frame, data.table oder tbl) und dann einen bloßen Spaltennamen. In Ihrem Test geben Sie den Spaltennamen als erstes Argument an, damit fill nicht unabhängig von data.table funktioniert.
  • Dann gibt fill nicht die von Ihnen angegebene Spalte zurück, sondern alle Daten, auf die Sie fill angewendet haben. Aus diesem Grund können Sie year durch das Ergebnis der Funktion fill in dt[, year := fill(year)] ersetzen. Bei LHS:=RHS muss RHS ein Vektor von Ersatzwerten sein, was bei year := fill(year) nicht der Fall ist.

Der Code, den Sie ausprobiert haben, hat diese Probleme unabhängig von der Kompatibilität von tidyr mit data.table .

Hier sind einige Möglichkeiten, fill innerhalb von data.table . Es könnte auch anderen Benutzern helfen.

library(data.table)
library(tidyr)
dt <- data.table(
  year = c(2015, NA, NA, NA), 
  trt = c("A", NA, "B", NA)
)
tracemem(dt)
#> [1] "<0000000019B796C8>"

Wenn Sie fill möchten, müssen Sie zuerst Daten und dann eine Spalte angeben. Verwenden Sie in einem data.table .SD und sehen Sie, dass fill alle Spalten von dt return

dt[, fill(.SD, year)]
#>    year trt
#> 1: 2015   A
#> 2: 2015  NA
#> 3: 2015   B
#> 4: 2015  NA

Sie können dann beispielsweise eine Spalte mit $ auswählen, die einen Vektor zurückgibt.

dt[, fill(.SD, year)$year]
#> [1] 2015 2015 2015 2015

Wenn Sie mehrere Spalten zum Füllen angeben, werden diese Spalten gefüllt zurückgegeben.

dt[, fill(.SD, year, trt)]
#>    year trt
#> 1: 2015   A
#> 2: 2015   A
#> 3: 2015   B
#> 4: 2015   B

Also, wie wenden Sie fill an, um Spalten in einem data.table zu ersetzen.

Sie könnten den gewünschten Vektor extrahieren und := . Und wiederholen Sie es für die andere Spalte.

dt[, year := fill(.SD, year)$year]
dt
#>    year trt
#> 1: 2015   A
#> 2: 2015  NA
#> 3: 2015   B
#> 4: 2015  NA
dt[, trt := fill(.SD, trt)$trt]
dt
#>    year trt
#> 1: 2015   A
#> 2: 2015   A
#> 3: 2015   B
#> 4: 2015   B

Sie können die Änderung auch sofort mit der Syntax data.table anwenden.

dt <- data.table(
  year = c(2015, NA, NA, NA), 
  trt = c("A", NA, "B", NA)
)
tracemem(dt)
#> [1] "<0000000006104428>"
dt[, c("year", "trt") := .(fill(.SD, year)$year, fill(.SD, trt)$trt)]
dt
#>    year trt
#> 1: 2015   A
#> 2: 2015   A
#> 3: 2015   B
#> 4: 2015   B

Da fill jedoch alle Spalten des Arguments data zurückgibt, können Sie auf das Extrahieren der Spalte mit $

dt <- data.table(
  year = c(2015, NA, NA, NA), 
  trt = c("A", NA, "B", NA)
)
tracemem(dt)
#> [1] "<0000000005B57B98>"
dt[, c("year", "trt") := fill(.SD, year, trt)]
dt
#>    year trt
#> 1: 2015   A
#> 2: 2015   A
#> 3: 2015   B
#> 4: 2015   B

Schließlich könnten Sie einen Ansatz verwenden, der besser für die Programmierung geeignet ist.

dt <- data.table(
  year = c(2015, NA, NA, NA), 
  trt = c("A", NA, "B", NA)
)
tracemem(dt)
#> [1] "<00000000187CF230>"
cols <- c("year", "trt")
dt
#>    year trt
#> 1: 2015   A
#> 2:   NA  NA
#> 3:   NA   B
#> 4:   NA  NA
dt[, (cols) := fill_(.SD, cols), .SDcols = cols]
dt
#>    year trt
#> 1: 2015   A
#> 2: 2015   A
#> 3: 2015   B
#> 4: 2015   B

Beachten Sie, dass Sie auf diese Weise fill leicht auf eine Teilmenge von Spalten anwenden können

dt <- data.table(
  year = c(2015, NA, NA, NA), 
  trt = c("A", NA, "B", NA),
  trt2 = c(NA, "C", NA, "D")
)
tracemem(dt)
#> [1] "<00000000049EFD30>"
cols <- c("year", "trt")
dt
#>    year trt trt2
#> 1: 2015   A   NA
#> 2:   NA  NA    C
#> 3:   NA   B   NA
#> 4:   NA  NA    D
dt[, (cols) := fill_(.SD, cols), .SDcols = cols]
dt
#>    year trt trt2
#> 1: 2015   A   NA
#> 2: 2015   A    C
#> 3: 2015   B   NA
#> 4: 2015   B    D

Ich hoffe, dies beantwortet Ihre Frage und die gewünschte Funktionsanforderung. Beachten Sie, dass ich tracemem zu zeigen, dass es keine Kopien gibt und dass tidyr mit dem data.table Konzept kompatibel ist, da die Syntax und das Konzept von data.table korrekt verwendet werden.

@hadley , ich denke, Sie haben keine PR zu diesem Thema.

Alle 3 Kommentare

Ich wäre bereit, Pull-Requests zu überprüfen, um dies zu tun, aber ich habe nicht die Zeit (oder das Wissen über data.table), um dies selbst zu tun.

@mindymallory , Was Sie beschreiben, ist kein Problem mit der Funktion tidyr oder einer neuen zu implementierenden Funktion. Es kommt vielmehr darauf an, wie Sie tidyr Funktionen mit data.table .

Bei den Tests, die Sie mit dt[, year := fill(year)] durchgeführt haben, sind nur wenige Fehler

  • Zuerst nimmt fill 2 Argumente, zuerst ein data (data.frame, data.table oder tbl) und dann einen bloßen Spaltennamen. In Ihrem Test geben Sie den Spaltennamen als erstes Argument an, damit fill nicht unabhängig von data.table funktioniert.
  • Dann gibt fill nicht die von Ihnen angegebene Spalte zurück, sondern alle Daten, auf die Sie fill angewendet haben. Aus diesem Grund können Sie year durch das Ergebnis der Funktion fill in dt[, year := fill(year)] ersetzen. Bei LHS:=RHS muss RHS ein Vektor von Ersatzwerten sein, was bei year := fill(year) nicht der Fall ist.

Der Code, den Sie ausprobiert haben, hat diese Probleme unabhängig von der Kompatibilität von tidyr mit data.table .

Hier sind einige Möglichkeiten, fill innerhalb von data.table . Es könnte auch anderen Benutzern helfen.

library(data.table)
library(tidyr)
dt <- data.table(
  year = c(2015, NA, NA, NA), 
  trt = c("A", NA, "B", NA)
)
tracemem(dt)
#> [1] "<0000000019B796C8>"

Wenn Sie fill möchten, müssen Sie zuerst Daten und dann eine Spalte angeben. Verwenden Sie in einem data.table .SD und sehen Sie, dass fill alle Spalten von dt return

dt[, fill(.SD, year)]
#>    year trt
#> 1: 2015   A
#> 2: 2015  NA
#> 3: 2015   B
#> 4: 2015  NA

Sie können dann beispielsweise eine Spalte mit $ auswählen, die einen Vektor zurückgibt.

dt[, fill(.SD, year)$year]
#> [1] 2015 2015 2015 2015

Wenn Sie mehrere Spalten zum Füllen angeben, werden diese Spalten gefüllt zurückgegeben.

dt[, fill(.SD, year, trt)]
#>    year trt
#> 1: 2015   A
#> 2: 2015   A
#> 3: 2015   B
#> 4: 2015   B

Also, wie wenden Sie fill an, um Spalten in einem data.table zu ersetzen.

Sie könnten den gewünschten Vektor extrahieren und := . Und wiederholen Sie es für die andere Spalte.

dt[, year := fill(.SD, year)$year]
dt
#>    year trt
#> 1: 2015   A
#> 2: 2015  NA
#> 3: 2015   B
#> 4: 2015  NA
dt[, trt := fill(.SD, trt)$trt]
dt
#>    year trt
#> 1: 2015   A
#> 2: 2015   A
#> 3: 2015   B
#> 4: 2015   B

Sie können die Änderung auch sofort mit der Syntax data.table anwenden.

dt <- data.table(
  year = c(2015, NA, NA, NA), 
  trt = c("A", NA, "B", NA)
)
tracemem(dt)
#> [1] "<0000000006104428>"
dt[, c("year", "trt") := .(fill(.SD, year)$year, fill(.SD, trt)$trt)]
dt
#>    year trt
#> 1: 2015   A
#> 2: 2015   A
#> 3: 2015   B
#> 4: 2015   B

Da fill jedoch alle Spalten des Arguments data zurückgibt, können Sie auf das Extrahieren der Spalte mit $

dt <- data.table(
  year = c(2015, NA, NA, NA), 
  trt = c("A", NA, "B", NA)
)
tracemem(dt)
#> [1] "<0000000005B57B98>"
dt[, c("year", "trt") := fill(.SD, year, trt)]
dt
#>    year trt
#> 1: 2015   A
#> 2: 2015   A
#> 3: 2015   B
#> 4: 2015   B

Schließlich könnten Sie einen Ansatz verwenden, der besser für die Programmierung geeignet ist.

dt <- data.table(
  year = c(2015, NA, NA, NA), 
  trt = c("A", NA, "B", NA)
)
tracemem(dt)
#> [1] "<00000000187CF230>"
cols <- c("year", "trt")
dt
#>    year trt
#> 1: 2015   A
#> 2:   NA  NA
#> 3:   NA   B
#> 4:   NA  NA
dt[, (cols) := fill_(.SD, cols), .SDcols = cols]
dt
#>    year trt
#> 1: 2015   A
#> 2: 2015   A
#> 3: 2015   B
#> 4: 2015   B

Beachten Sie, dass Sie auf diese Weise fill leicht auf eine Teilmenge von Spalten anwenden können

dt <- data.table(
  year = c(2015, NA, NA, NA), 
  trt = c("A", NA, "B", NA),
  trt2 = c(NA, "C", NA, "D")
)
tracemem(dt)
#> [1] "<00000000049EFD30>"
cols <- c("year", "trt")
dt
#>    year trt trt2
#> 1: 2015   A   NA
#> 2:   NA  NA    C
#> 3:   NA   B   NA
#> 4:   NA  NA    D
dt[, (cols) := fill_(.SD, cols), .SDcols = cols]
dt
#>    year trt trt2
#> 1: 2015   A   NA
#> 2: 2015   A    C
#> 3: 2015   B   NA
#> 4: 2015   B    D

Ich hoffe, dies beantwortet Ihre Frage und die gewünschte Funktionsanforderung. Beachten Sie, dass ich tracemem zu zeigen, dass es keine Kopien gibt und dass tidyr mit dem data.table Konzept kompatibel ist, da die Syntax und das Konzept von data.table korrekt verwendet werden.

@hadley , ich denke, Sie haben keine PR zu diesem Thema.

@cderv Danke für diese ausführliche Erklärung! Sie haben mir viel Speicher gespart!

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

jennybc picture jennybc  ·  5Kommentare

kendonB picture kendonB  ·  5Kommentare

davidhunterwalsh picture davidhunterwalsh  ·  4Kommentare

romagnolid picture romagnolid  ·  8Kommentare

georgevbsantiago picture georgevbsantiago  ·  6Kommentare