2016-09-14 58 views
2

我想知道是否有辦法將一個函數應用於data.frame的每一行,從而保留列類?讓我們看一個例子來闡明我的意思:將函數應用於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) 

然而,這是不可能的,因爲apply電話as.matrixtest,因而它被轉換爲字符矩陣和所有列類都將丟失。

我已經找到了兩個解決方法,你可以在下面找到,所以我的問題更具哲學性。

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),這將工作?

+0

如果你想保留這個類,使用'lapply'循環遍歷行的順序,而不是'apply' – akrun

+0

@akrun謝謝你的建議,但是這不正是我在「解決方法2」中所做的嗎?如果沒有請詳細說明你的意思。謝謝! – AEF

+0

是的,你是對的。我用'data.table'發佈了一個解決方案。請檢查是否使它更好 – akrun

回答

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) 
2

另一個想法:

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 
相關問題