2023-programowanie-w-pythonie/zajecia3/sklearn cz. 1.ipynb

588 lines
30 KiB
Plaintext
Raw Normal View History

2023-11-19 12:33:16 +01:00
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Kkolejna część zajęć będzie wprowadzeniem do drugiej, szeroko używanej biblioteki w Pythonie: `sklearn`. Zajęcia będą miały charaktere case-study poprzeplatane zadaniami do wykonania. Zacznijmy od załadowania odpowiednich bibliotek."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
"\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Zacznijmy od załadowania danych. Na dzisiejszych zajęciach będziemy korzystać z danych z portalu [gapminder.org](https://www.gapminder.org/data/)."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"df = pd.read_csv('gapminder.csv', index_col=0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Dane zawierają różne informacje z większość państw świata (z roku 2008). Poniżej znajduje się opis kolumn:\n",
" * female_BMI - średnie BMI u kobiet\n",
" * male_BMI - średnie BMI u mężczyzn\n",
" * gdp - PKB na obywatela\n",
" * population - wielkość populacji\n",
" * under5mortality - wskaźnik śmiertelności dzieni pon. 5 roku życia (na 1000 urodzonych dzieci)\n",
" * life_expectancy - średnia długość życia\n",
" * fertility - wskaźnik dzietności"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**zad. 1**\n",
"Na podstawie danych zawartych w `df` odpowiedz na następujące pytania:\n",
" * Jaki był współczynniki dzietności w Polsce w 2018?\n",
" * W którym kraju ludzie żyją najdłużej?\n",
" * Z ilu krajów zostały zebrane dane?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**zad. 2** Stwórz kolumnę `gdp_log`, która powstanie z kolumny `gdp` poprzez zastowanie funkcji `log` (logarytm). \n",
"\n",
"Hint 1: Wykorzystaj funkcję `apply` (https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.apply.html#pandas.Series.apply).\n",
"\n",
"Hint 2: Wykorzystaj fukcję `log` z pakietu `np`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Naszym zadaniem będzie oszacowanie długości życia (kolumna `life_expectancy`) na podstawie pozostałych zmiennych. Na samym początku, zastosujemy regresje jednowymiarową na `fertility`."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Y shape: (175,)\n",
"X shape: (175,)\n"
]
}
],
"source": [
"y = df['life_expectancy'].values\n",
"X = df['fertility'].values\n",
"\n",
"print(\"Y shape:\", y.shape)\n",
"print(\"X shape:\", X.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Będziemy korzystać z gotowej implementacji regreji liniowej z pakietu sklearn. Żeby móc wykorzystać, musimy napierw zmienić shape na dwuwymiarowy."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Y shape: (175, 1)\n",
"X shape: (175, 1)\n"
]
}
],
"source": [
"y = y.reshape(-1, 1)\n",
"X = X.reshape(-1, 1)\n",
"\n",
"print(\"Y shape:\", y.shape)\n",
"print(\"X shape:\", X.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Jeszcze przed właściwą analizą, narysujmy wykres i zobaczny czy istnieje \"wizualny\" związek pomiędzy kolumnami."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.axes._subplots.AxesSubplot at 0x7fc2c92991f0>"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"df.plot.scatter('fertility', 'life_expectancy')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**zad. 3** Zaimportuj `LinearRegression` z pakietu `sklearn.linear_model`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Tworzymy obiekt modelu regresji liniowej."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"model = LinearRegression()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Trening modelu ogranicza się do wywołania metodu `fit`, która przyjmuje dwa argumenty:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"LinearRegression()"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.fit(X, y)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Współczynniki modelu:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Wyraz wolny (bias): [83.2025629]\n",
"Współczynniki cech: [[-4.41400624]]\n"
]
}
],
"source": [
"print(\"Wyraz wolny (bias):\", model.intercept_)\n",
"print(\"Współczynniki cech:\", model.coef_)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**zad. 4** Wytrenuj nowy model `model2`, który będzie jako X przyjmie kolumnę `gdp_log`. Wyświetl parametry nowego modelu."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Mając wytrenowany model możemy wykorzystać go do predykcji. Wystarczy wywołać metodę `predict`."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"input: 6.2\t predicted: 55.835724214829476\t expected: 52.8\n",
"input: 1.76\t predicted: 75.43391191760767\t expected: 76.8\n",
"input: 2.73\t predicted: 71.15232586542415\t expected: 75.5\n",
"input: 6.43\t predicted: 54.82050277977565\t expected: 56.7\n",
"input: 2.16\t predicted: 73.66830942186188\t expected: 75.5\n"
]
}
],
"source": [
"X_test = X[:5,:]\n",
"y_test = y[:5,:]\n",
"output = model.predict(X_test)\n",
"\n",
"for i in range(5):\n",
" print(\"input: {}\\t predicted: {}\\t expected: {}\".format(X_test[i,0], output[i,0], y_test[i,0]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Sprawdzenie jakości modelu - metryki: $R^2$ i $MSE$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Istnieją 3 metryki, które określają jak dobry jest nasz model:\n",
" * $R^2$: [Współczynnik determinacji](https://pl.wikipedia.org/wiki/Wsp%C3%B3%C5%82czynnik_determinacji)\n",
" * $MSE$: [błąd średnio-kwadratowy](https://pl.wikipedia.org/wiki/B%C5%82%C4%85d_%C5%9Bredniokwadratowy) \n",
" * $RMSE = \\sqrt{MSE}$"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"R^2: 0.5955222955385271\n",
"Root Mean Squared Error: 5.682032704570357\n"
]
}
],
"source": [
"from sklearn.metrics import mean_squared_error\n",
"\n",
"print(\"R^2: {}\".format(model.score(X, y)))\n",
"rmse = np.sqrt(mean_squared_error(y, model.predict(X)))\n",
"print(\"Root Mean Squared Error: {}\".format(rmse))"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"R^2: 0.6742169953190864\n",
"Root Mean Squared Error: 4.874062378427941\n"
]
}
],
"source": [
"# Import necessary modules\n",
"from sklearn.linear_model import LinearRegression\n",
"from sklearn.metrics import mean_squared_error\n",
"from sklearn.model_selection import train_test_split\n",
"\n",
"# Create training and test sets\n",
"X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.30, random_state=42)\n",
"\n",
"# Create the regressor: reg_all\n",
"reg_all = LinearRegression()\n",
"\n",
"# Fit the regressor to the training data\n",
"reg_all.fit(X_train, y_train)\n",
"\n",
"# Predict on the test data: y_pred\n",
"y_pred = reg_all.predict(X_test)\n",
"\n",
"# Compute and print R^2 and RMSE\n",
"print(\"R^2: {}\".format(reg_all.score(X_test, y_test)))\n",
"rmse = np.sqrt(mean_squared_error(y_test, y_pred))\n",
"print(\"Root Mean Squared Error: {}\".format(rmse))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Regresja wielu zmiennych"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Model regresji liniowej wielu zmiennych nie różni się istotnie od modelu jednej zmiennej. Np. chcąc zbudować model oparty o dwie kolumny: `fertility` i `gdp` wystarczy zmienić X (cechy wejściowe):"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(175,)\n",
"(175, 1)\n"
]
},
{
"data": {
"text/plain": [
"0.6566838211706549"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X = df[['fertility', 'gdp']]\n",
"print(df['fertility'].shape)\n",
"print(df[['fertility']].shape)\n",
"\n",
"model_mv = LinearRegression()\n",
"model_mv.fit(X, y)\n",
"\n",
"model_mv.score(X, y)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**zad. 6** Która kombinacja dwóch kolumn daje najlepszy wynik w metryce $R^2$? Tak jak poprzednio, próbujemy przewidzieć zawartosć kolumny `life_expectancy`.\n",
"\n",
"Uwaga: Należy wyłączyć kolumnę `life_expectancy` spośród szukanych."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**zad. 7** \n",
" * Zbuduj model regresji liniowej, która oszacuje wartność kolumny `life_expectancy` na podstawie pozostałych kolumn.\n",
" * Wyświetl współczynniki modelu? Dla jakich cech współczynniki modelu są bliskie 0? Dlaczego?\n",
" * Oblicz wartości obu metryk na zbiorze trenującym.\n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**zad. 6**\n",
"Wykonaj jedno z zadań 6.1 lub 6.2."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**zad. 6.1** Zaimplementuj metrykę $R^2$ jako fukcję `r2` (szablon poniżej). Fukcja `r2` przyjmuje dwa parametry typu *list* i ma zwrócić wartość metryki $R^2$."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Real R2: 0.6566838211706549\n",
"Calculated: None\n"
]
}
],
"source": [
"def r2(expected, predicted):\n",
" \"\"\"\n",
" argumenty:\n",
" expected (type: list): poprawne wartości\n",
" predicted (type: list): oszacowanie z modelu\n",
" \"\"\"\n",
" pass\n",
"\n",
"y = df['life_expectancy'].values\n",
"X = df[['fertility', 'gdp']].values\n",
"\n",
"test_model = LinearRegression()\n",
"test_model.fit(X, y)\n",
"\n",
"print(\"Real R2:\", test_model.score(X, y))\n",
"\n",
"predicted = list(test_model.predict(X))\n",
"expected = list(y)\n",
"\n",
"print(\"Calculated:\", r2(expected, predicted))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" Zaimplementuj metrykę $RMSE$ jako fukcję rmse (szablon poniżej). Fukcja rmse przyjmuje dwa parametry typu list i ma zwrócić wartość metryki $RMSE$ ."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Real R2: 5.234841906276239\n",
"Calculated: None\n"
]
}
],
"source": [
"def rmse(expected, predicted):\n",
" \"\"\"\n",
" argumenty:\n",
" expected (type: list): poprawne wartości\n",
" predicted (type: list): oszacowanie z modelu\n",
" \"\"\"\n",
" pass\n",
"\n",
"y = df['life_expectancy'].values\n",
"X = df[['fertility', 'gdp']].values\n",
"\n",
"test_model = LinearRegression()\n",
"test_model.fit(X, y)\n",
"\n",
"print(\"Real R2:\", np.sqrt(mean_squared_error(y, test_model.predict(X))))\n",
"\n",
"predicted = list(test_model.predict(X))\n",
"expected = list(y)\n",
"\n",
"print(\"Calculated:\", r2(expected, predicted))"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.8.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}