【问题标题】:Apply function to each row of data.frame and preserve column classes将函数应用于 data.frame 的每一行并保留列类
【发布时间】:2016-09-14 11:08:23
【问题描述】:

我想知道是否有一种方法可以将函数应用于 data.frame 的每一行以保留列类?让我们看一个例子来阐明我的意思:

test <- data.frame(startdate = as.Date(c("2010-03-07", "2013-09-13", "2011-11-12")),
                   enddate = as.Date(c("2010-03-23", "2013-12-01", "2012-01-05")),
                   nEvents = c(123, 456, 789))

假设我想通过在 startdateenddate 之间插入所有日期来扩展 data.frame test 并分配这些天的事件数。我的第一次尝试是这样的:

eventsPerDay1 <- function(row) {
    n_days <- as.numeric(row$enddate - row$startdate) + 1
    data.frame(date = seq(row$startdate, row$enddate, by = "1 day"),
               nEvents = rmultinom(1, row$nEvents, rep(1/n_days, n_days)))
}

apply(test, 1, eventsPerDay1)

然而,这是不可能的,因为applytest 上调用as.matrix,因此它被转换为字符矩阵并且所有列类都丢失了。

我已经找到了两种解决方法,您可以在下面找到,所以我的问题更多的是哲学性质。

library(magrittr)
############# Workaround 1
eventsPerDay2 <- function(startdate, enddate, nEvents) {
    n_days <- as.numeric(enddate - startdate) + 1
    data.frame(date = seq(startdate, enddate, by = "1 day"),
               nEvents = rmultinom(1, nEvents, rep(1/n_days, n_days)))
}

mapply(eventsPerDay2, test$startdate, test$enddate, test$nEvents, SIMPLIFY = F) %>%
    do.call(rbind, .)


############# Workaround 2
seq_along(test) %>%
    lapply(function(i) test[i, ]) %>%
    lapply(eventsPerDay1) %>%
    do.call(rbind, .)

我对变通办法的“问题”如下:

  • 解决方法 1:这可能不是最好的理由,但我就是不喜欢 mapply。它与其他 *apply 函数的签名不同(因为参数的顺序不同),我总觉得 for 循环会更清晰。
  • 解决方法 2:虽然非常灵活,但我认为乍一看并不清楚发生了什么。

那么有谁知道一个函数,它的调用看起来像apply(test, 1, eventsPerDay1) 并且可以工作?

【问题讨论】:

  • 如果要保留类,请使用 lapply 循环遍历行序列,而不是 apply
  • @akrun 感谢您的建议,但这不正是我在“解决方法 2”中所做的吗?如果不是,请详细说明您的意思。谢谢!
  • 是的,你是对的。我使用data.table 发布了一个解决方案。请检查这是否使它变得更好
  • 解决方法 1 是最好的。 apply() 用于处理矩阵(如果传入 data.frame,它会通过as.matrix 进行转换),矩阵只能有一个原子数据表。不要将apply()data.frames 一起使用。

标签: r dataframe apply


【解决方案1】:

另一个想法:

library(dplyr)
library(tidyr)

test %>%
  mutate(id = row_number()) %>%
  group_by(startdate) %>%
  complete(startdate = seq.Date(startdate, enddate, 1), nesting(id)) %>%
  group_by(id) %>%
  mutate(nEvents = rmultinom(1, first(nEvents), rep(1/n(), n()))) %>%
  select(startdate, nEvents)

这给出了:

#Source: local data frame [152 x 3]
#Groups: id [3]
#
#      id  startdate nEvents
#   <int>     <date>   <int>
#1      1 2010-03-07       6
#2      1 2010-03-08       6
#3      1 2010-03-09       6
#4      1 2010-03-10       7
#5      1 2010-03-11      12
#6      1 2010-03-12       5
#7      1 2010-03-13       8
#8      1 2010-03-14       5
#9      1 2010-03-15       5
#10     1 2010-03-16       9
## ... with 142 more rows

【讨论】:

    【解决方案2】:

    我们可以通过data.table 做到这一点

    library(data.table)
    res <- setDT(test)[,n_days := as.numeric(enddate - startdate) + 1 
               ][, .(date = seq(startdate, enddate, by= "1 day"),
              nEvents = c(rmultinom(1, nEvents, rep(1/n_days, n_days)))),
            by =  1:nrow(test)][, nrow := NULL]
    str(res)
    #Classes ‘data.table’ and 'data.frame':  152 obs. of  2 variables:
    # $ date   : Date, format: "2010-03-07" "2010-03-08" "2010-03-09" "2010-03-10" ...
    # $ nEvents: int  5 9 7 11 6 6 10 7 12 3 ...
    

    上面可以包装在一个函数中

    eventsPerDay <- function(dat){  
          as.data.table(dat)[, n_days:= as.numeric(enddate - startdate) + 1
           ][, .(date = seq(startdate, enddate, by= "1 day"),
        nEvents = c(rmultinom(1, nEvents, rep(1/n_days, n_days)))) , 1:nrow(dat)
            ][, nrow := NULL][]
      }
    
    eventsPerDay(test)
    

    【讨论】:

      【解决方案3】:

      我也问过自己同样的问题。

      我要么最终将 df 拆分为一个列表(基本方式)

      xy <- data.frame()
      xy.list <- split(xy, 1:nrow(xy))
      out <- lapply(xy.list, function(x) ...)
      answer <- unlist(out)
      

      或者尝试使用 rowwise 的 hadleyverse dplyr 方式(黑盒方式)

      xy %>%
      rowwise() %>%
      mutate(newcol = function(x) ....)
      

      我同意他们应该是 apply(xy, 1, function(x)) 的基本实现,它不会强制转换为字符,但我想 R 古人出于一个高级原因实现了矩阵转换,我的原始思维可以不明白。

      【讨论】:

        猜你喜欢
        • 2019-05-09
        • 2015-02-23
        • 2015-07-12
        • 2011-12-28
        • 2018-07-17
        • 2013-01-27
        • 2021-06-30
        • 2020-05-30
        • 2017-01-16
        相关资源
        最近更新 更多