如何在R中将每个组内的值向上移动?

6

我需要将每个id内的有效值移到数据框的顶部。以下是一个示例数据集:

df <- data.frame(id = c(1,1,1,2,2,2,3,3,3,3),
                 itemid = c(1,2,3,1,2,3,1,2,3,4),
                 values = c(1,NA,0,NA,NA,0,1,NA,0,NA))
    
df
   id itemid values
1   1      1      1
2   1      2     NA
3   1      3      0
4   2      1     NA
5   2      2     NA
6   2      3      0
7   3      1      1
8   3      2     NA
9   3      3      0
10  3      4     NA

除 id 列外,当 values 列中有缺失值时,我想要将每个 id 的所有值向顶部对齐。

如何获得下面的所需数据集?

df1
   id itemid values
1   1      1      1
2   1      2      0
3   1      3     NA
4   2      1      0
5   2      2     NA
6   2      3     NA
7   3      1      1
8   3      2      0
9   3      3     NA
10  3      4     NA
4个回答

8

使用 tidyverse,您可以通过是否存在缺失值来进行arrange操作(这将把缺失值放在底部)。

library(tidyverse)

df %>%
  arrange(id, is.na(values))

输出

      id itemid values
   <dbl>  <dbl>  <dbl>
 1     1      1      1
 2     1      3      0
 3     1      2     NA
 4     2      3      0
 5     2      1     NA
 6     2      2     NA
 7     3      1      1
 8     3      3      0
 9     3      2     NA
10     3      4     NA

或者,如果您希望保留itemid和其他列的相同顺序,则可以使用mutate来特定地排序感兴趣的列(如values)。其他回答提供了很好的解决方案,例如@Santiago和@ThomasIsCoding。如果您有多个感兴趣的列需要将NA移动到每个组的底部,也可以尝试以下方法:

df %>%
  group_by(id) %>%
  mutate(across(.cols = values, ~values[order(is.na(.))]))

其中.cols参数将包含要独立转换和重新排序的列。

输出

      id itemid values
   <dbl>  <dbl>  <dbl>
 1     1      1      1
 2     1      2      0
 3     1      3     NA
 4     2      1      0
 5     2      2     NA
 6     2      3     NA
 7     3      1      1
 8     3      2      0
 9     3      3     NA
10     3      4     NA

1
@RitchieSacramento 我刚看到这个!感谢您的评论-已经更正。 - Ben
1
但是这样做会丢失itemid的顺序。与问题中所需的输出进行比较。 - Santiago

3
我们可以尝试使用 ave + order
> transform(df,  values = ave(values, id, FUN = function(x) x[order(is.na(x))]))
   id itemid values
1   1      1      1
2   1      2      0
3   1      3     NA
4   2      1      0
5   2      2     NA
6   2      3     NA
7   3      1      1
8   3      2      0
9   3      3     NA
10  3      4     NA

2

使用 data.table

library(data.table)

setDT(df)[, values := values[order(is.na(values))], id][]
#>     id itemid values
#>  1:  1      1      1
#>  2:  1      2      0
#>  3:  1      3     NA
#>  4:  2      1      0
#>  5:  2      2     NA
#>  6:  2      3     NA
#>  7:  3      1      1
#>  8:  3      2      0
#>  9:  3      3     NA
#> 10:  3      4     NA

2
我会定义一个函数来完成你想要的操作,然后按照 id 进行分组:
completed_first <- function(x) {
  completed <- x[!is.na(x)]
  length(completed) <- length(x)
  completed
}

library(dplyr)

df %>%
  group_by(id) %>%
  mutate(
    values = completed_first(values)
  ) %>%
  ungroup()
# # A tibble: 10 × 3
#       id itemid values
#    <dbl>  <dbl>  <dbl>
#  1     1      1      1
#  2     1      2      0
#  3     1      3     NA
#  4     2      1      0
#  5     2      2     NA
#  6     2      3     NA
#  7     3      1      1
#  8     3      2      0
#  9     3      3     NA
# 10     3      4     NA

这种方法保留了itemid的顺序。


或者基于ThomasIsCoding的答案:

library(dplyr)

df %>%
  group_by(id) %>%
  mutate(
    values = values[order(is.na(values))]
  ) %>%
  ungroup()
# # A tibble: 10 × 3
#       id itemid values
#    <dbl>  <dbl>  <dbl>
#  1     1      1      1
#  2     1      2      0
#  3     1      3     NA
#  4     2      1      0
#  5     2      2     NA
#  6     2      3     NA
#  7     3      1      1
#  8     3      2      0
#  9     3      3     NA
# 10     3      4     NA

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接