某些列的行式 NA 计数 - 按 id 分组
row wise NA count across some columns - grouped by id
我有一个数据框 df
如下:
输入
id na_count task q1 q2 q3 q4 q5
7 3 a 1 NA NA 2 NA
7 1 b 1 0 0 NA 0
7 3 c NA NA 1 NA 1
9 0 a 1 1 0 2 1
9 1 b 1 0 0 1 NA
9 0 c 1 1 0 1 0
9 1 d 1 0 NA 1 1
3 3 a 1 NA NA 1 NA
3 1 b 1 1 NA 2 1
1 2 b 1 1 NA 1 NA
1 2 c 1 1 NA 1 NA
1 3 d NA NA 1 NA 1
2 4 a 1 NA NA NA NA
2 2 b 1 2 NA 1 NA
2 1 c 1 1 2 NA 2
2 1 d NA 1 3 3 3
2 0 e 2 2 3 3 4
我有兴趣添加一个二进制列或标志 evidence
,这是通过查看每个 id
的数据然后确定 id
是否满足非 NA 值的最小阈值。
例如,我将最小非 NA 阈值设置为 10。因此,如果对于任何 id
至少有 10 个非 NA 值(多行),则我要设置证据为Yes
,否则我要设置证据为No
(Prefered) 如果可能的话,我想使用 na_count
列中非 NA 值的计数,而不是实际计算 NA在列 q1:q5
输出
对于阈值为 10 非 NA 的示例,我的输出如下:
id na_count task q1 q2 q3 q4 q5 evidence
7 3 a 1 NA NA 2 NA no
7 1 b 1 0 0 NA 0 no
7 3 c NA NA 1 NA 1 no
9 0 a 1 1 0 2 1 yes
9 1 b 1 0 0 1 NA yes
9 0 c 1 1 0 1 0 yes
9 1 d 1 0 NA 1 1 yes
3 3 a 1 NA NA 1 NA no
3 1 b 1 1 NA 2 1 no
1 2 b 1 1 NA 1 NA no
1 2 c 1 1 NA 1 NA no
1 3 d NA NA 1 NA 1 no
2 4 a 1 NA NA NA NA yes
2 2 b 1 2 NA 1 NA yes
2 1 c 1 1 2 NA 2 yes
2 1 d NA 1 3 3 3 yes
2 0 e 2 2 3 3 4 yes
部分解决方案
我尝试了以下方法,但它只计算行数,而不是该 ID 的多行的非 NA 值。
library(dplyr)
df = df %>%
group_by(id) %>%
mutate(rows = n())
相关帖子
以下帖子是相关的,但没有解决我的问题,
and
dput()
为了编码,我也在复制数据帧的dput()
# dput(df)
structure(list(
id = c(7L, 7L, 7L, 9L, 9L, 9L, 9L, 3L, 3L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L),
na_count = c(3L, 1L, 3L, 0L, 1L, 0L, 1L, 3L, 1L, 2L, 2L, 3L, 4L, 2L, 1L, 1L, 0L),
task = c("a", "b", "c", "a", "b", "c", "d", "a", "b", "b", "c", "d", "a", "b", "c", "d", "e"),
q1 = c(1L, 1L, NA, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, NA, 1L, 1L, 1L, NA, 2L),
q2 = c(NA, 0L, NA, 1L, 0L, 1L, 0L, NA, 1L, 1L, 1L, NA, NA, 2L, 1L, 1L, 2L),
q3 = c(NA, 0L, 1L, 0L, 0L, 0L, NA, NA, NA, NA, NA, 1L, NA, NA, 2L, 3L, 3L),
q4 = c(2L, NA, NA, 2L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, NA, NA, 1L, NA, 3L, 3L),
q5 = c(NA, 0L, 1L, 1L, NA, 0L, 1L, NA, 1L, NA, NA, 1L, NA, NA, 2L, 3L, 4L)),
row.names = c(NA, -17L), class = "data.frame")
如有任何帮助,我们将不胜感激,谢谢!
使用 tidyverse
包的解决方案。我们可以定义一个辅助函数来计算非 NA 值,嵌套数据框,将函数应用于每个 id,然后取消嵌套数据框。
library(tidyverse)
count_non_na <- function(x, threshold = 10){
x2 <- x %>%
dplyr::select(starts_with("q")) %>%
unlist()
non_na <- sum(!is.na(x2)) >= threshold
if (non_na){
result <- "yes"
} else {
result <- "no"
}
return(result)
}
df2 <- df %>%
group_by(id) %>%
nest() %>%
mutate(evidence = map_chr(data, count_non_na)) %>%
unnest(cols = data) %>%
ungroup()
df2
# # A tibble: 17 x 9
# id na_count task q1 q2 q3 q4 q5 evidence
# <int> <int> <chr> <int> <int> <int> <int> <int> <chr>
# 1 7 3 a 1 NA NA 2 NA no
# 2 7 1 b 1 0 0 NA 0 no
# 3 7 3 c NA NA 1 NA 1 no
# 4 9 0 a 1 1 0 2 1 yes
# 5 9 1 b 1 0 0 1 NA yes
# 6 9 0 c 1 1 0 1 0 yes
# 7 9 1 d 1 0 NA 1 1 yes
# 8 3 3 a 1 NA NA 1 NA no
# 9 3 1 b 1 1 NA 2 1 no
# 10 1 2 b 1 1 NA 1 NA no
# 11 1 2 c 1 1 NA 1 NA no
# 12 1 3 d NA NA 1 NA 1 no
# 13 2 4 a 1 NA NA NA NA yes
# 14 2 2 b 1 2 NA 1 NA yes
# 15 2 1 c 1 1 2 NA 2 yes
# 16 2 1 d NA 1 3 3 3 yes
# 17 2 0 e 2 2 3 3 4 yes
这是另一个想法。此解决方案只需要 dplyr
包,而不是整个 tidyverse
包。
df3 <- df %>%
group_by(id) %>%
summarize(across(starts_with("q"), .fns = ~sum(!is.na(.)))) %>%
mutate(Total = rowSums(select(., starts_with("q")))) %>%
mutate(evidence = ifelse(Total >= 10, "yes", "no")) %>%
select(id, evidence) %>%
right_join(df, by = "id") %>%
relocate(evidence, .after = q5)
df3
# # A tibble: 17 x 9
# id na_count task q1 q2 q3 q4 q5 evidence
# <int> <int> <chr> <int> <int> <int> <int> <int> <chr>
# 1 1 2 b 1 1 NA 1 NA no
# 2 1 2 c 1 1 NA 1 NA no
# 3 1 3 d NA NA 1 NA 1 no
# 4 2 4 a 1 NA NA NA NA yes
# 5 2 2 b 1 2 NA 1 NA yes
# 6 2 1 c 1 1 2 NA 2 yes
# 7 2 1 d NA 1 3 3 3 yes
# 8 2 0 e 2 2 3 3 4 yes
# 9 3 3 a 1 NA NA 1 NA no
# 10 3 1 b 1 1 NA 2 1 no
# 11 7 3 a 1 NA NA 2 NA no
# 12 7 1 b 1 0 0 NA 0 no
# 13 7 3 c NA NA 1 NA 1 no
# 14 9 0 a 1 1 0 2 1 yes
# 15 9 1 b 1 0 0 1 NA yes
# 16 9 0 c 1 1 0 1 0 yes
# 17 9 1 d 1 0 NA 1 1 yes
library(tidyverse)
threshold = 10
df %>% group_by(id) %>%
mutate(evidence = ifelse(n()*5 - sum(na_count) >= threshold, "yes", "no"))
5 来自您拥有的列数,q1:q5。
我有一个数据框 df
如下:
输入
id na_count task q1 q2 q3 q4 q5
7 3 a 1 NA NA 2 NA
7 1 b 1 0 0 NA 0
7 3 c NA NA 1 NA 1
9 0 a 1 1 0 2 1
9 1 b 1 0 0 1 NA
9 0 c 1 1 0 1 0
9 1 d 1 0 NA 1 1
3 3 a 1 NA NA 1 NA
3 1 b 1 1 NA 2 1
1 2 b 1 1 NA 1 NA
1 2 c 1 1 NA 1 NA
1 3 d NA NA 1 NA 1
2 4 a 1 NA NA NA NA
2 2 b 1 2 NA 1 NA
2 1 c 1 1 2 NA 2
2 1 d NA 1 3 3 3
2 0 e 2 2 3 3 4
我有兴趣添加一个二进制列或标志
evidence
,这是通过查看每个id
的数据然后确定id
是否满足非 NA 值的最小阈值。例如,我将最小非 NA 阈值设置为 10。因此,如果对于任何
id
至少有 10 个非 NA 值(多行),则我要设置证据为Yes
,否则我要设置证据为No
(Prefered) 如果可能的话,我想使用
na_count
列中非 NA 值的计数,而不是实际计算 NA在列 q1:q5
输出
对于阈值为 10 非 NA 的示例,我的输出如下:
id na_count task q1 q2 q3 q4 q5 evidence
7 3 a 1 NA NA 2 NA no
7 1 b 1 0 0 NA 0 no
7 3 c NA NA 1 NA 1 no
9 0 a 1 1 0 2 1 yes
9 1 b 1 0 0 1 NA yes
9 0 c 1 1 0 1 0 yes
9 1 d 1 0 NA 1 1 yes
3 3 a 1 NA NA 1 NA no
3 1 b 1 1 NA 2 1 no
1 2 b 1 1 NA 1 NA no
1 2 c 1 1 NA 1 NA no
1 3 d NA NA 1 NA 1 no
2 4 a 1 NA NA NA NA yes
2 2 b 1 2 NA 1 NA yes
2 1 c 1 1 2 NA 2 yes
2 1 d NA 1 3 3 3 yes
2 0 e 2 2 3 3 4 yes
部分解决方案
我尝试了以下方法,但它只计算行数,而不是该 ID 的多行的非 NA 值。
library(dplyr)
df = df %>%
group_by(id) %>%
mutate(rows = n())
相关帖子
以下帖子是相关的,但没有解决我的问题
dput()
为了编码,我也在复制数据帧的dput()
# dput(df)
structure(list(
id = c(7L, 7L, 7L, 9L, 9L, 9L, 9L, 3L, 3L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L),
na_count = c(3L, 1L, 3L, 0L, 1L, 0L, 1L, 3L, 1L, 2L, 2L, 3L, 4L, 2L, 1L, 1L, 0L),
task = c("a", "b", "c", "a", "b", "c", "d", "a", "b", "b", "c", "d", "a", "b", "c", "d", "e"),
q1 = c(1L, 1L, NA, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, NA, 1L, 1L, 1L, NA, 2L),
q2 = c(NA, 0L, NA, 1L, 0L, 1L, 0L, NA, 1L, 1L, 1L, NA, NA, 2L, 1L, 1L, 2L),
q3 = c(NA, 0L, 1L, 0L, 0L, 0L, NA, NA, NA, NA, NA, 1L, NA, NA, 2L, 3L, 3L),
q4 = c(2L, NA, NA, 2L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, NA, NA, 1L, NA, 3L, 3L),
q5 = c(NA, 0L, 1L, 1L, NA, 0L, 1L, NA, 1L, NA, NA, 1L, NA, NA, 2L, 3L, 4L)),
row.names = c(NA, -17L), class = "data.frame")
如有任何帮助,我们将不胜感激,谢谢!
使用 tidyverse
包的解决方案。我们可以定义一个辅助函数来计算非 NA 值,嵌套数据框,将函数应用于每个 id,然后取消嵌套数据框。
library(tidyverse)
count_non_na <- function(x, threshold = 10){
x2 <- x %>%
dplyr::select(starts_with("q")) %>%
unlist()
non_na <- sum(!is.na(x2)) >= threshold
if (non_na){
result <- "yes"
} else {
result <- "no"
}
return(result)
}
df2 <- df %>%
group_by(id) %>%
nest() %>%
mutate(evidence = map_chr(data, count_non_na)) %>%
unnest(cols = data) %>%
ungroup()
df2
# # A tibble: 17 x 9
# id na_count task q1 q2 q3 q4 q5 evidence
# <int> <int> <chr> <int> <int> <int> <int> <int> <chr>
# 1 7 3 a 1 NA NA 2 NA no
# 2 7 1 b 1 0 0 NA 0 no
# 3 7 3 c NA NA 1 NA 1 no
# 4 9 0 a 1 1 0 2 1 yes
# 5 9 1 b 1 0 0 1 NA yes
# 6 9 0 c 1 1 0 1 0 yes
# 7 9 1 d 1 0 NA 1 1 yes
# 8 3 3 a 1 NA NA 1 NA no
# 9 3 1 b 1 1 NA 2 1 no
# 10 1 2 b 1 1 NA 1 NA no
# 11 1 2 c 1 1 NA 1 NA no
# 12 1 3 d NA NA 1 NA 1 no
# 13 2 4 a 1 NA NA NA NA yes
# 14 2 2 b 1 2 NA 1 NA yes
# 15 2 1 c 1 1 2 NA 2 yes
# 16 2 1 d NA 1 3 3 3 yes
# 17 2 0 e 2 2 3 3 4 yes
这是另一个想法。此解决方案只需要 dplyr
包,而不是整个 tidyverse
包。
df3 <- df %>%
group_by(id) %>%
summarize(across(starts_with("q"), .fns = ~sum(!is.na(.)))) %>%
mutate(Total = rowSums(select(., starts_with("q")))) %>%
mutate(evidence = ifelse(Total >= 10, "yes", "no")) %>%
select(id, evidence) %>%
right_join(df, by = "id") %>%
relocate(evidence, .after = q5)
df3
# # A tibble: 17 x 9
# id na_count task q1 q2 q3 q4 q5 evidence
# <int> <int> <chr> <int> <int> <int> <int> <int> <chr>
# 1 1 2 b 1 1 NA 1 NA no
# 2 1 2 c 1 1 NA 1 NA no
# 3 1 3 d NA NA 1 NA 1 no
# 4 2 4 a 1 NA NA NA NA yes
# 5 2 2 b 1 2 NA 1 NA yes
# 6 2 1 c 1 1 2 NA 2 yes
# 7 2 1 d NA 1 3 3 3 yes
# 8 2 0 e 2 2 3 3 4 yes
# 9 3 3 a 1 NA NA 1 NA no
# 10 3 1 b 1 1 NA 2 1 no
# 11 7 3 a 1 NA NA 2 NA no
# 12 7 1 b 1 0 0 NA 0 no
# 13 7 3 c NA NA 1 NA 1 no
# 14 9 0 a 1 1 0 2 1 yes
# 15 9 1 b 1 0 0 1 NA yes
# 16 9 0 c 1 1 0 1 0 yes
# 17 9 1 d 1 0 NA 1 1 yes
library(tidyverse)
threshold = 10
df %>% group_by(id) %>%
mutate(evidence = ifelse(n()*5 - sum(na_count) >= threshold, "yes", "no"))
5 来自您拥有的列数,q1:q5。