Tidyr: 機能のリクエスト:tidyr関数をdata.tableでうまく機能させる

作成日 2016年03月04日  ·  3コメント  ·  ソース: tidyverse/tidyr

tidyr関数はdata.table機能では機能しないようです。 たとえば、大きなdata.tableの欠落している値を次のように埋めたいと思います。

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

ただし、上記のコードは次のエラーをスローします。

# > 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')"

各列にfill()を順番に適用してもその場で変更されないため、以下の方法は大きなdata.tablesの場合はコストがかかります。

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

ご覧いただきありがとうございます!

最も参考になるコメント

@mindymallory 、あなたが説明することは、 tidyr関数または実装する新機能の問題ではありません。 むしろ、 tidyr関数をdata.tableでどのように使用するかが問題になります。

dt[, year := fill(year)]行ったテストにはほとんどエラーがありません

  • まず、 fillは2つの引数を取り、最初にデータ(data.frame、data.table、またはtbl)、次に裸の列名を取ります。 テストでは、最初の引数として列名を指定するため、 filldata.tableとは独立して機能しません。
  • 次に、 fillは指定した列を返しませんが、 fillを適用したすべてのデータを返します。 これが、 yeardt[, year := fill(year)]fill関数の結果に置き換えることができない理由です。 LHS:=RHS場合、 RHSは置換値のベクトルである必要がありますが、 year := fill(year)場合はそうではありません。

したがって、試したコードには、 data.tableとのtidyr互換性とは関係なく、これらの問題があります。

data.table内でfillを使用するいくつかの方法があります。 他のユーザーにも役立つ可能性があります。

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

fillを使用する場合は、最初にデータを指定し、次に列を指定する必要があります。 data.table 、そのために.SDを使用し、 filldtすべての列を返すことを確認します。

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

次に、たとえば$列を選択すると、ベクトルが返されます。

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

埋める列を複数指定すると、これらの列が埋められて返されます。

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

したがって、 fillを適用して、 data.table内の列を置き換える方法を説明します。

必要なベクターを抽出して、 :=を使用できます。 そして、他の列のためにそれをもう一度やりなさい。

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

data.table構文を使用して、変更を一度に適用することもできます。

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

ただし、 filldata引数のすべての列を返すため、 $列を抽出せずに実行できます。

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

最後に、プログラミングにより適したアプローチを使用できます。

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

この方法を使用すると、列のサブセットにfill簡単に適用できることに注意してください

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

これがあなたの質問とあなたが望む機能要求に答えることを願っています。 data.table構文と概念が正しく使用されているため、コピーがなく、 tidyrdata.table概念と互換性があることを示すために、 tracememを使用していることに注意してください。

@hadley 、このテーマについてレビューするPRはないと思います。

全てのコメント3件

これを行うためのプルリクエストを確認したいと思いますが、自分でそれを行う時間(またはdata.tableの知識)がありません。

@mindymallory 、あなたが説明することは、 tidyr関数または実装する新機能の問題ではありません。 むしろ、 tidyr関数をdata.tableでどのように使用するかが問題になります。

dt[, year := fill(year)]行ったテストにはほとんどエラーがありません

  • まず、 fillは2つの引数を取り、最初にデータ(data.frame、data.table、またはtbl)、次に裸の列名を取ります。 テストでは、最初の引数として列名を指定するため、 filldata.tableとは独立して機能しません。
  • 次に、 fillは指定した列を返しませんが、 fillを適用したすべてのデータを返します。 これが、 yeardt[, year := fill(year)]fill関数の結果に置き換えることができない理由です。 LHS:=RHS場合、 RHSは置換値のベクトルである必要がありますが、 year := fill(year)場合はそうではありません。

したがって、試したコードには、 data.tableとのtidyr互換性とは関係なく、これらの問題があります。

data.table内でfillを使用するいくつかの方法があります。 他のユーザーにも役立つ可能性があります。

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

fillを使用する場合は、最初にデータを指定し、次に列を指定する必要があります。 data.table 、そのために.SDを使用し、 filldtすべての列を返すことを確認します。

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

次に、たとえば$列を選択すると、ベクトルが返されます。

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

埋める列を複数指定すると、これらの列が埋められて返されます。

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

したがって、 fillを適用して、 data.table内の列を置き換える方法を説明します。

必要なベクターを抽出して、 :=を使用できます。 そして、他の列のためにそれをもう一度やりなさい。

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

data.table構文を使用して、変更を一度に適用することもできます。

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

ただし、 filldata引数のすべての列を返すため、 $列を抽出せずに実行できます。

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

最後に、プログラミングにより適したアプローチを使用できます。

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

この方法を使用すると、列のサブセットにfill簡単に適用できることに注意してください

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

これがあなたの質問とあなたが望む機能要求に答えることを願っています。 data.table構文と概念が正しく使用されているため、コピーがなく、 tidyrdata.table概念と互換性があることを示すために、 tracememを使用していることに注意してください。

@hadley 、このテーマについてレビューするPRはないと思います。

@cdervこの詳細な説明をありがとう! あなたは私にたくさんのメモリ使用量を節約しました!

このページは役に立ちましたか?
0 / 5 - 0 評価