在数据帧中逐行应用矢量化多元函数

时间:2020-04-30 16:14:32

标签: r apply do.call

我已经在SO上进行了广泛的搜索,但找不到与我的问题完全相同的答案。

我正在尝试使用从inputs数据帧读取多变量参数的函数来生成一些时间序列条目。 {x,y,z,...}中的每组变量inputs都会生成一个时间序列数据帧;的收藏需要不公开。

inputs数据帧本身是混合类型(字符和双精度),因此我遇到了apply函数的问题,据我了解,该函数在内部会转换为矩阵对象,因此会失败。 mapply似乎是理想的候选对象(并且过程运行,但是结果无效,因为时间序列生成函数本身在生成正态分布时被矢量化了

下面的代码可以运行,但是给出错误的结果

library(dplyr)
library(truncnorm)

forecast_curve <- function(case_id,
                           wal,
                           wal_sd,
                           amt,
                           n_qrtr) {

  result <- 
    tibble(case_id = case_id, 
           quarter = seq(1, n_qrtr, 1)
    ) %>%
    mutate(
      amt_qrtr = amt * 
        dtruncnorm(seq(1, n_qrtr, 1),a = 1,b = n_qrtr,mean = wal, sd = wal_sd)
    )
  return(result)
}

#Generate inputs
inputs <- 
  tibble(
    case_id = letters[1:10],
    wal = seq(5,14,1),
    wal_sd = rep(4,10),
    total_amt_FC = c(10,9,8,7,6,5,4,3,2,1),            
    n_qrtr = rep(12,10)
  )

#outputs function
outputs <- function(){
  tmp <-
      mapply(
        forecast_curve,
        inputs$case_id,
        inputs$wal,
        inputs$wal_sd,
        inputs$total_amt_FC,
        inputs$n_qrtr
      )

  tmp <-
    as.data.frame(apply(tmp, 1, unlist)) %>% 
    tibble() %>% 
    mutate(
      quarter = as.numeric(quarter),
      amt_qrtr = as.numeric(amt_qrtr)
    ) %>% 
    arrange(case_id,quarter)

  return(tmp)
}

如果仔细查看case_id == a的结果,则结果看起来像这样

print(outputs() %>% filter (case_id == 'a'), n= 30)

   case_id quarter amt_qrtr
   <fct>     <dbl>    <dbl>
 1 a             1       80
 2 a             2       65
 3 a             3       52
 4 a             4       39
 5 a             5       89
 6 a             6       94
 7 a             7       95
 8 a             8       96
 9 a             9       95
10 a            10       94
11 a            11       89
12 a            12       80

但是,相同参数(与inputs的第一行匹配)的正确结果是

#Correct example output
forecast_curve('a',5,4,10,12)
   case_id quarter amt_qrtr
   <chr>     <dbl>    <dbl>
 1 a             1    0.755
 2 a             2    0.940
 3 a             3    1.10 
 4 a             4    1.21 
 5 a             5    1.24 
 6 a             6    1.21 
 7 a             7    1.10 
 8 a             8    0.940
 9 a             9    0.755
10 a            10    0.570
11 a            11    0.404
12 a            12    0.269

从类似的问题上看来,do.call是解决方案,但是我无法在下面的案例中使用它。

在此先感谢您的指导

1 个答案:

答案 0 :(得分:1)

您使问题变得更加棘手。假设您有一个类似forecast_curve的函数,则可以直接使用mapply调用该函数。不需要outputs函数。

在控制台窗口中,输入?mapply以查看mapply的帮助,以便您可以看到所需的参数。 mapply将调用为FUN指定的函数,并将FUN参数中每个向量的第一个值传递给...。然后它将使用...参数中每个向量的第二个值再次调用该函数。等等。如果设置SIMPLIFY = F,则结果将始终以列表形式返回。

由于forecast_curve返回了一个小标题,因此当您将mapplyFUN = forecast_curve一起使用时,您将返回一个小标题列表。因此,以下代码将返回一个包含10个小节的列表,inputs小节的每一行一个。

listOfTibbles = 
  mapply(
    forecast_curve,
    inputs$case_id,
    inputs$wal,
    inputs$wal_sd,
    inputs$total_amt_FC, 
    inputs$n_qrtr,
    SIMPLIFY = F
  )

如果要将所有这些小节合并为一个小节,则需要使用rbind,而不是unlist。您可以这样做:

singleTibble = rbind(listOfTibbles[[1]], listOfTibbles[[2]], listOfTibbles[[3]], listOfTibbles[[4]], listOfTibbles[[5]], listOfTibbles[[6]], listOfTibbles[[7]], listOfTibbles[[8]], listOfTibbles[[9]], listOfTibbles[[10]])

但是do.call提供了一种更简单的方法。 do.call使用列表中的值作为该函数的参数来调用一个函数(在本例中为rbind)。因此,您可以使用以下方法获得相同的结果:

singleTibble = do.call(rbind, listOfTibbles)