【发布时间】:2020-04-04 00:08:00
【问题描述】:
这个问题几乎等同于: Create new group based on cumulative sum and group
但是,当我将接受的解决方案应用于我的数据时,它没有得到预期的结果。
简而言之,我有一个包含两个变量的数据:domain 和 value。 Domain 是一个具有多个观察值的组变量,value 是我想通过domain 和一个新的组变量newgroup 累积的一些连续值。主要有三个规则:
- 我只在每个
domain内累积。如果我到达domain的末尾,则将重置累积。 - 如果累计总和至少为 1.0,则其值加起来至少为 1.0 的观测值将分配给
group1的不同值。请注意,只需一次观察即可满足此规则。 - 如果
domain中的最后一个组的累积和小于 1.0,则将其与同一domain中的倒数第二组合并。这反映在变量group2
下面的数据已经过简化。数据通常由 10^5 - 10^6 行组成,因此矢量化解决方案是理想的。
示例数据
domain <- c(rep(1,5),rep(2,8))
value <- c(1,0,2,2.5,0.1,0.1,0.5,0,0.2,0.6,0,0,0.1)
df_raw <- data.frame(domain,value)
domain value
1 1.0
1 0.0
1 2.0
1 2.5
1 0.1
2 0.1
2 0.5
2 0.0
2 0.2
2 0.6
2 0.0
2 0.0
2 0.1
期望的输出
cumsum_val <- c(1,0,2,2.5,0.1,0.1,0.6,0.6,0.8,1.4,0,0,0.1)
group1 <- c(1,2,2,3,4,5,5,5,5,5,6,6,6)
group2 <- c(1,2,2,3,3,4,4,4,4,4,4,4,4) #Satisfies Rule #3
df_want <- data.frame(domain,value,cumsum_val,group1,group2)
domain value cumsum_val group1 group2
1 1.0 1.0 1 1
1 0.0 0.0 2 2
1 2.0 2.0 2 2
1 2.5 2.5 3 3
1 0.1 0.1 4 3
2 0.1 0.1 5 4
2 0.5 0.6 5 4
2 0.0 0.6 5 4
2 0.2 0.8 5 4
2 0.6 1.4 5 4
2 0.0 0.0 6 4
2 0.0 0.0 6 4
2 0.1 0.1 6 4
我使用了以下代码:
sum0 <- function(x, y) { if (x + y >= 1.0) 0 else x + y }
is_start <- function(x) head(c(TRUE, Reduce(sum0, init=0, x, acc = TRUE)[-1] == 0), -1)
cumsum(ave(df_raw$value, df_raw$domain, FUN = is_start))
## 1 2 3 4 5 6 6 6 6 6 7 8 9
但最后一行产生的值与上面的group1 不同。生成group1 输出是主要导致我出现问题的原因。有人可以帮我理解is_start 的功能以及它应该如何产生分组吗?
编辑
akrun 在 cmets 中为上面的简化示例提供了一些工作代码。但是,仍然存在一些不起作用的情况。例如,
domain <- c(rep(1,7),rep(2,8))
value <- c(1,0,1,0,2,2.5,0.1,0.1,0.5,0,0.2,0.6,0,0,0.1)
df_raw <- data.frame(domain,value)
输出如下所示,new 来自 akrun 的代码,group1 和 group2 是基于规则 #2 和 #3 的所需分组。 new 和 group2 之间的差异主要出现在前 3 行。
domain value new group1 group2
1 1.0 1 1 1
1 0.0 2 2 2
1 1.0 3 2 2
1 0.0 4 3 3
1 2.0 4 3 3
1 2.5 5 4 4
1 0.1 5 5 4
2 0.1 6 6 5
2 0.5 6 6 5
2 0.0 6 6 5
2 0.2 6 6 5
2 0.6 6 6 5
2 0.0 6 7 5
2 0.0 6 7 5
2 0.1 6 7 5
编辑 2 我已经更新了一个有效的答案。
【问题讨论】:
-
@akrun 是的,我已经更新了帖子以更清楚地说明我在问什么。我将数据示例更改为“交换”版本。
-
是的,最终,但生成
group1是导致我出现问题的原因。 -
当它在第2行时,1 +0 = 1满足>=1,所以,在group1中为其分配了新ID,在第三行,不是还是
0 +2 = 2吗?满足 >=1, -> group2 = 3? -
可能是
df_want %>% group_by(domain) %>% mutate(new = cumsum(c(0, abs(diff(value)))<= 1), new = if(n_distinct(new) == n()) 1 else new) %>% ungroup %>% mutate(new = rleid(new)) -
由于第 1 行符合规则 #2,因此第 2 行应具有
group1和group2的新值。它不应该与第 1 行累积。第 2 行不满足任何条件,但是当它与第 3 行相加时,满足规则 #2。由于将第 2 行和第 3 行相加以满足规则 #2,因此它们应该位于相同的group1和group2