Site icon R-bloggers

How to Share Flask APIs with Shiny as Applications

[This article was first published on R – Predictive Hacks, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

As a Data Scientist, you may work in both R and Python and it is common to prefer one language over the other for some specific tasks. For example, you may prefer R for Statistics, Data Cleansing and Data Visualizations and you may prefer Python for NLP tasks and Deep Learning. Also, when it comes to Restful APIs, Python Flask APIs have an advantage over R Plumber and Restrserve.

The Scenario

Assume that you have built a model in Python and on top of that, you have built a Flask API. Regarding the UI, you prefer to work with Shiny. So, the scenario is that you want to share your Python Flask API using R Shiny. Let’s see how we can do it with a hands-on example.

The Flask API

We have provided an example of a Flask API for Sentiment Analysis. For convenience, we provide the code below:

The requirements.txt file is the following:

certifi==2020.12.5
chardet==4.0.0
click==7.1.2
Flask==1.1.2
idna==2.10
itsdangerous==1.1.0
Jinja2==2.11.3
MarkupSafe==1.1.1
requests==2.25.1
urllib3==1.26.3
vaderSentiment==3.3.2
Werkzeug==1.0.1

The application.py file:

from flask import Flask, request, jsonify
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
 
 
analyzer = SentimentIntensityAnalyzer()
 
 
application = Flask(__name__)
 
def get_sentiment(my_text):
    vs = analyzer.polarity_scores(my_text)
 
    sentiment = ''
    if vs['compound'] >= 0.05:
        sentiment = 'Positive'
    elif vs['compound'] <= -0.05:
        sentiment = 'Negative'
    else:
        sentiment = 'Neutral'
 
    return(sentiment)
 
 
 
@application.route("/endpoint", methods=['GET','POST'])
def sentiment_endpoint():
    if request.method == 'POST':
        json_dict = request.get_json()
        if 'my_text' in json_dict:
            result = get_sentiment(json_dict['my_text'])
            return jsonify({'output' : result})
        else:
            return jsonify({
                "status": "failed",
                "message": "parameter 'my_text' is required!"
            })
 
    if request.method == 'GET':
    
        my_text = request.args.get('my_text')
        result = get_sentiment(my_text)
        return jsonify({'output' : result})
 
 
 
 
 
if __name__=='__main__':
    application.run()

Call an API with R

We will provide an example of how you can call the above Flask API with R. For this example, we run the API locally, that is why the URL is http://127.0.0.1:5000 and the sentiment analysis, the route is the endpoint that is why we will call the http://127.0.0.1:5000/endpoint URL.

Let’s get the sentiment of the sentence:

What a great experience! I feel really happy

library(jsonlite)
library(httr)


url="http://127.0.0.1:5000/endpoint"

body<-list(my_text="What a great experience! I feel really happy :)")

b<-POST(url, body = body, encode = "json")

t1<-content(b, type="application/json")

t1
 

Output:

$output
[1] "Positive"

As expected, the sentiment of the sentence was positive.

Build a Shiny Application

We have provided an example of How to Share your Machine Learning Models with Shiny. It would be helpful to have a look at it since we will apply the same architecture.

The Shiny Application will have two functionalities

Let’s build the Shiny App:

library(shiny)
library(DT)
library(tidyverse)
library(jsonlite)
library(httr)


# Define UI for application that draws a histogram
ui <- fluidPage(
    
    # Application title
    titlePanel("Sentiment Analysis"),
    
    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            
            textInput("caption", label="Enter your text here.", value="", placeholder = "Phrase to get a Sentiment..."),
            verbatimTextOutput("value"),
            
            
            # Input: Select a file ----
            fileInput("file1", "upload csv file here",
                      multiple = FALSE,
                      accept = c("text/csv",
                                 "text/comma-separated-values,text/plain",
                                 ".csv")), 
            
            
            # Button
            downloadButton("downloadData", "Download the Predictions")
        ),
        
        # Show the table with the predictions
        mainPanel(
            verbatimTextOutput("Sentiment"),
            DT::dataTableOutput("mytable")
        )
    )
)

# Define server logic required to draw a histogram
server <- function(input, output) {
    
    
    reactiveDF<-reactive({
        req(input$file1)
        df <- read.csv(input$file1$datapath, sep="\t", stringsAsFactors = FALSE)
        
        url="http://127.0.0.1:5000/endpoint"
        
        
        fdf<-NULL
        for (i in 1:nrow(df)) {
            body<-list(my_text=df[i,1])
            b<-POST(url, body = body, encode = "json")
            t1<-content(b, type="application/json")
            tmpdf<-data.frame(InputText=df[i,1], Sentiment=t1$output)
            
            fdf<-rbind(fdf, tmpdf)                   
        }
        return(fdf)
        
        
        
    })
    
    output$mytable <- DT::renderDataTable({
        req(input$file1)
        
        return(DT::datatable(reactiveDF(),  options = list(pageLength = 100), filter = c("top")))
    })
    
    reactiveInput<-reactive({
        req(input$caption)
        
        
        url="http://127.0.0.1:5000/endpoint"
        
        
        body<-list(my_text=input$caption)
        
        b<-POST(url, body = body, encode = "json")
        
        t1<-content(b, type="application/json")
        
        
        df<-data.frame(Sentiment=t1$output)
        
        return(df)
        
    })
    
    output$Sentiment<-renderText({
        
        req(input$caption)
        reactiveInput()$Sentiment
        
    })
    
    
    # Downloadable csv of selected dataset ----
    output$downloadData <- downloadHandler(
        filename = function() {
            paste("data-", Sys.Date(), ".csv", sep="")
        },
        content = function(file) {
            write.csv(reactiveDF(), file, row.names = FALSE)
        }
    )
    
    
    
}

# Run the application 
shinyApp(ui = ui, server = server)
 

Get the Sentiments

Let’s see how the Shiny App works.

Get the Sentiment of a document in an interactive way

Let give as input the following input:

kudos! Great job!

As we can see, the Shiny App give us the chance to give the input in and it returns the sentiment in an interactive way

Get the Sentiment of multiple documents in an interactive way

In case we have many documents, like thousands of reviews, and we want to get the sentiment of each one, the Shiny App gives us the option to upload the data and it also to download them with an extra column of the sentiment score.

The input file

Let’s upload it to Shiny and get the results:

As we can see, it accepted as input the txt file and it returned the input text with the predicted sentiments. We can also download the file which is the following:

The Takeaway

The takeaway is that you can work with Python and Flask APIs for the backend part and to work with the Shiny for the front end to share your models as applications. So, let’s keep in mind that Shiny can be an alternative to Flask jinja.

To leave a comment for the author, please follow the link and comment on their blog: R – Predictive Hacks.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.