将函数应用于数据框中的每个列,观察每个列的现有数据类型

时间:2011-09-05 02:06:42

标签: r apply sapply

我正在尝试获取大型数据框中每列的最小值/最大值,作为了解我的数据的一部分。我的第一次尝试是:

apply(t,2,max,na.rm=1)

它将所有内容视为字符向量,因为前几列是字符类型。因此,某些数字列的最大值将显示为" -99.5"

然后我尝试了这个:

sapply(t,max,na.rm=1)

但它抱怨 max对因素无意义。 (lapply是一样的。)令我困惑的是apply认为max对于因素非常有意义,例如它为第1列返回了“ZEBRA”。

顺便说一下,我看了Using sapply on vector of POSIXct,其中一个答案说“当你使用sapply时,你的对象被强制为数字,...... ”。这是发生在我身上的事吗?如果是这样,是否有一个不强制的替代应用功能?当然这是一个常见的需求,因为数据框类型的一个关键特征是每列可以是不同的类型。

7 个答案:

答案 0 :(得分:35)

如果是“有序因素”,情况会有所不同。这并不是说我喜欢“有序因素”,我不这么说,只是说有些关系是针对“因素”定义的“有序因子”而定义的。因素被认为是普通的分类变量。您正在查看因素的自然排序顺序,这些因素是您的语言环境的字母词汇顺序。如果你想为每一列,...日期和因素以及所有列自动强制为“数字”,那么试试:

sapply(df, function(x) max(as.numeric(x)) )   # not generally a useful result

或者,如果您想先测试因子并按预期返回:

sapply( df, function(x) if("factor" %in% class(x) ) { 
            max(as.numeric(as.character(x)))
            } else { max(x) } )
@Darrens的评论确实更好:

 sapply(df, function(x) max(as.character(x)) )  

max确实使用了字符向量。

答案 1 :(得分:17)

maxapply一起使用的原因是apply首先将数据框强制转换为矩阵,而矩阵只能容纳一种数据类型。所以你最终会得到一个字符矩阵。 sapply只是lapply的包装器,因此两者都会产生相同的错误并不奇怪。

创建数据框时的默认行为是将分类列存储为因子。除非您指定它是 ordered 因子,否则maxmin之类的操作将是未定义的,因为R假设您已创建无序 factor。

您可以通过指定options(stringsAsFactors = FALSE)来更改此行为,这将更改整个会话的默认值,或者您可以在stringsAsFactors = FALSE构造调用本身中传递data.frame()。请注意,这只是意味着默认情况下minmax将采用“按字母顺序”排序。

或者您可以手动指定每个因素的排序,但我怀疑这是您想要做的。

无论如何,sapply通常会产生一个原子向量,这需要在很多情况下将所有内容转换为字符。解决这个问题的方法如下:

#Some test data
d <- data.frame(v1 = runif(10), v2 = letters[1:10], 
                v3 = rnorm(10), v4 = LETTERS[1:10],stringsAsFactors = TRUE)

d[4,] <- NA

#Similar function to DWin's answer          
fun <- function(x){
    if(is.numeric(x)){max(x,na.rm = 1)}
    else{max(as.character(x),na.rm=1)}
}   

#Use colwise from plyr package
colwise(fun)(d)
         v1 v2       v3 v4
1 0.8478983  j 1.999435  J

答案 2 :(得分:7)

如果您想了解您的数据summary (df)提供最小值,第1个分位数,中位数和平均值,第3个分位数和最大数值列以及因子列最高级别的频率。

答案 3 :(得分:2)

建立@ ltamar的回答:
使用摘要并将输出变为有用的东西!

library(tidyr)
library(dplyr)

df %>% 
  summary %>% 
  data.frame %>%
  select(-Var1) %>%
  separate(data=.,col=Freq,into = c('metric','value'),sep = ':') %>%
  rename(column_name=Var2) %>%
  mutate(value=as.numeric(value),
         metric = trimws(metric,'both') 
  ) %>%  
  filter(!is.na(value)) -> metrics

它不漂亮,肯定不会很快,但它完成了工作!

答案 4 :(得分:0)

使用retype()的解决方案,从可行性到强制性因素,再到字符或数字类型,具体取决于可行性。我会使用dplyr将max应用于每一列。

代码

library(dplyr)
library(hablar)

# Retype() simplifies each columns type, e.g. always removes factors
d <- d %>% retype()

# Check max for each column
d %>% summarise_all(max)

结果

不是新的列类型。

     v1 v2       v3 v4   
  <dbl> <chr> <dbl> <chr>
1 0.974 j      1.09 J   

数据

# Sample data borrowed from @joran
d <- data.frame(v1 = runif(10), v2 = letters[1:10], 
                v3 = rnorm(10), v4 = LETTERS[1:10],stringsAsFactors = TRUE)

答案 5 :(得分:0)

执行此操作的绝对最佳方法是避免使用* apply基函数,该函数将整个数据帧强制转换为数组,并使用 plyr 中的colwise。 (我很惊讶没有人提到这一点)

使用parse_guess作为对所有矢量数据类型进行操作的函数的示例:

colwise(parse_guess)(t)

缺少有趣的答案:我们可以使用for循环将其应用于每列:

for (i in 1:nrow(t)) { t[, i] <- parse_guess(t[, i]) }

我不知道doing assignment with *apply while preserving data frame structure的好方法。

答案 6 :(得分:0)

df <- head(mtcars)
df$string <- c("a","b", "c", "d","e", "f"); df

my.min <- unlist(lapply(df, min))
my.max <- unlist(lapply(df, max))