在 dplyr 中用 `group_by` 中的随机数替换 NA
Replace NA with random numbers within `group_by` in dplyr
我有一个长格式的数据框,我想用随机数替换缺失值,但我想用不同的设置明智地进行分组...
library(dplyr)
set.seed(1)
imp_df <-
data.frame(exp=rep(letters[1:3], each=2),
rep=1:2,
mean=1:6,
sd=seq(0,0.5,0.1))
df <-
data.frame(
exp=rep(letters[1:3], each=20),
rep=1:2,
int=rnorm(60,10,5)
)
df[sample(1:60,25,replace=F), 'int'] <- NA
所以我的数据如上所示,在 imp_df
中,我根据实验 exp
和复制 rep
对 rnorm
函数进行了设置。
我的数据框有一些缺失值,我想用随机数替换 NA
。
如何使用 dplyr
或 tidyr
来实现?
编辑
在@starja 的回答后,我找到了一个快速但可能很慢的解决方案,方法是将 rowwise
与 left_join
一起使用。
df %>%
left_join(imp_df) %>%
rowwise() %>%
mutate(imp.int=if_else(
is.na(int),
rnorm(1, mean, sd),
int
)) %>%
print(n=60)
还有其他方法吗?
编辑 2
由于 rowwise
方法非常慢,我无法在某些 dplyr 代码中得到它 运行,我使用了一个 for 循环来完成 imp_df
插补设置。
这是一个非常快速的解决方案,但不如我希望的那样可读:
df$imp.int <- df$int
for(line in 1:nrow(imp_df)) {
imp_settings <- as.list(imp_df[line,])
rows_missing_values <- which(
df$exp == imp_settings$exp &
df$rep == imp_settings$rep &
is.na(df$imp.int)
)
df$imp.int[rows_missing_values] <-
stats::rnorm(length(rows_missing_values), imp_settings$mean, imp_settings$sd)
}
所以我们首先为估算值添加一列 imp.int
,现在 运行 通过替换每个组的 NA 逐行添加不同的估算设置。
我想有使用矢量化的更聪明的解决方案,但如果你没有超大数据,我喜欢为此使用 purrr::map
函数和一个小的自定义函数:
library(dplyr)
set.seed(1)
imp_df <-
data.frame(exp=rep(letters[1:3], each=2),
rep=1:2,
mean=1:6,
sd=seq(0,0.5,0.1))
df <-
data.frame(
exp=rep(letters[1:3], each=20),
rep=1:2,
int=rnorm(60,10,5)
)
df[sample(1:60,25,replace=F), 'int'] <- NA
replace_fun <- function(x, mean, sd) {
if (is.na(x)) {
rnorm(1, mean, sd)
} else {
x
}
}
df %>%
left_join(imp_df, by = c("exp", "rep")) %>%
mutate(int = purrr::pmap_dbl(list(int, mean, sd), replace_fun)) %>%
head()
#> exp rep int mean sd
#> 1 a 1 1.000000 1 0.0
#> 2 a 2 10.918217 2 0.1
#> 3 a 1 5.821857 1 0.0
#> 4 a 2 17.976404 2 0.1
#> 5 a 1 11.647539 1 0.0
#> 6 a 2 5.897658 2 0.1
由 reprex package (v0.3.0)
于 2021-05-27 创建
(如果需要,可以使用 select(-c(mean, sd))
删除 mean/sd 列。)
也可以这样做:
library(dplyr)
library(purrr)
df %>%
left_join(imp_df, by = c("exp", "rep")) %>%
mutate(int = ifelse(is.na(int),
map2(mean, sd, ~ rnorm(1, .x, .y)), int))
exp rep int mean sd
1 a 1 1 1 0.0
2 a 2 10.91822 2 0.1
3 a 1 5.821857 1 0.0
4 a 2 17.9764 2 0.1
5 a 1 11.64754 1 0.0
6 a 2 5.897658 2 0.1
7 a 1 12.43715 1 0.0
8 a 2 13.69162 2 0.1
9 a 1 12.87891 1 0.0
10 a 2 1.986482 2 0.1
我有一个长格式的数据框,我想用随机数替换缺失值,但我想用不同的设置明智地进行分组...
library(dplyr)
set.seed(1)
imp_df <-
data.frame(exp=rep(letters[1:3], each=2),
rep=1:2,
mean=1:6,
sd=seq(0,0.5,0.1))
df <-
data.frame(
exp=rep(letters[1:3], each=20),
rep=1:2,
int=rnorm(60,10,5)
)
df[sample(1:60,25,replace=F), 'int'] <- NA
所以我的数据如上所示,在 imp_df
中,我根据实验 exp
和复制 rep
对 rnorm
函数进行了设置。
我的数据框有一些缺失值,我想用随机数替换 NA
。
如何使用 dplyr
或 tidyr
来实现?
编辑
在@starja 的回答后,我找到了一个快速但可能很慢的解决方案,方法是将 rowwise
与 left_join
一起使用。
df %>%
left_join(imp_df) %>%
rowwise() %>%
mutate(imp.int=if_else(
is.na(int),
rnorm(1, mean, sd),
int
)) %>%
print(n=60)
还有其他方法吗?
编辑 2
由于 rowwise
方法非常慢,我无法在某些 dplyr 代码中得到它 运行,我使用了一个 for 循环来完成 imp_df
插补设置。
这是一个非常快速的解决方案,但不如我希望的那样可读:
df$imp.int <- df$int
for(line in 1:nrow(imp_df)) {
imp_settings <- as.list(imp_df[line,])
rows_missing_values <- which(
df$exp == imp_settings$exp &
df$rep == imp_settings$rep &
is.na(df$imp.int)
)
df$imp.int[rows_missing_values] <-
stats::rnorm(length(rows_missing_values), imp_settings$mean, imp_settings$sd)
}
所以我们首先为估算值添加一列 imp.int
,现在 运行 通过替换每个组的 NA 逐行添加不同的估算设置。
我想有使用矢量化的更聪明的解决方案,但如果你没有超大数据,我喜欢为此使用 purrr::map
函数和一个小的自定义函数:
library(dplyr)
set.seed(1)
imp_df <-
data.frame(exp=rep(letters[1:3], each=2),
rep=1:2,
mean=1:6,
sd=seq(0,0.5,0.1))
df <-
data.frame(
exp=rep(letters[1:3], each=20),
rep=1:2,
int=rnorm(60,10,5)
)
df[sample(1:60,25,replace=F), 'int'] <- NA
replace_fun <- function(x, mean, sd) {
if (is.na(x)) {
rnorm(1, mean, sd)
} else {
x
}
}
df %>%
left_join(imp_df, by = c("exp", "rep")) %>%
mutate(int = purrr::pmap_dbl(list(int, mean, sd), replace_fun)) %>%
head()
#> exp rep int mean sd
#> 1 a 1 1.000000 1 0.0
#> 2 a 2 10.918217 2 0.1
#> 3 a 1 5.821857 1 0.0
#> 4 a 2 17.976404 2 0.1
#> 5 a 1 11.647539 1 0.0
#> 6 a 2 5.897658 2 0.1
由 reprex package (v0.3.0)
于 2021-05-27 创建(如果需要,可以使用 select(-c(mean, sd))
删除 mean/sd 列。)
也可以这样做:
library(dplyr)
library(purrr)
df %>%
left_join(imp_df, by = c("exp", "rep")) %>%
mutate(int = ifelse(is.na(int),
map2(mean, sd, ~ rnorm(1, .x, .y)), int))
exp rep int mean sd
1 a 1 1 1 0.0
2 a 2 10.91822 2 0.1
3 a 1 5.821857 1 0.0
4 a 2 17.9764 2 0.1
5 a 1 11.64754 1 0.0
6 a 2 5.897658 2 0.1
7 a 1 12.43715 1 0.0
8 a 2 13.69162 2 0.1
9 a 1 12.87891 1 0.0
10 a 2 1.986482 2 0.1