Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add REST API example with plumber #260

Merged
merged 6 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions examples/plumber/Dockerfile
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"]
78 changes: 78 additions & 0 deletions examples/plumber/README.md
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'
```
3 changes: 3 additions & 0 deletions examples/plumber/main.R
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")
Binary file added examples/plumber/plot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 69 additions & 0 deletions examples/plumber/plumber.R
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
}