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

Posted

技术标签:

【中文标题】将函数应用于数据框中的每一列,观察每一列现有的数据类型【英文标题】:Apply function to each column in a data frame observing each columns existing data type 【发布时间】:2011-11-10 07:49:10 【问题描述】:

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

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 时,您的对象被强制转换为数字,...”。这是发生在我身上的事吗?如果是这样,是否有不强制的替代应用功能?当然这是一种常见的需求,因为数据框类型的一个关键特性是每一列都可以是不同的类型。

【问题讨论】:

我只会传递具有有意义数据类型的列来计算您的统计数据。 @Roman 谢谢,这实际上是我昨天所做的,因为在这种特殊情况下,我已经有了一个数字列名列表。但对于大型数据帧,它可能会变得很耗时。 您可以找到数字列并自动执行该过程。 @DarrenCook 作为一种方法,如果您使用 stringsAsFactors = FALSE 读取文件并且在使用 apply 之前将列设置为它们应该属于的类,例如日期为 as.POSIXct,数字为数字等,这比在sapply 中使用强制转换更容易吗? 这是一个很好的问题,但仍然没有一种令人满意的方法可以将函数应用于具有混合类型的 data.frame。保留每列类型的唯一解决方案是使用 for 循环; data.frames 没有 lapply 方法。 【参考方案1】:

如果它是一个“有序因素”,情况会有所不同。这并不是说我喜欢“有序因子”,我不喜欢,只是说有些关系是为“有序因子”定义的,而“因子”没有定义。因子被认为是普通的分类变量。您会看到因素的自然排序顺序,即您所在地区的字母词汇顺序。如果您想为每一列、...日期和因素等自动强制转换为“数字”,请尝试:

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 使用字符向量确实成功。

【讨论】:

谢谢。第二个 sapply 示例有效并完美地回答了问题(我发现如果删除 as.numeric() 子句并让 max 直接在字符串上工作,效果会更好) 是的,这通常会更有用。【参考方案2】:

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

创建数据框时的默认行为是将分类列存储为因子。除非您指定它是一个 有序 因子,否则像 maxmin 这样的操作将是未定义的,因为 R 假设您已经创建了一个 无序 因子。

您可以通过指定options(stringsAsFactors = FALSE) 来更改此行为,这将更改整个会话的默认值,或者您可以在data.frame() 构造调用本身中传递stringsAsFactors = FALSE。请注意,这只是意味着 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)
    elsemax(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

【讨论】:

感谢详细的解释,很有帮助。 stringsAsFactors = FALSE 确实使 max() 按预期工作(但后来我意识到我实际上希望这些字段成为因子;因此在运行 max() 时将因子转换为字符串最适合我)。【参考方案3】:

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

【讨论】:

是的,事后看来,我应该使用那个 :-) 它的输出有点难看(我想要每行一个字段,一列最小值,一列最大值等.) 但我想我只需要追踪如何重新格式化表格对象。 我建议的另一件事是查看来自summary() 的代码。很多时候,我会找到一个与我正在寻找的功能接近的基本函数,并从那里获取代码的一般想法。 遗憾的是,summary() 也是不可扩展的。例如,没有简单的方法可以为其添加均值函数。【参考方案4】:

最好的方法是避免使用基本的*apply 函数,这会将整个数据帧强制为一个数组,可能会丢失信息。

如果您想将函数as.numeric 应用于每一列,一个简单的方法是使用mutate_all from dplyr:

t %>% mutate_all(as.numeric)

或者使用colwise from plyr,它将“将一个对向量进行操作的函数转换为对数据帧按列进行操作的函数。”

t %>% (colwise(as.numeric))

在读取字符向量数据表并将列强制转换为正确数据类型的特殊情况下,请使用 readr 中的type.converttype_convert


不太有趣的答案:我们可以用 for 循环对每一列应用:

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

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

【讨论】:

请注意colwise 不再要求对象是数组才能工作,它要求基类型为data.frame【参考方案5】:

以@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

它不漂亮,当然也不快,但它可以完成工作!

【讨论】:

【参考方案6】:

使用 retype() 从 hablar 强制转换为字符或数字类型的解决方案,具体取决于可行性。我会使用 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)

【讨论】:

【参考方案7】:
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))

【讨论】:

以上是关于将函数应用于数据框中的每一列,观察每一列现有的数据类型的主要内容,如果未能解决你的问题,请参考以下文章

将函数应用于熊猫中数据框的每一列

如何在数据框中的每一列上运行 udf?

用 Python 用该列的平均值减去数据框中的每一列

如何遍历熊猫数据框中的每一列和每个单元格

请问如何获得GridView选中行的每一列的信息?

R语言ggplot2可视化:应用pivot_longer函数将数据从宽格式转换为长格式为dataframe的每一列绘制密度图和直方图(堆叠)