簡體   English   中英

在 R 中使用 min() 返回 NA 而不是 Inf

[英]With min() in R return NA instead of Inf

請考慮以下幾點:

我最近“發現”了很棒的plyrdplyr包,並使用它們來分析數據框中可用的患者數據。 這樣的數據框可能如下所示:

df <- data.frame(id = c(1, 1, 1, 2, 2), # patient ID
                 diag = c(rep("dia1", 3), rep("dia2", 2)), # diagnosis
                 age = c(7.8, NA, 7.9, NA, NA)) # patient age

我想用中位數和平均值總結所有患者的最小患者年齡。 我做了以下事情:

min.age <- df %>% 
  group_by(id) %>% 
  summarise(min.age = min(age, na.rm = T))

由於數據框中有NAs ,我收到警告:

`Warning message: In min(age, na.rm = T) :
no non-missing arguments to min; returning Inf`

使用Inf我無法以有意義的方式調用summary(df$min.age)

使用pmin()而不是min返回錯誤消息:

Error in summarise_impl(.data, dots) :
 Column 'in.age' must be length 1 (a summary value), not 3

我能做些什么來避免任何Inf而是得到NA以便我可以進一步繼續: summary(df$min.age)

非常感謝!

您可以使用is.infinite()來檢測無窮大,並使用ifelse有條件地將它們設置為NA

#using your df and the dplyr package
min.age <- 
  df %>% 
  group_by(id) %>% 
  summarise(min.age = min(age, na.rm = T)) %>%
  mutate(min.age = ifelse(is.infinite(min.age), NA, min.age))

您的代碼執行以下操作:

  1. 將數據框按id分組
  2. 將每個組內的min函數應用於age變量,並啟用na.rm=TRUE選項。

因此,對於1 id ,您會得到min(c(7.8, NA, 7.9), na.rm=TRUE) ,這與min(c(7.8, 7.9)) ,僅為 7.8。

然后,對於2 id ,你得到min(c(NA, NA), na.rm=TRUE) ,這與min(c())

現在,一組空數字的最小值是多少? “最小值”的定義是“一個小於集合中所有值的值”,並且必須滿足 min(A) <= min(B) 只要 B 是 A 的一個子集。定義最小值的一種方法空集是說它是“無窮大”,這就是 R 處理這種情況的方式。

在這種情況下,您真的無法避免獲得Inf 但是您可以向您的鏈中添加另一個mutate以將任何Inf更改為您喜歡的任何內容,例如NA


df %>% group_by(id) %>% summarize(min_age = min(age, na.rm = TRUE)) %>% 
    mutate(min_age = ifelse(is.infinite(min_age), NA, min_age))
(min.age <- df %>% 
    group_by(id) %>% 
    summarise(min.age = ifelse(all(is.na(age)),NA,min(age, na.rm = T))))
# A tibble: 2 x 2
     id min.age
  <dbl>   <dbl>
1     1     7.8
2     2      NA

一個更簡單的解決方案是 hablar 包中的 s 函數。 在以最小/最大計算之前,它用 NA 替換空向量。 @awchisholm 的代碼塊可能是:

library(hablar)

min.age <- df %>% 
  group_by(id) %>% 
  summarise(min.age = min(s(age)))

免責聲明我對這個解決方案有偏見,因為我創作了這個包。

問題已得到解答,但有必要指出,如果所討論的列是日期或日期時間,那么它在匯總表中仍會顯示為 NA,但實際上並非如此。 這令人倍感困惑! 考慮:

library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
df <- data.frame(date = as.Date(c("2013-01-01", "2013-05-23", "", "2017-04-15", "", "")),
                 int = c(1L, 2L, NA, 4L, NA, NA),
                 group = rep(LETTERS[1:3],2))

s1 <- df %>% group_by(group) %>% summarise(min_date = min(date), min_int = min(int)) %>% mutate(min_date_missing = is.na(min_date), min_int_missing = is.na(min_int))
#> Warning: package 'bindrcpp' was built under R version 3.4.4
s2 <- df %>% group_by(group) %>% summarise(min_date = min(date, na.rm = TRUE), min_int = min(int, na.rm = TRUE)) %>% mutate(min_date_missing = is.na(min_date), min_int_missing = is.na(min_int))

df
#>         date int group
#> 1 2013-01-01   1     A
#> 2 2013-05-23   2     B
#> 3       <NA>  NA     C
#> 4 2017-04-15   4     A
#> 5       <NA>  NA     B
#> 6       <NA>  NA     C
s1
#> # A tibble: 3 x 5
#>   group min_date   min_int min_date_missing min_int_missing
#>   <fct> <date>       <dbl> <lgl>            <lgl>          
#> 1 A     2013-01-01      1. FALSE            FALSE          
#> 2 B     NA             NA  TRUE             TRUE           
#> 3 C     NA             NA  TRUE             TRUE
s2
#> # A tibble: 3 x 5
#>   group min_date   min_int min_date_missing min_int_missing
#>   <fct> <date>       <dbl> <lgl>            <lgl>          
#> 1 A     2013-01-01      1. FALSE            FALSE          
#> 2 B     2013-05-23      2. FALSE            FALSE          
#> 3 C     NA            Inf  FALSE            FALSE

