我想做下面的NA替换,我不知道怎么解决。
我有两列,现在在我的数据框中如下所示:
> head(Metaverse[c("Q7","Q8")],10)
# A tibble: 10 × 2
Q7 Q8
<chr> <chr>
1 NA 1
2 1 NA
3 NA 1
4 1 NA
5 NA NA
6 NA 1
7 1 NA
8 NA 1
9 1 NA
10 1 NA
现在,每当两列中都有 NA(见第 5 行)时,我想在 Q7 列中放置一个 1(同时将 NA 留在 Q8 中)。
有人知道怎么做吗?
谢谢
dplyr 替代方案,使用
rowSums
和 is.na
:
Metaverse %>%
mutate(Q7b = if_else(rowSums(!is.na(cbind(Q7, Q8))) == 0, "1", Q7))
# # A tibble: 10 × 3
# Q7 Q8 Q7b
# <chr> <chr> <chr>
# 1 <NA> 1 <NA>
# 2 1 <NA> 1
# 3 <NA> 1 <NA>
# 4 1 <NA> 1
# 5 <NA> <NA> 1
# 6 <NA> 1 <NA>
# 7 1 <NA> 1
# 8 <NA> 1 <NA>
# 9 1 <NA> 1
# 10 1 <NA> 1
(不同的名称只是为了并排比较。)
(如果您使用的是
pick
或更新版本,我们可以使用 cbind
代替 dplyr_1.1.0
。)
使用
cbind
/pick
和 rowSums
组合的一个优点是,如果您有两个以上的列,只需将所需的列添加到调用中就很简单,无需在每个列上写 is.na
其中。
数据
Metaverse <- structure(list(Q7 = c(NA, "1", NA, "1", NA, NA, "1", NA, "1", "1"), Q8 = c("1", NA, "1", NA, NA, "1", NA, "1", NA, NA)), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, -10L))
您可以使用
rowSums
和 is.na
来检查是否等于这样的列数:
library(dplyr)
Metaverse %>%
mutate(Q7 = ifelse(rowSums(is.na(.)) == ncol(.), 1, Q7))
#> Q7 Q8
#> 1 NA 1
#> 2 1 NA
#> 3 NA 1
#> 4 1 NA
#> 5 1 NA
#> 6 NA 1
#> 7 1 NA
#> 8 NA 1
#> 9 1 NA
#> 10 1 NA
创建于 2023-05-03 与 reprex v2.0.2
试试这个,它很快,因为没有
ifelse
涉及。
dat[with(dat, is.na(Q7) & is.na(Q8)), 'Q7'] <- 1
这推广到
dat[Reduce(`&`, lapply(dat, is.na)), 'Q7'] <- 1
对于任意数量的列(速度应该相同)。
另一种选择是
rowSums
dat[rowSums(is.na(dat)) == 2, 'Q7'] <- 1
dat
# Q7 Q8
# 1 NA 1
# 2 1 NA
# 3 1 NA
# 4 1 NA
# 5 1 NA
# 6 NA 1
# 7 1 NA
# 8 NA 1
# 9 1 NA
# 10 1 NA
rowSums
对比Reduce
这里是关于
rowSums
和 Reduce
解决方案如何在不同行数 m 和列数 n 下表现的基准(行和列被上采样;行和 Q8 被简单地复制)。请注意,y 轴是对数刻度。
有趣的是,
rowSums
在这项研究中几乎总是显着变慢,这可能表明Reduce
在NA
的子集时更可取。
AMD FX(tm)-8350 八核处理器,cpu MHz:1417.954, 缓存大小:2048 KB,在 Linux 上使用
R --vanilla
执行
资料:
dat <- structure(list(Q7 = c(NA, 1L, NA, 1L, NA, NA, 1L, NA, 1L, 1L),
Q8 = c(1L, NA, NA, NA, NA, 1L, NA, 1L, NA, NA)), class = "data.frame", row.names = c(NA,
-10L))
当
ifelse()
和Q7
都缺失时,您可以使用Q8
来评估条件。如果为真,则结果为 1,否则,保留 Q7
中的值。这就是下面的代码所做的。请注意,您发布的数据没有任何此类条目,因此我将第二次观察的 Q7
值更改为 NA
,您可以在结果中看到它变为 1.
dat <- read.table(textConnection("Q7 Q8
NA 1
1 NA
NA NA
1 NA
NA NA
NA 1
1 NA
NA 1
1 NA
1 NA"), header=TRUE)
dat$Q7 <- ifelse(is.na(dat$Q7) & is.na(dat$Q8), 1, dat$Q7)
dat
#> Q7 Q8
#> 1 NA 1
#> 2 1 NA
#> 3 1 NA
#> 4 1 NA
#> 5 1 NA
#> 6 NA 1
#> 7 1 NA
#> 8 NA 1
#> 9 1 NA
#> 10 1 NA
创建于 2023-05-03 与 reprex v2.0.2
评论里有一些关于速度的讨论。以下是所提出的不同方法的基准。毫不奇怪,
dplyr
管道在这里比较慢。我的经验是,他们的 dplyr
对于小问题比 baseR 慢,而对于大问题比 baseR 快(尽管在任何特定情况下都是 YMMV)。两种 baseR 方法彼此没有显着差异,尽管 ifelse()
略有优势。
library(microbenchmark)
microbenchmark(
"ifelse_baseR" = {dat$Q7 <- ifelse(is.na(dat$Q7) & is.na(dat$Q8), 1, dat$Q7)},
"rowSums_baseR" = {dat[rowSums(is.na(dat)) == 2, 'Q7'] <- 1},
"dplyr1" = {dat <- dat %>% mutate(Q7b = if_else(rowSums(!is.na(cbind(Q7, Q8))) == 0, 1, Q7))},
"dplyr2" = {dat <- dat %>% mutate(Q7 = ifelse(rowSums(is.na(.)) == ncol(.), 1, Q7))}
)
#> Warning in microbenchmark(ifelse_baseR = {: less accurate nanosecond times to
#> avoid potential integer overflows
#> Unit: microseconds
#> expr min lq mean median uq max neval cld
#> ifelse_baseR 5.863 7.4210 9.48658 9.7580 10.9470 14.391 100 a
#> rowSums_baseR 12.423 14.5755 18.33192 18.7575 20.2540 33.948 100 a
#> dplyr1 762.067 778.2620 898.97338 799.7665 842.2015 7422.886 100 b
#> dplyr2 664.692 691.5060 783.42718 704.4825 757.5980 2851.222 100 b
创建于 2023-05-03 与 reprex v2.0.2