0x-74 commited on
Commit
e941256
·
1 Parent(s): b973975

final_deployment

Browse files
Files changed (11) hide show
  1. .gitattributes +0 -35
  2. .gitignore +1 -0
  3. Dockerfile +13 -0
  4. README.md +0 -10
  5. data.csv +0 -0
  6. encoder.pkl +0 -0
  7. helper.py +39 -0
  8. main.ipynb +875 -0
  9. main.py +83 -0
  10. model.pkl +0 -0
  11. requirements.txt +7 -0
.gitattributes DELETED
@@ -1,35 +0,0 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ __pycache__
Dockerfile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9
2
+
3
+ RUN useradd -m -u 1000 user
4
+ USER user
5
+ ENV PATH="/home/user/.local/bin:$PATH"
6
+
7
+ WORKDIR /app
8
+
9
+ COPY --chown=user ./requirements.txt requirements.txt
10
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
11
+
12
+ COPY --chown=user . /app
13
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md DELETED
@@ -1,10 +0,0 @@
1
- ---
2
- title: Dps Challenge
3
- emoji: 🔥
4
- colorFrom: indigo
5
- colorTo: indigo
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
data.csv ADDED
The diff for this file is too large to render. See raw diff
 
encoder.pkl ADDED
Binary file (1.14 kB). View file
 
helper.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import calendar
2
+ import pandas as pd
3
+ from sklearn.preprocessing import OneHotEncoder
4
+
5
+ def convert_to_month_name(year_month):
6
+
7
+ if year_month == 'Summe':
8
+ return 'Summe'
9
+ month = str(year_month)[4:6]
10
+
11
+
12
+ month_name = calendar.month_name[int(month)]
13
+ return month_name
14
+
15
+
16
+ def transform_new_data(new_data, encoder, original_one_hot_columns):
17
+
18
+ new_data_copy = new_data.copy()
19
+
20
+
21
+ encoded_columns = encoder.transform(new_data_copy[original_one_hot_columns])
22
+
23
+
24
+ encoded_column_names = encoder.get_feature_names_out(original_one_hot_columns)
25
+
26
+
27
+ encoded_df = pd.DataFrame(
28
+ encoded_columns,
29
+ columns=encoded_column_names,
30
+ index=new_data_copy.index
31
+ )
32
+
33
+
34
+ result_df = pd.concat([
35
+ new_data_copy.drop(columns=original_one_hot_columns),
36
+ encoded_df
37
+ ], axis=1)
38
+
39
+ return result_df
main.ipynb ADDED
@@ -0,0 +1,875 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# 1. loading data\n",
8
+ "## installing packages "
9
+ ]
10
+ },
11
+ {
12
+ "cell_type": "code",
13
+ "execution_count": 79,
14
+ "metadata": {
15
+ "id": "E7Ae3ZiczQVT"
16
+ },
17
+ "outputs": [],
18
+ "source": [
19
+ "%pip install seaborn numpy pandas xgboost -qqU"
20
+ ]
21
+ },
22
+ {
23
+ "cell_type": "markdown",
24
+ "metadata": {},
25
+ "source": [
26
+ "# Importing libs"
27
+ ]
28
+ },
29
+ {
30
+ "cell_type": "code",
31
+ "execution_count": null,
32
+ "metadata": {
33
+ "id": "Sesct3fTzQVW"
34
+ },
35
+ "outputs": [],
36
+ "source": [
37
+ "import pandas as pd\n",
38
+ "import numpy as np\n",
39
+ "import seaborn as sns\n",
40
+ "import matplotlib.pyplot as plt\n",
41
+ "import pandas as pd\n",
42
+ "from sklearn.model_selection import train_test_split, GridSearchCV\n",
43
+ "from xgboost import XGBRegressor\n",
44
+ "import xgboost\n",
45
+ "from sklearn.metrics import mean_squared_error\n",
46
+ "import seaborn as sns\n",
47
+ "import matplotlib.pyplot as plt\n",
48
+ "import calendar\n",
49
+ "from sklearn.preprocessing import OneHotEncoder\n",
50
+ "import pickle"
51
+ ]
52
+ },
53
+ {
54
+ "cell_type": "markdown",
55
+ "metadata": {},
56
+ "source": [
57
+ "# reading data and discarding data of years after 2019"
58
+ ]
59
+ },
60
+ {
61
+ "cell_type": "code",
62
+ "execution_count": 122,
63
+ "metadata": {
64
+ "id": "WGqXaM-XzQVX"
65
+ },
66
+ "outputs": [],
67
+ "source": [
68
+ "data = pd.read_csv('data.csv')"
69
+ ]
70
+ },
71
+ {
72
+ "cell_type": "code",
73
+ "execution_count": 123,
74
+ "metadata": {
75
+ "id": "zBBpsXqszQVY"
76
+ },
77
+ "outputs": [],
78
+ "source": [
79
+ "data = data[~(data['JAHR'] > 2019)]\n",
80
+ "data = data[data.columns[:5]]"
81
+ ]
82
+ },
83
+ {
84
+ "cell_type": "code",
85
+ "execution_count": 124,
86
+ "metadata": {
87
+ "id": "r5SG6rjMzQVY"
88
+ },
89
+ "outputs": [],
90
+ "source": [
91
+ "data.reset_index(drop=True, inplace=True)"
92
+ ]
93
+ },
94
+ {
95
+ "cell_type": "markdown",
96
+ "metadata": {},
97
+ "source": [
98
+ "## here I removed the outliers outside z-threshold because these values often dont provide any useful information or help converge the model"
99
+ ]
100
+ },
101
+ {
102
+ "cell_type": "code",
103
+ "execution_count": 125,
104
+ "metadata": {
105
+ "id": "jJVSlcgGzQVZ"
106
+ },
107
+ "outputs": [],
108
+ "source": [
109
+ "def remove_outliers(df, columns=None, z_threshold=3):\n",
110
+ "\n",
111
+ " if columns is None:\n",
112
+ " columns = df.select_dtypes(include=[np.number]).columns\n",
113
+ "\n",
114
+ "\n",
115
+ " df_clean = df.copy()\n",
116
+ "\n",
117
+ "\n",
118
+ " for col in columns:\n",
119
+ " z_scores = np.abs((df_clean[col] - df_clean[col].mean()) / df_clean[col].std())\n",
120
+ " df_clean = df_clean[z_scores < z_threshold]\n",
121
+ "\n",
122
+ " return df_clean\n",
123
+ "\n",
124
+ "\n",
125
+ "data = remove_outliers(data)\n"
126
+ ]
127
+ },
128
+ {
129
+ "cell_type": "code",
130
+ "execution_count": null,
131
+ "metadata": {
132
+ "colab": {
133
+ "base_uri": "https://localhost:8080/",
134
+ "height": 234
135
+ },
136
+ "id": "hYPXBmXfzQVa",
137
+ "outputId": "a153268a-04a1-483a-f4f1-7fd250224248"
138
+ },
139
+ "outputs": [
140
+ {
141
+ "data": {
142
+ "text/html": [
143
+ "<div>\n",
144
+ "<style scoped>\n",
145
+ " .dataframe tbody tr th:only-of-type {\n",
146
+ " vertical-align: middle;\n",
147
+ " }\n",
148
+ "\n",
149
+ " .dataframe tbody tr th {\n",
150
+ " vertical-align: top;\n",
151
+ " }\n",
152
+ "\n",
153
+ " .dataframe thead th {\n",
154
+ " text-align: right;\n",
155
+ " }\n",
156
+ "</style>\n",
157
+ "<table border=\"1\" class=\"dataframe\">\n",
158
+ " <thead>\n",
159
+ " <tr style=\"text-align: right;\">\n",
160
+ " <th></th>\n",
161
+ " <th>0</th>\n",
162
+ " </tr>\n",
163
+ " </thead>\n",
164
+ " <tbody>\n",
165
+ " <tr>\n",
166
+ " <th>MONATSZAHL</th>\n",
167
+ " <td>0</td>\n",
168
+ " </tr>\n",
169
+ " <tr>\n",
170
+ " <th>AUSPRAEGUNG</th>\n",
171
+ " <td>0</td>\n",
172
+ " </tr>\n",
173
+ " <tr>\n",
174
+ " <th>JAHR</th>\n",
175
+ " <td>0</td>\n",
176
+ " </tr>\n",
177
+ " <tr>\n",
178
+ " <th>MONAT</th>\n",
179
+ " <td>0</td>\n",
180
+ " </tr>\n",
181
+ " <tr>\n",
182
+ " <th>WERT</th>\n",
183
+ " <td>0</td>\n",
184
+ " </tr>\n",
185
+ " </tbody>\n",
186
+ "</table>\n",
187
+ "</div><br><label><b>dtype:</b> int64</label>"
188
+ ],
189
+ "text/plain": [
190
+ "MONATSZAHL 0\n",
191
+ "AUSPRAEGUNG 0\n",
192
+ "JAHR 0\n",
193
+ "MONAT 0\n",
194
+ "WERT 0\n",
195
+ "dtype: int64"
196
+ ]
197
+ },
198
+ "execution_count": 126,
199
+ "metadata": {},
200
+ "output_type": "execute_result"
201
+ }
202
+ ],
203
+ "source": [
204
+ "# checked for null values\n",
205
+ "data.isna().sum()"
206
+ ]
207
+ },
208
+ {
209
+ "cell_type": "markdown",
210
+ "metadata": {},
211
+ "source": [
212
+ "# 2 . visualizing the data as per the assignment"
213
+ ]
214
+ },
215
+ {
216
+ "cell_type": "code",
217
+ "execution_count": 127,
218
+ "metadata": {
219
+ "colab": {
220
+ "base_uri": "https://localhost:8080/",
221
+ "height": 421
222
+ },
223
+ "id": "2KxsVKNB4bCE",
224
+ "outputId": "06590c0a-c2b2-44a9-f293-5513eeae6a56"
225
+ },
226
+ "outputs": [
227
+ {
228
+ "data": {
229
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1IAAAIjCAYAAAAJLyrXAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAABZDklEQVR4nO3deXgN1+PH8c/NdmNLCJIIEWsrsRYtUWoLsbbaqJ1QS6toUapKLdFWq7W1tbT9aqJItVrUUmpfSijxpa3ti9paEmqLNUjm90efzM+VhEyaiPJ+Pc88j3vOuWfO3Ny57ufOzBmbYRiGAAAAAAAZ5pTTAwAAAACAfxuCFAAAAABYRJACAAAAAIsIUgAAAABgEUEKAAAAACwiSAEAAACARQQpAAAAALCIIAUAAAAAFhGkAAAAAMAighSALDNq1CjZbLZ7sq569eqpXr165uN169bJZrPp22+/vSfr79q1q0qUKHFP1pVZly5dUo8ePeTr6yubzab+/fvf8zEcOXJENptNUVFR93zdOen29+f9JCoqSjabTUeOHMnpoQDAvxpBCkCaUr5spSzu7u7y8/NTaGioPvroI128eDFL1nPixAmNGjVKO3fuzJL+stL9PLaMePfddxUVFaXevXtr1qxZ6ty5c7ptbTab+vbtm2bdt99+K5vNpnXr1mXTSHPO/PnzZbPZ9J///CfdNitXrpTNZtNHH310D0f275LyOdGjR48064cNG2a2+euvv1LVL1myRE2aNFHBggXl7u6uRx55RIMGDdKZM2dSte3atatsNpsqVaokwzDSHEt67+W9e/ean2fnz59P1efdlq5du0qSkpOT9eWXX6pGjRry8vJSvnz59Mgjj6hLly7asmWL2W+JEiUy3OetkpKS5OfnJ5vNpmXLlqW5LSk/XKX1eqasu0WLFhl+bQBY55LTAwBwf4uIiFDJkiV148YNxcXFad26derfv78mTJigRYsWqVKlSmbb4cOH64033rDU/4kTJzR69GiVKFFCVapUyfDzVqxYYWk9mXGnsX3++edKTk7O9jH8E2vWrFHNmjU1cuTIHBtDQECArl69KldX1xwbw500b95cnp6eio6OTjcEREdHy9nZWe3atbvHo8senTt3Vrt27WS327O0X3d3d3333XeaOnWq3NzcHOq++uorubu769q1a6meN2jQII0fP16VK1fWkCFD5OXlpR07duiTTz7R3LlztXr1aj366KOpnvfrr79q/vz5CgsLy/AYZ8+eLV9fX507d07ffvut+Td/8cUXFRISYrY7fPiwRowYoV69eqlOnTpmeenSpSVJr7zyiqZMmaJnnnlGHTt2lIuLi/bv369ly5apVKlSqlmzpiRp0qRJunTpUppj+eSTT7R161az7a3WrFmjkydPqkSJEpozZ46aNm2a4W0EcO8QpADcUdOmTVW9enXz8dChQ7VmzRq1aNFCTz/9tPbu3atcuXJJklxcXOTikr0fK1euXFHu3LlTfVG71+7XYHCrU6dOKSgoKEfHkPLr//3KbrerdevWioyM1IkTJ+Tn5+dQf+3aNS1YsECNGjWSt7d3Do0yazk7O8vZ2TnL+23SpIkWLVqkZcuW6ZlnnjHLN2/erMOHDyssLEzfffedw3O++uorjR8/Xm3bttWcOXMcxtW1a1fVr19fzz//vHbs2OHw2ZIrVy75+/srIiJCzz33XIZOKTYMQ9HR0erQoYMOHz6sOXPmmEEqODhYwcHBZtvt27drxIgRCg4OVqdOnRz6iY+P19SpU9WzZ0999tlnDnWTJk3S6dOnzcetWrVKcywrVqzQzz//rKefflovvfRSqvrZs2eratWqCg8P15tvvqnLly8rT548d91GAPcWp/YBsKxBgwZ66623dPToUc2ePdssT+saqZUrV6p27drKnz+/8ubNq0cffVRvvvmmpL+va3r88cclSd26dTNPdUm5nqZevXqqUKGCYmNj9dRTTyl37tzmc9O7BiUpKUlvvvmmfH19lSdPHj399NM6fvy4Q5sSJUqkeTrNrX3ebWxpXSN1+fJlvfbaa/L395fdbtejjz6qDz/8MNXpRymn1yxcuFAVKlSQ3W5X+fLltXz58rRf8NucOnVK3bt3l4+Pj9zd3VW5cmXNnDnTrE+5Xuzw4cNaunSpOfasvCYm5W+zZ88e1a9fX7lz51bRokU1btw4h3bpXSOVsu3u7u6qUKGCFixYkOo1TdmO208pTK/Pffv2qXXr1vLy8pK7u7uqV6+uRYsW3XVbOnXqpOTkZM2dOzdV3dKlS3XhwgV17NhRkhQZGakGDRrI29tbdrtdQUFBmjZt2l3Xkd51Selt49atW9WkSRN5enoqd+7cqlu3rjZt2uTQ5uLFi+rfv79KlCghu90ub29vNWrUSDt27LA8lpTTwH766Sc98cQTcnd3V6lSpfTll1/eddtSFC1aVE899ZSio6MdyufMmaOKFSuqQoUKqZ4zevRoFShQQJ999lmqcPfEE09oyJAh+vXXX1Nd++jk5KThw4frl19+0YIFCzI0vk2bNunIkSNq166d2rVrpw0bNuiPP/7I8PalOHz4sAzD0JNPPpmqzmaz3TVwx8XFqXPnzipatKgiIyNT1V+9elULFixQu3bt1KZNG129elXff/+95XECyH4EKQCZknK9zZ1Osdu9e7datGihxMRERUREaPz48Xr66afNL4SBgYGKiIiQJPXq1UuzZs3SrFmz9NRTT5l9nDlzRk2bNlWVKlU0adIk1a9f/47jeuedd7R06VINGTJEr7zyilauXKmQkBBdvXrV0vZlZGy3MgxDTz/9tCZOnKgmTZpowoQJevTRRzV48GANHDgwVfuffvpJL7/8stq1a6dx48bp2rVrCgsLS/OakFtdvXpV9erV06xZs9SxY0d98MEH8vT0VNeuXTV58mRz7LNmzVKhQoVUpUoVc+yFCxe29Brczblz59SkSRNVrlxZ48ePV7ly5TRkyJB0r+lIsWLFCoWFhclms2ns2LFq1aqVunXrpu3bt2d6LLt371bNmjW1d+9evfHGGxo/frzy5MmjVq1a3fWL9lNPPaVixYqlCgDS36f15c6d2zyyMG3aNAUEBOjNN9/U+PHj5e/vr5dffllTpkzJ9Nhvt2bNGj311FNKSEjQyJEj9e677+r8+fNq0KCBfv75Z7PdSy+9pGnTpiksLExTp07VoEGDlCtXLu3duzdT6z148KBat26tRo0aafz48SpQoIC6du2q3bt3Z7iPDh06aPHixebpbDdv3tS8efPUoUOHVG0PHDig/fv365lnnpGHh0ea/XXp0kXS39dQpbWusmXLKiIiIs1rpW43Z84clS5dWo8//rhatmyp3Llz66uvvsrwtqUICAiQJM2bN09Xrlyx9Nzk5GR16tRJZ86cUXR0tLy8vFK1WbRokS5duqR27drJ19dX9erV05w5c9Lt8+zZs/rrr79SLff7qcfAA8EAgDRERkYakoxt27al28bT09N47LHHzMcjR440bv1YmThxoiHJOH36dLp9bNu2zZBkREZGpqqrW7euIcmYPn16mnV169Y1H69du9aQZBQtWtRISEgwy7/55htD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/VZNmjRRmzZtdOjQIc2ePdth8gerY2vZsqXq16+vYcOG6ciRI6pcubJWrFih77//Xv3790/Vd2b16tVLn376qbp27arY2FiVKFFC3377rTZt2qRJkybd9cL99LzxxhuaN2+ennrqKb344osqV66cTpw4oaioKJ08eTJDYSGjxo4dq+bNm6t27dp64YUXdPbsWX388ccqX768Q3jw9PTU888/r48//lg2m02lS5fWkiVL0rzeacqUKapdu7YqVqyonj17qlSpUoqPj1dMTIz++OMP7dq1K0Nj69Spk3r16qUTJ05o2LBhDnWNGzeWm5ubWrZsqRdffFGXLl3S559/Lm9v7zQD3K3Kly+vmjVraujQoTp79qy8vLw0d+5c3bx506Gdk5OT/vOf/6hp06YqX768unXrpqJFi+rPP//U2rVr5eHhocWLF+vixYsqVqyYWrdurcqVKytv3rxatWqVtm3bpvHjx2doW7NT5cqVVbly5bu269ixo7Zt26bJkydrz5496tixowoUKKAdO3boiy++UMGCBfXtt9/e9QbYzs7OGjZsWKqJaU6cOKG1a9emmgQmhd1uV2hoqObNm6ePPvooQzfa/uOPP/TEE0+oQYMGatiwoXx9fXXq1Cl99dVX2rVrl/r3769ChQrpypUratu2rW7cuKEWLVqkO1mEj4+PGjVqZP5g4e/vn2a7p59+Wv369dOOHTtUtWrVu44zPdu3b9fbb7+dqrxevXpZclQOeKjk2HyBAO5rKVMkpyxubm6Gr6+v0ahRI2Py5MkO02ynuH3689WrVxvPPPOM4efnZ7i5uRl+fn5G+/btjf/9738Oz/v++++NoKAgw8XFxWHK4bp16xrly5dPc3zpTX/+1VdfGUOHDjW8vb2NXLlyGc2bNzeOHj2a6vnjx483ihYtatjtduPJJ580tm/fnqrPO43t9unPDcMwLl68aAwYMMDw8/MzXF1djbJlyxoffPCBwzTWhpH+FMTpTct+u/j4eKNbt25GoUKFDDc3N6NixYppTtNsZfpzwzCMP/74w+jRo4dRtGhRw8XFxfDy8jJatGhhbNmyJVXb9P42t78uaU1VbhiG8d133xmBgYGG3W43goKCjPnz56f5mp4+fdoICwszcufObRQoUMB48cUXjd9++y3NPg8dOmR06dLF8PX1NVxdXY2iRYsaLVq0ML799tsM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",
230
+ "text/plain": [
231
+ "<Figure size 1000x600 with 1 Axes>"
232
+ ]
233
+ },
234
+ "metadata": {},
235
+ "output_type": "display_data"
236
+ }
237
+ ],
238
+ "source": [
239
+ "value_counts = data['MONATSZAHL'].value_counts()\n",
240
+ "plt.figure(figsize=(10, 6))\n",
241
+ "sns.barplot(x=value_counts.index, y=value_counts.values)\n",
242
+ "plt.xlabel('MONATSZAHL')\n",
243
+ "plt.ylabel('Count')\n",
244
+ "plt.title('Distribution of Unique Values in MONATSZAHL')\n",
245
+ "plt.show()\n"
246
+ ]
247
+ },
248
+ {
249
+ "cell_type": "markdown",
250
+ "metadata": {},
251
+ "source": [
252
+ "## i saw some redundant information here so i converted the months to only have the months because the year would already provide relevant insight"
253
+ ]
254
+ },
255
+ {
256
+ "cell_type": "code",
257
+ "execution_count": 128,
258
+ "metadata": {
259
+ "id": "oYWFkNVw6GuR"
260
+ },
261
+ "outputs": [],
262
+ "source": [
263
+ "def convert_to_month_name(year_month):\n",
264
+ "\n",
265
+ " if year_month == 'Summe':\n",
266
+ " return 'Summe'\n",
267
+ " month = str(year_month)[4:6]\n",
268
+ "\n",
269
+ "\n",
270
+ " month_name = calendar.month_name[int(month)]\n",
271
+ " return month_name\n",
272
+ "data['MONAT'] = data['MONAT'].apply(convert_to_month_name)"
273
+ ]
274
+ },
275
+ {
276
+ "cell_type": "code",
277
+ "execution_count": 129,
278
+ "metadata": {
279
+ "colab": {
280
+ "base_uri": "https://localhost:8080/",
281
+ "height": 509
282
+ },
283
+ "id": "2nZgB2c28Vub",
284
+ "outputId": "96251141-a163-49e9-ea10-6bfedc892cc6"
285
+ },
286
+ "outputs": [
287
+ {
288
+ "data": {
289
+ "text/html": [
290
+ "<div>\n",
291
+ "<style scoped>\n",
292
+ " .dataframe tbody tr th:only-of-type {\n",
293
+ " vertical-align: middle;\n",
294
+ " }\n",
295
+ "\n",
296
+ " .dataframe tbody tr th {\n",
297
+ " vertical-align: top;\n",
298
+ " }\n",
299
+ "\n",
300
+ " .dataframe thead th {\n",
301
+ " text-align: right;\n",
302
+ " }\n",
303
+ "</style>\n",
304
+ "<table border=\"1\" class=\"dataframe\">\n",
305
+ " <thead>\n",
306
+ " <tr style=\"text-align: right;\">\n",
307
+ " <th></th>\n",
308
+ " <th>count</th>\n",
309
+ " </tr>\n",
310
+ " <tr>\n",
311
+ " <th>MONAT</th>\n",
312
+ " <th></th>\n",
313
+ " </tr>\n",
314
+ " </thead>\n",
315
+ " <tbody>\n",
316
+ " <tr>\n",
317
+ " <th>January</th>\n",
318
+ " <td>140</td>\n",
319
+ " </tr>\n",
320
+ " <tr>\n",
321
+ " <th>March</th>\n",
322
+ " <td>140</td>\n",
323
+ " </tr>\n",
324
+ " <tr>\n",
325
+ " <th>February</th>\n",
326
+ " <td>140</td>\n",
327
+ " </tr>\n",
328
+ " <tr>\n",
329
+ " <th>April</th>\n",
330
+ " <td>140</td>\n",
331
+ " </tr>\n",
332
+ " <tr>\n",
333
+ " <th>May</th>\n",
334
+ " <td>140</td>\n",
335
+ " </tr>\n",
336
+ " <tr>\n",
337
+ " <th>September</th>\n",
338
+ " <td>140</td>\n",
339
+ " </tr>\n",
340
+ " <tr>\n",
341
+ " <th>June</th>\n",
342
+ " <td>140</td>\n",
343
+ " </tr>\n",
344
+ " <tr>\n",
345
+ " <th>July</th>\n",
346
+ " <td>140</td>\n",
347
+ " </tr>\n",
348
+ " <tr>\n",
349
+ " <th>August</th>\n",
350
+ " <td>140</td>\n",
351
+ " </tr>\n",
352
+ " <tr>\n",
353
+ " <th>November</th>\n",
354
+ " <td>140</td>\n",
355
+ " </tr>\n",
356
+ " <tr>\n",
357
+ " <th>October</th>\n",
358
+ " <td>140</td>\n",
359
+ " </tr>\n",
360
+ " <tr>\n",
361
+ " <th>December</th>\n",
362
+ " <td>140</td>\n",
363
+ " </tr>\n",
364
+ " <tr>\n",
365
+ " <th>Summe</th>\n",
366
+ " <td>114</td>\n",
367
+ " </tr>\n",
368
+ " </tbody>\n",
369
+ "</table>\n",
370
+ "</div><br><label><b>dtype:</b> int64</label>"
371
+ ],
372
+ "text/plain": [
373
+ "MONAT\n",
374
+ "January 140\n",
375
+ "March 140\n",
376
+ "February 140\n",
377
+ "April 140\n",
378
+ "May 140\n",
379
+ "September 140\n",
380
+ "June 140\n",
381
+ "July 140\n",
382
+ "August 140\n",
383
+ "November 140\n",
384
+ "October 140\n",
385
+ "December 140\n",
386
+ "Summe 114\n",
387
+ "Name: count, dtype: int64"
388
+ ]
389
+ },
390
+ "execution_count": 129,
391
+ "metadata": {},
392
+ "output_type": "execute_result"
393
+ }
394
+ ],
395
+ "source": [
396
+ "data['MONAT'].value_counts()"
397
+ ]
398
+ },
399
+ {
400
+ "cell_type": "code",
401
+ "execution_count": 130,
402
+ "metadata": {
403
+ "colab": {
404
+ "base_uri": "https://localhost:8080/",
405
+ "height": 723
406
+ },
407
+ "id": "5Dh3SgWE82hi",
408
+ "outputId": "5455f4dd-88ea-4626-8fc8-000cfcc0afb5"
409
+ },
410
+ "outputs": [
411
+ {
412
+ "data": {
413
+ "text/html": [
414
+ "<div>\n",
415
+ "<style scoped>\n",
416
+ " .dataframe tbody tr th:only-of-type {\n",
417
+ " vertical-align: middle;\n",
418
+ " }\n",
419
+ "\n",
420
+ " .dataframe tbody tr th {\n",
421
+ " vertical-align: top;\n",
422
+ " }\n",
423
+ "\n",
424
+ " .dataframe thead th {\n",
425
+ " text-align: right;\n",
426
+ " }\n",
427
+ "</style>\n",
428
+ "<table border=\"1\" class=\"dataframe\">\n",
429
+ " <thead>\n",
430
+ " <tr style=\"text-align: right;\">\n",
431
+ " <th></th>\n",
432
+ " <th>count</th>\n",
433
+ " </tr>\n",
434
+ " <tr>\n",
435
+ " <th>JAHR</th>\n",
436
+ " <th></th>\n",
437
+ " </tr>\n",
438
+ " </thead>\n",
439
+ " <tbody>\n",
440
+ " <tr>\n",
441
+ " <th>2019</th>\n",
442
+ " <td>90</td>\n",
443
+ " </tr>\n",
444
+ " <tr>\n",
445
+ " <th>2018</th>\n",
446
+ " <td>90</td>\n",
447
+ " </tr>\n",
448
+ " <tr>\n",
449
+ " <th>2017</th>\n",
450
+ " <td>90</td>\n",
451
+ " </tr>\n",
452
+ " <tr>\n",
453
+ " <th>2016</th>\n",
454
+ " <td>90</td>\n",
455
+ " </tr>\n",
456
+ " <tr>\n",
457
+ " <th>2015</th>\n",
458
+ " <td>90</td>\n",
459
+ " </tr>\n",
460
+ " <tr>\n",
461
+ " <th>2014</th>\n",
462
+ " <td>90</td>\n",
463
+ " </tr>\n",
464
+ " <tr>\n",
465
+ " <th>2013</th>\n",
466
+ " <td>90</td>\n",
467
+ " </tr>\n",
468
+ " <tr>\n",
469
+ " <th>2012</th>\n",
470
+ " <td>90</td>\n",
471
+ " </tr>\n",
472
+ " <tr>\n",
473
+ " <th>2011</th>\n",
474
+ " <td>90</td>\n",
475
+ " </tr>\n",
476
+ " <tr>\n",
477
+ " <th>2010</th>\n",
478
+ " <td>90</td>\n",
479
+ " </tr>\n",
480
+ " <tr>\n",
481
+ " <th>2009</th>\n",
482
+ " <td>90</td>\n",
483
+ " </tr>\n",
484
+ " <tr>\n",
485
+ " <th>2008</th>\n",
486
+ " <td>90</td>\n",
487
+ " </tr>\n",
488
+ " <tr>\n",
489
+ " <th>2007</th>\n",
490
+ " <td>90</td>\n",
491
+ " </tr>\n",
492
+ " <tr>\n",
493
+ " <th>2006</th>\n",
494
+ " <td>90</td>\n",
495
+ " </tr>\n",
496
+ " <tr>\n",
497
+ " <th>2005</th>\n",
498
+ " <td>90</td>\n",
499
+ " </tr>\n",
500
+ " <tr>\n",
501
+ " <th>2004</th>\n",
502
+ " <td>90</td>\n",
503
+ " </tr>\n",
504
+ " <tr>\n",
505
+ " <th>2003</th>\n",
506
+ " <td>90</td>\n",
507
+ " </tr>\n",
508
+ " <tr>\n",
509
+ " <th>2002</th>\n",
510
+ " <td>90</td>\n",
511
+ " </tr>\n",
512
+ " <tr>\n",
513
+ " <th>2001</th>\n",
514
+ " <td>90</td>\n",
515
+ " </tr>\n",
516
+ " <tr>\n",
517
+ " <th>2000</th>\n",
518
+ " <td>84</td>\n",
519
+ " </tr>\n",
520
+ " </tbody>\n",
521
+ "</table>\n",
522
+ "</div><br><label><b>dtype:</b> int64</label>"
523
+ ],
524
+ "text/plain": [
525
+ "JAHR\n",
526
+ "2019 90\n",
527
+ "2018 90\n",
528
+ "2017 90\n",
529
+ "2016 90\n",
530
+ "2015 90\n",
531
+ "2014 90\n",
532
+ "2013 90\n",
533
+ "2012 90\n",
534
+ "2011 90\n",
535
+ "2010 90\n",
536
+ "2009 90\n",
537
+ "2008 90\n",
538
+ "2007 90\n",
539
+ "2006 90\n",
540
+ "2005 90\n",
541
+ "2004 90\n",
542
+ "2003 90\n",
543
+ "2002 90\n",
544
+ "2001 90\n",
545
+ "2000 84\n",
546
+ "Name: count, dtype: int64"
547
+ ]
548
+ },
549
+ "execution_count": 130,
550
+ "metadata": {},
551
+ "output_type": "execute_result"
552
+ }
553
+ ],
554
+ "source": [
555
+ "data['JAHR'].value_counts()"
556
+ ]
557
+ },
558
+ {
559
+ "cell_type": "markdown",
560
+ "metadata": {},
561
+ "source": [
562
+ "### after checking for data imbalances , now we can train the model, we first one hot all the categorical columns, another good approach according to me would be to just use cardinal encoding but my results didnt seem excessively differing so i went with one hot"
563
+ ]
564
+ },
565
+ {
566
+ "cell_type": "code",
567
+ "execution_count": 134,
568
+ "metadata": {
569
+ "id": "O1m3dRKbGTN9"
570
+ },
571
+ "outputs": [],
572
+ "source": [
573
+ "one_hot_columns = data.columns[0:4]"
574
+ ]
575
+ },
576
+ {
577
+ "cell_type": "code",
578
+ "execution_count": 136,
579
+ "metadata": {
580
+ "id": "poFSghx08_ig"
581
+ },
582
+ "outputs": [],
583
+ "source": [
584
+ "\n",
585
+ "def one_hot_encode(data, one_hot_columns):\n",
586
+ "\n",
587
+ " data_copy = data.copy()\n",
588
+ "\n",
589
+ "\n",
590
+ " encoder = OneHotEncoder(sparse_output=False)\n",
591
+ "\n",
592
+ "\n",
593
+ " encoded_columns = encoder.fit_transform(data_copy[one_hot_columns])\n",
594
+ "\n",
595
+ "\n",
596
+ " encoded_column_names = encoder.get_feature_names_out(one_hot_columns)\n",
597
+ "\n",
598
+ "\n",
599
+ " encoded_df = pd.DataFrame(\n",
600
+ " encoded_columns,\n",
601
+ " columns=encoded_column_names,\n",
602
+ " index=data_copy.index\n",
603
+ " )\n",
604
+ "\n",
605
+ "\n",
606
+ " result_df = pd.concat([\n",
607
+ " data_copy.drop(columns=one_hot_columns),\n",
608
+ " encoded_df\n",
609
+ " ], axis=1)\n",
610
+ "\n",
611
+ " return result_df, encoder\n",
612
+ "\n",
613
+ "def transform_new_data(new_data, encoder, original_one_hot_columns):\n",
614
+ "\n",
615
+ " new_data_copy = new_data.copy()\n",
616
+ "\n",
617
+ "\n",
618
+ " encoded_columns = encoder.transform(new_data_copy[original_one_hot_columns])\n",
619
+ "\n",
620
+ "\n",
621
+ " encoded_column_names = encoder.get_feature_names_out(original_one_hot_columns)\n",
622
+ "\n",
623
+ "\n",
624
+ " encoded_df = pd.DataFrame(\n",
625
+ " encoded_columns,\n",
626
+ " columns=encoded_column_names,\n",
627
+ " index=new_data_copy.index\n",
628
+ " )\n",
629
+ "\n",
630
+ "\n",
631
+ " result_df = pd.concat([\n",
632
+ " new_data_copy.drop(columns=original_one_hot_columns),\n",
633
+ " encoded_df\n",
634
+ " ], axis=1)\n",
635
+ "\n",
636
+ " return result_df"
637
+ ]
638
+ },
639
+ {
640
+ "cell_type": "code",
641
+ "execution_count": 139,
642
+ "metadata": {
643
+ "id": "3a18WFefHY3F"
644
+ },
645
+ "outputs": [],
646
+ "source": [
647
+ "data, encoder = one_hot_encode(data, one_hot_columns)"
648
+ ]
649
+ },
650
+ {
651
+ "cell_type": "code",
652
+ "execution_count": 93,
653
+ "metadata": {
654
+ "colab": {
655
+ "base_uri": "https://localhost:8080/"
656
+ },
657
+ "id": "n8x7Qli20z_-",
658
+ "outputId": "bd6fd971-0f61-4a08-b5e1-9863b285ea71"
659
+ },
660
+ "outputs": [
661
+ {
662
+ "name": "stdout",
663
+ "output_type": "stream",
664
+ "text": [
665
+ "{'BUILTIN_PREFETCH_PRESENT': True, 'CUDA_VERSION': [11, 8], 'DEBUG': False, 'GCC_VERSION': [10, 3, 1], 'MM_PREFETCH_PRESENT': True, 'NCCL_VERSION': [2, 16, 5], 'THRUST_VERSION': [1, 15, 1], 'USE_CUDA': True, 'USE_DLOPEN_NCCL': True, 'USE_FEDERATED': True, 'USE_NCCL': True, 'USE_OPENMP': True, 'USE_RMM': False, 'libxgboost': '/usr/local/lib/python3.10/dist-packages/xgboost/lib/libxgboost.so'}\n"
666
+ ]
667
+ }
668
+ ],
669
+ "source": [
670
+ "print(xgboost.build_info())"
671
+ ]
672
+ },
673
+ {
674
+ "cell_type": "markdown",
675
+ "metadata": {},
676
+ "source": [
677
+ "# 3. finally training the model and downloading it as pkl to use in api"
678
+ ]
679
+ },
680
+ {
681
+ "cell_type": "code",
682
+ "execution_count": 142,
683
+ "metadata": {
684
+ "colab": {
685
+ "base_uri": "https://localhost:8080/"
686
+ },
687
+ "id": "EVn2xhzhzQVa",
688
+ "outputId": "6218d7ed-0415-45d3-bb38-73cd009bdf5f"
689
+ },
690
+ "outputs": [
691
+ {
692
+ "name": "stdout",
693
+ "output_type": "stream",
694
+ "text": [
695
+ "Fitting 3 folds for each of 243 candidates, totalling 729 fits\n"
696
+ ]
697
+ },
698
+ {
699
+ "name": "stderr",
700
+ "output_type": "stream",
701
+ "text": [
702
+ "/usr/local/lib/python3.10/dist-packages/xgboost/core.py:158: UserWarning: [15:01:28] WARNING: /workspace/src/common/error_msg.cc:27: The tree method `gpu_hist` is deprecated since 2.0.0. To use GPU training, set the `device` parameter to CUDA instead.\n",
703
+ "\n",
704
+ " E.g. tree_method = \"hist\", device = \"cuda\"\n",
705
+ "\n",
706
+ " warnings.warn(smsg, UserWarning)\n",
707
+ "/usr/local/lib/python3.10/dist-packages/xgboost/core.py:158: UserWarning: [15:01:28] WARNING: /workspace/src/learner.cc:740: \n",
708
+ "Parameters: { \"predictor\" } are not used.\n",
709
+ "\n",
710
+ " warnings.warn(smsg, UserWarning)\n"
711
+ ]
712
+ },
713
+ {
714
+ "name": "stdout",
715
+ "output_type": "stream",
716
+ "text": [
717
+ "Best parameters found: {'colsample_bytree': 0.8, 'learning_rate': 0.1, 'max_depth': 5, 'n_estimators': 300, 'subsample': 0.7}\n",
718
+ " param_n_estimators param_learning_rate param_max_depth \\\n",
719
+ "0 100 0.01 3 \n",
720
+ "1 100 0.01 3 \n",
721
+ "2 100 0.01 3 \n",
722
+ "3 200 0.01 3 \n",
723
+ "4 200 0.01 3 \n",
724
+ ".. ... ... ... \n",
725
+ "238 200 0.20 7 \n",
726
+ "239 200 0.20 7 \n",
727
+ "240 300 0.20 7 \n",
728
+ "241 300 0.20 7 \n",
729
+ "242 300 0.20 7 \n",
730
+ "\n",
731
+ " param_subsample param_colsample_bytree mean_test_score \n",
732
+ "0 0.7 0.7 836731.772750 \n",
733
+ "1 0.8 0.7 829332.460518 \n",
734
+ "2 0.9 0.7 829277.959373 \n",
735
+ "3 0.7 0.7 359457.295642 \n",
736
+ "4 0.8 0.7 351281.456970 \n",
737
+ ".. ... ... ... \n",
738
+ "238 0.8 0.9 31653.504011 \n",
739
+ "239 0.9 0.9 31929.875660 \n",
740
+ "240 0.7 0.9 32102.913523 \n",
741
+ "241 0.8 0.9 31576.184742 \n",
742
+ "242 0.9 0.9 31894.048866 \n",
743
+ "\n",
744
+ "[243 rows x 6 columns]\n",
745
+ "Mean Squared Error on the test set: 28483.477323930427\n"
746
+ ]
747
+ },
748
+ {
749
+ "name": "stderr",
750
+ "output_type": "stream",
751
+ "text": [
752
+ "/usr/local/lib/python3.10/dist-packages/xgboost/core.py:158: UserWarning: [15:01:29] WARNING: /workspace/src/common/error_msg.cc:27: The tree method `gpu_hist` is deprecated since 2.0.0. To use GPU training, set the `device` parameter to CUDA instead.\n",
753
+ "\n",
754
+ " E.g. tree_method = \"hist\", device = \"cuda\"\n",
755
+ "\n",
756
+ " warnings.warn(smsg, UserWarning)\n"
757
+ ]
758
+ }
759
+ ],
760
+ "source": [
761
+ "X = data.drop(columns=['WERT'])\n",
762
+ "y = data['WERT']\n",
763
+ "\n",
764
+ "\n",
765
+ "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
766
+ "\n",
767
+ "\n",
768
+ "xgb = XGBRegressor(\n",
769
+ " tree_method='gpu_hist',\n",
770
+ " predictor='gpu_predictor',\n",
771
+ " verbosity=2\n",
772
+ ")\n",
773
+ "\n",
774
+ "\n",
775
+ "param_grid = {\n",
776
+ " 'n_estimators': [100, 200, 300],\n",
777
+ " 'learning_rate': [0.01, 0.1, 0.2],\n",
778
+ " 'max_depth': [3, 5, 7],\n",
779
+ " 'subsample': [0.7, 0.8, 0.9],\n",
780
+ " 'colsample_bytree': [0.7, 0.8, 0.9]\n",
781
+ "}\n",
782
+ "\n",
783
+ "\n",
784
+ "grid_search = GridSearchCV(\n",
785
+ " estimator=xgb,\n",
786
+ " param_grid=param_grid,\n",
787
+ " cv=3,\n",
788
+ " scoring='neg_mean_squared_error',\n",
789
+ " verbose=2,\n",
790
+ " n_jobs=-1\n",
791
+ ")\n",
792
+ "\n",
793
+ "grid_search.fit(X_train, y_train)\n",
794
+ "\n",
795
+ "\n",
796
+ "best_params = grid_search.best_params_\n",
797
+ "print(\"Best parameters found: \", best_params)\n",
798
+ "\n",
799
+ "\n",
800
+ "results = pd.DataFrame(grid_search.cv_results_)\n",
801
+ "\n",
802
+ "\n",
803
+ "results['mean_test_score'] = -results['mean_test_score']\n",
804
+ "\n",
805
+ "\n",
806
+ "print(results[['param_n_estimators', 'param_learning_rate', 'param_max_depth', 'param_subsample', 'param_colsample_bytree', 'mean_test_score']])\n",
807
+ "\n",
808
+ "\n",
809
+ "best_model = grid_search.best_estimator_\n",
810
+ "y_pred = best_model.predict(X_test)\n",
811
+ "\n",
812
+ "mse = mean_squared_error(y_test, y_pred)\n",
813
+ "print(\"Mean Squared Error on the test set: \", mse)\n"
814
+ ]
815
+ },
816
+ {
817
+ "cell_type": "code",
818
+ "execution_count": 165,
819
+ "metadata": {
820
+ "id": "zcb7oWV0HwH5"
821
+ },
822
+ "outputs": [],
823
+ "source": [
824
+ "ex = pd.DataFrame({\n",
825
+ " 'MONATSZAHL': ['Alkoholunfälle'],\n",
826
+ " 'AUSPRAEGUNG': ['Verletzte und Getötete'],\n",
827
+ " 'JAHR': [2012],\n",
828
+ " 'MONAT': ['201207']\n",
829
+ "})\n",
830
+ "ex['MONAT'] = ex['MONAT'].apply(convert_to_month_name)\n",
831
+ "new = transform_new_data(ex, encoder, one_hot_columns)\n"
832
+ ]
833
+ },
834
+ {
835
+ "cell_type": "code",
836
+ "execution_count": null,
837
+ "metadata": {
838
+ "id": "EhSjmlIAOAGk"
839
+ },
840
+ "outputs": [],
841
+ "source": [
842
+ "with open('xgb.pkl', 'wb') as file:\n",
843
+ " pickle.dump(best_model, file)\n",
844
+ "\n",
845
+ "with open('encoder.pkl', 'wb') as file:\n",
846
+ " pickle.dump(encoder, file)"
847
+ ]
848
+ }
849
+ ],
850
+ "metadata": {
851
+ "accelerator": "GPU",
852
+ "colab": {
853
+ "gpuType": "T4",
854
+ "provenance": []
855
+ },
856
+ "kernelspec": {
857
+ "display_name": "Python 3",
858
+ "name": "python3"
859
+ },
860
+ "language_info": {
861
+ "codemirror_mode": {
862
+ "name": "ipython",
863
+ "version": 3
864
+ },
865
+ "file_extension": ".py",
866
+ "mimetype": "text/x-python",
867
+ "name": "python",
868
+ "nbconvert_exporter": "python",
869
+ "pygments_lexer": "ipython3",
870
+ "version": "3.12.6"
871
+ }
872
+ },
873
+ "nbformat": 4,
874
+ "nbformat_minor": 0
875
+ }
main.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import joblib
2
+ from fastapi import FastAPI, HTTPException
3
+ from pydantic import BaseModel, ValidationError
4
+ import pandas as pd
5
+ from helper import convert_to_month_name, transform_new_data
6
+
7
+ # Load the model and encoder
8
+ model = joblib.load('model.pkl')
9
+ encoder = joblib.load('encoder.pkl')
10
+
11
+ app = FastAPI()
12
+
13
+ # Pydantic model for input data validation
14
+ class Item(BaseModel):
15
+ MONATSZAHL: str
16
+ AUSPRAEGUNG: str
17
+ JAHR: int
18
+ MONAT: str
19
+
20
+ # Endpoint for inference
21
+ @app.post("/predict/")
22
+ async def predict(item: Item):
23
+ try:
24
+ # Construct input data from request
25
+ input_data = {
26
+ "MONATSZAHL": item.MONATSZAHL,
27
+ "AUSPRAEGUNG": item.AUSPRAEGUNG,
28
+ "JAHR": item.JAHR,
29
+ "MONAT": item.MONAT
30
+ }
31
+
32
+ # Convert input data to DataFrame
33
+ input_df = pd.DataFrame([input_data])
34
+
35
+ # Convert 'MONAT' to month name
36
+ try:
37
+ input_df['MONAT'] = input_df['MONAT'].apply(convert_to_month_name)
38
+ except Exception as e:
39
+ raise HTTPException(
40
+ status_code=400,
41
+ detail=f"Error converting 'MONAT' to month name: {e}"
42
+ )
43
+
44
+ # Transform data with encoder
45
+ try:
46
+ transformed_df = transform_new_data(
47
+ input_df,
48
+ encoder,
49
+ original_one_hot_columns=['MONATSZAHL', 'AUSPRAEGUNG', "JAHR", 'MONAT']
50
+ )
51
+ except Exception as e:
52
+ raise HTTPException(
53
+ status_code=500,
54
+ detail=f"Error transforming data: {e}"
55
+ )
56
+
57
+ # Ensure the transformed data matches the model's expected input
58
+ try:
59
+ prediction = model.predict(transformed_df)
60
+ except Exception as e:
61
+ raise HTTPException(
62
+ status_code=500,
63
+ detail=f"Error during model prediction: {e}"
64
+ )
65
+
66
+ # Return prediction result
67
+ return {"prediction": prediction.tolist()}
68
+
69
+ except ValidationError as e:
70
+ raise HTTPException(
71
+ status_code=422,
72
+ detail=f"Validation error: {e}"
73
+ )
74
+ except KeyError as e:
75
+ raise HTTPException(
76
+ status_code=400,
77
+ detail=f"Missing expected column: {e}"
78
+ )
79
+ except Exception as e:
80
+ raise HTTPException(
81
+ status_code=500,
82
+ detail=f"Internal server error: {e}"
83
+ )
model.pkl ADDED
Binary file (712 kB). View file
 
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ pydantic
3
+ pandas
4
+ scikit-learn
5
+ joblib
6
+ uvicorn
7
+ xgboost