0

I would like to return two values, the model prediction, and the accuracy in my flask app, however the second element (RMSE accuracy is not returned). I would like to return prediction in the first line and then accuracy in the second one - how can I return two values from the app to the js function?

code in the flask app


from flask import Flask, render_template, request
import numpy as np
import pandas as pd
import pickle

app = Flask(__name__)
df = pd.read_csv("Cleaned_data_Wien.csv")
# pipe = pickle.load(open("RandomForestModel.pkl", "rb"))

@app.route('/')
def index():

    locations = sorted(df['location'].unique())
    rooms = sorted(df['rooms'].unique())
    return render_template('index.html', locations=locations, rooms=rooms)

@app.route('/predict', methods=['POST'])
def predict():
    location = request.form.get('location') # name
    room = request.form.get('room')
    m2 = request.form.get('m2')

    print(location, room, m2)

    # Splitting
    X = df.drop(columns=['price'])
    y = df.price

    from sklearn.model_selection import train_test_split
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

    # Preprocessing
    from sklearn.compose import make_column_transformer
    from sklearn.preprocessing import OneHotEncoder
    column_trans = make_column_transformer((OneHotEncoder(sparse=False), ['location']), # non-numeric
                                        remainder='passthrough')

    from sklearn.preprocessing import StandardScaler
    scaler = StandardScaler()

    # Random forest regression
    from sklearn.ensemble import RandomForestRegressor
    model = RandomForestRegressor(n_estimators=500, random_state=0)

    from sklearn.pipeline import make_pipeline
    pipe = make_pipeline(column_trans, scaler, model)
    pipe.fit(X_train, y_train)

    y_pred = pipe.predict(X_test)

    from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
    outcome = pd.DataFrame({'y_test':y_test, 'y_pred':y_pred})
    outcome['difference'] = outcome['y_test'] - outcome['y_pred']
    outcome['difference_percentage'] = round(outcome.difference/(outcome.y_test/100),6)

    PROC = round(outcome.difference_percentage.abs().mean(), 2)
    MAE = round(mean_absolute_error(y_test, y_pred),4)
    RMSE = round(np.sqrt(mean_squared_error(y_test, y_pred)),4)
    R2 = round(r2_score(y_test, y_pred),4)

    # sample
    input = pd.DataFrame([[room, m2, location]], columns=['rooms', 'm2', 'location'])
    input.location = input.location.astype(int) # if pickle --> must be str
    input.rooms = input.rooms.astype(int)
    input.m2 = input.m2.astype(float)
    prediction = round(pipe.predict(input)[0],2)
    
    return str(prediction), str(RMSE) #THE OUTPUT
if __name__ == "__main__":
    app.run(debug=True, port=5001)

index function

     function send_data()
        {
            document.querySelector('form').addEventListener("submit", form_handler);

            var fd= new FormData(document.querySelector('form'));

            var xhr= new XMLHttpRequest();

            xhr.open('POST', '/predict', true);
            document.getElementById("prediction").innerHTML = "Please wait predicting price...";

            xhr.onreadystatechange = function(){
                if(xhr.readyState == XMLHttpRequest.DONE){
                    document.getElementById('prediction').innerHTML="Prediction: EUR "+xhr.responseText
                    document.getElementById('model').innerHTML="Model: Random Forest Regression"
                    document.getElementById('RMSE').innerHTML="RMSE: "+xhr.responseText;
                };
            
            };

            xhr.onload = function(){};
            xhr.send(fd);

enter image description here

Jaroslav Kotrba
  • 283
  • 1
  • 14
  • 1
    `return str(prediction), str(RMSE)` Returning multiple variables is something you shouldn't expect to work. I'd suggest you to return JSON object *(using [`jsonify()`](https://flask.palletsprojects.com/en/2.0.x/api/#flask.json.jsonify))* and parse it using `JSON.parse(xhr.responseText)`. – Olvin Roght Mar 15 '22 at 21:28

1 Answers1

1

You shouldn't be returning a tuple from your predict route. Read here how a view function's response is interpreted by Flask.

You should return a dictionary containing the two values, e.g.

return {'prediction': prediction, 'RMSE': RMSE}

This will be automatically JSONified. In the frontend, you should decode the JSON object and appropriately display the two values (now you're showing the entire response in both elements), e.g.

let result = JSON.parse(xhr.responseText);
document.getElementById('prediction').innerHTML = `Prediction: EUR ${result.prediction}`;
document.getElementById('RMSE').innerHTML = `RMSE: ${result.RMSE}`;
nickie
  • 5,608
  • 2
  • 23
  • 37
  • Thank you very much, you made my day that was the first time using javascript in such a case - it is really nice solution and it makes sense. THANK YOU very much and have a beautiful day :) :) – Jaroslav Kotrba Mar 16 '22 at 07:11