s1[[3,2]]
#> [1] NA
s2[[3,2]]
#> [1] NA

is.na(s1[[3,2]])
#> [1] TRUE
is.na(s2[[3,2]])
#> [1] FALSE

s1[[3,2]] == Inf
#> [1] NA
s2[[3,2]] == Inf
#> [1] TRUE

s1[[3,3]]
#> [1] NA
s2[[3,3]]
#> [1] Inf

is.na(s1[[3,3]])
#> [1] TRUE
is.na(s2[[3,3]])
#> [1] FALSE

s1[[3,2]] == Inf
#> [1] NA
s2[[3,2]] == Inf
#> [1] TRUE

sessionInfo()
#> R version 3.4.3 (2017-11-30)
#> Platform: x86_64-apple-darwin15.6.0 (64-bit)
#> Running under: macOS High Sierra 10.13.5
#> 
#> Matrix products: default
#> BLAS: /Library/Frameworks/R.framework/Versions/3.4/Resources/lib/libRblas.0.dylib
#> LAPACK: /Library/Frameworks/R.framework/Versions/3.4/Resources/lib/libRlapack.dylib
#> 
#> locale:
#> [1] en_AU.UTF-8/en_AU.UTF-8/en_AU.UTF-8/C/en_AU.UTF-8/en_AU.UTF-8
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] bindrcpp_0.2.2 dplyr_0.7.4   
#> 
#> loaded via a namespace (and not attached):
#>  [1] Rcpp_0.12.17     utf8_1.1.3       crayon_1.3.4     digest_0.6.15   
#>  [5] rprojroot_1.3-2  assertthat_0.2.0 R6_2.2.2         backports_1.1.2 
#>  [9] magrittr_1.5     evaluate_0.10.1  pillar_1.2.1     cli_1.0.0       
#> [13] rlang_0.2.0.9001 stringi_1.1.7    rmarkdown_1.9    tools_3.4.3     
#> [17] stringr_1.3.0    glue_1.2.0       yaml_2.1.18      compiler_3.4.3  
#> [21] pkgconfig_2.0.1  htmltools_0.3.6  bindr_0.1.1      knitr_1.20      
#> [25] tibble_1.4.2

reprex 包(v0.2.0.9000) 於2018年 6 月 27 日創建。

這個看起來很有趣,因為它避免了警告:

myMin <- function(vec) {
      ifelse(length(vec[!is.na(vec)]) == 0, NA_real_, min(vec, na.rm = TRUE))
    }

我更喜歡選擇我自己的無效值。 200將是Age無效值。

現在可以稍微扭曲min函數的使用。 例如min(age, 200, na.rm = TRUE) 這確保當所有值都丟失時,年齡顯示為200而不是+Inf df的結果將是:

min.age <- df %>% 
  group_by(id) %>% 
  summarise(min.age = min(age, 200, na.rm = T))

> min.age
# A tibble: 2 x 2
#     id min.age
#  <dbl>   <dbl>
#1  1.00    7.80
#2  2.00  200 

現在,取決於程序員如何使用/替換這個無效值。

這是一個 function 可以與min一起使用,但也可以與maxmean一起使用,從而避免了這個問題,並使其更通用:

soft <- function(x, f, ...) ifelse(all(is.na(x)), NA, f(x, na.rm = TRUE, ...))

例如:

library(dplyr)
df <- data.frame(id = c(1, 1, 1, 2, 2), # patient ID
                 diag = c(rep("dia1", 3), rep("dia2", 2)), # diagnosis
                 age = c(7.8, NA, 7.9, NA, NA), # patient age
                 age2 = c(1, 2, 3, 4, 5)) # new column

df %>% 
  group_by(id) %>% 
  mutate(across(c(age, age2), list(min = ~ soft(.x, min),
                                   max = ~ soft(.x, max),
                                   mean = ~ soft(.x, mean))))

     id diag    age  age2 age_min age_max age_mean age2_min age2_max age2_mean
  <dbl> <chr> <dbl> <dbl>   <dbl>   <dbl>    <dbl>    <dbl>    <dbl>     <dbl>
1     1 dia1    7.8     1     7.8     7.9     7.85        1        3       2  
2     1 dia1   NA       2     7.8     7.9     7.85        1        3       2  
3     1 dia1    7.9     3     7.8     7.9     7.85        1        3       2  
4     2 dia2   NA       4    NA      NA      NA           4        5       4.5
5     2 dia2   NA       5    NA      NA      NA           4        5       4.5

使用collapse::fmin

fmin(NA, na.rm = TRUE)
# [1] NA

請注意, na.rm默認為TRUE ,因此fmin就足夠了。

fmin(c(NA, 1, 2))
# [1] 1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM