<jupyter_start><jupyter_text>Introducció a la base de dades L'Orient Mitjà es una de les zones del món on la importació de cotxes es molt comú, i constitueix molts països com per exemple: <NAME>, Qatar, Oman, etc...  En aquest Notebook analitzarem les diferències entre els diferents països i intentarem predir el preu que tindrà un cotxe en un determinat pais, ja que la divisa en cada pais es diferent, i el valor no es el mateix.Aquesta base de dades conté informació sobre diferents cotxes i les seves especificacions tecniques, com per exemple: la amplada, llargada, cilindrada, tipus de benzina, la velocitat maxima, etc... Doncs a partir d'aquestes caracteristiques es podria arribar a predir el preu o aproximar-nos molt.  Llibreries Les llibreries de Python que utilitzarem per realitzar l'analisi son les seguents:  Pandas: molt util per estructures de dades en forma de taula, similar a excel  Numpy: operacions molt eficients amb grans estructures de dades, com matrius o arrays  Matplotlib: ens permet generar grafiques de tot tipus  Scipy: similar a numpy, incorpora moltes funcions matematiques  Seaborn: utilitza com a base matplotlib, i genera grafiques més complexes i amb més estils  Sklearn: incorpora tots els models d'aprenentatge computacional que utilitzarem per predir, a més de funcions      per analitzar cada model<jupyter_code>import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import scipy
import seaborn as sns
from sklearn.preprocessing import MinMaxScaler
from sklearn.tree import DecisionTreeRegressor
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import RandomizedSearchCV, GridSearchCV, train_test_split
from sklearn.metrics import mean_squared_error
import math
from prettytable import PrettyTable<jupyter_output><empty_output><jupyter_text>Exploració de les dades Abans de començar a implementar els models predictius, hem d'analitzar una mica la base de dades que tenim per tal de eliminardades que no serveixin, o per determinar els millors atributs que ens maximitzaran el rendiment del model.<jupyter_code>dataset = pd.read_csv("BD/dataframe_YesIndex_YesHeader_C.csv")
dataset.head(5)<jupyter_output><empty_output><jupyter_text>Com es pot veure, la primera columna de la base de dades es una copia del index, i per tant, la eliminarem. També comprovarem que no tenim files duplicades.<jupyter_code>dataset = dataset.drop(columns=["Unnamed: 0"])
dataset[dataset.duplicated(keep=False)]<jupyter_output><empty_output><jupyter_text>La funció duplicated() ens ha donat 568 files repetides, per tant, hem d'eliminar algunes per deixar una unica fila.<jupyter_code>dataset = dataset.drop_duplicates()
dataset[dataset.duplicated(keep=False)]<jupyter_output><empty_output><jupyter_text>També hem de mirar que no existeixin valors NaN a la base de dades, i en cas afirmatiu, eliminar aquestes files.<jupyter_code>dataset.isnull().values.any()<jupyter_output><empty_output><jupyter_text>No hi han valors NaN.  Ara, veurem si un cotxe amb les mateixes caracteristiques existeix en diferents paisos. Això ens donarà pasa analitzar en que paisos es més rentable comprar el mateix cotxe.<jupyter_code>dataset[dataset["name"] == "Mitsubishi Attrage 2021 1.2 GLX (Base)"]<jupyter_output><empty_output><jupyter_text>Amb l'exemple del cotxe que hem buscat a la base de dades, el Mitsubishi Attrage 2021 1.2 GLX, trobem que existeix en 3 paisos diferents, amb les mateixes caracteristiques però amb preus diferents segons el pais.  Anem ara a explorar una mica més a fons cadascun dels atributs de la nostra base de dades. Primer mostrem una gràfica on es pot veure la diferencia de preus que hi ha entre cada pais.<jupyter_code>fig, ax = plt.subplots(figsize=(10,10))
ax = sns.barplot(x="Country", y="price", data=dataset[["price","Country"]],palette='pastel')
plt.hist(dataset['Country'],rwidth=0.7)<jupyter_output><empty_output><jupyter_text>Es pot veure que els ultims paisos o tenen cotxes que costen pocs diners, o la moneda d'aquells paisos es d'un valor baix en comparació amb els altres. També es pot veure que al ultim pais hi ha més cotxes que als altres.  Ara veurem els atributs de la BD:<jupyter_code>print("Els atributs de la nostra BD: ", np.array(dataset.columns))<jupyter_output>Els atributs de la nostra BD:  ['Engine Capacity' 'Cylinders' 'Drive Type' 'Fuel Tank Capacity'
 'Fuel Economy' 'Fuel Type' 'Horsepower' 'Torque' 'Transmission'
 'Top Speed' 'Seating Capacity' 'Acceleration' 'Length' 'Width' 'Height'
 'Wheelbase' 'Trunk Capacity' 'name' 'price' 'currency' 'Country']<jupyter_text>On 'price' es el atribut target, objectiu o atribut a predir. Escollim el preu com l'objectiu perque volem predir el preu d'un cotxe en diferents paisos per veure en quin sortirà més rentable comprar el cotxe.  Abans de descriure amb més detall els atributs, generarem una matriu de correlació entre els diferents atributs per tal de determinar els que tinguin major correlació amb el preu, i els altres els eliminarem. Després, dibuixarem un mapa de calor per veure millor les correlacions.<jupyter_code>fig, ax = plt.subplots(figsize=(16,16))
