Благодаря @BertilBaron я нашел способ избежать ожидания длинных вычислений в R-Shiny
. Статью, которую я использовал, можно найти здесь .
По сути, вы выполняете каждый процесс параллельно, используя пакет future
. С этим пакетом каждый раз, когда пользователь нажимает на кнопку, будет выполняться расчет. Так что имейте в виду, что вы должны создать блок для нетерпеливых пользователей.
Мой рабочий пример
library(shiny)
library(promises)
library(future)
plan(multiprocess)
ui <- fluidPage(
actionButton('button', 'klik'),
textOutput('first'),
textOutput('second')
)
server <- function(input, output) {
nclicks <- reactiveVal(0)
nclicks2 <- reactiveVal(0)
result_val <- reactiveVal()
result_val2 <- reactiveVal()
observeEvent(input$button,{
# Don't do anything if analysis is already being run
if(nclicks() != 0 | nclicks2() != 0){
showNotification("Already running analysis")
return(NULL)
}
# Increment clicks and prevent concurrent analyses
nclicks(nclicks() + 1)
nclicks2(nclicks2() + 1)
result <- future({
# Long Running Task
Sys.sleep(10)
#Some results
Sys.time()
}) %...>% result_val()
result2 <- future({
#Some results
Sys.time()
}) %...>% result_val2()
# Catch inturrupt (or any other error) and notify user
result <- catch(result,
function(e){
result_val(NULL)
print(e$message)
showNotification(e$message)
})
result2 <- catch(result2,
function(e){
result_val2(NULL)
print(e$message)
showNotification(e$message)
})
# After the promise has been evaluated set nclicks to 0 to allow for anlother Run
result <- finally(result,
function(){
nclicks(0)
})
result2 <- finally(result2,
function(){
nclicks2(0)
})
# Return something other than the promise so shiny remains responsive
NULL
})
output$first <- renderText({
req(result_val())
})
output$second <- renderText({
req(result_val2())
})
}
# Run the application
shinyApp(ui = ui, server = server)
Это довольно легко кодировать использование повторяющегося решателя, такого как метод деления пополам или метод Newton. Если Ваши потоки наличности C_t_j
происходите время от времени t_j
затем внутренняя рентабельность r
удовлетворяет сумму j = 1
кому: n
из
C_t_j / (1 + r)^t_j
равняется нулю. Назовите это f(r)
. Затем f'(r)
сумма j = 1
кому: n
из
-t_j * C_t_j / (1 + r)^(t_j+1).
Теперь можно применить метод Newton для решения для r
.
Вот является IRR макросом Excel, который я записал много лет назад. Я не могу объяснить, как это больше работает, но я думаю, что это делает правильную вещь:
Это вызывается как: =IrrCont (A8:A15, F8:F15), где первый диапазон является диапазоном дат и второго, является диапазоном значений. Некоторые значения должны быть положительными, и некоторые должны быть отрицательными.
Option Explicit
'
' Internal Rate of return -- Calculation
' Returns a result (Double) or an error message (String)
Private Function IrrCalc(DateRange As Object, ValueRange As Object)
Dim i As Integer
Dim it As Integer
Dim Count As Integer
Dim u As Double
Dim time As Double
Dim d_positive As Double
Dim positive As Double
Dim d_negative As Double
Dim negative As Double
Dim sum As Double
Const epsilon As Double = 0.000001
Const iterations As Integer = 20
Dim StartTime As Double
Dim pos As Boolean
Dim neg As Boolean
Dim value As Double
Dim temp As Double
Dim delta As Double
If DateRange.Count <> ValueRange.Count Then
IrrCalc = "*** Date Range (argument 1) and Value Range " & _
"(argument 2) must contain the same number of cells. ***"
Exit Function
End If
Count = DateRange.Count
For i = 1 To Count
If ValueRange.Cells(i).value > 0 Then pos = True
If ValueRange.Cells(i).value < 0 Then neg = True
If pos And neg Then Exit For
Next i
If Not pos Or Not neg Then
IrrCalc = "*** Cannot calculate IRR: Need both income and expenditure. ***"
Exit Function
End If
StartTime = Application.Min(DateRange)
u = 0 ' Initial interest rate guess
For it = 1 To iterations
positive = 0
d_positive = 0
negative = 0
d_negative = 0
For i = 1 To Count
value = ValueRange.Cells(i).value
time = (DateRange.Cells(i).value - StartTime) / 365.2425
If value > 0 Then
temp = value * Exp(u * time)
positive = positive + temp
d_positive = d_positive + temp * time
ElseIf value < 0 Then
temp = -value * Exp(u * time)
negative = negative + temp
d_negative = d_negative + temp * time
End If
Next i
delta = Log(negative / positive) / (d_negative / negative - d_positive / positive)
If Abs(delta) < epsilon Then Exit For
u = u - delta
Next it
If it > iterations Then
IrrCalc = "*** irr does not converge in " & Str(iterations) & " iterations ***"
Else
IrrCalc = u
End If
End Function
' ====================================================================================================
'
' Internal Rate of Return: Discrete interest calculation
Function IrrDiscrete(DateRange As Object, ValueRange As Object)
Dim result As Variant
result = IrrCalc(DateRange, ValueRange)
If VarType(result) = vbDouble Then
IrrDiscrete = Exp(-result) - 1#
Else
IrrDiscrete = result
End If
End Function
' ====================================================================================================
'
' Internal Rate of Return: Continuous (compounding) interest calculation
Function IrrCont(DateRange As Object, ValueRange As Object)
Dim result As Variant
result = IrrCalc(DateRange, ValueRange)
If VarType(result) = vbDouble Then
IrrCont = -result
Else
IrrCont = result
End If
End Function
Ниже моя повторяющаяся реализация, в ActionScript:
package xattam.net.math
{
public class Financial
{
public static const MAX_IRR_ITERATIONS:int = 1000;
public static function IRR(cashFlow:Array,guess:Number=0.1):Number {
var npv:Number;
var cnt:Number = 0;
do
{
npv = Financial.NPV(guess,cashFlow);
guess+= 0.001;
if(cnt > Financial.MAX_IRR_ITERATIONS) return NaN;
else cnt++;
}
while(npv > 0)
return guess;
}
public static function NPV(discountRate:Number,cashFlow:Array):Number {
var npv:Number = 0;
for(var t:int = 0; t < cashFlow.length;t++) {
npv += cashFlow[t] / Math.pow((1+ discountRate),t);
}
return npv;
}
}
}