Применение векторизованной функции с несколькими переменными строка за строкой в ​​кадре данных - PullRequest
0 голосов
/ 30 апреля 2020

Я много раз искал в 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

Из аналогичных вопросов по SO следует, что решение имеет do.call, однако я не могу заставить его работать с моим делом ниже.

Заранее большое спасибо за любые рекомендации

1 Ответ

1 голос
/ 01 мая 2020

Вы делаете проблему сложнее, чем она есть. Предполагая, что у вас есть такая функция, как forecast_curve, вы можете вызвать функцию напрямую с помощью mapply. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}}} *}}}}} 1006 * В окне консоли введите ?mapply, чтобы просмотреть справку для mapply, чтобы вы могли видеть требуемые параметры. mapply вызовет функцию, указанную для FUN, передав FUN первое значение из каждого вектора в параметре .... Затем он снова вызовет функцию со вторым значением из каждого вектора в параметре .... И так далее. Если вы установите SIMPLIFY = F, результат всегда будет возвращен в виде списка.

Поскольку forecast_curve возвращает столбик, когда вы mapply с помощью FUN = 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)
...