假设我有一个像这样的data.frame:
df <- data.frame(group = rep(c("A", "B"), each = 10),
value = c(0, 0, 11, 5, 9, 8, 0, 6, 0, 9,
4, 0, 0, 0, 18, 1, 1, 0, 3, 6),
index = c(0, 4, 3, 3, 2, 6, 0, 1, 0, 5,
3, 0, 6, 0, 2, 4, 5, 0, 2, 1))
使用 dplyr,我想基于每个组添加一个新列,该列使用“index”列中的非零值提取“value”列的反向排序非零值的第 n 个值" 作为索引 n。
我想要的输出如下所示:
group values index column_wanted
1 A 0 0 0
2 A 0 4 8
3 A 11 3 9
4 A 5 3 9
5 A 9 2 9
6 A 8 6 5
7 A 0 0 0
8 A 6 1 11
9 A 0 0 0
10 A 9 5 6
11 B 4 3 4
12 B 0 0 0
13 B 0 6 1
14 B 0 0 0
15 B 18 2 6
16 B 1 4 3
17 B 1 5 1
18 B 0 0 0
19 B 3 2 6
20 B 6 1 18
可以通过首先创建仅包含非零值的 df 子集,然后创建新列来实现该任务,如下所示:
df_no0 <- df %>% filter(index != 0)
df_no0 <- df_no0 %>%
group_by(group) %>%
mutate(correct_col = rev(sort(values))[index])
df_no0
# A tibble: 14 × 4
# Groups: group [2]
group values index correct_col
<chr> <dbl> <dbl> <dbl>
1 A 0 4 8
2 A 11 3 9
3 A 5 3 9
4 A 9 2 9
5 A 8 6 5
6 A 6 1 11
7 A 9 5 6
8 B 4 3 4
9 B 0 6 1
10 B 18 2 6
11 B 1 4 3
12 B 1 5 1
13 B 3 2 6
14 B 6 1 18
然后我们可以将
rbind
this 设为仅包含零值的 df 子集:
df_just0 <- df %>% filter(index == 0)
df_final <- df_no0 %>%
rbind(df_just0 %>% mutate(correct_col = 0))
arrange(group)
df_final
# A tibble: 20 × 4
# Groups: group [2]
group values index correct_col
<chr> <dbl> <dbl> <dbl>
1 A 0 4 8
2 A 11 3 9
3 A 5 3 9
4 A 9 2 9
5 A 8 6 5
6 A 6 1 11
7 A 9 5 6
8 A 0 0 0
9 A 0 0 0
10 A 0 0 0
11 B 4 3 4
12 B 0 6 1
13 B 18 2 6
14 B 1 4 3
15 B 1 5 1
16 B 3 2 6
17 B 6 1 18
18 B 0 0 0
19 B 0 0 0
20 B 0 0 0
这给了我想要的输出(行顺序并不重要)。
这可行,但我正在寻找一种“更干净”和更短的解决方案,以避免将 data.frame“切割”成几个部分并最终重新绑定它们(真实数据有数百个组和数千个观察值)。
所以我尝试了这样的事情:
df %>%
group_by(group) %>%
mutate(wrong_column = ifelse(index == 0, 0,
rev(sort(values[values != 0]))[index[index != 0]]))
# A tibble: 20 × 4
# Groups: group [2]
group values index wrong_column
<chr> <dbl> <dbl> <dbl>
1 A 0 0 0
2 A 0 4 9
3 A 11 3 9
4 A 5 3 9
5 A 9 2 5
6 A 8 6 11
7 A 0 0 0
8 A 6 1 8
9 A 0 0 0
10 A 9 5 9
11 B 4 3 4
12 B 0 0 0
13 B 0 6 6
14 B 0 0 0
15 B 18 2 1
16 B 1 4 6
17 B 1 5 18
18 B 0 0 0
19 B 3 2 1
20 B 6 1 6
我不太明白这里发生了什么,但这并没有给我我想要的东西。 我也尝试了
case_when
功能,但我无法很好地使用它,因为它不断抛出 LHS 和 RHS 不匹配的错误。
有人能指出我正确的方向吗?
(我正在使用 R 4.3.2。和 dplyr 版本 1.1.2)
您应该使用
replace
来仅填充非 0 值的向量。
library(dplyr)
df %>%
mutate(column_wanted = replace(index, index != 0, rev(sort(value[value != 0]))[index[index != 0]]),
.by = group)
# group value index column_wanted
# 1 A 0 0 0
# 2 A 0 4 8
# 3 A 11 3 9
# 4 A 5 3 9
# 5 A 9 2 9
# 6 A 8 6 5
# 7 A 0 0 0
# 8 A 6 1 11
# 9 A 0 0 0
# 10 A 9 5 6
# 11 B 4 3 4
# 12 B 0 0 0
# 13 B 0 6 1
# 14 B 0 0 0
# 15 B 18 2 6
# 16 B 1 4 3
# 17 B 1 5 1
# 18 B 0 0 0
# 19 B 3 2 6
# 20 B 6 1 18
解释
ifelse
与 replace
略有不同,因为它使用原始向量大小 (index
) 而不是新向量大小 (index[index != 0]
)。如果尺寸太短,它会回收。
如果您检查第一组的值:
with(df[df$group == "A",], rev(sort(value[value != 0]))[index[index != 0]], 0)
#[1] 8 9 9 9 5 11 6
ifelse
的作用是创建一个大小为index
的向量,当索引为0时为0,如果不是则为上面的值,但它不会replace
的值。
# sorted values to match group size (recycled)
#[1] 8 9 9 9 5 11 6 8 9 9
# with 0s (output of ifelse)
#[1] 0 9 9 9 5 11 0 8 0 9
# correct output (replace)
#[1] 0 8 9 9 9 5 0 11 0 6