【问题标题】:In R loop through one data frame to generate another data frame conditionally with one or more rows for each row in the first data frame在R中循环一个数据帧以有条件地生成另一个数据帧,第一个数据帧中的每一行都有一个或多个行
【发布时间】:2019-05-15 18:19:21
【问题描述】:

我有一个这样的数据集:

set.seed(71)
dat <- data.table(region = rep(c('A','B'), each=10),
    place = rep(c('C','D'), 10),
    start = sample.int(5, 20, replace = TRUE),
    end = sample.int(10, 20, replace = TRUE),
    count = sample.int(50, 20, replace = TRUE),
    para1 = rnorm(20,3,1),
    para2 = rnorm(20,4,1))

我想遍历这些数据以有条件地生成另一个包含以下列的表: 区域、地点、开始、结束、计数、计数0 dat 中的每一行可能不止一行。 在新表中,将从 dat 中复制 region、place 和 start 列的数据,并生成 end、count 和 count0 列的数据。

以下是遍历每一行 dat 的规则:

end = end +1
if (count=0) {
  count0=0
} else {
  count0=start*para1 + end*para2
}
if (count0>count) {
  count0=count
}
count = count -count0

我尝试使用 for 循环、if 语句和 mutate 的组合,但无法正确使用。

我希望在通过前两行 dat 后得到这样的表:

region  place   start   end       count         count0
     A      C       2     7  6.01673062    17.98326938
     A      C       2     8           0     6.01673062
     A      D       3     2  5.34392419     7.65607581
     A      D       3     3           0     5.34392419


the first two rows of dat I have are:
region  place   start   end count   para1         para2
     A      C       2     6    24   0.39412969  2.45643
     A      D       3     1    13   0.64372127  2.862456

【问题讨论】:

  • 我从您的伪代码中不明白“dat 中的每一行可能有多行”。是什么决定了单行中是否存在多行?
  • 注意事项:如果您检查的是布尔值(T/F 类似于 if 语句),您应该使用 ==,而不是 =
  • 函数调用count0=startpara1 + endpara2生成一个值,该值将与count变量中的值进行比较。如果它小于count,将生成一个新行并将新函数值与count值的其余部分进行比较。这个过程一直持续到 count 的值变为零。所以如果计数值很大,它可能是很多行。

标签: r


【解决方案1】:

编辑:这是一种仍然非常快的惰性方法,但代价是临时生成我们将在最后删除的行。我不是计算每行要复制多少份,而是为每一行制作一堆副本,然后应用快速矢量化计算来获取更新的endcountcount0 值,然后删除行我们不需要。

library(dplyr); library(tidyr)
output <-
  dat %>%
  mutate(orig_row = row_number()) %>%
  uncount(10) %>%   # I'm assuming here that 10 is enough columns
  group_by(orig_row) %>%
  mutate(row = row_number()) %>%
  mutate(
    end = end + row,
    count0 = pmin(count, start * para1 + end * para2), # Edit #2
    count = count - cumsum(count0)
  ) %>%
  filter(lag(count, default = 0) >= 0) %>%
  mutate(count = pmax(0, count),
         count0 = if_else(count == 0, lag(count), count0))
output


# A tibble: 4 x 10
# Groups:   orig_row [2]
  region place start   end count para1 para2 orig_row   row count0
  <chr>  <chr> <int> <int> <dbl> <dbl> <dbl>    <int> <int>  <dbl>
1 A      C         2     7  6.02 0.394  2.46        1     1  18.0 
2 A      C         2     8  0    0.394  2.46        1     2   6.02
3 A      D         3     2  5.34 0.644  2.86        2     1   7.66
4 A      D         3     3  0    0.644  2.86        2     2   5.34

初步答案:

我想这是在附近。

警告:我没有得到与您显示的相同的样本数据,我也不了解您提供的样本中的特定数字将如何生成建议的输出。例如,从您显示的dat 的第一行(与我得到的不同),第一个count0 应该是2*0.394 + 6*2.456 = 15.527,不是吗?

我在这里的方法是计算count0,然后计算出有多少count 适合它,然后复制该行的多个副本,每行将count 递减count0

library(dplyr); library(tidyr)
output <- dat %>%
  mutate(end = end + 1,
         orig_data = row_number(),
         count0 = if_else(count == 0, 0,
                          start*para1 + end*para2),
         copies = 1 + count %/% count0) %>%
  uncount(copies) %>%
  group_by(orig_data) %>%
  mutate(row = row_number() - 1,
         count = count - row * count0)

顺便说一句,我的 dat 使用 set.seed(71) 进行不同的初始化。您能否确认您的数据是否按照 OP 中的规定进行了初始化?如果我们可以从同一个地方开始,就会更容易对齐。

> head(dat)
   region place start end count    para1    para2
1:      A     C     2   7    19 3.400587 2.757140
2:      A     D     3   3    31 1.503740 6.089518
3:      A     C     2   8     2 2.561869 5.236298
4:      A     D     2   3    33 3.069835 3.770121
5:      A     C     2   2    21 2.989221 3.547926
6:      A     D     5   5    32 2.720636 5.379352

【讨论】:

  • 是的,这是正确的方向,但缺少一些细节。我确定这是因为我的问题描述不够清楚。变量 end 需要在每次迭代中加 1。因此,计算出的 count0 值也会在每一行中发生变化。此外,新表的多行中的count0 的总和需要相加到它们所基于的第一个表中的行的计数值。最后,新表中的计数值减少,必须以零结尾。
  • 第一个 count0 应该是 2*0.394 + 7*2.456 = 17.98326938。这是在每次迭代结束时加 1 后计算的。
  • 能否请您验证您提供的示例数据是运行初始化代码后立即生成的数据?我已将第一行 dat 添加到我的答案中;它与您的不同,因此更难理解差异所在。
  • 即使使用相同的种子,我的运行也会产生不同的数据。请改用你的。关键是新表每一行的变量end不同(end=end+1),count0需要用新的end值计算。对于每个count0,需要检查它以确保它不超过剩余的计数值。如果是这样,它应该取 count 的值。 count 的值应该在每一行中相应减少,直到它达到零。
  • 或者,您可以只使用我发布的两行数据而不尝试生成更多数据。
猜你喜欢
  • 1970-01-01
  • 2021-01-09
  • 2020-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-15
相关资源
最近更新 更多