cmap = sns.color_palette("Blues", as_cmap=True)
sns.heatmap(dataset.corr(), ax=ax, vmin=0, cmap=cmap, vmax=1, center=0,
            square=True, linewidths=.5, cbar_kws={"shrink": .5}, annot=True)<jupyter_output><empty_output><jupyter_text>Segons podem veure al mapa de calor, els atributs que tenen una forta correlació amb el preu son la cilindrada, la capacitatdel motor, els caballs, la torsió i la velocitat màxima a la que pot arribar un cotxe. Ens quedarem amb aquests 5 atributs,que ja son suficients per la recta de regressió i tenen una correlació acceptable amb el preu i descartarem els altres.<jupyter_code>dataset = dataset[["Torque","Cylinders","Horsepower","Top Speed","Engine Capacity","price"]]
dataset.describe()<jupyter_output><empty_output><jupyter_text>La taula del resum dels 5 atributs que hem escollit ens mostra un minim i un maxim molt diferent entre cada atribut. Això potarribar a donar problemes quan entrenem els models d'aprenentatge, i per tant, haurem de normalitzar/escalar les dades pertenir-les totes amb els mateixos rangs.  Anem a veure una mica com estan distribuides les dades en cada atribut escollit sobre el preu.<jupyter_code>dataset = dataset.values
for i in range(1,6):
    ax = plt.subplot(2,3,i)
    ax.scatter(dataset[:,i-1], dataset[:,-1])<jupyter_output><empty_output><jupyter_text>Es pot veure com les dades no estan molt ben distribuides, i per tant, la predicció no serà tan bona com podriem esperar.  Preparació de les dades Abans de començar a crear i entrenar els diferents models que utilitzarem per predir el preu d'un cotxe, primer hem de preparar les dades. Primer escalarem les dades per la diferencia d'escala que tenen (ho hem vist a l'apartat d'anàlisi de les dades), i després hem de dividir el conjunt de dades en un conjunt de train i un conjunt de test.<jupyter_code>scaler = MinMaxScaler()
scaler.fit(dataset)
dataset = scaler.transform(dataset)

x = dataset[:,:5]
y = dataset[:,-1]

