Diamonds Teil I: Exploration und Cleaning

Analyse von Diamanten anhand verschiedener Eigenschaften.
Quelle: https://www.kaggle.com/shivam2503/diamonds

In diesem Projekt wird ein Datensatz von Diamanten untersucht. Der Datensatz besteht aus 54000 Beobachtungen.
Jede Beobachtung setzt sich aus folgenden Variablen zusammen:

  • Carat: Gewicht
  • Cut: Schliff
  • Color: Farbe
  • Table: Maß
  • Depth: Maß
  • x, y, z: Geometrie
  • Zielvariable: Price

Bereinigung

Enfernen von fehlerhaften Beobachtungen

Exploration und Visualisierung

Korrelationen, Verteilung der Variablen und der Zielvariablen, Visualisierungen mit matplotlib, seaborn, plotly.

Feature Engineering

Transformieren der Variablen, Erstellen neuer Variablen mit Polynomial Features

Modeling

Modeltest, Modelauswahl, Hyperparameter Tuning

In [1]:
import pandas as pd
import numpy as np

import matplotlib.pyplot as plt
%matplotlib inline

import seaborn as sns
plt.style.use('Solarize_Light2')
from IPython.core.pylabtools import figsize

from sklearn.preprocessing import LabelEncoder

import os

pd.set_option('display.max_columns', None)
os.chdir('D:\Data\Projects\Regression\Diamanten Preise_Regularization')

Exploration und Cleaning

In [2]:
data = pd.read_csv('diamonds.csv')
data.head()
Out[2]:
Unnamed: 0 carat cut color clarity depth table price x y z
0 1 0.23 Ideal E SI2 61.5 55.0 326 3.95 3.98 2.43
1 2 0.21 Premium E SI1 59.8 61.0 326 3.89 3.84 2.31
2 3 0.23 Good E VS1 56.9 65.0 327 4.05 4.07 2.31
3 4 0.29 Premium I VS2 62.4 58.0 334 4.20 4.23 2.63
4 5 0.31 Good J SI2 63.3 58.0 335 4.34 4.35 2.75
In [3]:
data = data.drop('Unnamed: 0', axis = 1)
In [4]:
print(data.shape)
data.head()
(53940, 10)
Out[4]:
carat cut color clarity depth table price x y z
0 0.23 Ideal E SI2 61.5 55.0 326 3.95 3.98 2.43
1 0.21 Premium E SI1 59.8 61.0 326 3.89 3.84 2.31
2 0.23 Good E VS1 56.9 65.0 327 4.05 4.07 2.31
3 0.29 Premium I VS2 62.4 58.0 334 4.20 4.23 2.63
4 0.31 Good J SI2 63.3 58.0 335 4.34 4.35 2.75

Anzeige der Datentypen im DataFrame

In [5]:
data.dtypes.sort_values()
Out[5]:
price        int64
carat      float64
depth      float64
table      float64
x          float64
y          float64
z          float64
cut         object
color       object
clarity     object
dtype: object

Anzeige fehlender Werte

In [6]:
data.isnull().sum()
Out[6]:
carat      0
cut        0
color      0
clarity    0
depth      0
table      0
price      0
x          0
y          0
z          0
dtype: int64

Überblick über numerische Daten

In [7]:
data.describe()
Out[7]:
carat depth table price x y z
count 53940.000000 53940.000000 53940.000000 53940.000000 53940.000000 53940.000000 53940.000000
mean 0.797940 61.749405 57.457184 3932.799722 5.731157 5.734526 3.538734
std 0.474011 1.432621 2.234491 3989.439738 1.121761 1.142135 0.705699
min 0.200000 43.000000 43.000000 326.000000 0.000000 0.000000 0.000000
25% 0.400000 61.000000 56.000000 950.000000 4.710000 4.720000 2.910000
50% 0.700000 61.800000 57.000000 2401.000000 5.700000 5.710000 3.530000
75% 1.040000 62.500000 59.000000 5324.250000 6.540000 6.540000 4.040000
max 5.010000 79.000000 95.000000 18823.000000 10.740000 58.900000 31.800000

Die Maße x, y, z können nicht = 0 sein, entferne diese Beobachtungen

In [8]:
data.loc[(data.x == 0)| (data.y == 0)| (data.z == 0)].shape
Out[8]:
(20, 10)
In [9]:
data = data[(data.x != 0) & (data.y != 0) & (data.z != 0)]
In [10]:
data.shape
Out[10]:
(53920, 10)

Überblick über kategorische Daten

In [11]:
for col in data:
    if data[col].dtype == 'object':
        print(col.upper())
        print(data[col].value_counts())
CUT
Ideal        21548
Premium      13780
Very Good    12081
Good          4902
Fair          1609
Name: cut, dtype: int64
COLOR
G    11284
E     9797
F     9538
H     8298
D     6774
I     5421
J     2808
Name: color, dtype: int64
CLARITY
SI1     13063
VS2     12254
SI2      9185
VS1      8170
VVS2     5066
VVS1     3654
IF       1790
I1        738
Name: clarity, dtype: int64

