Predictive Hacks

How to Share Flask APIs with Shiny as Applications

shiny

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

  • To get a phrase as input and to return the sentiment such as “positive“, “neutral“, “negative“.
  • To has an option to upload a .txt file, tab-separated with many phrases and to return a .txt file by adding a column of the sentiments.

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.

Share This Post

Share on facebook
Share on linkedin
Share on twitter
Share on email

Leave a Comment

Subscribe To Our Newsletter

Get updates and learn from the best

More To Explore

Python

Image Captioning with HuggingFace

Image captioning with AI is a fascinating application of artificial intelligence (AI) that involves generating textual descriptions for images automatically.

Python

Intro to Chatbots with HuggingFace

In this tutorial, we will show you how to use the Transformers library from HuggingFace to build chatbot pipelines. Let’s