print("Valor minim: " + str(np.amin(dataset)) + " - Valor maxim: " + str(np.amax(dataset)))<jupyter_output>Valor minim: 0.0 - Valor maxim: 1.0<jupyter_text>Ara que totes les dades les tenim escalades amb el mateix rang de valors, anem a dividir el conjunt de dades. Dividirem el conjunt en un 80% de les dades per el train, i un 20% per validar cada un dels models.<jupyter_code>x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=1234)<jupyter_output><empty_output><jupyter_text>Creació dels models Per aquest problema implementarem 3 models diferents amb els parametres  per defecte  i els compararem per veure quin dona millors resultats.  Multivariable Linear Regressor Primer creem el model:<jupyter_code>linear_reg = LinearRegression()<jupyter_output><empty_output><jupyter_text>Després entrenem el model amb les dades de train:<jupyter_code>linear_reg.fit(x_train, y_train)<jupyter_output><empty_output><jupyter_text>Ara fem una predicció i evaluem el rendiment:<jupyter_code>prediccions = linear_reg.predict(x_test)
print("Coeficient de determinació del model: " + str(linear_reg.score(x_test, y_test)))
print("Error quadratic mitja: " + str(mean_squared_error(y_test, prediccions)))<jupyter_output>Coeficient de determinació del model: 0.2109561848947339
Error quadratic mitja: 0.0013321540248318617<jupyter_text>Com podem veure, el coeficient de determinació R2 ens dona 0.21, un valor bastant allunyat del millor possible, que seria 1. Per tant, la predicció no ha sigut molt bona.  Si visualitzem la predicció...<jupyter_code>plt.figure()
ax = plt.scatter(x_test[:,0], y_test)
plt.plot(x_test, prediccions, 'r')<jupyter_output><empty_output><jupyter_text>Com podem veure, la regressió ha pogut predir molts punts, però altres estan fora d'aquesta funció que ha definit la regressió lineal multivariable.  Decision Tree Regressor Primer creem el model:<jupyter_code>decision_tree = DecisionTreeRegressor()<jupyter_output><empty_output><jupyter_text>Després l'entrenem amb les dades de train:<jupyter_code>decision_tree.fit(x_train, y_train)<jupyter_output><empty_output><jupyter_text>Ara veurem una mica les caracteristiques d'aquest arbre que s'ens ha generat:<jupyter_code>print("Profunditat del arbre: " + str(decision_tree.get_depth()))
print("Numero de fulles del arbre: " + str(decision_tree.get_n_leaves()))<jupyter_output>Profunditat del arbre: 24
Numero de fulles del arbre: 809<jupyter_text>L'arbre que ha generat es molt gran, ja que té 809 nodes fulla i una profunditat de 24, però amb aquesta grandaria esperem que la regressió surti millor que la anterior.  Anem a fer una predicció i evaluar el rendiment:<jupyter_code>prediccions_arbre = decision_tree.predict(x_test)
print("Coeficient de determinació del model: " + str(decision_tree.score(x_test, y_test)))
print("Error quadratic mitja: " + str(mean_squared_error(y_test, prediccions_arbre)))<jupyter_output>Coeficient de determinació del model: 0.3153572203769254
Error quadratic mitja: 0.0011558922546338899<jupyter_text>L'arbre ha predit una mica millor, ha pujat el coeficient de determinació i l'error quadratic mitjà ha baixat una mica també.  Si visualitzem la predicció...<jupyter_code>ax = plt.scatter(x_test[:,0], y_test)
plt.plot(x_test, prediccions_arbre, 'r')<jupyter_output><empty_output><jupyter_text>Es pot veure com l'arbre de decisió ha intentat predir inclús els punts que s'allunyen dels altres. Aquesta diferencia respecte amb la regressió lineal multivariable es deguda al tamany del arbre, que també ha generat casos pels punts més allunyats dels altres.  Random Forest Regressor Primer creem el model:<jupyter_code>random_tree = RandomForestRegressor()<jupyter_output><empty_output><jupyter_text>Després l'entrenem amb les dades de train:<jupyter_code>random_tree.fit(x_train, y_train)<jupyter_output><empty_output><jupyter_text>Ara fem una predicció i evaluem el rendiment:<jupyter_code>prediccions_randtree = random_tree.predict(x_test)
print("Coeficient de determinació del model: " + str(random_tree.score(x_test, y_test)))
print("Error quadratic mitja: " + str(mean_squared_error(y_test, prediccions_randtree) ))<jupyter_output>Coeficient de determinació del model: 0.3801985054468796
Error quadratic mitja: 0.001046419780193816<jupyter_text>Com podem veure, el Random Forest ha pogut millorar encara més el rendiment dels altres dos models. El coeficient de determinació l'ha augmentat en un 10%, i també ha baixat una mica més el error quadratic mitjà.  Si visualitzem la predicció un cop més...<jupyter_code>ax = plt.scatter(x_test[:,0], y_test)
plt.plot(x_test, prediccions_randtree, 'r')<jupyter_output><empty_output><jupyter_text>El Random Forest ha fet la regressió de forma similar al Decision Tree, ja que es pot veure una funció de regressió molt semblant a la anterior.    En aquesta taula deixem un resum del rendiment dels 3 models:<jupyter_code>table = PrettyTable()
table.field_names = ["Model", "R2 score", "MSE"]
table.add_row(["Linear Regressor",linear_reg.score(x_test, y_test),mean_squared_error(y_test, prediccions)])
table.add_row(["Decision Tree",decision_tree.score(x_test, y_test),mean_squared_error(y_test, prediccions_arbre)])
table.add_row(["Random Forest",random_tree.score(x_test, y_test),mean_squared_error(y_test, prediccions_randtree)])
print(table)<jupyter_output>+------------------+--------------------+-----------------------+
|      Model       |      R2 score      |          MSE          |
+------------------+--------------------+-----------------------+
| Linear Regressor | 0.2109561848947339 | 0.0013321540248318617 |
|  Decision Tree   | 0.3153572203769254 | 0.0011558922546338899 |
|  Random Forest   | 0.3801985054468796 |  0.001046419780193816 |
+------------------+--------------------+-----------------------+<jupyter_text>Cerca dels millors hiperparametres Un cop hem vist el rendiment de cadascun dels 3 models amb els parametres per defecte, ara anem a buscar quins son els millors parametres que maximitzen el rendiment de cadascun dels 3 models, i així podrem escollir el que presenti major rendiment amb els seus parametres òptims.  Per trobat els hiperparametres òptims podem utilitzar moltes funcions destinades a aquesta tasca, però utilitzarem 2 que son molt conegudes i molt simples d'utilitzar: GridSearchCV i RandomizedSearchCV.  Per poder utilitzarles, primer hem de crear un diccionari on ficarem els parametres que volem probar i els diferents valors, i la funció trobara la combinació de valors que doni el score més alt.<jupyter_code>param_linearreg = {'fit_intercept': [True, False]}
param_decisiontree = {"splitter":["best","random"],
            "max_depth" : [1,3,5,10,15,25,30,50],
            "min_weight_fraction_leaf":[0.0,0.1,0.2,0.3,0.4,0.5],
           "max_features":["auto","log2","sqrt",None],
           "max_leaf_nodes":[None,10,20,50,80,100,250,500] }
