-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #260 from ploomber/restapi-plumber
Add REST API example with plumber
- Loading branch information
Showing
5 changed files
with
161 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
FROM rstudio/plumber | ||
|
||
WORKDIR /app | ||
|
||
COPY . /app | ||
|
||
RUN R -e "install.packages('caret', repos='http://cran.rstudio.com/')" | ||
|
||
EXPOSE 80 | ||
|
||
ENTRYPOINT ["Rscript", "main.R"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# REST API with Plumber | ||
|
||
A RESTful API built with the Plumber package in R. | ||
The API provides three main functions: | ||
- Checking the server status of the API with a GET request. | ||
- Predicting iris petal length based on various parameters with a POST request. | ||
- Displaying a plot that compares actual to predicted petal lengths with a GET request. | ||
|
||
## Steps for Testing Locally | ||
|
||
To run the API locally, use the following command in your terminal: | ||
```sh | ||
Rscript main.R | ||
``` | ||
|
||
Now you can explore your Swagger Docs at `http://127.0.0.1/__docs__/`. | ||
|
||
### Health Check (GET) | ||
Check the API's server status with: | ||
```sh | ||
curl -X 'GET' 'http://127.0.0.1/health_check' | ||
``` | ||
|
||
### Predict Petal Length (POST) | ||
|
||
To predict petal length with only the petal width: | ||
```sh | ||
curl -X 'POST' 'http://127.0.0.1/predict_petal_length' -d 'petal_width=10' | ||
``` | ||
|
||
To include petal width, sepal length, sepal width, and species of the flower: | ||
```sh | ||
curl -X 'POST' 'http://127.0.0.1/predict_petal_length' -d "petal_width=1.2" -d "sepal_length=3.5" -d "sepal_width=2.1" -d "species=setosa" | ||
``` | ||
|
||
### Plot Actual vs Predicted (GET) | ||
To download a plot comparing actual and predicted petal lengths: | ||
```sh | ||
curl -X 'GET' 'http://127.0.0.1/plot_actual_vs_predicted' --output plot.png | ||
``` | ||
|
||
## Steps for Deploying on Ploomber Cloud | ||
### Prerequisites | ||
- [Ploomber Cloud account](https://www.platform.ploomber.io/applications) | ||
- A Dockerfile | ||
- Your code | ||
|
||
Note: Docker deployment option is available exclusively to Pro, Teams, and Enterprise users. Start your 10-day free trial [here.](https://ploomber.io/pricing/) | ||
|
||
There are two ways you can deploy it on Ploomber Cloud: via (1) [Graphical User Interface](https://docs.cloud.ploomber.io/en/latest/quickstart/app.html), and (2) [Command Line Interface](https://docs.cloud.ploomber.io/en/latest/user-guide/cli.html). Let's take a look at the CLI method. | ||
|
||
### Command Line Interface | ||
|
||
If you haven't installed `ploomber-cloud`, run | ||
```sh | ||
pip install ploomber-cloud | ||
``` | ||
|
||
Then, set your API key following to [this documentation](https://docs.cloud.ploomber.io/en/latest/quickstart/apikey.html). | ||
```sh | ||
ploomber-cloud key YOURKEY | ||
``` | ||
|
||
Navigate to your project directory where your Dockerfile is located and initialize the project. Confirm the inferred project type (Docker) when prompted. | ||
```sh | ||
cd <project-name> | ||
ploomber-cloud init | ||
``` | ||
|
||
Now, deploy your application. | ||
```sh | ||
ploomber-cloud deploy | ||
``` | ||
|
||
Once its deployment is complete, access your endpoints deployed on Ploomber Cloud using your app's URL. For example, you can send a `GET` request with | ||
```sh | ||
curl -X 'GET' 'https://<id>.ploomberapp.io/health_check' | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
library(plumber) | ||
pr <- plumb("plumber.R") | ||
pr$run(port = 80, host = "0.0.0.0") |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
#* @apiTitle Iris Petal Length Prediction API | ||
#* @apiDescription This API allows users to interact with a linear regression model predicting iris petal length based on petal width, and optionally, sepal length, sepal width, and species. | ||
#* It provides endpoints for health checks, petal length predictions, and visualizations comparing actual to predicted lengths. | ||
|
||
library(caret) | ||
# Prepare the model | ||
dataset <- iris | ||
|
||
# Set seed for reproducibility | ||
set.seed(42) | ||
|
||
# Split data into training and testing sets | ||
train_index <- createDataPartition(iris$Petal.Length, p = 0.8, list = FALSE) | ||
train_data <- iris[train_index, ] | ||
test_data <- iris[-train_index, ] | ||
|
||
# Train the model using all parameters on the training data | ||
model_all <- lm(Petal.Length ~ Sepal.Length + Sepal.Width + Petal.Width + Species, data = train_data) | ||
|
||
# Train the model using only petal width on the training data | ||
model_petal_width <- lm(Petal.Length ~ Petal.Width, data = iris) | ||
|
||
#* Health check - Returns the API status and the current server time | ||
#* @get /health_check | ||
function() { | ||
list( | ||
status = "The API is running", | ||
time = Sys.time() | ||
) | ||
} | ||
|
||
#* Predict petal length - Returns a predicted petal length based on available parameters | ||
#* @param petal_width Numeric: Width of the petal (required) | ||
#* @param sepal_length Numeric: Length of the sepal | ||
#* @param sepal_width Numeric: Width of the sepal | ||
#* @param species Character: Species of the iris (setosa, versicolor, virginica) | ||
#* @post /predict_petal_length | ||
function(petal_width, sepal_length = NA, sepal_width = NA, species = NA) { | ||
# Validate petal_width | ||
if (is.na(petal_width) || is.na(as.numeric(petal_width))) { | ||
return(list(error = "Invalid or missing parameter: petal_width")) | ||
} | ||
# Check which parameters are provided and create the input data frame accordingly | ||
if (!is.na(sepal_length) && !is.na(sepal_width) && !is.na(species)) { | ||
input_data <- data.frame( | ||
Sepal.Length = as.numeric(sepal_length), | ||
Sepal.Width = as.numeric(sepal_width), | ||
Petal.Width = as.numeric(petal_width), | ||
Species = as.factor(species) | ||
) | ||
prediction <- predict(model_all, input_data) | ||
} else { | ||
input_data <- data.frame(Petal.Width = as.numeric(petal_width)) | ||
prediction <- predict(model_petal_width, input_data) | ||
} | ||
list(petal_width = petal_width, predicted_petal_length = prediction) | ||
} | ||
|
||
#* Plot actual vs predicted - Displays a plot comparing actual vs predicted petal lengths for model_all | ||
#* @serializer png | ||
#* @get /plot_actual_vs_predicted | ||
function() { | ||
predictions <- predict(model_all, test_data) | ||
plot(test_data$Petal.Length, predictions, | ||
xlab = "Actual Petal Length", ylab = "Predicted Petal Length", | ||
main = "Actual vs Predicted Petal Length" | ||
) | ||
abline(0, 1, col = "red") # 1:1 line | ||
} |