替换两列中的 NAs

问题描述 投票:0回答:4

我想做下面的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 中)。

有人知道怎么做吗?

谢谢

r na
4个回答
2
投票

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))

1
投票

您可以使用

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


1
投票

试试这个,它很快,因为没有

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))

0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.