====== Eksploracja Danych: Laboratorium 3 ======
Tematem będzie regresja z użyciem języka Python
:!: Konwersja notatnika Colab na PDF [[https://github.com/pszwed-ai/lab-eksploracja-danych/blob/main/ConvertNotebookToPDF.ipynb]]
===== Zbiór danych =====
*Zbiór danych dotyczących sprzedaży domów dostępny na [[https://www.kaggle.com/harlfoxem/housesalesprediction|Kaggle]].
*Jego lokalna kopia może zostać pobrana z {{ :med:kc_house_data.csv.zip |kc_house_data.csv.zip}}.
*Można też wykonać następujący kod w notatniku Colab
!wget https://dysk.agh.edu.pl/s/7mHMk2A8GT76Swk/download -O kc_house_data.csv.zip
!unzip kc_house_data.csv.zip
===== 3.1 Wczytaj do Pandas Data Frame =====
import pandas as pd
import numpy as np
# Wczytaj do DataFrame
df = pd.read_csv('kc_house_data.csv',parse_dates=['date'])
df.head()
===== 3.2 Wyświetl informacje o zbiorze danych =====
=== Ile jest kolumn (atrybutów). Ile jest obserwacji (instancji, rekordów danych)? ===
print(len(df))
print(df.shape)
df.info()
print(df.columns)
=== Zakresy i średnie wartości ===
# informacje o danych
print('--- Min values ---')
print(df.min())
print('--- Max values ---')
print(df.max())
print('--- Mean values ---')
print(df.mean())
===Histogramy dla poszczególnych atrybutów===
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = (15,15)
df.hist(bins=50)
===== 3.3 Usuwanie i konwersja danych =====
Zazwyczaj część danych z zewnętrznego źródła powinna zostać usunięta. Na pewno należy usunąć ''id'' (ogólnie klucze z bazy danych).
Co jeszcze? Może kod pocztowy?
Datę konwertujemy na wartość numeryczną.
df2 = df.drop(columns=['id',???? ])
df2['date']=pd.to_numeric(df2['date'])
df2.head()
===== 3.4 Regresja =====
==== 3.4.1 Przetwarzamy cały zbiór danych ====
import sklearn.linear_model
regr = sklearn.linear_model.LinearRegression()
# y to wektor wartości wyjściowych
y =df2['price'].to_numpy()
# nie chcemy używać ceny w regresji bo otrzymalibyśmy równanie price=1.0*price
df2_noprice=df2.drop(columns=['price'])
X=df2_noprice.to_numpy()
regr.fit(X, y)
regr.score(X,y)
==== 3.4.2 Obliczamy metryki ====
Metryki są obliczane przez porównanie wartości przewidywanych (zwróconych przez model) ''y_pred'' i znanych //ground truth// ''y''
import sklearn.metrics
y_pred = regr.predict(X)
scores={'r2':sklearn.metrics.r2_score,
'mse':sklearn.metrics.mean_squared_error,
'rmse':lambda y_true,y_pred : np.sqrt(sklearn.metrics.mean_squared_error(y_true,y_pred)),
'maxe':sklearn.metrics.max_error,
'med':sklearn.metrics.median_absolute_error,
'mae':sklearn.metrics.mean_absolute_error,
}
for k in scores:
r = scores[k](y,y_pred)
print(f'{k}:{r}')
==== 3.4.3 Train Test Split ====
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 123)
regr = sklearn.linear_model.LinearRegression()
regr.fit(X_train, y_train)
y_pred = regr.predict(X_test)
for k in scores:
r = scores[k](y_test,y_pred)
print(f'{k}:{r}')
Czy wyniki zależą od wielkości zbioru uczącego - pośrednio definiowanego przez parametr ''test_size''? Poeksperymentuj z różnymi wartościami.
Wypróbuj formułę:
''test_size = 1-k*len(df.columns)/len(df)'',
gdzie k=1,2,3,...10
Ile danych (w stosunku do liczby atrybutów) potrzeba do nauczenie modelu, aby uzyskać akceptowalne rezultaty?
** Przedstaw wyniki w formie tabelki **
==== 3.4.4 Wydruk równania regresji ====
Wydrukuj równanie regresji dla typowego modelu. Czy na podstawie wag można określić, które atrybuty mają mały/duży wpływ na wynik?
Jaką operację należałoby przeprowadzić, aby uzyskać wiarygodny wynik?
def print_formula(regr,labels,target):
print(f'{target} = ')
for i in range(len(regr.coef_)):
print(f'\t{regr.coef_[i]: .3g}\t* {labels[i]} +')
print(f'\t{regr.intercept_:.8}')
print_formula(regr,df2_noprice.columns,'price')
==== 3.4.5 k-fold CrossValidation ====
Wyznacz średnie wartości metryk otrzymanych podczas walidacji krzyżowej
from sklearn.model_selection import KFold
from sklearn.utils import Bunch
# słownik na składowanie wyników
b = Bunch()
for k in scores:
b[k]=[]
# b[k].append(0)
print(b)
# train_index, test_index to indeksy wierszy użytych jako zbiory treningowe i testowe
kf = KFold(n_splits=10)
for train_index, test_index in kf.split(X, y):
X_train,y_train = X[train_index],y[train_index]
X_test,y_test = X[test_index],y[test_index]
regr = sklearn.linear_model.LinearRegression()
regr.fit(X_train, y_train)
y_pred = regr.predict(X_test)
for k in scores:
r = scores[k](y_test,y_pred)
b[k].append(r)
for k in b:
oblicz i wypisz średnie wartości w listach b[k]
==== 3.4.6 Przetestuj Ridge i Lasso ====
Wpierw zmieńmy kod //split train test// na funkcję.
def train_and_test(X,y,regr=sklearn.linear_model.LinearRegression()):
# print(regr)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 123)
regr.fit(X_train, y_train)
y_pred = regr.predict(X_test)
for k in scores:
r = scores[k](y_test,y_pred)
print(f'{k}:{r}')
**Porównaj w tabelce wyniki **(zwłaszcza r2)** zwrócone przez**
- LinearRegression
- Ridge
- Lasso
===== 3.5 Analiza danych =====
==== 3.5.1 Czy atrybuty są skorelowane? ====
Obliczmy współczynniki korelacji Pearsona dla wszystkich atrybutów. Dane zostaną zebrane w postaci macierzy.
import scipy.stats
n = len(df2.columns)
rs = np.zeros((n,n))
# print(rs)
# print(df.iloc[:,0])
for i in range(n):
for j in range(n):
r,p = scipy.stats.pearsonr(df2.iloc[:,i],df2.iloc[:,j])
rs[i,j]= r
Można trochę to zoptymalizować, bo macierz jest symetryczna.
Wyświetlmy macierz w bardziej czytelnej postaci...
import itertools
def plot_matrix(cm, labels,
normalize=False,
title='',
cmap=plt.cm.Blues):
fig = plt.figure()
ax=fig.add_subplot(111)
ax.matshow(cm, interpolation='nearest', cmap=cmap)
plt.title(title)
# plt.colorbar()
tick_marks = np.arange(len(labels))
plt.xticks(tick_marks, labels, rotation=90)
ax.set_xticks(tick_marks)
ax.set_yticks(tick_marks)
ax.set_xticklabels(labels)
ax.set_yticklabels(['']+labels)
fmt = '.2f' #if normalize else 'd'
thresh = 0.5
for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
plt.text(j, i, format(cm[i, j], fmt),
horizontalalignment="center",
color="white" if cm[i, j] > thresh else "black")
plt.tight_layout()
plt.show()
plot_matrix(rs,labels=df2.columns)
==== 3.5.2 Przetestuj działanie dla podzbiorów atrybutów ====
* Wybierz atrybuty do usunięcia i sprawdź wyniki
* Poeksperymentuj z różnymi wariantami podzbiorów atrybutów
* Wybieraj słabo skorelowane z ceną lub mocno skorelowane ze sobą
* Porównaj LinearRegression, Ridge i Lasso
**Wyniki zbierz w tabelce**
df3 = df2_noprice.drop(columns = ['date',...])
X = df3.to_numpy()
regr = sklearn.linear_model.LinearRegression()
train_and_test(X,y,regr)
print_formula(regr,df3.columns,'price')
==== 3.5.3 A może transformacja danych? ====
Eksperymenty z przetranformowanymi danymi są elementami procesu nazywanego //data wrangling//
Zobacz co się stanie z ceną w wyniku zastąpienia wartości danych ich logarytmami?
plt.rcParams["figure.figsize"] = (10,10)
df2.hist('price',bins=50)
df2_log=pd.DataFrame(df2)
df2_log['logprice']=np.log(df2['price'])
df2_log.hist('logprice',bins=50)
Napiszemy funkcję, która
* obliczy logarytm y_train
* przeprowadzi regresję
* obliczy wartość przewidywaną y_pred i wyznaczy np.exp(y_pred)
* następnie wzynaczy metryki dla np.exp(y_pred)
def train_and_test_log(X,y,regr=sklearn.linear_model.LinearRegression()):
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 123)
regr.fit(X_train, ...)
y_pred = regr.predict(X_test)
y_pred=...
for k in scores:
r = scores[k](y_test,y_pred)
print(f'{k}:{r}')
df3=df2_noprice
X = df3.to_numpy()
regr = sklearn.linear_model.Ridge(solver='svd')
train_and_test_log(X,y,regr)
Jaki był rezultat?
==== 3.5.4 Normalizacja cech ====
Wypróbuj normalizację cech (sprowadzenie do średniej = 0 oraz podzielenie przez odchylenie standardowe)
from sklearn.preprocessing import StandardScaler
X=df2_noprice.to_numpy()
scaler = StandardScaler()
X_n=scaler.fit_transform(X)
Wykonaj testy używając X_n zamiast X. Czy poprawiły się wyniki? Wydrukuj równanie regresji. Które współczynniki mają teraz największy wpływ na cenę?
===== 3.6 Regresja wielomianowa =====
from sklearn.preprocessing import PolynomialFeatures
# df3 = df2_noprice.drop(columns = [....])
df3 = df2_noprice
X = df3.to_numpy()
poly = PolynomialFeatures(degree=2)
X=poly.fit_transform(X)
regr = sklearn.linear_model.Lasso()
train_and_test(X,y,regr)
# print_formula(regr,df3.columns,'price')
print(f'Liczba parametrów modelu: {len(regr.coef_)+1}')
Przetestuj LinearRegression, Ridge i Lasso dla rozszerzenia zbiorów cech do postaci wielomianów 2 i 3 stopnia.
**Wyniki zbierz w tabelce**
===== 3.7 Inne algorytmy regresji =====
W bibliotece scikit zdefiniowanych jest co najmniej kilkanaście algorytmów regresji...
**Zbierz wartości współczynika determinacji r2 w tabelce. Który algorytm jest najbardziej obiecujący?**
from sklearn.linear_model import ElasticNet, LassoLars, BayesianRidge, SGDRegressor, Perceptron
from sklearn.svm import SVR
from xgboost import XGBRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
df3 = df2_noprice
X = df3.to_numpy()
regressors = [ElasticNet(),
LassoLars(alpha=.1),
BayesianRidge(),
make_pipeline(StandardScaler(),SGDRegressor(max_iter=1000, tol=1e-3)),
Perceptron(),
SVR(kernel='poly',degree=2),
DecisionTreeRegressor(),
XGBRegressor(),
]
for regr in regressors:
print(f'------------ {regr.__class__.__name__} ---------------')
train_and_test(X,y,regr)