param_randomforest = {'n_estimators': [int(x) for x in np.linspace(start = 200, stop = 2000, num = 10)],
               'max_features': ['auto', 'sqrt'],
               'max_depth': [int(x) for x in np.linspace(10, 110, num = 11)],
               'min_samples_split': [2, 5, 10],
               'min_samples_leaf': [1, 2, 4],
               'bootstrap': [True, False]}<jupyter_output><empty_output><jupyter_text>Els diferents parametres que volem probar, per exemple, serien: en els arbres la profunditat màxima, el numero màxim de fulles, si volem aplicar la tecnica del bootstrapping, etc...  Per la regressió lineal nomes podem probar amb el fit_intercept. Doncs, anem ara a buscar els millors hiperparametres  Multivariable Linear Regression<jupyter_code>grid_linearreg = GridSearchCV(LinearRegression(), param_linearreg, cv=3, verbose=2, n_jobs=-1)
grid_linearreg.fit(x_train,y_train)
print("Els millors parametres: " + str(grid_linearreg.best_params_))
print("La millor score: " + str(grid_linearreg.best_score_))
print("Error quadratic mitja: " + str(math.sqrt(mean_squared_error(y_test,grid_linearreg.predict(x_test)))))<jupyter_output>Fitting 3 folds for each of 2 candidates, totalling 6 fits
Els millors parametres: {'fit_intercept': False}
La millor score: 0.24839482750864475
Error quadratic mitja: 0.0364908621770943<jupyter_text>Decision Tree<jupyter_code>grid_decisiontree = GridSearchCV(DecisionTreeRegressor(), param_decisiontree, cv=3, verbose=2, n_jobs=-1)
grid_decisiontree.fit(x_train,y_train)
print("Els millors parametres: " + str(grid_decisiontree.best_params_))
print("La millor score: " + str(grid_decisiontree.best_score_))
print("Error quadratic mitja: " + str(math.sqrt(mean_squared_error(y_test,grid_decisiontree.predict(x_test)))))<jupyter_output>Fitting 3 folds for each of 3072 candidates, totalling 9216 fits
Els millors parametres: {'max_depth': 5, 'max_features': 'sqrt', 'max_leaf_nodes': 80, 'min_weight_fraction_leaf': 0.0, 'splitter': 'best'}
La millor score: 0.6223633968905974
Error quadratic mitja: 0.03387168506701567<jupyter_text>Random Forest<jupyter_code>grid_randomforest = RandomizedSearchCV(RandomForestRegressor(), param_randomforest, 
                              n_iter = 100, cv = 3, verbose=2, random_state=42, n_jobs = -1)
