具有 NA 的多列的最小值和最大值

Min and Max across multiple columns with NAs

对于以下示例数据 dat,有没有办法在处理 NA 时计算 minmax。我的输入是:

dat <- read.table(text = "ID  Name   PM      TP2   Sigma
                          1   Tim    1       2    3
                          2   Sam    0       NA   1
                          3   Pam    2       1    NA
                          4   Ali    1       0    2
                          NA  NA     NA      NA   NA
                          6   Tim    2       0    7", header = TRUE)

我需要的输出是:

ID  Name  PM      TP2   Sigma  Min  Max
1    Tim  1       2    3       1    3
2    Sam  0       NA   1       0    1
3    Pam  2       1    NA      1    2
4    Ali  1       0    2       0    2
NA   NA   NA      NA   NA      NA   NA
6    Tim  2       0    7       0    7

我的努力

1- 我看过类似的帖子,但其中 none 讨论了列中所有条目都是 NA 的问题,例如 Get the min of two columns 基于此,我尝试了 pmin()pmax(),但它们对我不起作用。

2-另一个类似的问题是minimum (or maximum) value of each row across multiple columns。同样,无需处理 NAs.

3- 最后,这个问题 minimum (or maximum) value of each row across multiple columns 讨论了 NA 但并不是列中的所有元素都有缺失值。

4- 另外,一些解决方案要求手动输入要排除的列列表,我的原始数据相当 wide,我想有一个更简单的解决方案,我可以表达按数字而不是名称列。

部分解

我尝试了以下解决方案,但 Min 列最终具有 Inf,而 Max 列最终具有 -Inf.

dat$min = apply(dat[,c(2:4)], 1, min, na.rm = TRUE)
dat$max = apply(dat[,c(2:4)], 1, max, na.rm = TRUE)

我可以手动删除 Inf,方法如下:

dat$min[is.infinite(dat$min)] = NA

但我想知道是否有更好的方法来实现我想要的结果?任何建议将不胜感激。

感谢您的宝贵时间。

以下解决方案似乎适用于 transform() 函数:

dat <- transform(dat, min = pmin(PM, TP2, Sigma))
dat <- transform(dat, max = pmin(PM, TP2, Sigma))

没有使用transform()函数,数据好像乱七八糟。此外,上述命令要求明确写入所有列名。我不明白为什么写一个像下面这样的简短版本会失败。

pmin(dat[,2:4])) or
pmax(dat[,2:4]))

我发布了我能想到的唯一解决方案,以防其他人遇到类似问题。

您可以使用 hablarmin_max_ 函数,其中 returns NA 如果所有值都是 NA

library(dplyr)
library(hablar)

dat %>%
  rowwise() %>%
  mutate(min = min_(c_across(-ID)), 
         max = max_(c_across(-ID)))

您也可以将其与 apply -

一起使用
cbind(dat, t(apply(dat[-1], 1, function(x) c(min = min_(x), max = max_(x)))))

#  ID PM TP2 Sigma min max
#1  1  1   2     3   1   3
#2  2  0  NA     1   0   1
#3  3  2   1    NA   1   2
#4  4  1   0     2   0   2
#5 NA NA  NA    NA  NA  NA
#6  5  2   0     7   0   7

可能是将 pminpmaxdo.call:

一起使用
dat$min <- do.call(pmin, c(dat[,c(3:5)], na.rm=TRUE))
dat$max <- do.call(pmax, c(dat[,c(3:5)], na.rm=TRUE))
dat
#  ID Name PM TP2 Sigma min max
#1  1  Tim  1   2     3   1   3
#2  2  Sam  0  NA     1   0   1
#3  3  Pam  2   1    NA   1   2
#4  4  Ali  1   0     2   0   2
#5 NA <NA> NA  NA    NA  NA  NA
#6  6  Tim  2   0     7   0   7

我会用 data.table 来完成这个任务。我使用 rowSums 计算带有 na 的行数,并将其与总列数进行比较。我只是在 dat.new 中使用了至少有一个非 NA 值的所有列。然后你可以像往常一样使用na.rm=T。

希望这段小代码对您有所帮助。


library(data.table)

#your data
dat <- read.table(text = "ID    PM      TP2   Sigma
                          1      1       2    3
                  2      0       NA   1
                  3      2       1    NA
                  4      1       0    2
                  NA     NA      NA   NA
                  5      2       0    7", header = TRUE)

#generate data.table and add id
dat <- data.table(dat)
number.cols <- dim(dat)[2] #4
dat[,id:=c(1:dim(dat)[1])]
# > dat
#     ID PM TP2 Sigma id
# 1:  1  1   2     3  1
# 2:  2  0  NA     1  2
# 3:  3  2   1    NA  3
# 4:  4  1   0     2  4
# 5: NA NA  NA    NA  5
# 6:  5  2   0     7  6

#use new data.table to select all rows with at least one nonNA value
dat.new <- dat[rowSums(is.na(dat))<number.cols,]
dat.new[, MINv:=min(.SD, na.rm=T), by=id]
dat.new[, MAXv:=max(.SD, na.rm=T), by=id]

#if you need it merged to the old data
dat <- merge(dat, dat.new[,.(id,MINv,MAXv)], by="id")