首页 > 解决方案 > 如何在 R 中通过多个组运行循环

问题描述

我有一个包含多个变量的数据集,我想将两列的值逐行求和。如果总和低于设定的阈值,我想用总和值替换第二列(正在求和)的值。但是,我想按组执行此操作。

我的数据框设置了 18 个不同的列,包括“Closed_Grid”、“Closed_Sets”、“BestAvail”、“Best_Sets”和“Best_Distance”。“BestAvail”、“Best_Sets”和“Best_Distance”重复用于第二最佳、第三、第四和第五。我正在使用此信息来确定最终目标位置(第 18 列“Dest_Grid”),该位置将根据“Closed_Sets”和目标集的条件求和填充来自“BestAvail”、“2nd_Best”等的网格索引(最佳、第二等)。最后,如果两列的总和 <=150,则该网格单元(“BestAvail”)将是“Dest_Grid”。如果总和 > 150,那么它将转到下一个块并计算“Closed_Sets”之间的新总和

因此,为了帮助简化这一点,我的数据集的样本(和子集)如下所示:

Closed_Grid Closed_Sets BestAvail Best_Sets
GY38         72.875     GX38       91.75
GY37         87.125     GX38       91.75
GY36         39.875     GX38       91.75
GZ38         29         GX38       91.75
GZ37         80         GX38       91.75
GY35         2.375      GX38       91.75
GZ36         125.25     GX38       91.75
GZ35         29.875     GX38       91.75
GY39         17.5       GX39       54.125
HA35         34.375     GZ33       30.5
GZ41         109.625    GZ42       76.76
GY41         82.28571   GZ42       76.75
HA41         87.5       GZ42       76.75
GZ40         104.75     GZ42       76.75
GY40         60.625     GZ42       76.75
HA40         79.875     GZ42       76.75
GZ39         51.57143   GZ42       76.75
HA39         71         GZ42       76.75

我首先使用“BestAvail”和“Distance”(从最小到最大)排列我的数据:

Destination <- Destination %>% arrange(BestAvail, BestDistance)

这是一个重要的顺序,因为与 BestAvail 距离最小的 Closed_Grid 优先进入该网格。

所以现在我想在一个组内按行对“Closed_Sets”和“Best_Sets”求和(即“BestAvail”相同)。每当行的总和小于阈值 (150) 时,“Best_Sets”值将替换为先前的总和。所以,我想要的输出是这样的:

Closed_Grid Closed_Sets BestAvail Best_Sets BestSum
GY38         72.875     GX38       91.75    164.6250
GY37         87.125     GX38       91.75    178.8750
GY36         39.875     GX38       91.75    131.625
GZ38         29         GX38       131.625  160.625  
GZ37         80         GX38       131.625  211.625
GY35         2.375      GX38       131.625  134.00
GZ36         125.25     GX38       134.00   259.250
GZ35         29.875     GX38       134.00   163.8750
GY39         17.5       GX39       54.125   71.625
HA35         34.375     GZ33       30.5     64.875
GZ41         109.625    GZ42       76.75    186.375
GY41         82.28571   GZ42       76.75    159.03571
HA41         87.5       GZ42       76.75    164.25
GZ40         104.75     GZ42       76.75    181.5
GY40         60.625     GZ42       76.75    137.375
HA40         79.875     GZ42       137.375  217.25
GZ39         51.57143   GZ42       137.375  188.94643
HA39         71         GZ42       137.375  208.375

我可以通过使用这个循环部分地实现这一点:

for (i in 1:nrow(Destination)){
    Destination$BestSum[i] <- sum(Destination$Closed_Sets[i], Destination$Best_Sets[i])
    if (Destination$BestSum[i] <= 150){
      Destination [i:length(Destination),"Best_Sets"] <- Destination$BestSum[i]
    }
  }

但是,此代码将所有“Best_Sets”的值设为 134,并且在“BestAvail”值更改时不会重新启动,这反过来又会弄乱以下所有总和。最终,我试图对保持在 150 以下的组中的每个“Closed_Set”进行条件累积和。

这是我正在研究的模型的一部分,它将有超过 150 个单独的数据集贯穿其中,所有数据集都具有不同的长度和值。这段特殊的代码还需要遍历第二、第三等集合,因此它需要是可以重复的,并且变量很容易改变。

我尝试在循环中使用 unique() 函数,尝试让我自己的函数在 dplyr 中使用(这将是理想的!),尝试使用重置函数进行不同的累积和,并且此时已经搜索了数百个线程。

我对 R 和编程比较陌生,并且很难弄清楚如何做到这一点。我已经就与此相关的每个可能问题进行了多次讨论,但似乎无法让它在我的数据上起作用。

我希望我想要实现的目标是有意义的。

提前致谢!

标签: rfor-loopdplyr

解决方案


注意:下面的 R 代码不是很惯用,可能会很慢。我不建议您将这种样式用于常见任务。

# build the data frame
Closed_Grid = c(
  "GY38",
  "GY37",
  "GY36",
  "GZ38",
  "GZ37",
  "GY35",
  "GZ36",
  "GZ35",
  "GY39",
  "HA35",
  "GZ41",
  "GY41",
  "HA41",
  "GZ40",
  "GY40",
  "HA40",
  "GZ39",
  "HA39"
)

Closed_Sets = c(
  72.875,
  87.125,
  39.875,
  29,
  80,
  2.375,
  125.25,
  29.875,
  17.5,
  34.375,
  109.625,
  82.28571,
  87.5,
  104.75,
  60.625,
  79.875,
  51.57143,
  71
)

BestAvail = c(
  "GX38",
  "GX38",
  "GX38",
  "GX38",
  "GX38",
  "GX38",
  "GX38",
  "GX38",
  "GX39",
  "GZ33",
  "GZ42",
  "GZ42",
  "GZ42",
  "GZ42",
  "GZ42",
  "GZ42",
  "GZ42",
  "GZ42"
)

Best_Sets = c(
  91.75,
  91.75,
  91.75,
  91.75,
  91.75,
  91.75,
  91.75,
  91.75,
  54.125,
  30.5,
  76.76,
  76.75,
  76.75,
  76.75,
  76.75,
  76.75,
  76.75,
  76.75
)

dat <- data.frame(
  Closed_Grid, Closed_Sets, BestAvail, Best_Sets,
  stringsAsFactors = FALSE
)

# allocate a vector; this makes the for() loop use significantly
# less memory, see https://adv-r.hadley.nz/perf-improve.html#avoid-copies

dat$BestSum <- NA_real_

# split the data frame to work on one group of BestAvail at a time
Destination <- split(dat, factor(dat[["BestAvail"]]))

Destination <- lapply(Destination, function(dat) {
  for (i in seq_len(nrow(dat))) {

    BestSum <- rowSums(dat[i, c("Closed_Sets", "Best_Sets")])
    dat[i, "BestSum"] <- BestSum

    if (as.integer(i) > 1L) {
      if (BestSum < 150.0) {
        dat[i+1:(nrow(dat) - i), "Best_Sets"] <- dat[i, "BestSum"]
      }
    }

  }

  dat

})

# recombine
Destination <- do.call(rbind, Destination)

Destination

这段代码可能会很慢。如果您在大型数据集上运行它,则可能值得用 C++ 编写它。


推荐阅读