【问题标题】:use sapply on custom functions in R在 R 中的自定义函数上使用 sapply
【发布时间】:2020-11-11 05:41:25
【问题描述】:

(使用 mtcars 和 iris 进行再现)

我创建了一个 R 函数 get_col_info 来查找数据摘要,如下所示:

  1. 如果列是numeric/integer/double,则得到最小值、最大值、平均值

  2. 如果列是character/factor,则获取唯一值和唯一值的计数

     get_col_info <- function(data,col_name) {  
    
      c_name <- c(col_name)
      s <- data[,c_name]
      type <- typeof(s)
    
      if(type %in% c("numeric","double","integer")){
       min <- min(s)
       max <- max(s)
       mean <- mean(s)
       aa <- list(min=min, max=max,mean=mean)
       return(aa)
      }
    
     if(type %in% c("character","factor")){
      uni <- unique(s)
      len <- length(uni)
      aa <- list(n_values=len,unique_values=c(uni))
      return(aa)}
     }
    
    get_col_info(mtcars, "mpg")
    get_col_info(iris, "Petal.Width")
    get_col_info(iris, "Species")
    

前两次运行完美,第三次出错,不知道为什么?

但是,现在主要查询是我想一次为所有列名运行此函数,例如sapply(iris,mean),但我不确定如何执行此操作,因为该函数接受数据框和列名。我试过这样做,但它给了我一个错误

sapply(iris,get_col_info(iris,names(iris)))

Error in match.fun(FUN) : 
  'get_col_info(iris, names(iris))' is not a function, character or symbol

欢迎使用 apply 和 purrr 解决方案。我也在找人告诉我如何才能更好地编写函数,我怀疑我创建的 c_name 不是捕获列名的理想方法。

【问题讨论】:

  • 用你写的函数,你可以这样做:sapply(c("Petal.Length","Petal.Width"), get_col_info, data=iris)

标签: r dplyr tidyverse apply purrr


【解决方案1】:

你应该使用class而不是typeof来检查类型:

get_col_info <- function(data,col_name) {    
  s <- data[,col_name]
  type <- class(s)
  if(type %in% c("numeric","double","integer")){
    min <- min(s)
    max <- max(s)
    mean <- mean(s)
    aa <- list(min=min, max=max,mean=mean)
    return(aa)
  }
  else if(type %in% c("character","factor")){
    uni <- as.character(unique(s))
    len <- length(uni)
    aa <- list(n_values=len,unique_values=uni)
    return(aa)
  }
}

检查输出:

get_col_info(mtcars, "mpg")
#$min
#[1] 10.4

#$max
#[1] 33.9

#$mean
#[1] 20.09062

get_col_info(iris, "Species")
#$n_values
#[1] 3

#$unique_values
#[1] "setosa"     "versicolor" "virginica" 

要对多个列运行此操作,您可以使用:

sapply(names(iris), get_col_info, data = iris)

如果您对purrr 解决方案感兴趣,或者将sapply 替换为map


另一种方法是直接传递列值而不是名称。

get_col_info <- function(s) {    
  if(is.numeric(s)) {
    min <- min(s)
    max <- max(s)
    mean <- mean(s)
    aa <- list(min=min, max=max,mean=mean)
    return(aa)
  }
  else {
    uni <- as.character(unique(s))
    len <- length(uni)
    aa <- list(n_values=len,unique_values=uni)
    return(aa)
  }
}

sapply(iris, get_col_info)

【讨论】:

  • 公平,拿到第一部分。生病检查他们之间的区别。但主要部分是现在我想一次为所有列运行这个函数。如何使用命名列作为列表名称来做到这一点
  • 谢谢,如果有更好的方法来'catch'列名或者我写的没问题,你能向我解释一下,真的想改进函数编写&我觉得我写的不是很好
  • 我会将值而不是名称传递给函数。我已经更新了答案以表明这一点。
【解决方案2】:

您可以使用summariseacross 执行此操作,并进行类型检查(如is.numeric):

library(dplyr)

iris %>%
  summarise(across(where(is.numeric), list(min=min, max=max, mean=mean)),
            across(where(~is.factor(.) | is.character(.)), 
                   list(n_values = ~length(unique(.)), 
                        unique_values = ~as.character(unique(.))))) %>%
  glimpse()

输出:

Rows: 3
Columns: 14
$ Sepal.Length_min      <dbl> 4.3, 4.3, 4.3
$ Sepal.Length_max      <dbl> 7.9, 7.9, 7.9
$ Sepal.Length_mean     <dbl> 5.843333, 5.843333, 5.843333
$ Sepal.Width_min       <dbl> 2, 2, 2
$ Sepal.Width_max       <dbl> 4.4, 4.4, 4.4
$ Sepal.Width_mean      <dbl> 3.057333, 3.057333, 3.057333
$ Petal.Length_min      <dbl> 1, 1, 1
$ Petal.Length_max      <dbl> 6.9, 6.9, 6.9
$ Petal.Length_mean     <dbl> 3.758, 3.758, 3.758
$ Petal.Width_min       <dbl> 0.1, 0.1, 0.1
$ Petal.Width_max       <dbl> 2.5, 2.5, 2.5
$ Petal.Width_mean      <dbl> 1.199333, 1.199333, 1.199333
$ Species_n_values      <int> 3, 3, 3
$ Species_unique_values <chr> "setosa", "versicolor", "virginica"

注意:我添加了glimpse() 以使输出更具可读性,这不是必需的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-27
    • 2020-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-21
    相关资源
    最近更新 更多