如何根据另一列的值对 dplyr 中的分组数据子集建立索引?

问题描述 投票:0回答:1

假设我有一个像这样的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)

r dplyr tidyverse subset
1个回答
0
投票

您应该使用

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
© www.soinside.com 2019 - 2024. All rights reserved.