import pandas as pd
pd.set_option('display.max_columns', 150)
pd.set_option('display.max_row', 1000)
pd.set_option('display.max_colwidth', 50)
pd.set_option('display.float_format', lambda x: '%.2f' % x)
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('Solarize_Light2')
import seaborn as sns
import plotly_express as px
from plotly.offline import init_notebook_mode, iplot
import plotly.graph_objs as go
import plotly.plotly as py
from plotly import tools
import plotly.figure_factory as ff
init_notebook_mode(connected=True)
from warnings import filterwarnings
filterwarnings('ignore')
import os
os.chdir('D:\Data\Projects\Klassifikation\Predicting Household Poverty in Costa Rica')
train = pd.read_csv('train.csv')
train.shape
test = pd.read_csv('test.csv')
test.shape
train.shape, test.shape
test.Target = np.nan
df = train.append(test, ignore_index = True)
df.info()
Alle Daten sind int64 oder float 64 bis auf die Variablen dependency, edjefa, edjefe, idhogar und Id, die im Format object vorliegen
Dependency, Edjefa, Edjefe sollten in float64 umgewandelt werden. Vorher müssen strings ersetzt werden. mapping = { 'yes':1, 'no':0}. Mit replace werden alle yes und no im train und im test Datensatz ersetzt.
df.select_dtypes('object').columns
Finde nicht numerische Werte mit regular expression
for col in ['dependency', 'edjefa', 'edjefe']:
print(df[col].str.findall('[A-Za-z]').value_counts())
import re
df.edjefa.str.findall('(\D\D)').value_counts()
# Wert zuweisen und in float umwandeln
mapping = {'yes': 1, 'no': 0}
df.dependency = df.dependency.replace(mapping).astype(np.float64)
df.edjefa = df.edjefa.replace(mapping).astype(np.float64)
df.edjefe = df.edjefe.replace(mapping).astype(np.float64)
df[['dependency', 'edjefa', 'edjefe']].describe()
Alle werden true, bei denen im groupby nur ein Wert im Target vorkommt.
all_equal = train.groupby('idhogar')['Target'].apply(lambda x: x.nunique() == 1)
not_equal = all_equal[all_equal != True]
len(not_equal)
(all_equal == False).sum()
train[train['idhogar'] == not_equal.index[0]][['idhogar', 'parentesco1', 'Target']]
Vorgabe: Jeder Haushalt hat einen Kopf. Alle Mitglieder eines Haushaltes sollten den gleichen Poverty Level haben, wie der Kopf des Haushaltes.
Dies ist nicht so. Bei 15 Haushalten ist niemand als Kopf des Haushaltes identifiziert, bei 85 Haushalten sind die Labels der Mitglieder unterschiedlich. Die 15 Haushalte ohne Kopf können nicht verwendet werden und werden entfernt. Die Labels der Mitglieder, die vom Label des Kopfes abweichen, werden korrigiert.
households_leader = train.groupby('idhogar')['parentesco1'].sum()
(households_leader.values == 0).sum()
households_leader[households_leader == 0].index
households_no_head = train.loc[train.idhogar.isin(households_leader[households_leader == 0].index)]
households_no_head['idhogar'].nunique()
# Haushalte ohne leader und wo es verschiedene Targets gibt
no_head = households_no_head.groupby('idhogar')['Target'].apply(lambda x: x.nunique() == 1)
sum(no_head == False)
# Ersetze Targets von Mitglidern eines Haushaltes, die von Leader abweichen
for hh in not_equal.index:
target = train.loc[(train.idhogar == hh)&(train.parentesco1 == 1), ['Target']]
train.loc[train.idhogar == hh, 'Target']=target
all_equal = train.groupby('idhogar')['Target'].apply(lambda x: x.nunique() == 1)
(all_equal == False).sum()
missing = pd.DataFrame(df.isnull().sum()).rename(columns = {0: 'total'})
missing['percent'] = missing['total'] / len(df)*100
missing = missing[missing.total != 0]
missing.sort_values('percent', ascending = False)
df.v18q.value_counts()
Für die Anzahl an Tablets im Haushalt wurde ein nan eingetragen, wenn es kein Tablet im Haus gibt. Dies zeigt sich, wenn man die Spalte v18q, ein Boolean für Tablet im Haushalt, untersucht. Die Anzahl für 0 ist die gleiche wie die der fehlenden Werte in Anzahl der Tablets. Daher können diese mit 0 ersetzt werden.
df.v18q1 = df.loc[(df.v18q1).isnull(), 'v18q1']= 0
[x for x in df if x.startswith('tipo')]
own_variables = [x for x in df if x.startswith('tipo')]
# Plot of the home ownership variables for home missing rent payments
df.loc[df['v2a1'].isnull(), own_variables].sum().plot.bar(figsize = (10, 8), color = 'green', edgecolor = 'k', linewidth = 2);
plt.xticks([0, 1, 2, 3, 4], ['Owns and Paid Off', 'Owns and Paying', 'Rented', 'Precarious', 'Other'], rotation = 60)
plt.title('Kategorien mit fehlenden Werten für Mietzahlung', size = 18);
df.loc[df['v2a1'].isnull(), own_variables].sum()
Die Summe derer, die nicht gemietet sind, ist 24263. Damit können diese Werte mit 0 ersetzt werden.
df.v2a1 = df.v2a1.fillna(0)
Sind die fehlenden Werte für Schüler oder vielleicht Menschen, die nicht mehr in der Schule sind?
df.loc[df['rez_esc'].notnull()]['age'].describe()
df.loc[(df.rez_esc).isna() & (df.age.between(7, 19))].shape
df.rez_esc.value_counts(dropna=False)
Bei fünf Kindern im schulpflichtigen Alter sollten die Werte interpoliert werden, alle anderen werden = 0 gesetzt. Es gibt einen Ausreißer, da der maximale Wert aber bei 5 liegt, wird dieser = 5 gesetzt.
df.loc[df['rez_esc'] > 5, 'rez_esc'] = 5
df.loc[((df['age'] > 19) | (df['age'] < 7)) & (df['rez_esc'].isnull()), 'rez_esc'] = 0
df.rez_esc.mean()
df.loc[(df.rez_esc).isna() & (df.age.between(7, 19)), 'rez_esc'] = 0
square of the mean years of education of adults (>=18) in the household
meaneduc,average years of education for adults (18+)
escolari, years of schooling
age, Age in years
Da escolari nirgends fehlt, kann daraus meaneduc und sqbmeaned berechnet werden
Ja. Daher kann meaneduc nicht berechnet werden, da es nur für Erwachsene definiert wurde
Eigentlich müsste ich das Alter heruntersetzten, um den Durchschnittswert der Schulzeit für diese Haushalte zu berechnen. Da aber in der Beschreibung steht, dass meaneduc nur für 18+ gilt, geht das nicht. Daher sind diese Werte nans.
df = df.drop(df[(df.meaneduc).isna()].index)
df.shape
# Visualisierung mit plotly
target= df.loc[df.parentesco1 == 1, 'Target'].value_counts()
levels = ['1','2', '3', '4']
trace = go.Pie(labels=target.index,values=target.values, marker=dict(colors=('orange','green', 'blue', 'red')))
layout = dict(title="Verteilung der Zielvariablen", margin=dict(l=150), width=500, height=500)
figdata = [trace]
fig = go.Figure(data=figdata, layout=layout)
iplot(fig)
#print target class counts
print(target.sort_index())
Der Datensatz ist imbalanced, es gibt sehr viel weniger Haushalte der Klassen 1-3 als der Klasse 4.
from collections import OrderedDict
plt.figure(figsize = (20, 16))
# Farben mappen
colors = OrderedDict({1: 'red', 2: 'orange', 3: 'blue', 4: 'green'})
poverty_mapping = OrderedDict({1: 'extreme', 2: 'moderate', 3: 'vulnerable', 4: 'non vulnerable'})
# Durch Floats iterieren
for i, col in enumerate(train.select_dtypes('float').drop('Target', axis=1)):
ax = plt.subplot(4, 2, i + 1)
# Iterate through the poverty levels
for poverty_level, color in colors.items():
# Plot each poverty level as a separate line
sns.kdeplot(train.loc[train['Target'] == poverty_level, col].dropna(),
ax = ax, color = color, label = poverty_mapping[poverty_level])
plt.title(f'{col.capitalize()} Distribution'); plt.xlabel(f'{col}'); plt.ylabel('Density')
plt.subplots_adjust(top = 2)
def kdeplot(feature):
plt.figure(figsize=(12, 6))
plt.title("KDE für {}".format(feature.capitalize()))
ax0 = sns.kdeplot(df[df['Target'] == 1][feature].dropna(), color= 'red', label= '1 = extreme poverty')
ax1 = sns.kdeplot(df[df['Target'] == 2][feature].dropna(), color= 'orange', label= '2 = moderate poverty ')
ax2 = sns.kdeplot(df[df['Target'] == 3][feature].dropna(), color= 'navy', label= '3 = vulnerable households ')
ax3 = sns.kdeplot(df[df['Target'] == 4][feature].dropna(), color= 'green', label= '4 = non vulnerable households')
for i in ['edjefe', 'edjefa', 'meaneduc', 'age', 'escolari']:
kdeplot(i)
plt.figure(figsize=(12, 6))
m = df[df.Target == 1]['escolari']
f = df[df.Target == 2]['escolari']
o = df[df.Target == 3]['escolari']
b = df[df.Target == 4]['escolari']
plt.hist([m, f, o, b], label=['Extreme', 'Moderate', 'Vulnerable', 'Non Vulnerable'], normed=True)
plt.legend(loc='upper right')
plt.title('Schulzeit', fontsize=25)
corr = train.corr()['Target']
corrs = abs(corr).sort_values(ascending=False)[:20]
feature= abs(corr).sort_values(ascending=False)[:10].index.values
features = list(feature)
features
corr_feat = train[features].corr()
corr_feat
plt.figure(figsize = (12, 12))
sns.heatmap(corr_feat, annot = True, vmin = -1, vmax = 1, fmt = '.2f', cmap='PuBuGn');
#df.to_csv('df.csv', sep = ',', encoding='utf-8', index=False)
corrs = train[['meaneduc','hogar_nin','r4t1','SQBhogar_nin']].corr()
corrs
vals=[]
cols = []
rows = []
for i in range(len(corrs)-1):
i = i+1
for j in range(i):
j = j
val = corrs.iloc[i, j]
vals.append(val)
col = corrs.columns[i]
row = corrs.columns[j]
cols.append(col)
rows.append(row)
for val, col, row in zip(vals, cols, rows):
print(f'{val} is the correlation of this {col} and this {row} variable')
Entferne eines der Features, die über Threshold 0,6 liegen
for val, col, row in zip(vals, cols, rows):
if val > 0.6:
print(col, row)
vals_=[]
cols_ = []
rows_ = []
for i in range(len(corrs)-1):
i = i+1
for j in range(i):
val = corrs.iloc[i, j]
if val > 0.6:
vals_.append(val)
col = corrs.columns[i]
row = corrs.columns[j]
cols_.append(col)
rows_.append(row)
for val, col, row in zip(vals_, cols_, rows_):
print(f'{val} is the correlation between ***{col}*** and ***{row}***')