grid_randomforest.fit(x_train,y_train)
print("Els millors parametres: " + str(grid_randomforest.best_params_))
print("La millor score: " + str(grid_randomforest.best_score_))
print("Error quadratic mitja: " + str(math.sqrt(mean_squared_error(y_test,grid_randomforest.predict(x_test)))))<jupyter_output>Fitting 3 folds for each of 100 candidates, totalling 300 fits
Els millors parametres: {'n_estimators': 2000, 'min_samples_split': 10, 'min_samples_leaf': 1, 'max_features': 'sqrt', 'max_depth': 20, 'bootstrap': False}
La millor score: 0.5669421845492488
Error quadratic mitja: 0.03388837194569412<jupyter_text>La cerca per la regressió lineal i l'arbre de decisió la hem implementat amb GridSearchCV perque la cerca no ha trigat molt temps, però amb el Random Forest hem hagut d'utilitzar RandomizedSearchCV per reduir cost i temps, ja que el RandomizedSearchCV s' estalvia fer algunes combinacions inutils (inicia la cerca dels parametres de forma aleatoria i para quan ha trobat els millors). En aquesta taula farem un resum dels resultats obtinguts, i a continuació, farem un petit anàlisi.<jupyter_code>table = PrettyTable()
table.field_names = ["Model", "R2 score", "MSE"]
table.add_row(["Linear Regressor",grid_linearreg.best_score_, mean_squared_error(y_test,grid_linearreg.predict(x_test))])
table.add_row(["Decision Tree",grid_decisiontree.best_score_, mean_squared_error(y_test,grid_decisiontree.predict(x_test))])
table.add_row(["Random Forest",grid_randomforest.best_score_, mean_squared_error(y_test,grid_randomforest.predict(x_test))])
print(table)<jupyter_output>+------------------+---------------------+-----------------------+
|      Model       |       R2 score      |          MSE          |
+------------------+---------------------+-----------------------+
| Linear Regressor | 0.24839482750864475 | 0.0013315830224276915 |
|  Decision Tree   |  0.6223633968905974 | 0.0011472910492790922 |
|  Random Forest   |  0.5669421845492488 | 0.0011484217531297083 |
+------------------+---------------------+-----------------------+
{'fraction_non_alphanumeric': 0.10421890330317235, 'fraction_numerical': 0.03466695737490461, 'mean_word_length': 5.335290055248619, 'pattern_counts': {'":': 5, '<': 119, '<?xml version=': 0, '>': 119, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '32', 'dir': 'jupyter-structured-clean-dedup', 'id': '556837', 'license': 'Apache-2.0', 'n_tokens_mistral': 6722, 'n_tokens_neox': 6323, 'n_words': 1896, 'path': 'cars-in-the-middle-east.ipynb', 'repo_name': 'juankaUAB/cas_kaggle', 'size': '18352'}