rowSums 在不修改基础数据帧的情况下排除 dplyr 管道中的特定值

rowSums excluding a particular value in a dplyr pipe without modifying the underlying data frame

我有一个宽格式问卷数据的数据框,每一列代表一个问卷项目。

数据如下所示:

df <- data.frame(Q1 = c(1, 4, 2, 3, 1, 1, 4, 4, 1, 2), 
             Q2 = c(NA, 3, 1, 4, NA, NA, 3, 4, 1, 2),
             Q3 = c(3, 4, 1, 2, 4, NA, NA, 1, 1, 2),
             Q4 = c(NA, 4, 1, 1, 1, 3, NA, 2, 2, NA))

我想使用 rowSums 函数对每行中不是“4”的值求和并排除 NA 并将结果除以非 4 和非 NA 的数量列(使用 dplyr 管道)。我不想替换底层数据框中的 4s;我想保持原样。

因为我不知道如何将结果除以非 4 列和非 NA 列的数量,所以我只尝试尝试问题的第一部分。我已经使用以下代码尝试了第一部分,但没有成功:

library(dplyr)

df <- df %>%
  as.data.frame() %>%
  mutate(sum = rowSums(.[. != 4, ], na.rm = TRUE))

所需的输出如下所示:

在上面的屏幕截图中,"mean" 列是非 4 和非 NA 值的总和除以非 4 和非 NA 列的数量。

谢谢!

如果我们想在 dplyr 中严格执行此操作,我们可以将 rowwisedo 一起使用,并计算每行不为 4 的值的总和,然后将它们除以 length 的值。

library(dplyr)
df %>%
  rowwise() %>%
  do( (.) %>% as.data.frame %>% 
  mutate(mean = sum(.[. != 4], na.rm = TRUE)/length(.[.!=4 & !is.na(.)])))


#    Q1    Q2    Q3    Q4   mean
# * <dbl> <dbl> <dbl> <dbl> <dbl>
# 1  1.00 NA     3.00 NA     2.00
# 2  4.00  3.00  4.00  4.00  3.00
# 3  2.00  1.00  1.00  1.00  1.25
# 4  3.00  4.00  2.00  1.00  2.00
# 5  1.00 NA     4.00  1.00  1.00
# 6  1.00 NA    NA     3.00  2.00
# 7  4.00  3.00 NA    NA     3.00
# 8  4.00  4.00  1.00  2.00  1.50
# 9  1.00  1.00  1.00  2.00  1.25
#10  2.00  2.00  2.00 NA     2.00

EDIT - 在发布答案后,现在我意识到我们实际上可以使用 mean

df %>%
  rowwise() %>%
  do( (.) %>% as.data.frame %>% 
  mutate(mean = mean(.[. != 4], na.rm = TRUE)))
sp_mean <- function(x) mean(x[!is.na(x) & x != 4])
df$mean <- 
  df %>%
  apply(1, sp_mean)

df
   Q1 Q2 Q3 Q4 mean
1   1 NA  3 NA 2.00
2   4  3  4  4 3.00
3   2  1  1  1 1.25
4   3  4  2  1 2.00
5   1 NA  4  1 1.00
6   1 NA NA  3 2.00
7   4  3 NA NA 3.00
8   4  4  1  2 1.50
9   1  1  1  2 1.25
10  2  2  2 NA 2.00

编辑 1 - 稍微更健壮:

df$mean <- 
  df %>%
  select(matches("^Q\d+")) %>%
  apply(1, sp_mean)

matches("^Q\d+") 匹配以 Q1, Q2,..., Q199, Q200, ...

开头的列名

编辑 2 - 将我的 sp_mean() 与 Ronak 的解决方案相结合(不需要 do() 吗?):

df %>%
  rowwise() %>%
  mutate(mean = sp_mean(c(Q1, Q2, Q3, Q4)))

使用 base R 你可以做:

df$mean = rowMeans(`is.na<-`(df,df==4),T)#or rowMeans(replace(df,df==4,NA),T)
> df
   Q1 Q2 Q3 Q4 mean
1   1 NA  3 NA 2.00
2   4  3  4  4 3.00
3   2  1  1  1 1.25
4   3  4  2  1 2.00
5   1 NA  4  1 1.00
6   1 NA NA  3 2.00
7   4  3 NA NA 3.00
8   4  4  1  2 1.50
9   1  1  1  2 1.25
10  2  2  2 NA 2.00