Schneller Überblick über die Verteilung der Variablen mit Pandas

In [12]:
# Pandas hist Methode
data.hist(bins=25, grid=False, figsize=(12,8), color='#86bf91', zorder=2, rwidth=0.9);

Visualisierung der Verteilung von 'Carat' mit plotly

In [13]:
figsize(10, 8)
plt.hist(data.carat,  edgecolor = 'black', bins=30);
plt.title('Verteilung von Carat')
plt.xlabel('Carat', fontsize = 20)
plt.ylabel('Count', fontsize = 20); 

Verteilung von Cut

In [14]:
sns.catplot(x='cut', data=data , kind='count', aspect=2.5);

Cut und Price

In [15]:
sns.catplot(x='cut', y='price', data= data, kind='box' ,aspect= 2.5 )
Out[15]:
<seaborn.axisgrid.FacetGrid at 0xead4d68>

Visualisierung der Anteile von 'Clarity' mit Matplotlib

In [16]:
labels = data.clarity.unique().tolist();
sizes = data.clarity.value_counts().tolist();
colors = ['#006400', '#E40E00', '#A00994', '#613205', '#FFED0D', '#16F5A7','blue','#66b3ff'];
plt.pie(sizes, labels=labels, colors=colors, startangle=0, autopct='%1.1f%%');
plt.title('Anteile der Clarity Kategorien');

Density Plot von 'Price' mit Seaborn

In [17]:
sns.kdeplot( data["price"] , color="skyblue", shade=True);
plt.xlabel('Price'), plt.ylabel('Density'), plt.title('Density Plot of Diamond Prices');

Der Preis in Abhängigkeit von 'Carat' und 'Color'

In [18]:
d = data[:5000]
sns.scatterplot('carat', 'price', data = d, hue = 'cut' );
In [19]:
a = data[40000:]
sns.scatterplot('carat', 'price', data = a, hue = 'cut' );

Sortierte Daten können vom Algorithmus falsch interpretiert werden, daher sollten diese immer gut durchmischt sein. Dafür werden die Reihen am Index mit np.random.permutation gemischt.

In [20]:
data = data.reindex(np.random.permutation(data.index))
data = data.reset_index(drop=True)
In [21]:
d = data[:5000]
sns.scatterplot('carat', 'price', data = d, hue = 'cut' );

Feature Engineering

To make the price-carat relation more linear, let’s take a 10-based logarithm of the price and use it as the response variable instead. Adding or subtracting a constant affects the mean but does not affect variance . Therefore it is recommended to add a constant . The best constant to add in case of Cobb Douglas production function is to add the same constant to all values of the same variable which makes all values of the variable positive.

In [22]:
data['carat_log'] = np.log10(data.carat +10)
In [23]:
data.head()
Out[23]:
carat cut color clarity depth table price x y z carat_log
0 0.51 Ideal H IF 59.6 59.0 2063 5.20 5.24 3.11 1.021603
1 0.36 Ideal I VVS2 61.5 57.0 655 4.55 4.58 2.80 1.015360
2 0.31 Ideal G VVS1 60.2 58.0 676 4.37 4.43 2.65 1.013259
3 1.50 Very Good H IF 63.0 59.0 12988 7.23 7.30 4.58 1.060698
4 0.36 Ideal H VS2 61.0 56.0 587 4.61 4.63 2.82 1.015360
In [24]:
dd = data[:5000]
sns.scatterplot('carat_log', 'price', data = dd, hue = 'cut' );

Encoding der kategorischen Variablen mit Pandas get_dummies.

Um Kollinearität zu vermeiden, wird eine der generierten Spalten entfernt mit drop_first=True

In [25]:
df = pd.get_dummies(data, drop_first=True)
df.shape
Out[25]:
(53920, 25)
In [26]:
df.head()
Out[26]:
carat depth table price x y z carat_log cut_Good cut_Ideal cut_Premium cut_Very Good color_E color_F color_G color_H color_I color_J clarity_IF clarity_SI1 clarity_SI2 clarity_VS1 clarity_VS2 clarity_VVS1 clarity_VVS2
0 0.51 59.6 59.0 2063 5.20 5.24 3.11 1.021603 0 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0
1 0.36 61.5 57.0 655 4.55 4.58 2.80 1.015360 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1
2 0.31 60.2 58.0 676 4.37 4.43 2.65 1.013259 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0
3 1.50 63.0 59.0 12988 7.23 7.30 4.58 1.060698 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0
4 0.36 61.0 56.0 587 4.61 4.63 2.82 1.015360 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0

Korrelationen der Variablen mit der Zielvariablen

