删除函数内因子的 NA 值

Dropping NA values of factors within a function

玩具资料:

假设我有这个 df

df <- structure(list(x = structure(c(NA, 7L, NA, NA, 4L, 6L, 6L, 2L, 
3L, 5L, 8L, 4L, 7L, 3L, 5L, 1L, 5L, 5L, 5L, NA), .Label = c("1", 
"2", "3", "4", "5", "6", "7", "8"), class = "factor"), y = structure(c(NA, 
2L, 3L, 2L, 2L, 2L, 2L, 1L, 3L, NA, 2L, 3L, 1L, 1L, 3L, 2L, 2L, 
3L, 2L, 2L), .Label = c("1", "2", "3"), class = "factor"), z = structure(c(NA, 
4L, 4L, 4L, 5L, 4L, 5L, 5L, 2L, NA, 4L, 1L, 1L, 3L, 2L, 5L, 2L, 
2L, 4L, NA), .Label = c("1", "2", "3", "4", "5"), class = "factor"), 
    a = c(-32L, -51L, -22L, 44L, 55L, -24L, -50L, 67L, 1L, -47L, 
    66L, -98L, -91L, -42L, -89L, -31L, -8L, -33L, 38L, 61L), 
    b = c(46L, -19L, -37L, 47L, -28L, -48L, 14L, -10L, -13L, 
    -31L, 32L, 21L, -21L, 25L, -8L, 42L, -26L, -24L, 36L, -39L
    )), row.names = c(NA, -20L), class = c("tbl_df", "tbl", "data.frame"
))

df

# A tibble: 20 × 5
   x     y     z         a     b
   <fct> <fct> <fct> <int> <int>
 1 NA    NA    NA      -32    46
 2 7     2     4       -51   -19
 3 NA    3     4       -22   -37
 4 NA    2     4        44    47
 5 4     2     5        55   -28
 6 6     2     4       -24   -48
 7 6     2     5       -50    14
 8 2     1     5        67   -10
 9 3     3     2         1   -13
10 5     NA    NA      -47   -31
11 8     2     4        66    32
12 4     3     1       -98    21
13 7     1     1       -91   -21
14 3     1     3       -42    25
15 5     3     2       -89    -8
16 1     2     5       -31    42
17 5     2     2        -8   -26
18 5     3     2       -33   -24
19 5     2     4        38    36
20 NA    2     NA       61   -39

我想在 0-1 范围内对变量 xyz 进行标准化,然后生成一些关于它们的摘要统计信息。我可以使用下面的代码生成摘要统计数据

有效代码:

library(tidyverse)

vars <- c('x', 'y', 'z')
names(vars) <- vars

summary_stats <- function(data){
       tibble(
           n = sum(!is.na(data)), 
           mean = round(mean(as.numeric(data), na.rm = T), digits = 3), 
           sd = round(sd(as.numeric(data), na.rm = T), digits = 3), 
           se = round(sd/sqrt(n), digits = 3) 
        ) 
}

table <- map_df(
    df %>% 
      dplyr::select(vars), 
    summary_stats, 
    .id = "covariate")

table

# A tibble: 3 × 5
  covariate     n  mean    sd    se
  <chr>     <int> <dbl> <dbl> <dbl> 
1 x            16  4.75 1.88  0.47  
2 y            18  2.11 0.676 0.159  
3 z            17  3.35 1.41  0.342 

无效的代码:

但我正在努力弄清楚如何规范化变量。我最近的尝试是试试这个

summary_stats <- function(data){
 
  data_norm <- drop_na(data) %>% dplyr::summarize(
    (as.numeric(data) - min(as.numeric(data))) /
    (max(as.numeric(data)) - min(as.numeric(data)))
    )
       tibble(
           n = sum(!is.na(data_norm)), 
           mean = round(mean(as.numeric(data_norm), na.rm = T), digits = 3), 
           sd = round(sd(as.numeric(data_norm), na.rm = T), digits = 3), 
           se = round(sd/sqrt(n), digits = 3) 
        ) 
}

table <- map_df(
    df %>% 
      dplyr::select(vars), 
    summary_stats, 
    .id = "covariate")

错误:

但是这个returns错误

Error in UseMethod("drop_na_") : no applicable method for 'drop_na_' applied to an object of class "factor"

如果我即时将它转换为数字,所以我有 data_norm <- drop_na(as.numeric(data)) 等,然后我得到一个非常相似的错误说

Error in UseMethod("drop_na_") : no applicable method for 'drop_na_' applied to an object of class "c('double', 'numeric')"

但是,如果我在函数之外执行此操作,它会正常工作

df %>% drop_na(x) %>% summarise(std_mean = (as.numeric(x) - min(as.numeric(x))) / (max(as.numeric(x)) - min(as.numeric(x))))

# A tibble: 16 × 1
   std_mean
      <dbl>
 1    0.857
 2    0.429
 3    0.714
 4    0.714
 5    0.143
 6    0.286
 7    0.571
....

我需要删除 NA 值,或者当我尝试规范化返回的变量时,如果该列中至少有 1 个 NA,则返回的变量将全部为 NA。如果我在函数外应用 drop_na()(我输入到 map_dfr 函数的主标题),它将删除 df 的任何变量中至少有 1 个 NA 值的任何行,而不是不仅仅是该列中的 NA 值。

有人可以帮忙吗?

更新:

如果我从函数中删除 drop_na() 调用,我会收到以下错误

Error in UseMethod("summarise") : 
  no applicable method for 'summarise' applied to an object of class "c('double', 'numeric')"

这对我来说毫无意义(我可能不理解)因为总结绝对适用于数字变量...

看起来你正在尝试编写一个函数来将整个数据框作为参数,但是当你去映射它时,你实际上只传递了一个向量(例如 df$x) 作为函数的参数。这对于您的函数的第一个版本工作正常,但在第二个版本中 drop_na 无法工作,因为它需要一个 entire data frame for an argumentsummarize 也是如此,这就是您遇到类似错误的原因。它也可以在您的函数之外工作,因为您可以指定单个向量。

所以,我所做的是将 drop_na 换成 na_omit,并且稍微重新组织了您的代码。

首先,让我们定义一个单独的 std_mean 函数,这样我们就不必处​​理 summarize:

std_mean <- function(x){
  x <- na.omit(x)
  (as.numeric(x) - min(as.numeric(x)))/(max(as.numeric(x)) - min(as.numeric(x)))
}

现在我们可以返回并修复您的原始功能:

summary_stats <- function(vec){
  
  data_norm <- std_mean(vec)
  n = length(data_norm)
  sd = round(sd(as.numeric(data_norm), na.rm = T), digits = 3)
  
  data.frame(
    n = n, 
    mean = round(mean(as.numeric(data_norm), na.rm = T), digits = 3), 
    sd = sd, 
    se = round(sd/sqrt(n), digits = 3) 
  ) 
}

我们必须事先定义 nsd,因为它们被用作数据框其他列的参数。虽然 data.frame 计算第一列然后允许您输入后面的列会很酷,但事实并非如此。

现在我们准备好映射了:

map(df[vars],summary_stats)

$x
   n  mean    sd    se
1 16 0.536 0.269 0.067

$y
   n  mean    sd   se
1 18 0.556 0.338 0.08

$z
   n  mean    sd    se
1 17 0.588 0.353 0.086