3

Bert-toolkit is a very nice package to call R functions from Excel. See: https://bert-toolkit.com/

I have used bert-toolkit to call a fitted neuralnet (avNNnet fitted with Caret) within a wrapper function in R from Excel VBA. This runs perfect. This is the code to load the model within the wrapper function in bert-toolkit:

    load("D:/my_model_avNNet.rda")

    neuraln <- function(x1,x2,x3){
    xx <- data.frame(x1,x2,x3)
    z <- predict(my_model_avNNET, xx)
    z
}

Currently I tried to do this with a fitted GAM (fitted with package mgcv). Although I do not succeed. If I call the fitted GAM from Excel VBA it gives error 2015. If I call the fitted GAM from a cell it gives #VALUE! At the same time the correct outcome of the calculation is shown in the bert-console!

This is the code to load the model in the wrapperfunction in bert-toolkit:

library(mgcv)
load("D:/gam_y_model.rda")

testfunction <- function(k1,k2){
  z <- predict(gam_y, data.frame(x = k1, x2 = k2))
  print (z)
}

The difference between the avNNnet-model (Caret) and the GAM-model (mgcv) is that the avNNnet-model does NOT need the Caret library to be loaded to generate a prediction, while the GAM-model DOES need the mgcv library to be loaded.

It seems to be not sufficient to load the mgvc-library in the script with the GAM-model which loads the GAM-model in a wrapper function in bert-toolkit, as I did in the code above. Although the correct outcome of the model is shown in the bert-console. It does not generate the correct outcome in Excel. I wonder how this is possible and can be solved. It seems to me that maybe there are two instances of R running in bert-toolkit.

How can I load the the mgcv-library in such a way that it can be used by the GAM-model within the function called from Excel? This is some example code to fit the GAM with mgcv and save to model (after running this code the model can uploaded in bert-toolkit with the code above) :

library(mgcv)

# construct some sample data:
x <- seq(0, pi * 2, 0.1)
x2 <- seq(0, pi * 20, 1)
sin_x <- sin(x)
tan_x2 <- tan(x2)
y <- sin_x + rnorm(n = length(x), mean = 0, sd = sd(sin_x / 2))
Sample_data <- data.frame(y,x,x2)

# fit gam:
gam_y <- gam(y ~ s(x) + s(x2), method = "REML")

# Make predictions with the fitted model:
  x_new <- seq(0, max(x), length.out = 100)
  x2_new <- seq(0, max(x2), length.out = 100)
y_pred <- predict(gam_y, data.frame(x = x_new, x2 = x2_new))

# save model, to load it later in bert-toolkit:
setwd("D:/")
save(gam_y, file = "gam_y_model.rda")
user2165379
  • 445
  • 4
  • 20
  • Have you tried explicitely calling the predict.gam function from mgcv? In your example: `z <- mgcv::predict.gam(gam_y, data.frame(x = k1, x2 = k2))` – FM Kerckhof Mar 13 '20 at 11:37
  • @ FM Kerckhof Thank you, good idea! I just tried it, although the result is still the same (error 2015). – user2165379 Mar 13 '20 at 12:45
  • I was wondering @user2165379: why do you require the `print()` call around the prediction in case of the GAM, and not with the avNNnet? I have seen from work with R markdown that print() calls can lead to unexpected results sometimes. – FM Kerckhof Mar 13 '20 at 18:59
  • @ fm kerckhof First I has it without print(). The function was not working in R and Bert. In R the function works with print. It also works in the Bert console. Thanks – user2165379 Mar 13 '20 at 19:10
  • and what about `return(z)` ? – FM Kerckhof Mar 13 '20 at 19:11
  • @ fm kerckhof This gives the same error in Excel and no result in bert console. – user2165379 Mar 13 '20 at 19:25
  • Please post a sample output of *z* in both models into body of post. For the second that errs in Excel, please post what *z* renders in R itself. – Parfait Mar 15 '20 at 03:26

1 Answers1

1

One of R's signatures is method dispatching where users call the same named method such as predict but internally a different variant is run such as predict.lm, predict.glm, or predict.gam depending on the model object passed into it. Therefore, calling predict on an avNNet model is not the same predict on a gam model. Similarly, just as the function changes due to the input, so does the output change.

According to MSDN documents regarding the Excel #Value! error exposed as Error 2015:

#VALUE is Excel's way of saying, "There's something wrong with the way your formula is typed. Or, there's something wrong with the cells you are referencing."

Fundamentally, without seeing actual results, Excel may not be able to interpret or translate into Excel range or VBA type the result R returns from gam model especially as you describe R raises no error.

For example, per docs, the return value of the standard predict.lm is:

predict.lm produces a vector of predictions or a matrix of predictions...

However, per docs, the return value of predict.gam is a bit more nuanced:

If type=="lpmatrix" then a matrix is returned which will give a vector of linear predictor values (minus any offest) at the supplied covariate values, when applied to the model coefficient vector. Otherwise, if se.fit is TRUE then a 2 item list is returned with items (both arrays) fit and se.fit containing predictions and associated standard error estimates, otherwise an array of predictions is returned. The dimensions of the returned arrays depends on whether type is "terms" or not: if it is then the array is 2 dimensional with each term in the linear predictor separate, otherwise the array is 1 dimensional and contains the linear predictor/predicted values (or corresponding s.e.s). The linear predictor returned termwise will not include the offset or the intercept.

Altogether, consider adjusting parameters of your predict call to render a numeric vector for easy Excel interpretation and not a matrix/array or some other higher dimension R type that Excel cannot render:

testfunction <- function(k1,k2){
  z <- mgcv::predict.gam(gam_y, data.frame(x = k1, x2 = k2), type=="response")
  return(z)
}

testfunction <- function(k1,k2){
  z <- mgcv::predict.gam(gam_y, data.frame(x = k1, x2 = k2), type=="lpmatrix")
  return(z)
}

testfunction <- function(k1,k2){
  z <- mgcv::predict.gam(gam_y, data.frame(x = k1, x2 = k2), type=="linked")
  return(z$fit)   # NOTICE fit ELEMENT USED
}
...

Further diagnostics:

  • Check returned object of predict.glm with str(obj) and class(obj)/ typeof(obj) to see dimensions and underlying elements and compare with predict in caret;
  • Check if high precision of decimal numbers is the case such as Excel's limits of 15 decimal points;
  • Check amount of data returned (exceeds Excel's sheet row limit of 220 or cell limit of 32,767 characters?).
Parfait
  • 104,375
  • 17
  • 94
  • 125
  • @ Parfait Thank you VERY MUCH for your extensive answer!! The cause of the problem was indeed the returned object (z) of predict.glm. It was a "named number". I have added z <- unname (z) in the function and now it works! For information: type = "response" gives same error. type = "lpmatrix" returns table with coefficients in console and type mismatch in VBA (with no error in function call, indicating connection is ok) and empty cell in excel. type = "linked + z$fit" gives same errors with "unkown, reset to terms" in console.Good to know that type and form of z can vary so and is so important. – user2165379 Mar 15 '20 at 12:21
  • Awesome! Great to hear and glad to help! – Parfait Mar 15 '20 at 16:39
  • 1
    thanks again! I am really delighted with the solution and comprehensive explanation! – user2165379 Mar 15 '20 at 22:12
  • 1
    Aha! :-) You mean upvotinh? I did not realize I could! – user2165379 Mar 15 '20 at 22:51
  • 1
    (I thought the bounty was awarded automatically) – user2165379 Mar 16 '20 at 09:20