【问题标题】:Vectorization/data.table - increase efficiency of for loop for 12kk records DFVectorization/data.table - 提高 12kk 记录 DF 循环的效率
【发布时间】:2017-11-24 07:34:37
【问题描述】:

我需要将该组关联到 20k 个组,这些组总共有 1200 万行。

为了解决这个问题,我编写了一个 for 循环,但它显然完全没有效率,我相信这个任务可以很容易地向量化。但是,我很难理解如何以矢量化方式编写此指令。

问题如下: 我有一个具有 3 个功能的辅助表:ID、start_row、end_Row。
start_row 是 my_DF 中属于 ID x 的第一个元素的行索引;
end_row 是 my_DF 中属于 ID x 的最后一个元素的行索引。

矢量化指令应执行以下操作:

考虑如下的辅助表:

auxiliary_table <- data.frame(ID = c(1,2,3,4), start_row = c(1,4,8,13), end_row = c(3,7,12,14))

考虑如下 DF:

  my_df <- data.frame(Var_a = c(1,2,3,1,2,3,4,6,4,3,1,2,1,1)

我们需要根据auxiliary_table中包含的start_row和end_row索引信息来关联ID。

solution_df 是:

solution_df <- data.frame(my_df, ID=(1,1,1,2,2,2,2,3,3,3,3,3,4,4)

我要求对 for 循环进行矢量化,但我对 data.table 解决方案持开放态度。

我希望我清楚并正确地提出了我的问题。

【问题讨论】:

  • 如果有一些数据示例将不胜感激;输入是什么样子,输出应该是什么样子......
  • 对不起,我忘记将 my_df 格式化为代码,但是,我提出了 my_df,用于索引 my_df 和 solution_df 的辅助表,表示任务的解决方案。我应该介绍其他东西吗??

标签: r loops data.table vectorization


【解决方案1】:

auxiliary_table 是一种运行长度编码。因此,我建议尝试使用经过适当转换的auxiliary_tableinverse.rle() 函数:

1。 dplyr

library(dplyr)
my_df %>%
  mutate(ID = auxiliary_table %>% 
           transmute(lengths = end_row - start_row + 1L, values = ID) %>% 
           inverse.rle())
   Var_a ID
1      1  1
2      2  1
3      3  1
4      1  2
5      2  2
6      3  2
7      4  2
8      6  3
9      4  3
10     3  3
11     1  3
12     2  3
13     1  4
14     1  4

2。数据表

这会添加ID 列而不复制my_df

library(data.table)
setDT(my_df)[, ID := inverse.rle(setDT(auxiliary_table)[
  , .(lengths = end_row - start_row + 1L, values = ID)])][]

根据auxiliary_table 的大小,下面的代码可能会更高效,因为它将auxiliary_table 原地转换

setDT(my_df)[, ID := inverse.rle(setDT(auxiliary_table)[
  , lengths := end_row - start_row + 1L][
    , c("end_row", "start_row") := NULL][
      , setnames(.SD, "ID", "values")])][]

【讨论】:

  • 非常感谢。我发现学习数据表很困难,请问您有什么材料/网站可以建议我了解如何编写这些非常有用的东西吗?
  • 一个很好的起点是data.table 主页github.com/Rdatatable/data.table/wiki/Getting-started 上提供的链接,尤其是小插图。如果您了解 SQL,它会有所帮助。
  • 真的非常感谢 Uwe,你无法理解你对我的帮助有多大!
  • 我将您的算法应用于类似的任务。新任务之间的区别在于,有时start_row == end_row,因此,在这种情况下,ID 必须只与row.number == start_row == end_row 的一个元素相关联。但是,我收到以下问题:Supplied 16449900 items to be assigned to 13812131 items of column 'ID' (2637769 unused)
  • 嗯,inverse.rle() 也应该在长度为 1 (start_row == end_row) 的情况下工作。所以,我怀疑其他数据集还有另一个问题。请发布一个显示其他数据的新问题。
【解决方案2】:

我设计了一个用户定义的函数并将其应用于auxillary_table。看看这是否有帮助 -

auxiliary_table <- data.frame(ID = c(1,2,3,4), start_row = c(1,4,8,13), end_row = c(3,7,12,14))
my_df <- data.frame(Var_a = c(1,2,3,1,2,3,4,6,4,3,1,2,1,1))
solution_df <- data.frame(my_df, ID=c(1,1,1,2,2,2,2,3,3,3,3,3,4,4))

aux_to_df <- function(aux_row){
  # 1,2,3 can be replaced by column names
  value = aux_row[1]
  start_row = aux_row[2]
  end_row = aux_row[3]

  my_df[start_row:end_row, "ID"] <<- value # <<- means assigning to global out of scope variable
}

apply(auxiliary_table, 1, aux_to_df)
my_df

【讨论】:

  • 不幸的是,应用函数并不比使用 for 循环执行相同的操作更有效。
  • 您可能想使用as.matrix() 进行投射,这可能会有所帮助。没有这个,我猜你将无能为力
  • 我确信有一种非常有效的方法来做到这一点
猜你喜欢
  • 2015-10-08
  • 2014-03-08
  • 2019-11-06
  • 2018-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多