0

I am cutting my teeth on exposing an R machine learning classification model as a web service using Plumber and Swagger. I have trained a model and saved it as "j48.model.rda". I am now loading the model in a file called "myFile.R". This file contains the following R code:

library(rJava)
jsDirData <- "C:/AA Research/Playpen/Data"
setwd(jsDirData)

#Load the saved model
load(file="j48.model.rda", envir = parent.frame(), verbose = FALSE) 

#' @param naasra90th:numeric The 90th Percentile Naasra value for the segment
#' @param rut90th:numeric The 90th Percentile Rut Depth for the segment
#' @param surfAge:numeric The surface age of the segment, in years (fractions are OK)
#' @param rutRate90th:numeric The rut rate on the 90th Percentile Rut depth (mm/year)
#' @param maintCount:int The number of maintenance acions
#' @get /getTreatment
#' @html
#' @response 200 Returns the treatment class (ThinAC or none) prediction from the j48 model;
#' @default  Bonk!
getTreatment <- function(naasra90th, rut90th, surfAge, rutRate90th, maintCount) {
  xVals <- list(naasra90th = naasra90th, rut90th = rut90th, surfAge = surfAge, 
                rutRate90th = rutRate90th, maintCount = maintCount)
  nData <- as.data.frame(xVals)
  pred <- predict(j48.model,newdata = nData)
  res <- as.character(pred)
  return(res)
}

t <- getTreatment(50,8.8,5,0.3,0)  #should return "none"
t    #"none" Correct!

t <- getTreatment(888,888,888,888,888) #should return "ThinAC"
t    #"ThinAC" Correct!

As you can see from the last lines, when I call the function directly in R-Studio, it gives the correct classification. But now I try to call this method via a Plumber/Swagger web service, as follows:

library(plumber)

jsDirData <- "C:/AA Research/Playpen/Data"
setwd(jsDirData)

r <- plumb("myfile.R")
r$run(port=8000)

When I run this code, Swagger opens a browser and shows the API correctly. However, when I use the "Try It" button to test the API, then it always shows the result as "none", no matter what parameters I pass into the method. For example, if I enter the same set of parameters as in the second method call above (i.e. 888 for all parameters), then it returns "none" when it should return "ThinAC".

What am I doing wrong?

Fritz45
  • 752
  • 1
  • 10
  • 23

1 Answers1

1

I believe values you receive from the swagger call are still in character class as plumber does not do any conversion of query string parameters.

Before doing as.data.frame, try changing the class of the values in xVals

xVals <- lapply(xVals, as.numeric)

To confirm this hypothesis, you could insert a browser() after as.data.frame and check the class of the values in nData with lapply(nData, class).

Good luck

Bruno Tremblay
  • 756
  • 4
  • 9
  • Thanks Bruno I tried that but it does not seem to help. I doubt this is the issue because - as I show in my post above - when I call the functions directly in R-studio, I get the correct results regardless of whether I insert your line above. BTW I am using R version 4.0.0 (2020-04-24). But thanks for the suggestion! – Fritz45 May 23 '20 at 00:19
  • What about `getTreatment("888","888","888","888","888")`? does it still give the right answer? – Bruno Tremblay May 23 '20 at 00:22
  • From using the code above, but just keeping the API, here is what I have in nData before calling the model. ```rBrowse[1]> xVals $naasra90th [1] "888" $rut90th [1] "888" $surfAge [1] "888" $rutRate90th [1] "888" $maintCount [1] "888" Browse[1]> nData naasra90th rut90th surfAge rutRate90th maintCount 1 888 888 888 888 888 Browse[1]> lapply(nData, class) $naasra90th [1] "character" $rut90th [1] "character" $surfAge [1] "character" $rutRate90th [1] "character" $maintCount [1] "character"``` – Bruno Tremblay May 23 '20 at 00:31
  • Thanks so much Bruno for sticking with this! I see now it is working now! Not sure why it did not work first time. OK, so the issue with this API is the parameters are always coming in as strings and need to be explicitly converted to numbers where needed? – Fritz45 May 23 '20 at 00:36
  • When they come in from a query string (i.e. ?a=1&b=3) yes. The only reason you define them in `@param val:num` is to describe your API and add some validation on the swagger side. If you think plumber should attempt class conversion of incoming queries, I would suggest creating an issue in the github project rstudio/plumber. There might be a way to implement the feature without breaking existing code. – Bruno Tremblay May 23 '20 at 00:42
  • OK understood. Thanks again! – Fritz45 May 23 '20 at 00:43
  • Path parameters are explicitly converted. https://www.rplumber.io/docs/routing-and-input.html#dynamic-routes – Bruno Tremblay May 23 '20 at 00:44