In [27]:
correlations = df.corr()['price'].sort_values(ascending=False)
correlations
Out[27]:
price            1.000000
carat            0.921592
carat_log        0.919813
x                0.887231
z                0.868206
y                0.867864
clarity_SI2      0.128354
table            0.127245
color_I          0.097180
cut_Premium      0.095324
color_J          0.081877
color_H          0.058823
clarity_SI1      0.008951
color_G          0.008501
cut_Very Good    0.006829
cut_Good        -0.000364
clarity_VS2     -0.001105
clarity_VS1     -0.009735
depth           -0.010729
color_F         -0.023941
clarity_IF      -0.049548
clarity_VVS2    -0.052274
clarity_VVS1    -0.095444
cut_Ideal       -0.097000
color_E         -0.100954
Name: price, dtype: float64
In [28]:
abs_correlations = abs(correlations).sort_values(ascending=False)
In [29]:
print(list(abs_correlations[:10].index))
['price', 'carat', 'carat_log', 'x', 'z', 'y', 'clarity_SI2', 'table', 'color_E', 'color_I']

Gewicht und Größe des Diamanten haben den meisten Einfluss auf den Preis.

Gibt es kollineare Variablen, die ausgeschlossen werden sollten?

In [ ]:
# Iteriere duch Corr Matrix und berechne für jedes Paar VIF
# VIF = 1/(1-R^2)
In [30]:
corr = data.corr()
corr
Out[30]:
carat depth table price x y z carat_log
carat 1.000000 0.028259 0.181646 0.921592 0.977779 0.953991 0.961048 0.999578
depth 0.028259 1.000000 -0.295733 -0.010729 -0.025017 -0.029069 0.095023 0.028437
table 0.181646 -0.295733 1.000000 0.127245 0.196097 0.184493 0.152483 0.183445
price 0.921592 -0.010729 0.127245 1.000000 0.887231 0.867864 0.868206 0.919813
x 0.977779 -0.025017 0.196097 0.887231 1.000000 0.974918 0.975435 0.982851
y 0.953991 -0.029069 0.184493 0.867864 0.974918 1.000000 0.956744 0.958943
z 0.961048 0.095023 0.152483 0.868206 0.975435 0.956744 1.000000 0.966022
carat_log 0.999578 0.028437 0.183445 0.919813 0.982851 0.958943 0.966022 1.000000

Loop über Matrix.
Verwendung von iloc, j=i+1, um nur die Korrs des oberen Dreiecks zu vergleichen

In [31]:
var1 = []
var2 = []
r = []

for i in range(len(corr.columns)):
    for j in range((i)):
        if corr.iloc[i,j] > 0.7 or corr.iloc[i,j] < -0.7:
            var1.append(corr.columns[i])
            var2.append(corr.columns[j])
            r.append(corr.iloc[i,j])
            
In [32]:
h_corr = pd.DataFrame(columns = ['var1', 'var2', 'R'])
h_corr['var1']= var1
h_corr['var2']= var2
h_corr['R']= r
h_corr
Out[32]:
var1 var2 R
0 price carat 0.921592
1 x carat 0.977779
2 x price 0.887231
3 y carat 0.953991
4 y price 0.867864
5 y x 0.974918
6 z carat 0.961048
7 z price 0.868206
8 z x 0.975435
9 z y 0.956744
10 carat_log carat 0.999578
11 carat_log price 0.919813
12 carat_log x 0.982851
13 carat_log y 0.958943
14 carat_log z 0.966022
In [33]:
h_corr['R_sq'] = h_corr.R**2
h_corr['VIF'] = 1/(1-h_corr.R_sq)
h_corr
Out[33]:
var1 var2 R R_sq VIF
0 price carat 0.921592 0.849332 6.637106
1 x carat 0.977779 0.956051 22.753662
2 x price 0.887231 0.787180 4.698796
3 y carat 0.953991 0.910098 11.123264
4 y price 0.867864 0.753188 4.051673
5 y x 0.974918 0.950466 20.188006
6 z carat 0.961048 0.923612 13.091139
7 z price 0.868206 0.753782 4.061447
8 z x 0.975435 0.951474 20.607335
9 z y 0.956744 0.915359 11.814545
10 carat_log carat 0.999578 0.999156 1185.113433
11 carat_log price 0.919813 0.846056 6.495888
12 carat_log x 0.982851 0.965996 29.408406
13 carat_log y 0.958943 0.919571 12.433355
14 carat_log z 0.966022 0.933198 14.969566

Es gibt einige Variablen, die hoch kollinear sind. Größe und Gewicht hängen über die Dichte, 3,51 g/cm³, zusammen. Test in Model mit und ohne Variablen x, y, z

In [36]:
df_nonkol = df.drop(columns=['x', 'y', 'z'], axis = 1)

Die beiden DF sind df und df_nonkol

DataFrames für Modeling speichern

In [37]:
#df.to_csv('df_clean.csv', sep=',', encoding='utf-8', index=False)
#df_nonkol.to_csv('df_nonkol.csv', sep=',', encoding='utf-8', index=False)