【问题标题】:Speed up fill function R加速填充函数 R
【发布时间】:2017-11-01 20:49:57
【问题描述】:

我有一个缺失值的数据框,我编写了一个函数来使用 R 3.3.2 进行填充

pkgs <- c("dplyr", "ggplot2", "tidyr", 'data.table', 'lazyeval')
lapply(pkgs, require, character.only = TRUE)

UID <- c('A', 'A', 'A', 'B', 'B', 'B', 'C', 'C')
Col1 <- c(1, 0, 0, 0, 1, 0, 0, 0)
df <- data.frame(UID, Col1)

Col1 填写函数:

AggregatedColumns <- function(DF, columnToUse, NewCol1) {
  # Setting up column names to use
  columnToUse <- deparse(substitute(columnToUse))
  NewCol1 <- deparse(substitute(NewCol1))

  # Creating new columns 
  DF[[NewCol1]] <- ifelse(DF[[columnToUse]] == 1, 1, NA)
  DF <- DF %>% group_by_("UID") %>% sort(DF[[columnToUse]], decreasing = TRUE) %>% fill_(NewCol1)
  DF <- DF %>% group_by_("UID") %>% sort(DF$columnToUse, decreasing = TRUE) %>% fill_(NewCol1, .direction = 'up')
  DF[[NewCol1]] <- ifelse(is.na(DF[[NewCol1]]), 0, DF[[NewCol1]])

  DF
}

我已经删除了这部分功能,因为这是减慢功能的部分。我对编写函数非常陌生,任何关于如何/是否可以加快速度的建议将不胜感激。我已将速度问题隔离到函数的 fill_ 部分。

我想要做的是将一个虚拟变量从 Col1 传递到 New_Column,然后将填充转发到其他相同的 ID。例如:

UID             Col1
John Smith        1
John Smith        0

应该变成

UID             Col1  New_Column
John Smith        1      1
John Smith        0      1

编辑功能 我编辑了函数以符合@HubertL 的建议。该功能仍然相当慢,但希望通过这些编辑,该示例是可重现的。

AggregatedColumns <- function(DF, columnToUse, NewCol1) {
  # Setting up column names to use
  columnToUse <- deparse(substitute(columnToUse))
  NewCol1 <- deparse(substitute(NewCol1))

  # Creating new columns 
  DF[[NewCol1]] <- ifelse(DF[[columnToUse]] == 1, 1, NA)
    DF <- DF %>% group_by_("UID") %>% fill_(NewCol1) %>% fill_(NewCol1, .direction = 'up')
  DF[[NewCol1]] <- ifelse(is.na(DF[[NewCol1]]), 0, DF[[NewCol1]])

  DF
}

期望的输出:

UID Col1 New
A    1    1 
A    0    1
A    0    1 
B    0    1
B    1    1
B    0    1
C    0    0
C    0    0

【问题讨论】:

  • 你能显示你想要的输出,这个函数是如何使用的等等吗?我做不到。
  • 你为什么不直接DF %&gt;% group_by(UID) %&gt;% fill(NewCol1) %&gt;% fill(NewCol1, .direction = 'up')
  • 我无法运行此功能。请在此处显示您正在使用的所有软件包。并用语言解释你想做什么。如果我们能理解它的实际作用以及如何运行它,您的代码可以很容易地加速。
  • 我也无法运行你的函数。获取Error: Can't use matrix or array for column indexing
  • 这是一种简单有效的方法,无需使用单个包,只需一步即可DF[[NewCol1]] &lt;- as.integer(DF$UID %in% DF[DF[[columnToUse]] == 1, "UID"])

标签: r performance function vectorization


【解决方案1】:

首先,这里有几点:

  1. 你不必要地打电话给ifelse(两次)while this function is very inefficient
  2. 当您可以仅使用基本 R 对过程进行矢量化时,您不必要地使用外部包中的低效函数(按组)(也是两次)。

这是一个简单的单行程序,不使用任何外部包,可在 5e7 数据集上将性能提高 x72 倍(对于更大的数据集可能更多)

AggregatedColumns2 <- function(DF, columnToUse, NewCol1) {
    # Setting up column names to use
    columnToUse <- deparse(substitute(columnToUse))
    NewCol1 <- deparse(substitute(NewCol1))

    # Creating the new column (one simple line)
    DF[[NewCol1]] <- as.integer(DF$UID %in% DF$UID[DF[[columnToUse]] == 1])

    # returning new data set back
    DF
}

基准测试

set.seed(123)
library(stringi)
N <- 5e7
UID <- stri_rand_strings(N, 2)
Col1 <- sample(0:1, N, replace = TRUE)
df <- data.frame(UID, Col1)


system.time(res <- AggregatedColumns(df, Col1, NewCol1))
#   user  system elapsed 
# 198.67    3.94  203.07 

system.time(res2 <- AggregatedColumns2(df, Col1, NewCol1))
# user  system elapsed 
# 2.82    0.00    2.82  

现在为了比较它们,我将对它们重新排序并转换为矩阵,因为 Hadleyverses 包添加了大量不必要的属性(比较 str(res) 中创建的混乱与 str(res2) 中的简单结构)

identical(arrange(res, UID) %>% as.matrix, arrange(res2, UID) %>% as.matrix)
## [1] TRUE

【讨论】:

    【解决方案2】:

    如果速度是一个问题,您可以尝试使用zoo 包中的data.tablena.locf()LOCF 表示最后一次观察结转

    library(data.table)
    setDT(df)[Col1 != 0, New := Col1 ][, New := zoo::na.locf(New), UID][is.na(New), New := 0][]
    #   UID Col1 New
    #1:   A    1   1
    #2:   A    0   1
    #3:   A    0   1
    #4:   B    0   1
    #5:   B    1   1
    #6:   B    0   1
    #7:   C    0   0
    #8:   C    0   0
    

    这只是提供一个想法。它仍然需要包装在函数调用中。

    假定Col1 中的值0 被视为缺失。

    【讨论】:

      猜你喜欢
      • 2018-01-23
      • 1970-01-01
      • 1970-01-01
      • 2017-02-22
      • 1970-01-01
      • 1970-01-01
      • 2015-11-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多