R使用tidyverse用其他值的总和填充缺失值
R fill missing values with the sum of other values using tidyverse
我有一个包含很多列和很多行的数据框。
col_1 | col_2 | ... | col_n
35 | NA | ... | 2
. | . | . | .
. | . | . | .
. | . | . | .
123 | 90 | ... | NA
有些行包含 NA 值(可以超过 1 个 NA)。
我希望找到恰好包含 1 个 NA 的所有行,并将其替换为其他列的总和。
如何使用 tidyverse 实现它?
对之前的回答稍作修改,仅检查连续 1 NA
-
df<- t(apply(df, 1, function(x) {
if(sum(is.na(x)) == 1) replace(x, is.na(x), sum(x, na.rm = TRUE))
else x
}))
或 purrr::pmap_df
:
df <- purrr::pmap_df(df, ~{
x <- c(...)
if(sum(is.na(x)) == 1) replace(x, is.na(x), sum(x, na.rm = TRUE))
else x
})
两种tidyverse
方法
#toy data
df <- data.frame(
col_1 = c(35L, 43L, 234L, NA, 23L),
col_2 = c(421L, 54L, NA, 8L, 45L),
col_3 = c(NA, 435L, NA, 687L, 78L),
col_4 = c(767L, 78L, 65L, 89L, NA)
)
df
#> col_1 col_2 col_3 col_4
#> 1 35 421 NA 767
#> 2 43 54 435 78
#> 3 234 NA NA 65
#> 4 NA 8 687 89
#> 5 23 45 78 NA
#load libraries
library(tidyverse)
#1st approach
df %>% mutate(across(everything(), ~ ifelse(rowSums(is.na(cur_data())) == 1 & is.na(.), rowSums(cur_data(), na.rm = T), .)))
#> col_1 col_2 col_3 col_4
#> 1 35 421 1223 767
#> 2 43 54 435 78
#> 3 234 NA NA 65
#> 4 784 8 687 89
#> 5 23 45 78 146
#2nd approach
df %>% rowwise() %>%
mutate(replace(cur_data(), is.na(cur_data()) & rowSums(is.na(cur_data())) == 1, rowSums(cur_data(), na.rm = T)))
#> # A tibble: 5 x 4
#> # Rowwise:
#> col_1 col_2 col_3 col_4
#> <int> <int> <int> <int>
#> 1 35 421 1223 767
#> 2 43 54 435 78
#> 3 234 NA NA 65
#> 4 784 8 687 89
#> 5 23 45 78 146
由 reprex package (v2.0.0)
于 2021-06-05 创建
我们可以使用 coalesce
和 rowSums
来提高效率
library(dplyr)
v1 <- rowSums(df, na.rm = TRUE) * NA^(rowSums(is.na(df)) != 1)
df[] <- coalesce(unlist(df),v1[row(df)])
-输出
df
col_1 col_2 col_3 col_4
1 35 421 1223 767
2 43 54 435 78
3 234 NA NA 65
4 784 8 687 89
5 23 45 78 146
或者上面也可以写成
df %>%
mutate(rS = rowSums(cur_data(), na.rm = TRUE) *
NA^(rowSums(is.na(cur_data())) != 1),
across(-rS, ~ coalesce(., rS)), rS = NULL)
# col_1 col_2 col_3 col_4
#1 35 421 1223 767
#2 43 54 435 78
#3 234 NA NA 65
#4 784 8 687 89
#5 23 45 78 146
您还可以使用以下解决方案,它是 replace
功能的替代方案:
library(dplyr)
library(purrr)
df %>%
pmap_df(., ~ if(sum(is.na(c(...))) == 1) {
coalesce(c(...), sum(c(...), na.rm = TRUE))
} else {
c(...)
}
)
# A tibble: 2 x 4
col_1 col_2 col_3 col_n
<dbl> <dbl> <dbl> <dbl>
1 35 42 5 2
2 123 90 NA NA
数据
structure(list(col_1 = c(35, 123), col_2 = c(NA, 90), col_3 = c(5,
NA), col_n = c(2, NA)), class = "data.frame", row.names = c(NA,
-2L))
我使用了 Anil Goyal 的玩具数据(谢谢!)
今天有一个类似的问题请看这里:
这里我们:
- 对行求和
- 算上 NA 的
- and with across with 在 col1-4 上应用所需的条件
- 我最喜欢的部分是
.keep = "unused"
,它删除了“助手”列。
df %>%
mutate(rowsum1 = rowSums(., na.rm=TRUE)) %>%
mutate(count_na = rowSums(is.na(select(.,everything())))) %>%
mutate(across(starts_with("col"), ~case_when(count_na ==1 ~coalesce(.,rowsum1),
TRUE ~ as.numeric(.))
), .keep ="unused"
)
输出:
col_1 col_2 col_3 col_4
1 35 421 1223 767
2 43 54 435 78
3 234 NA NA 65
4 784 8 687 89
5 23 45 78 146
使用两个中间变量的替代解决方案:
q_na
每行 NA 数
s_row
不包括 NA 的行值总和。
library(tidyverse)
df <- tribble(
~col_1, ~col_2, ~col_3,
NA, 1, 3,
NA, NA, 2,
1, 5, 6,
NA, NA, NA,
3, NA, 2)
df %>%
rowwise() %>%
mutate(q_na = sum(is.na(c_across(col_1:col_3))),
s_row = sum(c_across(col_1:col_3), na.rm = TRUE)) %>%
ungroup() %>%
filter(q_na == 1) %>%
mutate(across(col_1:col_3, ~if_else(is.na(.x), s_row, .x))) %>%
dplyr::select(col_1:col_3)
#> # A tibble: 2 x 3
#> col_1 col_2 col_3
#> <dbl> <dbl> <dbl>
#> 1 4 1 3
#> 2 3 5 2
如果要获取所有行,只需删除 filter
并将其包含在 if_else
:
df %>%
rowwise() %>%
mutate(q_na = sum(is.na(c_across(col_1:col_3))),
s_row = sum(c_across(col_1:col_3), na.rm = TRUE)) %>%
ungroup() %>%
filter() %>%
mutate(across(col_1:col_3,
~if_else(q_na == 1 & is.na(.x), s_row, .x))) %>%
dplyr::select(col_1:col_3)
#> # A tibble: 5 x 3
#> col_1 col_2 col_3
#> <dbl> <dbl> <dbl>
#> 1 4 1 3
#> 2 NA NA 2
#> 3 1 5 6
#> 4 NA NA NA
#> 5 3 5 2
由 reprex package (v0.3.0)
于 2021-06-05 创建
我有一个包含很多列和很多行的数据框。
col_1 | col_2 | ... | col_n
35 | NA | ... | 2
. | . | . | .
. | . | . | .
. | . | . | .
123 | 90 | ... | NA
有些行包含 NA 值(可以超过 1 个 NA)。
我希望找到恰好包含 1 个 NA 的所有行,并将其替换为其他列的总和。
如何使用 tidyverse 实现它?
对之前的回答NA
-
df<- t(apply(df, 1, function(x) {
if(sum(is.na(x)) == 1) replace(x, is.na(x), sum(x, na.rm = TRUE))
else x
}))
或 purrr::pmap_df
:
df <- purrr::pmap_df(df, ~{
x <- c(...)
if(sum(is.na(x)) == 1) replace(x, is.na(x), sum(x, na.rm = TRUE))
else x
})
两种tidyverse
方法
#toy data
df <- data.frame(
col_1 = c(35L, 43L, 234L, NA, 23L),
col_2 = c(421L, 54L, NA, 8L, 45L),
col_3 = c(NA, 435L, NA, 687L, 78L),
col_4 = c(767L, 78L, 65L, 89L, NA)
)
df
#> col_1 col_2 col_3 col_4
#> 1 35 421 NA 767
#> 2 43 54 435 78
#> 3 234 NA NA 65
#> 4 NA 8 687 89
#> 5 23 45 78 NA
#load libraries
library(tidyverse)
#1st approach
df %>% mutate(across(everything(), ~ ifelse(rowSums(is.na(cur_data())) == 1 & is.na(.), rowSums(cur_data(), na.rm = T), .)))
#> col_1 col_2 col_3 col_4
#> 1 35 421 1223 767
#> 2 43 54 435 78
#> 3 234 NA NA 65
#> 4 784 8 687 89
#> 5 23 45 78 146
#2nd approach
df %>% rowwise() %>%
mutate(replace(cur_data(), is.na(cur_data()) & rowSums(is.na(cur_data())) == 1, rowSums(cur_data(), na.rm = T)))
#> # A tibble: 5 x 4
#> # Rowwise:
#> col_1 col_2 col_3 col_4
#> <int> <int> <int> <int>
#> 1 35 421 1223 767
#> 2 43 54 435 78
#> 3 234 NA NA 65
#> 4 784 8 687 89
#> 5 23 45 78 146
由 reprex package (v2.0.0)
于 2021-06-05 创建我们可以使用 coalesce
和 rowSums
来提高效率
library(dplyr)
v1 <- rowSums(df, na.rm = TRUE) * NA^(rowSums(is.na(df)) != 1)
df[] <- coalesce(unlist(df),v1[row(df)])
-输出
df
col_1 col_2 col_3 col_4
1 35 421 1223 767
2 43 54 435 78
3 234 NA NA 65
4 784 8 687 89
5 23 45 78 146
或者上面也可以写成
df %>%
mutate(rS = rowSums(cur_data(), na.rm = TRUE) *
NA^(rowSums(is.na(cur_data())) != 1),
across(-rS, ~ coalesce(., rS)), rS = NULL)
# col_1 col_2 col_3 col_4
#1 35 421 1223 767
#2 43 54 435 78
#3 234 NA NA 65
#4 784 8 687 89
#5 23 45 78 146
您还可以使用以下解决方案,它是 replace
功能的替代方案:
library(dplyr)
library(purrr)
df %>%
pmap_df(., ~ if(sum(is.na(c(...))) == 1) {
coalesce(c(...), sum(c(...), na.rm = TRUE))
} else {
c(...)
}
)
# A tibble: 2 x 4
col_1 col_2 col_3 col_n
<dbl> <dbl> <dbl> <dbl>
1 35 42 5 2
2 123 90 NA NA
数据
structure(list(col_1 = c(35, 123), col_2 = c(NA, 90), col_3 = c(5,
NA), col_n = c(2, NA)), class = "data.frame", row.names = c(NA,
-2L))
我使用了 Anil Goyal 的玩具数据(谢谢!)
今天有一个类似的问题请看这里:
这里我们:
- 对行求和
- 算上 NA 的
- and with across with 在 col1-4 上应用所需的条件
- 我最喜欢的部分是
.keep = "unused"
,它删除了“助手”列。
df %>%
mutate(rowsum1 = rowSums(., na.rm=TRUE)) %>%
mutate(count_na = rowSums(is.na(select(.,everything())))) %>%
mutate(across(starts_with("col"), ~case_when(count_na ==1 ~coalesce(.,rowsum1),
TRUE ~ as.numeric(.))
), .keep ="unused"
)
输出:
col_1 col_2 col_3 col_4
1 35 421 1223 767
2 43 54 435 78
3 234 NA NA 65
4 784 8 687 89
5 23 45 78 146
使用两个中间变量的替代解决方案:
q_na
每行 NA 数s_row
不包括 NA 的行值总和。
library(tidyverse)
df <- tribble(
~col_1, ~col_2, ~col_3,
NA, 1, 3,
NA, NA, 2,
1, 5, 6,
NA, NA, NA,
3, NA, 2)
df %>%
rowwise() %>%
mutate(q_na = sum(is.na(c_across(col_1:col_3))),
s_row = sum(c_across(col_1:col_3), na.rm = TRUE)) %>%
ungroup() %>%
filter(q_na == 1) %>%
mutate(across(col_1:col_3, ~if_else(is.na(.x), s_row, .x))) %>%
dplyr::select(col_1:col_3)
#> # A tibble: 2 x 3
#> col_1 col_2 col_3
#> <dbl> <dbl> <dbl>
#> 1 4 1 3
#> 2 3 5 2
如果要获取所有行,只需删除 filter
并将其包含在 if_else
:
df %>%
rowwise() %>%
mutate(q_na = sum(is.na(c_across(col_1:col_3))),
s_row = sum(c_across(col_1:col_3), na.rm = TRUE)) %>%
ungroup() %>%
filter() %>%
mutate(across(col_1:col_3,
~if_else(q_na == 1 & is.na(.x), s_row, .x))) %>%
dplyr::select(col_1:col_3)
#> # A tibble: 5 x 3
#> col_1 col_2 col_3
#> <dbl> <dbl> <dbl>
#> 1 4 1 3
#> 2 NA NA 2
#> 3 1 5 6
#> 4 NA NA NA
#> 5 3 5 2
由 reprex package (v0.3.0)
于 2021-06-05 创建