diff --git a/3-Web-App/1-Web-App/notebook.ipynb b/3-Web-App/1-Web-App/notebook.ipynb index e69de29b..24e313fa 100644 --- a/3-Web-App/1-Web-App/notebook.ipynb +++ b/3-Web-App/1-Web-App/notebook.ipynb @@ -0,0 +1,427 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
datetimecitystatecountryshapeduration (seconds)duration (hours/min)commentsdate postedlatitudelongitude
010/10/1949 20:30san marcostxuscylinder2700.045 minutesThis event took place in early fall around 194...4/27/200429.883056-97.941111
110/10/1949 21:00lackland afbtxNaNlight7200.01-2 hrs1949 Lackland AFB&#44 TX. Lights racing acros...12/16/200529.384210-98.581082
210/10/1955 17:00chester (uk/england)NaNgbcircle20.020 secondsGreen/Orange circular disc over Chester&#44 En...1/21/200853.200000-2.916667
310/10/1956 21:00ednatxuscircle20.01/2 hourMy older brother and twin sister were leaving ...1/17/200428.978333-96.645833
410/10/1960 20:00kaneohehiuslight900.015 minutesAS a Marine 1st Lt. flying an FJ4B fighter/att...1/22/200421.418056-157.803611
\n", + "
" + ], + "text/plain": [ + " datetime city state country shape \\\n", + "0 10/10/1949 20:30 san marcos tx us cylinder \n", + "1 10/10/1949 21:00 lackland afb tx NaN light \n", + "2 10/10/1955 17:00 chester (uk/england) NaN gb circle \n", + "3 10/10/1956 21:00 edna tx us circle \n", + "4 10/10/1960 20:00 kaneohe hi us light \n", + "\n", + " duration (seconds) duration (hours/min) \\\n", + "0 2700.0 45 minutes \n", + "1 7200.0 1-2 hrs \n", + "2 20.0 20 seconds \n", + "3 20.0 1/2 hour \n", + "4 900.0 15 minutes \n", + "\n", + " comments date posted latitude \\\n", + "0 This event took place in early fall around 194... 4/27/2004 29.883056 \n", + "1 1949 Lackland AFB, TX. Lights racing acros... 12/16/2005 29.384210 \n", + "2 Green/Orange circular disc over Chester, En... 1/21/2008 53.200000 \n", + "3 My older brother and twin sister were leaving ... 1/17/2004 28.978333 \n", + "4 AS a Marine 1st Lt. flying an FJ4B fighter/att... 1/22/2004 21.418056 \n", + "\n", + " longitude \n", + "0 -97.941111 \n", + "1 -98.581082 \n", + "2 -2.916667 \n", + "3 -96.645833 \n", + "4 -157.803611 " + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "ufos = pd.read_csv('./data/ufos.csv')\n", + "ufos.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array(['us', nan, 'gb', 'ca', 'au', 'de'], dtype=object)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ufos = pd.DataFrame({'Seconds': ufos['duration (seconds)'], 'Country': ufos['country'],'Latitude': ufos['latitude'],'Longitude': ufos['longitude']})\n", + "\n", + "ufos.Country.unique()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Index: 25863 entries, 2 to 80330\n", + "Data columns (total 4 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 Seconds 25863 non-null float64\n", + " 1 Country 25863 non-null object \n", + " 2 Latitude 25863 non-null float64\n", + " 3 Longitude 25863 non-null float64\n", + "dtypes: float64(3), object(1)\n", + "memory usage: 1010.3+ KB\n" + ] + } + ], + "source": [ + "ufos.dropna(inplace=True)\n", + "\n", + "ufos = ufos[(ufos['Seconds'] >= 1) & (ufos['Seconds'] <= 60)]\n", + "\n", + "ufos.info()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
SecondsCountryLatitudeLongitude
220.0353.200000-2.916667
320.0428.978333-96.645833
1430.0435.823889-80.253611
2360.0445.582778-122.352222
243.0351.783333-0.783333
\n", + "
" + ], + "text/plain": [ + " Seconds Country Latitude Longitude\n", + "2 20.0 3 53.200000 -2.916667\n", + "3 20.0 4 28.978333 -96.645833\n", + "14 30.0 4 35.823889 -80.253611\n", + "23 60.0 4 45.582778 -122.352222\n", + "24 3.0 3 51.783333 -0.783333" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.preprocessing import LabelEncoder\n", + "\n", + "ufos['Country'] = LabelEncoder().fit_transform(ufos['Country'])\n", + "\n", + "ufos.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.model_selection import train_test_split\n", + "\n", + "Selected_features = ['Seconds','Latitude','Longitude']\n", + "\n", + "X = ufos[Selected_features]\n", + "y = ufos['Country']\n", + "\n", + "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " precision recall f1-score support\n", + "\n", + " 0 1.00 1.00 1.00 41\n", + " 1 0.82 0.40 0.54 250\n", + " 2 1.00 0.88 0.93 8\n", + " 3 0.99 1.00 1.00 131\n", + " 4 0.97 1.00 0.98 4743\n", + "\n", + " accuracy 0.97 5173\n", + " macro avg 0.96 0.85 0.89 5173\n", + "weighted avg 0.96 0.97 0.96 5173\n", + "\n", + "Predicted labels: [4 4 4 ... 3 4 4]\n", + "Accuracy: 0.9665571235260004\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/ray/Desktop/ML-For-Beginners/.venv/lib/python3.12/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):\n", + "STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.\n", + "\n", + "Increase the number of iterations (max_iter) or scale the data as shown in:\n", + " https://scikit-learn.org/stable/modules/preprocessing.html\n", + "Please also refer to the documentation for alternative solver options:\n", + " https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression\n", + " n_iter_i = _check_optimize_result(\n" + ] + } + ], + "source": [ + "from sklearn.metrics import accuracy_score, classification_report\n", + "from sklearn.linear_model import LogisticRegression\n", + "model = LogisticRegression()\n", + "model.fit(X_train, y_train)\n", + "predictions = model.predict(X_test)\n", + "\n", + "print(classification_report(y_test, predictions))\n", + "print('Predicted labels: ', predictions)\n", + "print('Accuracy: ', accuracy_score(y_test, predictions))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/ray/Desktop/ML-For-Beginners/.venv/lib/python3.12/site-packages/sklearn/base.py:465: UserWarning: X does not have valid feature names, but LogisticRegression was fitted with feature names\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "import pickle\n", + "model_filename = 'ufo-model.pkl'\n", + "pickle.dump(model, open(model_filename,'wb'))\n", + "\n", + "model = pickle.load(open('ufo-model.pkl','rb'))\n", + "print(model.predict([[50,44,-12]]))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/3-Web-App/1-Web-App/web-app/app.py b/3-Web-App/1-Web-App/web-app/app.py new file mode 100644 index 00000000..9ee110cf --- /dev/null +++ b/3-Web-App/1-Web-App/web-app/app.py @@ -0,0 +1,32 @@ +import numpy as np +from flask import Flask, request, render_template +import pickle + +app = Flask(__name__) + +model = pickle.load(open("./ufo-model.pkl", "rb")) + + +@app.route("/") +def home(): + return render_template("index.html") + + +@app.route("/predict", methods=["POST"]) +def predict(): + + int_features = [int(x) for x in request.form.values()] + final_features = [np.array(int_features)] + prediction = model.predict(final_features) + + output = prediction[0] + + countries = ["Australia", "Canada", "Germany", "UK", "US"] + + return render_template( + "index.html", prediction_text="Likely country: {}".format(countries[output]) + ) + + +if __name__ == "__main__": + app.run(debug=True) \ No newline at end of file diff --git a/3-Web-App/1-Web-App/web-app/requirements.txt b/3-Web-App/1-Web-App/web-app/requirements.txt new file mode 100644 index 00000000..b0dd1377 --- /dev/null +++ b/3-Web-App/1-Web-App/web-app/requirements.txt @@ -0,0 +1,4 @@ +scikit-learn +pandas +numpy +flask \ No newline at end of file diff --git a/3-Web-App/1-Web-App/web-app/static/styles.css b/3-Web-App/1-Web-App/web-app/static/styles.css new file mode 100644 index 00000000..00ae9f9c --- /dev/null +++ b/3-Web-App/1-Web-App/web-app/static/styles.css @@ -0,0 +1,29 @@ +body { + width: 100%; + height: 100%; + font-family: 'Helvetica'; + background: black; + color: #fff; + text-align: center; + letter-spacing: 1.4px; + font-size: 30px; +} + +input { + min-width: 150px; +} + +.grid { + width: 300px; + border: 1px solid #2d2d2d; + display: grid; + justify-content: center; + margin: 20px auto; +} + +.box { + color: #fff; + background: #2d2d2d; + padding: 12px; + display: inline-block; +} \ No newline at end of file diff --git a/3-Web-App/1-Web-App/web-app/templates/index.html b/3-Web-App/1-Web-App/web-app/templates/index.html new file mode 100644 index 00000000..265b866e --- /dev/null +++ b/3-Web-App/1-Web-App/web-app/templates/index.html @@ -0,0 +1,30 @@ + + + + + 🛸 UFO Appearance Prediction! 👽 + + + + +
+ +
+ +

According to the number of seconds, latitude and longitude, which country is likely to have reported seeing a UFO?

+ +
+ + + + +
+ +

{{ prediction_text }}

+ +
+ +
+ + + \ No newline at end of file diff --git a/3-Web-App/1-Web-App/web-app/ufo-model.pkl b/3-Web-App/1-Web-App/web-app/ufo-model.pkl new file mode 100644 index 00000000..907ea23a Binary files /dev/null and b/3-Web-App/1-Web-App/web-app/ufo-model.pkl differ