【问题标题】:Speed-tune a for-loop in R快速调整 R 中的 for 循环
【发布时间】:2015-04-11 04:36:22
【问题描述】:

我已经阅读了矢量化作为加速 for 循环的解决方案。但是,我在 for 循环中创建的数据结构似乎需要是 data.frame/table。

这是场景:

我有一个包含序列号和时间戳的大表。多个时间戳可以应用于同一个序列号。我只想要每个序列号的最新时间戳。

我现在的方法是创建一个具有唯一序列号的向量。然后对于通过这个向量的每个循环,我创建一个临时表,其中包含序列号/时间戳组合('temp')的所有观察结果。然后我取出这个临时表的最后一个条目(使用 tail 命令)并将其放入另一个表中,该表最终将保存所有唯一序列号及其最新时间戳('last.pass')。最后,我只是从起始表序列中删除无法找到数字/时间戳组合'last.pass'的行

这是我的代码:

#create list of unique serial numbers found in merged 9000 table
hddsn.unique <- unique(merge.data$HDDSN)

#create empty data.table to populate
last.pass < data.table(HDDSN=as.character(1:length(hddsn.unique)),
   ENDDATE=as.character(1:length(hddsn.unique)))

#populate last.pass with the combination of serial numbers and their latest timestamps
for (i in 1:length(hddsn.unique)) {
  #create temporary table that finds all serial number/timestamp combinations
  temp <- merge.data[merge.data$HDDSN %in% hddsn.unique[i],][,.(HDDSN, ENDDATE)]
  #populate last.pass with the latest timestamp record for every serial number
  last.pass[i,] <- tail(temp, n=1)
}

match <- which(merge.data[,(merge.data$HDDSN %in% last.pass$HDDSN) &
         (merge.data$ENDDATE %in% last.pass$ENDDATE)]==TRUE)
final <- merge.data[match]

我的终极问题是,我如何保持这个脚本的自动化特性,同时加快它的速度,比如通过矢量化或将它变成一个函数。

谢谢!!!

【问题讨论】:

  • 您应该包含示例输入数据和该示例数据的所需输出,以解决您的问题reproducible
  • 根据您的描述,这听起来很简单,无论是使用 dplyr 还是数据表,甚至是基本功能。但是,正如弗里克先生所说,我们需要一个例子。只需 5-10 行输入和您想要的输出。使用dput() 分享您的数据,我们将获得所有的写入类。
  • 另外,当你说“大”时,最好有一个规模。你的意思是> 1000万行? 1-1000万?一个数量级的估计就足够了。
  • @Martin Morgan 说得对。这就是数据的样子。大小接近 100 万行。我正在尝试他的方法,并会回复您。

标签: r performance for-loop automation vectorization


【解决方案1】:

这个怎么样。在不清楚您的输入数据是什么样子的情况下,我猜测了一下。

# make some dummy data with multiple visits per serial
merge.data <- data.frame(HDDSN = 1001:1020, 
    timestamps = sample(1:9999, 100))

# create a function to find the final visit for a given serial
fun <- function(serial) {
    this.serial <- subset(merge.data, HDDSN==serial)
    this.serial[which.max(this.serial$timestamps), ]
}

# apply the function to each serial number and clean up the result
final <- as.data.frame(t(sapply(unique(merge.data$HDDSN), fun)))

【讨论】:

    【解决方案2】:

    此数据对于每个 HDDSN 都有多个 ENDDATE

    merge.data <- data.frame(HDDSN = 1001:1100, ENDDATE = sample(9999, 1000))
    

    按顺序排列,首先按 HDDSN,然后按 ENDDATE

    df = merge.data[do.call("order", merge.data),]
    

    然后找到每个 HDDSN 的最后一个条目

    df[!duplicated(df[["HDDSN"]], fromLast=TRUE),]
    

    以下说明关键步骤

    > head(df, 12)
        HDDSN    ENDDATE
    701  1001          4
    101  1001        101
    1    1001       1225
    301  1001       2800
    201  1001       6051
    501  1001       6714
    801  1001       6956
    601  1001       7894
    401  1001       8234
    901  1001       8676
    802  1002        247
    402  1002        274
    > head(df[!duplicated(df[["HDDSN"]], fromLast=TRUE),])
        HDDSN    ENDDATE
    901  1001       8676
    902  1002       6329
    803  1003       9947
    204  1004       8825
    505  1005       8472
    606  1006       9743
    

    如果有复合键,则在 data.frame 而不是向量 !duplicated(df[, c("key1", "key2")]) 上查找重复项,如下所示:

    > df = data.frame(k0=c(1:3, 1:6), k1=1:3)
    > df[!duplicated(df, fromLast=TRUE),]
      k0 k1
    1  1  1
    2  2  2
    3  3  3
    7  4  1
    8  5  2
    9  6  3
    

    (行号来自原始数据框,因此第 4-6 行是重复的)。 (可能需要注意,尤其是其中一列是数字时,因为 duplicated.data.frame 会将列粘贴到一个字符串中,并且可能会出现舍入误差)。

    【讨论】:

    • Martin,你如何调整最后一个命令以适应来自 df 的 2 个变量。也就是说,它适用于复合键场景。
    • @PabloBoswell 我尝试根据对您问题的理解来更新回复。
    • 谢谢!你知道 unique() 函数是否类似地工作。你能写 last.pass[unique(last.pass[, c("HDDSN","PHEADNO")], fromLast=TRUE),]
    猜你喜欢
    • 2023-03-31
    • 2020-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-13
    • 1970-01-01
    • 1970-01-01
    • 2017-08-16
    相关资源
    最近更新 更多