Chargement des données avec Pandas¶
import pandas as pd
df = pd.read_csv('./prices_train.csv')
SalePrice = df['SalePrice']
df = df.drop(['Id', 'SalePrice'], axis=1)
len( df.columns )
Est-ce que les variables sont quantitatives ou bien qualitatives ?¶
On cherche le nombre de valeurs uniques par variables
class_size = [ (c, len( df[c].unique() ) ) for c in df.columns ]
class_size.sort( key=lambda x:x[1] )
print( class_size )
On peut regarder si ce sont des valeurs numériques, ou simplement des catégories:
for classname, k in class_size[:50]:
print( '%s (%i): %s' % (classname , k, ' - '.join( [str(s) for s in df[classname].unique() ])) )
Variable continue: Graphe Price vs Living Area ?¶
La corrélation la plus intuitive semble être avec la surface de la zone habitable ?
plt.plot( df['GrLivArea'], SalePrice, '.', alpha=.5);
plt.xlabel('Living Area'); plt.ylabel('Sale Price');
Distribution des prix de vente¶
xFRep = sorted( SalePrice )
yFRep = np.linspace( 0, 1, len(SalePrice) )
plt.plot( xFRep, yFRep );
Graphe écart-type vs nombre de valeurs, pour chaque catégorie¶
On peut essayer de faire un graphique écart-type VS nombre d'individus pour chaque sous groupe correspondant à une propriété, par exemple 'Pool Area' = 576
allN, allstd, allval = [], [], []
for col in df.columns:
values = df[col].unique()
if len(values)>30: continue
for val in values:
if type(val) == float and np.isnan( val ): continue
prices = SalePrice[ df[col]==val ]
N = prices.shape[0]
if N < 10: continue
std = prices.std()
allN.append( N )
allstd.append( std )
allval.append( (col, val) )
plt.plot(allN, allstd, '.')
plt.plot([0, maxN], [maxstd, maxstd], 'r-')
plt.xlabel('N'); plt.ylabel('std');
la ligne rouge est l'écart-type de l'ensemble des données.
La dispersion de l'écart-type augmente quand la taille de l'échantillon diminue. On peut comparer cette dispersion avec une distribution aléatoire, soit de façon théorique, ou bien en tirant au hazard des valeurs dans les données
Dispersion de l'écart-type¶
maxstd = SalePrice.std()
maxN = df.shape[0]
De façon experimentale :¶
n_span = range( 20, maxN, int(maxN/30) )
n_random, std_random = [], []
for N in n_span:
echantillon = []
for k in range( 200 ):
V = np.random.choice(SalePrice, size=int(N), replace=True)
echantillon.append( V.std() )
n_random.append( N )
std_random.append( np.std( echantillon ) )
std_random = np.array(std_random)
De façon théorique :¶
# https://fr.wikipedia.org/wiki/Estimateur_(statistique)#Estimateur_de_la_variance_de_Y
# Pour une distribution gaussienne
# Avec ou sans remise
n_span_theo = np.linspace(20, maxN, 100)
std_std_theo_sans = np.sqrt(1/n_span_theo )*maxstd * (maxN - n_span_theo)/(maxN-1)
std_std_theo_avec = np.sqrt(1/n_span_theo )*maxstd
plt.plot(allN, allstd, '.')
plt.plot([0, maxN], [maxstd, maxstd], 'r-')
plt.plot(n_random, maxstd - 3*std_random,'--r', alpha=.6, label='exp, avec remise')
plt.plot(n_span_theo, maxstd - 3*std_std_theo_avec,'-r', alpha=.6, label='theo, avec remise')
plt.plot(n_span_theo, maxstd - 3*std_std_theo_sans,':r', alpha=.6, label='theo, sans remise')
plt.xlabel('N'); plt.ylabel('std');
plt.ylim([0, maxstd*1.101]); plt.legend();
La courbe théorique correspond bien à la courbe issus des données 'avec remise'.
Rq: Sans remise la courbe théorique devient égale à std_max, ce qui se comprend, c'est le même échantillon, mais pour la suite, ce n'est pas le mieux (norme) .. moins sensible aux valeurs abérrantes...
Les meillieurs prédicteurs sont ceux, à priori, en bas au millieu: un faible écart-type avec un nombre raisonable de valeurs...
on peut faire une transformation des coordonnées en normant l'écart-type par la valeur théorique
allN = np.array(allN)
allstd = np.array(allstd)
std_std_theo = np.sqrt(1/allN )*maxstd # * (maxN - allN)/(maxN-1) # avec/sans remise
allstd_star = (maxstd - allstd)/std_std_theo
plt.plot(allN, allstd_star, '.')
plt.xlabel('N'); plt.ylabel('(std_max - std)/std_theo');
Les prédicteurs d'intérets sont maintenant en haut, au millieu.
Et si on regarde les variables en tête :
val_sorted = sorted( zip( allval, allstd_star ), key=lambda x:x[1], reverse=True )
print( val_sorted[:10] )
Visualisation des catégories :¶
plt.plot( df['GrLivArea'], SalePrice, '.', alpha=.1)
k = 0
col, val = val_sorted[k][0]
plt.plot( df[ df[col]!=val ]['GrLivArea'], SalePrice[ df[col]!=val ], '.', alpha=.7);
plt.title('en dehors');
plt.figure();
plt.plot( df['GrLivArea'], SalePrice, '.', alpha=.1)
plt.plot( df[ df[col]==val ]['GrLivArea'], SalePrice[ df[col]==val ], '.', alpha=.7);
plt.title('dedans')
print( col, val )
Remarque: si il y a une corrélation linéaire, typiquement avec la surface, alors l'écart-type va être important. Il y a un problème de regression (linéaire), en plus du choix des prédicteurs donnant la variance la plus faible.
Aussi un indicateur seul n'est pas suffisant... on peut faire des combinaissons, c'est-à-dire construire un arbre... prendre le meilieur et refaire le raisonement sur les deux sous ensembles formés.
On peut aussi rechercher le critère qui minimise la variance des deux sous groupes crées (== et !=) On retrouve la formule (9.13) page 307 du livre (ESL) :
std_12, colval = [], []
for col in df.columns:
values = df[col].unique()
if len(values)>30: continue
for val in values:
if type(val) == float and np.isnan( val ): continue
prices_in = SalePrice[ df[col]==val ]
prices_out = SalePrice[ df[col]!=val ]
N_in = prices_in.shape[0]
N_out = prices_out.shape[0]
if N_in < 10 or N_out<10 : continue
std_12.append( prices_in.std()*N_in + prices_out.std()*N_out )
colval.append( (col, val) )
val12_sorted = sorted( zip( colval, std_12 ), key=lambda x:x[1], reverse=False )
print( val12_sorted[:10] )
plt.plot( df['GrLivArea'], SalePrice, '.', alpha=.1)
k = 1
col, val = val12_sorted[k][0]
plt.plot( df[ df[col]!=val ]['GrLivArea'], SalePrice[ df[col]!=val ], '.', alpha=.7);
plt.title('en dehors')
plt.figure();
plt.title('dedans')
plt.plot( df['GrLivArea'], SalePrice, '.', alpha=.1)
plt.plot( df[ df[col]==val ]['GrLivArea'], SalePrice[ df[col]==val ], '.', alpha=.7);
print( col, val )
On retrouve grosso-modo les mêmes: ('ExterQual', 'TA'), ('KitchenQual', 'TA'), ('FullBath', 1)...
Algorithme de construction d'arbre¶
Besoin pour Sciki d'encoder les features... https://datascience.stackexchange.com/questions/5226/strings-as-features-in-decision-tree-random-forest
à suivre...
from sklearn import preprocessing
from sklearn.pipeline import Pipeline
im = preprocessing.Imputer(strategy='mean', axis=1)
le = preprocessing.LabelEncoder()
enc = preprocessing.OneHotEncoder()
process = []
process.append( ('missing' , preprocessing.Imputer(strategy='mean', axis=1)) )
process.append( ('label' , preprocessing.LabelEncoder() ) )
pipeline = Pipeline(process)
im = preprocessing.Imputer(strategy='mean', axis=1)