如何用另一个数据框填充一个数据框,同时保留第一个数据框的 NA
How to fill one data frame with data from another while retaining NAs from the first
我有两个列名相同但行数不同的数据框。第一个数据框 (a) 看起来类似于:
a = data.frame("Site"=c(1,2,3,4,7,9,10,11,13,14),
"v1"=c(0,0,0,0,0,0,0,0,0,0),
"v2"=c(0,0,0,0,NA,NA,NA,0,0,0),
"v3"=c(0,0,0,NA,0,NA,0,0,0,0),
"v4"=c(0,0,0,0,0,0,0,0,NA,NA),
"v5"=c(0,0,0,0,0,NA,0,NA,0,0))
注意:站点 5、6、8 和 12 是故意缺失的。
第二个数据框 (b) 看起来像这样:
b = data.frame("Site"=c(2,3,4,7,10,14),
"v1"=c(1,NA,2,1,NA,NA),
"v2"=c(1,1,NA,NA,NA,NA),
"v3"=c(NA,1,NA,NA,NA,1),
"v4"=c(1,NA,4,1,NA,NA),
"v5"=c(1,NA,2,1,1,3))
我想达到的效果是这样的:
desired = data.frame("Site"=c(1,2,3,4,7,9,10,11,13,14),
"v1"=c(0,1,0,2,1,0,0,0,0,0),
"v2"=c(0,1,1,0,NA,NA,NA,0,0,0),
"v3"=c(0,0,1,NA,0,NA,0,0,0,1),
"v4"=c(0,1,0,4,1,0,0,0,NA,NA),
"v5"=c(0,1,0,2,1,NA,1,NA,0,3))
我将数据帧 b 中的数据“注入”(我确定有更好的术语)到数据帧 a 中,但是我想替换任何 NA来自 b 的零,并保持原样来自 a 的 NA。
我发现并尝试过这段代码:
cols <- colnames(a)[colnames(a) %in% colnames(b)]
rows <- rownames(a)[rownames(a) %in% rownames(b)]
a[rows, cols] <- b[rows, cols]
但它带来了 NA。我考虑先用零替换 NA,但即便如此,它也会擦除我当前在数据框 a 中想要保留的 NA。
也许 for 循环或 tidyverse 中的某些东西是可行的方法,但我什至不知道从哪里开始。任何帮助将不胜感激!
我建议您先将 b
中的每个 NA
值替换为 0
,然后使用 inner_join
将结果与相应的 Site
值合并在 a
。然后,您可以将 a
的非 NA
值替换为它们在 b
中的相应值,而 a
中的 NA
值保持不变。最后,我们将修改后的数据框与 a
的子集绑定,其 Site
值不存在于 b
.
中
library(dplyr)
a %>%
inner_join(b %>%
mutate(across(!Site, ~ replace(.x, is.na(.x), 0))),
by = "Site") %>%
mutate(across(ends_with(".x"), ~ ifelse(!is.na(.x), get(gsub("(.*\.)x", "\1y", cur_column())),
.x))) %>%
select(!ends_with("y")) %>%
rename_with(~ gsub("(.*)\.x", "\1", .), ends_with(".x")) %>%
bind_rows(a %>%
filter(!Site %in% unique(b$Site))) %>%
arrange(Site)
Site v1 v2 v3 v4 v5
1 1 0 0 0 0 0
2 2 1 1 0 1 1
3 3 0 1 1 0 0
4 4 2 0 NA 4 2
5 7 1 NA 0 1 1
6 9 0 NA NA 0 NA
7 10 0 NA 0 0 1
8 11 0 0 0 0 NA
9 13 0 0 0 NA 0
10 14 0 0 1 NA 3
我的好朋友Onyambu:
推荐的也是一个精明而简洁的解决方案
rbind(a, b) %>%
group_by(Site) %>%
summarise(across(everything(), ~
if(any(!is.na(.x))) max(.x, na.rm = TRUE) else NA))
merge(b, a, by = 'Site', all = TRUE) %>%
split.default(sub('.x|.y', '', names(.))) %>%
map_df(~coalesce(!!!.x))
# A tibble: 10 x 6
Site v1 v2 v3 v4 v5
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 0 0 0 0 0
2 2 1 1 0 1 1
3 3 0 1 1 0 0
4 4 2 0 NA 4 2
5 7 1 NA 0 1 1
6 9 0 NA NA 0 NA
7 10 0 NA 0 0 1
8 11 0 0 0 0 NA
9 13 0 0 0 NA 0
10 14 0 0 1 NA 3
i <- match(b$Site, a$Site)
a_nas <- is.na(a)
for (j in seq(2, ncol(a))) {
a[i, j] <- ifelse(is.na(b[[j]]), 0, b[[j]])
}
a[a_nas] <- NA
all.equal(desired, a)
# [1] TRUE
我们可以使用{powerjoin}
library(powerjoin)
power_full_join(a, b, by = "Site", conflict = ~ifelse(is.na(.x), NA, coalesce_yx(.x, .y)))
#> Site v1 v2 v3 v4 v5
#> 1 1 0 0 0 0 0
#> 2 2 1 1 0 1 1
#> 3 3 0 1 1 0 0
#> 4 4 2 0 NA 4 2
#> 5 7 1 NA 0 1 1
#> 6 9 0 NA NA 0 NA
#> 7 10 0 NA 0 0 1
#> 8 11 0 0 0 0 NA
#> 9 13 0 0 0 NA 0
#> 10 14 0 0 1 NA 3
我有两个列名相同但行数不同的数据框。第一个数据框 (a) 看起来类似于:
a = data.frame("Site"=c(1,2,3,4,7,9,10,11,13,14),
"v1"=c(0,0,0,0,0,0,0,0,0,0),
"v2"=c(0,0,0,0,NA,NA,NA,0,0,0),
"v3"=c(0,0,0,NA,0,NA,0,0,0,0),
"v4"=c(0,0,0,0,0,0,0,0,NA,NA),
"v5"=c(0,0,0,0,0,NA,0,NA,0,0))
注意:站点 5、6、8 和 12 是故意缺失的。
第二个数据框 (b) 看起来像这样:
b = data.frame("Site"=c(2,3,4,7,10,14),
"v1"=c(1,NA,2,1,NA,NA),
"v2"=c(1,1,NA,NA,NA,NA),
"v3"=c(NA,1,NA,NA,NA,1),
"v4"=c(1,NA,4,1,NA,NA),
"v5"=c(1,NA,2,1,1,3))
我想达到的效果是这样的:
desired = data.frame("Site"=c(1,2,3,4,7,9,10,11,13,14),
"v1"=c(0,1,0,2,1,0,0,0,0,0),
"v2"=c(0,1,1,0,NA,NA,NA,0,0,0),
"v3"=c(0,0,1,NA,0,NA,0,0,0,1),
"v4"=c(0,1,0,4,1,0,0,0,NA,NA),
"v5"=c(0,1,0,2,1,NA,1,NA,0,3))
我将数据帧 b 中的数据“注入”(我确定有更好的术语)到数据帧 a 中,但是我想替换任何 NA来自 b 的零,并保持原样来自 a 的 NA。
我发现并尝试过这段代码:
cols <- colnames(a)[colnames(a) %in% colnames(b)]
rows <- rownames(a)[rownames(a) %in% rownames(b)]
a[rows, cols] <- b[rows, cols]
但它带来了 NA。我考虑先用零替换 NA,但即便如此,它也会擦除我当前在数据框 a 中想要保留的 NA。
也许 for 循环或 tidyverse 中的某些东西是可行的方法,但我什至不知道从哪里开始。任何帮助将不胜感激!
我建议您先将 b
中的每个 NA
值替换为 0
,然后使用 inner_join
将结果与相应的 Site
值合并在 a
。然后,您可以将 a
的非 NA
值替换为它们在 b
中的相应值,而 a
中的 NA
值保持不变。最后,我们将修改后的数据框与 a
的子集绑定,其 Site
值不存在于 b
.
library(dplyr)
a %>%
inner_join(b %>%
mutate(across(!Site, ~ replace(.x, is.na(.x), 0))),
by = "Site") %>%
mutate(across(ends_with(".x"), ~ ifelse(!is.na(.x), get(gsub("(.*\.)x", "\1y", cur_column())),
.x))) %>%
select(!ends_with("y")) %>%
rename_with(~ gsub("(.*)\.x", "\1", .), ends_with(".x")) %>%
bind_rows(a %>%
filter(!Site %in% unique(b$Site))) %>%
arrange(Site)
Site v1 v2 v3 v4 v5
1 1 0 0 0 0 0
2 2 1 1 0 1 1
3 3 0 1 1 0 0
4 4 2 0 NA 4 2
5 7 1 NA 0 1 1
6 9 0 NA NA 0 NA
7 10 0 NA 0 0 1
8 11 0 0 0 0 NA
9 13 0 0 0 NA 0
10 14 0 0 1 NA 3
我的好朋友Onyambu:
推荐的也是一个精明而简洁的解决方案rbind(a, b) %>%
group_by(Site) %>%
summarise(across(everything(), ~
if(any(!is.na(.x))) max(.x, na.rm = TRUE) else NA))
merge(b, a, by = 'Site', all = TRUE) %>%
split.default(sub('.x|.y', '', names(.))) %>%
map_df(~coalesce(!!!.x))
# A tibble: 10 x 6
Site v1 v2 v3 v4 v5
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 0 0 0 0 0
2 2 1 1 0 1 1
3 3 0 1 1 0 0
4 4 2 0 NA 4 2
5 7 1 NA 0 1 1
6 9 0 NA NA 0 NA
7 10 0 NA 0 0 1
8 11 0 0 0 0 NA
9 13 0 0 0 NA 0
10 14 0 0 1 NA 3
i <- match(b$Site, a$Site)
a_nas <- is.na(a)
for (j in seq(2, ncol(a))) {
a[i, j] <- ifelse(is.na(b[[j]]), 0, b[[j]])
}
a[a_nas] <- NA
all.equal(desired, a)
# [1] TRUE
我们可以使用{powerjoin}
library(powerjoin)
power_full_join(a, b, by = "Site", conflict = ~ifelse(is.na(.x), NA, coalesce_yx(.x, .y)))
#> Site v1 v2 v3 v4 v5
#> 1 1 0 0 0 0 0
#> 2 2 1 1 0 1 1
#> 3 3 0 1 1 0 0
#> 4 4 2 0 NA 4 2
#> 5 7 1 NA 0 1 1
#> 6 9 0 NA NA 0 NA
#> 7 10 0 NA 0 0 1
#> 8 11 0 0 0 0 NA
#> 9 13 0 0 0 NA 0
#> 10 14 0 0 1 NA 3