【问题标题】:I need to loop over three loops efficiently我需要有效地循环三个循环
【发布时间】:2020-09-29 04:37:24
【问题描述】:

所以在充分解释之前需要一些背景知识。我有一份美国各县之间的距离清单,以及天数。我需要每个县,每天该县与其他县之间的距离总和。因此,例如,如果有四个县,A、B、C 和 D,以及 10 天,我想要距离 AB+AC+AD(A 与其他每个县之间的距离之和)BA+BC+BD, CA+CB+CD,10 天中的每一天。我知道这在断章取义的情况下听起来很奇怪,但如果不充分解释我的研究,就很难解释为什么。

所以对于一个reprex。考虑 3 个县和 3 天。

dist <- runif(9)
source <- c("A", "A", "A", "B", "B", "B", "C", "C", "C")

dest <- rep(c("A", "B", "C"), 3)
a
d1 <- as.data.frame(cbind(dest, source, dist))
d1$dist[b == c] <- 0
d1
county <- c("A", "A", "A", "B", "B", "B", "C", "C", "C")
lockdown <- c(0, 1, 0, 1, 1, 0, 1, 0, 1)
day <- c(1, 2, 3)
day <- rep(day, 3)

d2 <- as.data.frame(cbind(day, county, lockdown))

这给了我们:

 +------+--------+-------------------+--+--+
|      |        |                   |  |  |
+------+--------+-------------------+--+--+
| dest | source | dist              |  |  |
| A    | A      | 0                 |  |  |
| B    | A      | 0.815869745099917 |  |  |
| C    | A      | 0.554592022672296 |  |  |
| A    | B      | 0.193494795123115 |  |  |
| B    | B      | 0                 |  |  |
| C    | B      | 0.708462748210877 |  |  |
| A    | C      | 0.729813229991123 |  |  |
| B    | C      | 0.351678870152682 |  |  |
| C    | C      | 0                 |  |  |
+------+--------+-------------------+--+--+

这是三个县之间的距离表。

然后我每天都有一些县的面板数据:

+-----+--------+----------+--+--+
| day | county | lockdown |  |  |
+-----+--------+----------+--+--+
|   1 | A      |        0 |  |  |
|   2 | A      |        1 |  |  |
|   3 | A      |        0 |  |  |
|   1 | B      |        1 |  |  |
|   2 | B      |        1 |  |  |
|   3 | B      |        0 |  |  |
|   1 | C      |        1 |  |  |
|   2 | C      |        0 |  |  |
|   3 | C      |        1 |  |  |
+-----+--------+----------+--+--+

现在我想遍历县,并在 d2 中创建一个列,其中包含 该县与未处于锁定状态的其他县之间的距离总和。 所以如果 B、C 和 D 在那一天没有处于锁定状态,则认为 AB+AC+AD。我使用这样的循环:

# these aren't numeric for some reason
d1$dist <- as.numeric(d1$dist)
d2$lockdown <- as.numeric(d2$lockdown)

# First loop over the source counties
for (i in unique(d1$source)) {
  # Next loop over the days
  for (j in unique(d2$day)) {
    # finally, for each destination county, fill in the entry in d2 with the sum of the distance times if the county is in lockdown

    for (k in unique(d1$dest)) {
      ifelse(i == k, d2$ma[d2$county == i & d2$day == j] <- d2$ma[d2$county == i & d2$day == j],
        d2$ma[d2$county == i & d2$day == j] <- d2$ma[d2$county == i & d2$day == j] + d1$dist[d1$source == i & d1$dest == k] * d2$lockdown[d2$day == j & d2$county == i]
      )
    }
  }
}

这适用于如此小的数据集,但有 3000 个县,150 天需要循环超过十亿次迭代。有没有办法更有效地编写这个循环?我尝试使用矢量化表示法,但由于需要以新列的形式将输出与 d2 匹配,因此实际上并不可行。

【问题讨论】:

    标签: r loops for-loop dplyr


    【解决方案1】:

    这可以使用data.table 来完成,这比循环更有效:

    1. 将 d1 和 d2 连接在一起
    2. 按source,day 分组并为lockdown==1 求和距离
    set.seed(1)
    dist <- runif(9)
    source <- c("A", "A", "A", "B", "B", "B", "C", "C", "C")
    
    dest <- rep(c("A", "B", "C"), 3)
    d1 <- data.frame(dest = dest,source = source,dist =  dist)
    d1$dist[d1$source == d1$dest] <- 0
    d1
    county <- c("A", "A", "A", "B", "B", "B", "C", "C", "C")
    lockdown <- c(0, 1, 0, 1, 1, 0, 1, 0, 1)
    day <- c(1, 2, 3)
    day <- rep(day, 3)
    
    d2 <- data.frame(day = day, county = county, lockdown = lockdown)
    library(data.table)
    setDT(d1)
    setDT(d2)
    d <- d1[d2, on = .(dest = county),allow.cartesian=T]
    d[,.(distance = sum(dist*lockdown)),by = .(source,day)]
    
       source day  distance
    1:      A   1 0.9449773
    2:      B   1 0.8983897
    3:      C   1 0.6607978
    4:      A   2 0.3721239
    5:      B   2 0.9082078
    6:      C   2 1.6054731
    7:      A   3 0.5728534
    8:      B   3 0.8983897
    9:      C   3 0.0000000
    

    请注意,我简化了 d1 和 d2 创建脚本以消除由于cbind 将所有列转换为第一列(字符)的类型而遇到的数字问题

    【讨论】:

    • 谢谢!这很有帮助。 Datatable 无疑帮助我节省了大量空间。不幸的是,在这种情况下,d2 数据集是 300 万个观测值,而 d1 大约是 400,000 个。使用笛卡尔坐标时,仍然存在内存问题。我的笔记本电脑有 32 GB 的 RAM,所以升级可能不是问题。问题在于合并步骤。我希望有某种方式可以在不合并的情况下循环。
    • 也许您只需要相邻的县,这样可以避免全笛卡尔积。查看类似的example
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-26
    • 2013-06-03
    相关资源
    最近更新 更多