【问题标题】:How to select and apply a function to multiple column within a list of dataframes如何选择函数并将其应用于数据框列表中的多列
【发布时间】:2014-03-27 21:38:30
【问题描述】:

我正在使用共享几个相似列的两个数据帧的列表,我希望能够使用它们的列名而不是列位置一次性转换每个数据帧中多个列的类。

我搜索了 StockOverflow 并在此处找到了类似的问题:此处:Using lists to change columns in multiple dataframes in R 和此处:applying a function for a list of dataframes。但是,我被困在尝试使用多个列名来转换日期。 这是一个示例数据来说明我的问题:

df1 <- data.frame("t1" = c(20070103, 20070104, 20070105, 20070108, 20070109), "t2" = c(20070110,20070111, 20070112, 20070113, 20070114), A = 1:5)
df2 <- data.frame("t1" = c(20080103, 20080104, 20080105, 20080108, 20080109), "t2" = c(20080110,20080111, 20080112, 20080113, 20080114), B = 1:5)
l <- list(df1 = df1, df2=df2)

到目前为止,我已经找到了两个解决方案,我可以对要转换为日期的每一列重复这些解决方案:

#1
l2 <-lapply(l, function(x) transform(x, t1 = as.Date(as.character(t1), "%Y%m%d")))

#2
f <- function(df){
    within(df, t1 <- as.date(date))
}
l2 <- lapply(l, f)

但是,有没有办法我可以使用任何一种方法一次获取多个列(不是整个数据框或列表)并使用列名?我尝试了以下代码无济于事:

periods <- c( "t1", "t2" )
ls2 <-lapply(ls, function(x) transform(x, periods = as.Date(as.character(periods), "%Y%m%d")) 

f <- function(df) {
     within(df, t1 <- as.Date(as.character(t1), "%Y%m%d"))
     within(df, t2 <- as.Date(as.character(t2), "%Y%m%d"))
         }
l2 <- lapply(l, f)

for (i in periods)
    l2 <-lapply(l, function(x) transform(x, i = as.Date(as.character(i), "%Y%m%d")))

【问题讨论】:

  • 这很容易。您希望如何以编程方式标识您的列?
  • 您对我们可以帮助解决的 apply 概念也存在根本性的误解。
  • 我是 R 新手,所以我不确定我是否在回答你的问题,但我只想指定我想转换为日期的列的名称。我可以在我的函数中或通过使用 lapply 使用字符向量“句点”吗?

标签: r


【解决方案1】:
  • 建议#1,简单:

    lapply(l, function(dfrm, periods, fmt) {
        for (ff in which(colnames(dfrm) %in% periods))
            dfrm[,ff] <- as.Date(as.character(dfrm[,ff]), fmt)
        dfrm
    }, periods=c('t1', 't2'), fmt='%Y%m%d')
    

    使用ff in which(...) 允许我们指定列标题 可能包括也可能不包括在内,如果部分或全部不做任何更改 在特定的 data.frame 中是空的。

    lapply 的第二个和第三个参数,periods=c('t1','t2'), 允许您指定格式和列名和(干净) 将它们带入内部循环(没有内部循环 循环到达外部获取数据,如果/何时会咬你的东西 您将代码复制/粘贴到不同的项目中)。

  • 建议2,尝试转换所有列:

    lapply(l, function(dfrm, fmt) {
        for (cc in seq.int(ncol(dfrm)))
            if (! is.na(as.Date(as.character(dfrm[1,cc]), format=fmt)))
                dfrm[,cc] <- as.Date(as.character(dfrm[,cc]), format=fmt)
        dfrm
    }, fmt='%Y%m%d')
    

    如果您有其他列可以被推断出来,这可能会失败 作为日期(使用这些启发式),但不是这样的。

    我将检查限制在第一行以提高性能,以防万一 大量数据会导致这成为性能瓶颈。

  • 建议 #3,同样的事情,但对误报更稳健:

    lapply(l, function(dfrm, fmt) {
        for (cc in seq.int(ncol(dfrm))) {
            tmp <- as.Date(as.character(dfrm[,cc]), format=fmt)
            if (! any(is.na(tmp))) dfrm[,cc] <- tmp
        }
        dfrm
    }, fmt='%Y%m%d')
    

    好的,我们通过检查减少了误报的数量 将某些 all 值转换为日期,但这意味着 如果任何一个单元格在其他有效的日期列中失败,则 整个专栏都受到影响。您可以通过以下方式解决此问题 检查失败的百分比,但现在我们得到 有点可笑...

  • 建议#4,在列名上使用正则表达式:

    lapply(l, function(dfrm, regex, fmt) {
        for (cc in grep(regex, colnames(dfrm)))
            dfrm[,cc] <- as.Date(as.character(dfrm[,cc]), format=fmt)
        dfrm
    }, regex='^t[0-9]+$', fmt='%Y%m%d')
    

    如果您不满意,这可能会引发其他问题 正则表达式。

这些可以通过嵌套的*apply 而不是for 完成 循环,但由于 R 现在在这样的循环中表现得很好,我 不要认为这是一个大问题。 (这将取决于你的大小 数据。)

如果您更熟悉列的命名约定 标题,那么#4可能是您的答案。如果不是(或者你不是 熟悉正则表达式),但您确信 非日期列不会被误解,那么 #2 或 #3 也可以正常工作。

【讨论】:

    【解决方案2】:
    l.new <- lapply(l, function(x) {x[periods] <- lapply(x[periods], as.character); x})
    str(l.new)
    

    生产

    List of 2
     $ df1:'data.frame':    5 obs. of  3 variables:
      ..$ t1: chr [1:5] "20070103" "20070104" "20070105" "20070108" ...
      ..$ t2: chr [1:5] "20070110" "20070111" "20070112" "20070113" ...
      ..$ A : int [1:5] 1 2 3 4 5
     $ df2:'data.frame':    5 obs. of  3 variables:
      ..$ t1: chr [1:5] "20080103" "20080104" "20080105" "20080108" ...
      ..$ t2: chr [1:5] "20080110" "20080111" "20080112" "20080113" ...
      ..$ B : int [1:5] 1 2 3 4 5
    

    更新:为了获取日期,您可以使用:

    lapply(
      l, 
      function(x) {
        x[periods] <- lapply(x[periods], function(x) as.Date(as.character(x), format="%Y%m%d")); 
        x
    } )
    

    【讨论】:

    • BrodieG,当我在运行原始代码后使用 as.Date 代替 as.character 时,代码似乎崩溃了。如果我使用 as.Date,我可以在代码的哪个位置指定非标准的明确格式?例如:l.new
    • @Hakeem82,它崩溃了,因为您的日期不是标准的明确格式(yyyy-mm-dd)。您可以使用 format 参数告诉 as.Date 它们采用什么格式。查看更新。
    猜你喜欢
    • 1970-01-01
    • 2020-04-24
    • 1970-01-01
    • 1970-01-01
    • 2021-03-20
    • 2017-04-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多