1. Business Understanding¶
Ein Einzelhändler braucht einen sehr hohen Umsatz und eine Just-in-time-Lieferung von Produkten, um erfolgreich zu sein. Dazu werden sehr genaue Prognosen benötigt, die genaue Aussagen darüber erlauben, welche Filialen in welchen Abteilungen wie viel Umsatz pro Woche machen. Die Fragestellung lautet daher wie folgt: Wie hoch ist die wöchentliche Umsatzprognose für alle Filialen eines fiktiven Marktes, wenn wir die Daten der letzten drei Jahre, demographische Informationen (VPI, Arbeitslosigkeit, Temperatur, etc.) berücksichtigen? Wie hoch ist dann der Wochenumsatz pro Abteilung in einer dieser Filialen?
2. Daten und Datenverständnis¶
Die für das Notebook verwendeten Daten sind in drei Dateien unterteilt. Sie enthalten verschiedene Merkmale und die gemeinsamen Attributsspeicher.
2.1. Import von relevanten Modulen¶
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
2.2. Trainingsdaten einlesen¶
walmart_features_data = pd.read_csv('https://storage.googleapis.com/ml-service-repository-datastorage/Sales_Forecast_for_retail_store_features.csv')
walmart_train_data = pd.read_csv('https://storage.googleapis.com/ml-service-repository-datastorage/Sales_Forecast_for_retail_store_train.csv')
walmart_stores_data = pd.read_csv('https://storage.googleapis.com/ml-service-repository-datastorage/Sales_Forecast_for_retail_store_stores.csv')
walmart_train_data.head(5)
Store | Dept | Date | Weekly_Sales | IsHoliday | |
---|---|---|---|---|---|
0 | 1 | 1 | 2010-02-05 | 24924.50 | False |
1 | 1 | 1 | 2010-02-12 | 46039.49 | True |
2 | 1 | 1 | 2010-02-19 | 41595.55 | False |
3 | 1 | 1 | 2010-02-26 | 19403.54 | False |
4 | 1 | 1 | 2010-03-05 | 21827.90 | False |
walmart_train_data.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 421570 entries, 0 to 421569 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Store 421570 non-null int64 1 Dept 421570 non-null int64 2 Date 421570 non-null object 3 Weekly_Sales 421570 non-null float64 4 IsHoliday 421570 non-null bool dtypes: bool(1), float64(1), int64(2), object(1) memory usage: 13.3+ MB
Daten beschreiben: Wir haben einen Datensatz mit 421.570 Einträgen. Jede Zeile enthält die Werte "Store", "Dept", "Date", "Weekly_Sales" und "IsHoliday". Diese Tabelle enthält also bereits den Wert, der für unsere Betrachtung relevant ist: "Wöchentliche_Umsätze". Wir werden eine Prognose für diesen Wert erstellen und nun nach Merkmalen suchen, die diesen Wert beeinflussen. Das Format dieser Werte ist ein boolescher Wert, eine Gleitkommazahl, zwei Ganzzahlen und ein Objekt.
walmart_train_data.describe(include='all')
Store | Dept | Date | Weekly_Sales | IsHoliday | |
---|---|---|---|---|---|
count | 421570.000000 | 421570.000000 | 421570 | 421570.000000 | 421570 |
unique | NaN | NaN | 143 | NaN | 2 |
top | NaN | NaN | 2011-12-23 | NaN | False |
freq | NaN | NaN | 3027 | NaN | 391909 |
mean | 22.200546 | 44.260317 | NaN | 15981.258123 | NaN |
std | 12.785297 | 30.492054 | NaN | 22711.183519 | NaN |
min | 1.000000 | 1.000000 | NaN | -4988.940000 | NaN |
25% | 11.000000 | 18.000000 | NaN | 2079.650000 | NaN |
50% | 22.000000 | 37.000000 | NaN | 7612.030000 | NaN |
75% | 33.000000 | 74.000000 | NaN | 20205.852500 | NaN |
max | 45.000000 | 99.000000 | NaN | 693099.360000 | NaN |
walmart_train_data.columns
Index(['Store', 'Dept', 'Date', 'Weekly_Sales', 'IsHoliday'], dtype='object')
walmart_train_data[walmart_train_data.duplicated(keep=False)] # existieren duplizierte Reihen ?
Store | Dept | Date | Weekly_Sales | IsHoliday |
---|
walmart_train_data.isnull().sum() # existieren Nullwerte ? Die Summe aller vorhandenen Nullwerte --> keine vorhanden
Store 0 Dept 0 Date 0 Weekly_Sales 0 IsHoliday 0 dtype: int64
2.2.1. Deskriptive Analyse¶
sns.distplot(walmart_train_data['Weekly_Sales']) # wie sieht die Verteilung unserer dependent variable aus ?
C:\Users\eebal\Anaconda3\lib\site-packages\seaborn\distributions.py:2551: FutureWarning: `distplot` is a deprecated function and will be removed in a future version. Please adapt your code to use either `displot` (a figure-level function with similar flexibility) or `histplot` (an axes-level function for histograms). warnings.warn(msg, FutureWarning)
<AxesSubplot:xlabel='Weekly_Sales', ylabel='Density'>
sns.heatmap(walmart_train_data.corr())
<AxesSubplot:>
2.3. Merkmalsdaten einlesen¶
walmart_features_data.head(5)
Store | Date | Temperature | Fuel_Price | MarkDown1 | MarkDown2 | MarkDown3 | MarkDown4 | MarkDown5 | CPI | Unemployment | IsHoliday | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2010-02-05 | 42.31 | 2.572 | NaN | NaN | NaN | NaN | NaN | 211.096358 | 8.106 | False |
1 | 1 | 2010-02-12 | 38.51 | 2.548 | NaN | NaN | NaN | NaN | NaN | 211.242170 | 8.106 | True |
2 | 1 | 2010-02-19 | 39.93 | 2.514 | NaN | NaN | NaN | NaN | NaN | 211.289143 | 8.106 | False |
3 | 1 | 2010-02-26 | 46.63 | 2.561 | NaN | NaN | NaN | NaN | NaN | 211.319643 | 8.106 | False |
4 | 1 | 2010-03-05 | 46.50 | 2.625 | NaN | NaN | NaN | NaN | NaN | 211.350143 | 8.106 | False |
walmart_features_data.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 8190 entries, 0 to 8189 Data columns (total 12 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Store 8190 non-null int64 1 Date 8190 non-null object 2 Temperature 8190 non-null float64 3 Fuel_Price 8190 non-null float64 4 MarkDown1 4032 non-null float64 5 MarkDown2 2921 non-null float64 6 MarkDown3 3613 non-null float64 7 MarkDown4 3464 non-null float64 8 MarkDown5 4050 non-null float64 9 CPI 7605 non-null float64 10 Unemployment 7605 non-null float64 11 IsHoliday 8190 non-null bool dtypes: bool(1), float64(9), int64(1), object(1) memory usage: 712.0+ KB
Daten beschreiben: Wir haben einen Datensatz mit 8.190 Einträgen. Jede Zeile enthält die Werte "Store", "Date", "Temperature", "Fuel_Price", "MarkDown1", "MarkDown2", "MarkDown3", "MarkDown4", "MarkDown5", "CPI", "Unemployment" und "IsHoliday". Das Format dieser Werte ist ein Boolean, 9 Float, eine Integer und 1 Object.
walmart_features_data.describe(include='all')
Store | Date | Temperature | Fuel_Price | MarkDown1 | MarkDown2 | MarkDown3 | MarkDown4 | MarkDown5 | CPI | Unemployment | IsHoliday | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 8190.000000 | 8190 | 8190.000000 | 8190.000000 | 4032.000000 | 2921.000000 | 3613.000000 | 3464.000000 | 4050.000000 | 7605.000000 | 7605.000000 | 8190 |
unique | NaN | 182 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2 |
top | NaN | 2011-12-16 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | False |
freq | NaN | 45 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 7605 |
mean | 23.000000 | NaN | 59.356198 | 3.405992 | 7032.371786 | 3384.176594 | 1760.100180 | 3292.935886 | 4132.216422 | 172.460809 | 7.826821 | NaN |
std | 12.987966 | NaN | 18.678607 | 0.431337 | 9262.747448 | 8793.583016 | 11276.462208 | 6792.329861 | 13086.690278 | 39.738346 | 1.877259 | NaN |
min | 1.000000 | NaN | -7.290000 | 2.472000 | -2781.450000 | -265.760000 | -179.260000 | 0.220000 | -185.170000 | 126.064000 | 3.684000 | NaN |
25% | 12.000000 | NaN | 45.902500 | 3.041000 | 1577.532500 | 68.880000 | 6.600000 | 304.687500 | 1440.827500 | 132.364839 | 6.634000 | NaN |
50% | 23.000000 | NaN | 60.710000 | 3.513000 | 4743.580000 | 364.570000 | 36.260000 | 1176.425000 | 2727.135000 | 182.764003 | 7.806000 | NaN |
75% | 34.000000 | NaN | 73.880000 | 3.743000 | 8923.310000 | 2153.350000 | 163.150000 | 3310.007500 | 4832.555000 | 213.932412 | 8.567000 | NaN |
max | 45.000000 | NaN | 101.950000 | 4.468000 | 103184.980000 | 104519.540000 | 149483.310000 | 67474.850000 | 771448.100000 | 228.976456 | 14.313000 | NaN |
Daten beschreiben: Wir haben nicht für alle Werte entsprechende Zahlen. Es fehlen Werte für MarkDown1-MarkDown5, CPI und Arbeitslosigkeit. Interessant ist, dass es negative Werte bei "MarkDown" gibt. Laut der Beschreibung der Daten sind die MarkDown-Werte nicht für alle Filialen und erst ab einem bestimmten Datum verfügbar. Dies erklärt die Diskrepanz in der Menge.
walmart_features_data.columns
Index(['Store', 'Date', 'Temperature', 'Fuel_Price', 'MarkDown1', 'MarkDown2', 'MarkDown3', 'MarkDown4', 'MarkDown5', 'CPI', 'Unemployment', 'IsHoliday'], dtype='object')
walmart_features_data.isnull().sum()
Store 0 Date 0 Temperature 0 Fuel_Price 0 MarkDown1 4158 MarkDown2 5269 MarkDown3 4577 MarkDown4 4726 MarkDown5 4140 CPI 585 Unemployment 585 IsHoliday 0 dtype: int64
null = pd.DataFrame(walmart_features_data.dtypes).T.rename(index={0:'column Type'})
null = null.append(pd.DataFrame(walmart_features_data.isnull().sum()).T.rename(index={0:'null values (nb)'}))
null = null.append(pd.DataFrame(walmart_features_data.isnull().sum()/walmart_features_data.shape[0]*100).T.
rename(index={0: 'null values (%)'}))
null
Store | Date | Temperature | Fuel_Price | MarkDown1 | MarkDown2 | MarkDown3 | MarkDown4 | MarkDown5 | CPI | Unemployment | IsHoliday | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
column Type | int64 | object | float64 | float64 | float64 | float64 | float64 | float64 | float64 | float64 | float64 | bool |
null values (nb) | 0 | 0 | 0 | 0 | 4158 | 5269 | 4577 | 4726 | 4140 | 585 | 585 | 0 |
null values (%) | 0 | 0 | 0 | 0 | 50.7692 | 64.3346 | 55.8852 | 57.7045 | 50.5495 | 7.14286 | 7.14286 | 0 |
from statistics import mean
walmart_features_data['CPI'] = walmart_features_data['CPI'].fillna(mean(walmart_features_data['CPI']))
walmart_features_data['Unemployment'] = walmart_features_data['Unemployment'].fillna(mean(walmart_features_data['Unemployment']))
walmart_features_data['MarkDown1'] = walmart_features_data['MarkDown1'].fillna(0)
walmart_features_data['MarkDown2'] = walmart_features_data['MarkDown2'].fillna(0)
walmart_features_data['MarkDown3'] = walmart_features_data['MarkDown3'].fillna(0)
walmart_features_data['MarkDown4'] = walmart_features_data['MarkDown4'].fillna(0)
walmart_features_data['MarkDown5'] = walmart_features_data['MarkDown5'].fillna(0)
2.3.2. Deskriptive Analyse¶
sns.heatmap(walmart_features_data.corr())
<AxesSubplot:>
2.4. Ladendaten einlesen¶
walmart_stores_data.head(5)
Store | Type | Size | |
---|---|---|---|
0 | 1 | A | 151315 |
1 | 2 | A | 202307 |
2 | 3 | B | 37392 |
3 | 4 | A | 205863 |
4 | 5 | B | 34875 |
walmart_stores_data.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 45 entries, 0 to 44 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Store 45 non-null int64 1 Type 45 non-null object 2 Size 45 non-null int64 dtypes: int64(2), object(1) memory usage: 1.2+ KB
Daten beschreiben: Wir haben einen Datensatz mit 45 Einträgen. Jede Zeile enthält die Werte "Store", "Type" und "Size". Das Format dieser Werte sind zwei Ganzzahlen und ein Objekt.
walmart_stores_data.describe(include='all')
Store | Type | Size | |
---|---|---|---|
count | 45.000000 | 45 | 45.000000 |
unique | NaN | 3 | NaN |
top | NaN | A | NaN |
freq | NaN | 22 | NaN |
mean | 23.000000 | NaN | 130287.600000 |
std | 13.133926 | NaN | 63825.271991 |
min | 1.000000 | NaN | 34875.000000 |
25% | 12.000000 | NaN | 70713.000000 |
50% | 23.000000 | NaN | 126512.000000 |
75% | 34.000000 | NaN | 202307.000000 |
max | 45.000000 | NaN | 219622.000000 |
Daten beschreiben: Da wir bereits wissen, dass es 45 Läden gibt, ist dies ein Datensatz, der Einzelheiten über jeden Laden enthält. Es gibt 45 Läden, es gibt 3 Typen (A, B und C) und die Größe der Läden variiert zwischen 34.875 und 219.622 Quadratmetern.
2.4.1. Deskriptive Analyse¶
#sns.pairplot(walmart_stores_data, vars=['Store', 'Size'], hue='Type')
sns.swarmplot(x='Type',y='Size',data=walmart_stores_data,palette='Set2')
<AxesSubplot:xlabel='Type', ylabel='Size'>
Es sieht so aus, als ob der "Typ" anhand der Größe bestimmt werden kann. Sie können aber auch sehen, dass es mindestens 4 Geschäfte gibt, die wahrscheinlich falsch zugeordnet wurden, da es einige im Größenbereich zwischen 25.000 und 50.000 in Typ B und A gibt, die wahrscheinlich eher dem Typ C hätten zugeordnet werden müssen.
walmart_stores_data.loc[walmart_stores_data.Size < 50000, "Type"] = "C"
walmart_stores_data.loc[walmart_stores_data.Size >= 50000, "Type"] = "B"
walmart_stores_data.loc[walmart_stores_data.Size >= 150000, "Type"] = "A"
sns.swarmplot(x='Type',y='Size',data=walmart_stores_data,palette='Set2')
<AxesSubplot:xlabel='Type', ylabel='Size'>
sns.swarmplot(x='Type',y='Store',data=walmart_stores_data,palette='Set2') # welche Ladennummer zu welchem Typ gehört
<AxesSubplot:xlabel='Type', ylabel='Store'>
sns.countplot(x='Type',data=walmart_stores_data) # Betrachtung, wieviele Läden wir von welchem Type haben
<AxesSubplot:xlabel='Type', ylabel='count'>
sizes=walmart_stores_data.count()['Size'].round(1)
print(sizes)
45
g = sns.FacetGrid(data=walmart_stores_data,col='Type') # Betrachtung, wie sich inerhalb eines Types dann noch die Größe verteilt
g.map(plt.hist,'Size')
<seaborn.axisgrid.FacetGrid at 0x1fcd2f4f370>
walmart_stores_data.columns
Index(['Store', 'Type', 'Size'], dtype='object')
walmart_stores_data.isnull().sum() # existieren Nullwerte ?
Store 0 Type 0 Size 0 dtype: int64
3. Datenaufbereitung¶
Wir kombinieren alle 3 verfügbaren Datensätze (Tabellen), da alle 3 einen gemeinsamen Schlüssel ("Store") haben, über den eine Zusammenfassung möglich ist. Die weitere Betrachtung wird erst möglich, wenn wir die Daten aus allen 3 Datensätzen zusammen betrachten, welche Abhängigkeiten hier bestehen.
Ziel ist es, eine Vorhersage über die Daten von "Weekly_Sales" zu machen, daher suchen wir alle Daten, die einen Einfluss auf diese haben.
3.1. Daten kombinieren¶
festo_data = walmart_features_data.merge(walmart_stores_data, how='inner', on='Store')
comb_data = walmart_train_data.merge(festo_data, how='inner', on=['Store','Date','IsHoliday']).sort_values(by=['Store',
'Dept', 'Date']).reset_index(drop=True)
comb_data.head(5)
Store | Dept | Date | Weekly_Sales | IsHoliday | Temperature | Fuel_Price | MarkDown1 | MarkDown2 | MarkDown3 | MarkDown4 | MarkDown5 | CPI | Unemployment | Type | Size | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 1 | 2010-02-05 | 24924.50 | False | 42.31 | 2.572 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 211.096358 | 8.106 | A | 151315 |
1 | 1 | 1 | 2010-02-12 | 46039.49 | True | 38.51 | 2.548 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 211.242170 | 8.106 | A | 151315 |
2 | 1 | 1 | 2010-02-19 | 41595.55 | False | 39.93 | 2.514 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 211.289143 | 8.106 | A | 151315 |
3 | 1 | 1 | 2010-02-26 | 19403.54 | False | 46.63 | 2.561 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 211.319643 | 8.106 | A | 151315 |
4 | 1 | 1 | 2010-03-05 | 21827.90 | False | 46.50 | 2.625 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 211.350143 | 8.106 | A | 151315 |
comb_data.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 421570 entries, 0 to 421569 Data columns (total 16 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Store 421570 non-null int64 1 Dept 421570 non-null int64 2 Date 421570 non-null object 3 Weekly_Sales 421570 non-null float64 4 IsHoliday 421570 non-null bool 5 Temperature 421570 non-null float64 6 Fuel_Price 421570 non-null float64 7 MarkDown1 421570 non-null float64 8 MarkDown2 421570 non-null float64 9 MarkDown3 421570 non-null float64 10 MarkDown4 421570 non-null float64 11 MarkDown5 421570 non-null float64 12 CPI 421570 non-null float64 13 Unemployment 421570 non-null float64 14 Type 421570 non-null object 15 Size 421570 non-null int64 dtypes: bool(1), float64(10), int64(3), object(2) memory usage: 48.6+ MB
#sns.pairplot(comb_data, vars=['Weekly_Sales', 'Dept', 'Store', 'Size', 'CPI', 'Unemployment', 'Fuel_Price', 'Temperature'], hue='Type')
sns.catplot(data=comb_data, kind="bar",
x="Type", y="Weekly_Sales", hue="IsHoliday",
ci="sd", palette="dark", alpha=.6, height=6)
<seaborn.axisgrid.FacetGrid at 0x1fcd328cb20>
3.2. Kombinierte Daten bereinigen¶
Wir konvertieren das Feld "IsHoliday" von boolean in ein int mit 0 und 1
comb_data = comb_data.applymap(lambda x: 1 if x == True else x)
comb_data = comb_data.applymap(lambda x: 0 if x == False else x)
Wir konvertieren das Feld "Datum" von einem Objekt in ein Datumsfeld
#comb_data["Date"] = pd.to_datetime(comb_data["Date"])
comb_data.Date = pd.to_datetime(comb_data.Date)
# delete negative values for the feature Weekly_Sales and create a new DataFrame
clean_data = comb_data[comb_data['Weekly_Sales']>0]
# add a field for "week" and "year" to allow further viewing on calendar week
#clean_data['Week'] = clean_data1['Date'].isocalendar()[1]
clean_data['Week'] = clean_data.Date.dt.week
clean_data['Year'] = clean_data.Date.dt.year
<ipython-input-41-df9ed1724864>:3: FutureWarning: Series.dt.weekofyear and Series.dt.week have been deprecated. Please use Series.dt.isocalendar().week instead. clean_data['Week'] = clean_data.Date.dt.week <ipython-input-41-df9ed1724864>:3: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy clean_data['Week'] = clean_data.Date.dt.week <ipython-input-41-df9ed1724864>:4: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy clean_data['Year'] = clean_data.Date.dt.year
clean_data.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 420212 entries, 0 to 421569 Data columns (total 18 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Store 420212 non-null int64 1 Dept 420212 non-null int64 2 Date 420212 non-null datetime64[ns] 3 Weekly_Sales 420212 non-null float64 4 IsHoliday 420212 non-null int64 5 Temperature 420212 non-null float64 6 Fuel_Price 420212 non-null float64 7 MarkDown1 420212 non-null float64 8 MarkDown2 420212 non-null float64 9 MarkDown3 420212 non-null float64 10 MarkDown4 420212 non-null float64 11 MarkDown5 420212 non-null float64 12 CPI 420212 non-null float64 13 Unemployment 420212 non-null float64 14 Type 420212 non-null object 15 Size 420212 non-null int64 16 Week 420212 non-null int64 17 Year 420212 non-null int64 dtypes: datetime64[ns](1), float64(10), int64(6), object(1) memory usage: 60.9+ MB
# save date local
clean_data.to_csv('clean_data.csv', index = False)
# We convert the field "Type", since it is a categorical value using One-Hot Encoding
clean_data1 = pd.get_dummies(clean_data, columns=["Type"])
clean_data1.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 420212 entries, 0 to 421569 Data columns (total 20 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Store 420212 non-null int64 1 Dept 420212 non-null int64 2 Date 420212 non-null datetime64[ns] 3 Weekly_Sales 420212 non-null float64 4 IsHoliday 420212 non-null int64 5 Temperature 420212 non-null float64 6 Fuel_Price 420212 non-null float64 7 MarkDown1 420212 non-null float64 8 MarkDown2 420212 non-null float64 9 MarkDown3 420212 non-null float64 10 MarkDown4 420212 non-null float64 11 MarkDown5 420212 non-null float64 12 CPI 420212 non-null float64 13 Unemployment 420212 non-null float64 14 Size 420212 non-null int64 15 Week 420212 non-null int64 16 Year 420212 non-null int64 17 Type_A 420212 non-null uint8 18 Type_B 420212 non-null uint8 19 Type_C 420212 non-null uint8 dtypes: datetime64[ns](1), float64(10), int64(6), uint8(3) memory usage: 58.9 MB
clean_data1.describe(include='all')
<ipython-input-46-fbf7f56c5ae5>:1: FutureWarning: Treating datetime data as categorical rather than numeric in `.describe` is deprecated and will be removed in a future version of pandas. Specify `datetime_is_numeric=True` to silence this warning and adopt the future behavior now. clean_data1.describe(include='all')
Store | Dept | Date | Weekly_Sales | IsHoliday | Temperature | Fuel_Price | MarkDown1 | MarkDown2 | MarkDown3 | MarkDown4 | MarkDown5 | CPI | Unemployment | Size | Week | Year | Type_A | Type_B | Type_C | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 420212.000000 | 420212.000000 | 420212 | 420212.000000 | 420212.000000 | 420212.000000 | 420212.000000 | 420212.000000 | 420212.000000 | 420212.000000 | 420212.000000 | 420212.000000 | 420212.000000 | 420212.000000 | 420212.000000 | 420212.000000 | 420212.000000 | 420212.000000 | 420212.000000 | 420212.000000 |
unique | NaN | NaN | 143 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
top | NaN | NaN | 2011-12-23 00:00:00 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
freq | NaN | NaN | 3018 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
first | NaN | NaN | 2010-02-05 00:00:00 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
last | NaN | NaN | 2012-10-26 00:00:00 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
mean | 22.195611 | 44.241309 | NaN | 16033.114591 | 0.070345 | 60.090599 | 3.360890 | 2590.323565 | 878.905242 | 468.845949 | 1083.534361 | 1662.805002 | 171.212496 | 7.960000 | 136749.732787 | 25.828408 | 2010.968454 | 0.481390 | 0.344548 | 0.174062 |
std | 12.787236 | 30.508819 | NaN | 22729.492116 | 0.255729 | 18.447857 | 0.458519 | 6053.415601 | 5076.928566 | 5534.069859 | 3896.068938 | 4206.209357 | 39.162445 | 1.863879 | 60993.084568 | 14.152489 | 0.796898 | 0.499654 | 0.475221 | 0.379163 |
min | 1.000000 | 1.000000 | NaN | 0.010000 | 0.000000 | -2.060000 | 2.472000 | 0.000000 | -265.760000 | -29.100000 | 0.000000 | 0.000000 | 126.064000 | 3.879000 | 34875.000000 | 1.000000 | 2010.000000 | 0.000000 | 0.000000 | 0.000000 |
25% | 11.000000 | 18.000000 | NaN | 2120.130000 | 0.000000 | 46.680000 | 2.933000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 132.022667 | 6.891000 | 93638.000000 | 14.000000 | 2010.000000 | 0.000000 | 0.000000 | 0.000000 |
50% | 22.000000 | 37.000000 | NaN | 7661.700000 | 0.000000 | 62.090000 | 3.452000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 182.350989 | 7.866000 | 140167.000000 | 26.000000 | 2011.000000 | 0.000000 | 0.000000 | 0.000000 |
75% | 33.000000 | 74.000000 | NaN | 20271.265000 | 0.000000 | 74.280000 | 3.738000 | 2809.050000 | 2.400000 | 4.540000 | 425.290000 | 2168.040000 | 212.445487 | 8.567000 | 202505.000000 | 38.000000 | 2012.000000 | 1.000000 | 1.000000 | 0.000000 |
max | 45.000000 | 99.000000 | NaN | 693099.360000 | 1.000000 | 100.140000 | 4.468000 | 88646.760000 | 104519.540000 | 141630.610000 | 67474.850000 | 108519.280000 | 227.232807 | 14.313000 | 219622.000000 | 52.000000 | 2012.000000 | 1.000000 | 1.000000 | 1.000000 |
3.3. Deskriptive Analyse neue Daten¶
weekly_sales_2010 = clean_data1[clean_data1.Year==2010]['Weekly_Sales'].groupby(clean_data1['Week']).mean()
weekly_sales_2011 = clean_data1[clean_data1.Year==2011]['Weekly_Sales'].groupby(clean_data1['Week']).mean()
weekly_sales_2012 = clean_data1[clean_data1.Year==2012]['Weekly_Sales'].groupby(clean_data1['Week']).mean()
plt.figure(figsize=(20,8))
sns.lineplot(weekly_sales_2010.index, weekly_sales_2010.values)
sns.lineplot(weekly_sales_2011.index, weekly_sales_2011.values)
sns.lineplot(weekly_sales_2012.index, weekly_sales_2012.values)
plt.grid()
plt.xticks(np.arange(1, 53, step=1))
plt.legend(['2010', '2011', '2012'], loc='best', fontsize=16)
plt.title('Average Weekly Sales - Per Year', fontsize=18)
plt.ylabel('Sales', fontsize=16)
plt.xlabel('Week', fontsize=16)
plt.show()
C:\Users\eebal\Anaconda3\lib\site-packages\seaborn\_decorators.py:36: FutureWarning: Pass the following variables as keyword args: x, y. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation. warnings.warn( C:\Users\eebal\Anaconda3\lib\site-packages\seaborn\_decorators.py:36: FutureWarning: Pass the following variables as keyword args: x, y. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation. warnings.warn( C:\Users\eebal\Anaconda3\lib\site-packages\seaborn\_decorators.py:36: FutureWarning: Pass the following variables as keyword args: x, y. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation. warnings.warn(
Wir sehen, dass sich die Verkäufe in den 3 Jahren ähnlich verhalten. Es gibt Spitzenwerte vor Woche 7 (Super Bowl), vor Woche 48 (Thanksgiving) und vor Woche 52 (Weihnachten), was vermutlich auf den schwarzen Freitag und das Weihnachtsgeschäft zurückzuführen ist. Vor Woche 37 (Tag der Arbeit) gehen die Verkäufe zurück.
clean_data1[['Dept', 'Weekly_Sales']].groupby(['Dept'], as_index=False).mean().sort_values(by='Weekly_Sales', ascending=False)
Dept | Weekly_Sales | |
---|---|---|
73 | 92 | 75204.870531 |
76 | 95 | 69824.423080 |
36 | 38 | 61090.619568 |
60 | 72 | 50852.993977 |
57 | 65 | 45441.706224 |
... | ... | ... |
43 | 45 | 23.807907 |
49 | 51 | 22.718938 |
37 | 39 | 11.123750 |
63 | 78 | 10.765156 |
41 | 43 | 1.193333 |
81 rows × 2 columns
weekly_sales = clean_data1['Weekly_Sales'].groupby(clean_data1['Dept']).mean()
plt.figure(figsize=(25,8))
sns.barplot(weekly_sales.index, weekly_sales.values, palette='bright')
plt.grid()
plt.title('Durchschnittlicher Umsatz pro Abteilung', fontsize=18)
plt.ylabel('Sales', fontsize=16)
plt.xlabel('Dept', fontsize=16)
plt.show()
C:\Users\eebal\Anaconda3\lib\site-packages\seaborn\_decorators.py:36: FutureWarning: Pass the following variables as keyword args: x, y. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation. warnings.warn(
Die Abteilung 92 bringt insgesamt und im Durchschnitt die meisten Umsätze ein
clean_data1[['Store', 'Weekly_Sales']].groupby(['Store'], as_index=False).mean().sort_values(by='Weekly_Sales', ascending=False)
Store | Weekly_Sales | |
---|---|---|
19 | 20 | 29627.580994 |
3 | 4 | 29175.540012 |
13 | 14 | 28885.743572 |
12 | 13 | 27399.631860 |
1 | 2 | 26961.734433 |
9 | 10 | 26474.084736 |
26 | 27 | 24897.723614 |
5 | 6 | 21960.799719 |
0 | 1 | 21749.145978 |
38 | 39 | 21084.103489 |
18 | 19 | 20430.857011 |
22 | 23 | 19839.563326 |
30 | 31 | 19761.897868 |
10 | 11 | 19332.641482 |
23 | 24 | 19002.604957 |
27 | 28 | 18741.608875 |
40 | 41 | 18031.506002 |
31 | 32 | 16403.222829 |
17 | 18 | 15820.075220 |
21 | 22 | 15247.436339 |
11 | 12 | 14929.244341 |
25 | 26 | 14568.937406 |
34 | 35 | 13989.575528 |
39 | 40 | 13783.028706 |
33 | 34 | 13546.210778 |
42 | 43 | 13463.138546 |
7 | 8 | 13148.994199 |
16 | 17 | 13023.154252 |
44 | 45 | 11695.871003 |
41 | 42 | 11519.913664 |
20 | 21 | 11328.537256 |
24 | 25 | 10346.216563 |
36 | 37 | 10308.816413 |
14 | 15 | 9045.551883 |
8 | 9 | 8805.882422 |
29 | 30 | 8785.125694 |
35 | 36 | 8619.227546 |
6 | 7 | 8379.492279 |
28 | 29 | 8185.783800 |
15 | 16 | 7897.132309 |
37 | 38 | 7503.727479 |
2 | 3 | 6380.226664 |
43 | 44 | 6061.841438 |
32 | 33 | 5736.388608 |
4 | 5 | 5065.283995 |
weekly_sales = clean_data1['Weekly_Sales'].groupby(clean_data1['Store']).mean()
plt.figure(figsize=(25,8))
sns.barplot(weekly_sales.index, weekly_sales.values, palette='dark')
plt.grid()
plt.title('Durchschnittlicher Umsatz pro Laden', fontsize=18)
plt.ylabel('Umsatz', fontsize=16)
plt.xlabel('Laden', fontsize=16)
plt.show()
C:\Users\eebal\Anaconda3\lib\site-packages\seaborn\_decorators.py:36: FutureWarning: Pass the following variables as keyword args: x, y. From version 0.12, the only valid positional argument will be `data`, and passing other arguments without an explicit keyword will result in an error or misinterpretation. warnings.warn(
sns.distplot(clean_data1['Weekly_Sales'])
C:\Users\eebal\Anaconda3\lib\site-packages\seaborn\distributions.py:2551: FutureWarning: `distplot` is a deprecated function and will be removed in a future version. Please adapt your code to use either `displot` (a figure-level function with similar flexibility) or `histplot` (an axes-level function for histograms). warnings.warn(msg, FutureWarning)
<AxesSubplot:xlabel='Weekly_Sales', ylabel='Density'>
plt.figure(figsize=(20,1))
sns.lineplot(x="Week", y="IsHoliday", hue="Year", data=clean_data1)
plt.grid()
plt.xticks(np.arange(1, 53, step=1))
plt.title('holidays - Per Year', fontsize=18)
plt.ylabel('holidays', fontsize=16)
plt.xlabel('Week', fontsize=16)
plt.show()
Die Feiertage fallen jedes Jahr in dieselbe Woche. Betrachtet man die Kalenderwoche des Feiertags und die Kurve der wöchentlichen Verkäufe, so zeigt sich, dass Thanksgiving und Weihnachten haben einen Einfluss auf den Umsatz, die beiden anderen sind jedoch nicht signifikant. Aus diesem Grund lassen wir IsHoliday in der Tabelle stehen.
plt.figure(figsize=(20,8))
plt.axvline(6, 0,25000, color="r")
plt.axvline(36, 0,25000, color="r")
plt.axvline(47, 0,25000, color="r")
plt.axvline(52, 0,25000, color="r")
sns.lineplot(x="Week", y="Weekly_Sales", data=clean_data1)
plt.grid()
plt.xticks(np.arange(1, 53, step=1))
plt.legend(['IsHoliday', 'Weekly_Sales'], loc='best', fontsize=16)
plt.title('Betrachtung von Holidays und Sales', fontsize=18)
plt.ylabel('Sales', fontsize=16)
plt.xlabel('Date', fontsize=16)
plt.show()
plt.figure(figsize=(20,8))
sns.lineplot(x="Week", y="MarkDown1", hue="Year", data=clean_data1, marker="s")
sns.lineplot(x="Week", y="MarkDown2", hue="Year", data=clean_data1, marker=">") #, palette="tab10")
sns.lineplot(x="Week", y="MarkDown3", hue="Year", data=clean_data1, marker="+", palette="tab10")
sns.lineplot(x="Week", y="MarkDown4", hue="Year", data=clean_data1, marker="o")
sns.lineplot(x="Week", y="MarkDown5", hue="Year", data=clean_data1, marker="x", palette="Spectral")
plt.grid()
plt.xticks(np.arange(1, 53, step=1))
plt.title('MarkDowns - Pro Jahr', fontsize=18)
plt.ylabel('MarkDowns', fontsize=16)
plt.xlabel('Week', fontsize=16)
plt.show()
MarkDown1: viele Einzelaktionen, große Aktion kurz vor Woche 7 mit Super Bowl; MarkDown2: Aktion in Woche 52 vor Weihnachten, kleinere Aktion vor Woche 7 mit Super Bowl; MarkDown3: einzige Aktion in Woche 47 vor ThanksGiving; MarkDown4: viele Einzelaktionen, größere Aktion in Woche 5 vor Super Bowl; MarkDown5: viele Einzelaktionen, größere in Woche 48 zu ThanksGiving evtl. auch Black Friday
plt.figure(figsize=(20,8))
sns.lineplot(x="Date", y="MarkDown1", data=clean_data1)
sns.lineplot(x="Date", y="MarkDown2", data=clean_data1)
sns.lineplot(x="Date", y="MarkDown3", data=clean_data1)
sns.lineplot(x="Date", y="MarkDown4", data=clean_data1)
sns.lineplot(x="Date", y="MarkDown5", data=clean_data1)
sns.lineplot(x="Date", y="Weekly_Sales", data=clean_data1)
plt.grid()
plt.legend(['MarkDown1', 'MarkDown2', 'MarkDown3' 'MarkDown4', 'MarkDown5', 'Weekly_Sales'], loc='best', fontsize=16)
plt.title('Weekly Sales - MarkDowns', fontsize=18)
plt.ylabel('Sales', fontsize=16)
plt.xlabel('Date', fontsize=16)
plt.show()
# We include the holidays in our table to see where the holidays are located.
holidays = {'Super Bowl': pd.to_datetime(['12-Feb-10', '11-Feb-11', '10-Feb-12', '8-Feb-13']), 'Labor Day': pd.to_datetime(['10-Sep-10', '9-Sep-11', '7-Sep-12', '6-Sep-13']), 'Thanksgiving': pd.to_datetime(['26-Nov-10', '25-Nov-11', '23-Nov-12', '29-Nov-13']), 'Christmas': pd.to_datetime(['31-Dec-10', '30-Dec-11', '28-Dec-12', '27-Dec-13']) }
# Add column indicating which holiday follows
clean_data1.insert(clean_data1.columns.tolist().index('IsHoliday')+1, 'Holiday', np.nan)
for holiday in holidays:
clean_data1.loc[clean_data1['Date'].isin(holidays[holiday]), 'Holiday'] = holiday
clean_data1.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 420212 entries, 0 to 421569 Data columns (total 21 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Store 420212 non-null int64 1 Dept 420212 non-null int64 2 Date 420212 non-null datetime64[ns] 3 Weekly_Sales 420212 non-null float64 4 IsHoliday 420212 non-null int64 5 Holiday 29560 non-null object 6 Temperature 420212 non-null float64 7 Fuel_Price 420212 non-null float64 8 MarkDown1 420212 non-null float64 9 MarkDown2 420212 non-null float64 10 MarkDown3 420212 non-null float64 11 MarkDown4 420212 non-null float64 12 MarkDown5 420212 non-null float64 13 CPI 420212 non-null float64 14 Unemployment 420212 non-null float64 15 Size 420212 non-null int64 16 Week 420212 non-null int64 17 Year 420212 non-null int64 18 Type_A 420212 non-null uint8 19 Type_B 420212 non-null uint8 20 Type_C 420212 non-null uint8 dtypes: datetime64[ns](1), float64(10), int64(6), object(1), uint8(3) memory usage: 82.1+ MB
clean_data1.head(5)
Store | Dept | Date | Weekly_Sales | IsHoliday | Holiday | Temperature | Fuel_Price | MarkDown1 | MarkDown2 | ... | MarkDown4 | MarkDown5 | CPI | Unemployment | Size | Week | Year | Type_A | Type_B | Type_C | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 1 | 2010-02-05 | 24924.50 | 0 | NaN | 42.31 | 2.572 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 211.096358 | 8.106 | 151315 | 5 | 2010 | 1 | 0 | 0 |
1 | 1 | 1 | 2010-02-12 | 46039.49 | 1 | Super Bowl | 38.51 | 2.548 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 211.242170 | 8.106 | 151315 | 6 | 2010 | 1 | 0 | 0 |
2 | 1 | 1 | 2010-02-19 | 41595.55 | 0 | NaN | 39.93 | 2.514 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 211.289143 | 8.106 | 151315 | 7 | 2010 | 1 | 0 | 0 |
3 | 1 | 1 | 2010-02-26 | 19403.54 | 0 | NaN | 46.63 | 2.561 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 211.319643 | 8.106 | 151315 | 8 | 2010 | 1 | 0 | 0 |
4 | 1 | 1 | 2010-03-05 | 21827.90 | 0 | NaN | 46.50 | 2.625 | 0.0 | 0.0 | ... | 0.0 | 0.0 | 211.350143 | 8.106 | 151315 | 9 | 2010 | 1 | 0 | 0 |
5 rows × 21 columns
sns.lmplot(x='CPI',y='Weekly_Sales',data=clean_data1)
<seaborn.axisgrid.FacetGrid at 0x1fcd5383940>
sns.jointplot(x="Size", y="Weekly_Sales", data=clean_data1, kind="reg", truncate=False, height=7)
<seaborn.axisgrid.JointGrid at 0x22464a460d0>
sns.lmplot(x='Temperature',y='Weekly_Sales',data=clean_data1)
<seaborn.axisgrid.FacetGrid at 0x22400e1eca0>
sns.lmplot(x='Fuel_Price',y='Weekly_Sales',data=clean_data1)
<seaborn.axisgrid.FacetGrid at 0x22401055850>
sns.lmplot(x='Unemployment',y='Weekly_Sales',data=clean_data1)
<seaborn.axisgrid.FacetGrid at 0x22401074130>
sns.lmplot(x='Week',y='Weekly_Sales',data=clean_data1)
<seaborn.axisgrid.FacetGrid at 0x22401715d90>
3.4. Test auf Korrelation¶
sns.heatmap(clean_data1.corr()) # Betrachtung der Korrelation über eine heatmap
<AxesSubplot:>
plt.figure(figsize=(8, 12))
heatmap = sns.heatmap(clean_data1.corr()[['Weekly_Sales']].sort_values(by='Weekly_Sales', ascending=False), vmin=-1, vmax=1, annot=True, cmap='PiYG')
heatmap.set_title('which features influence Weekly Sales the most ?', fontdict={'fontsize':18}, pad=16);
clean_data1['Holiday'] = clean_data1['Holiday'].fillna('')
clean_data1.isnull().sum()
Store 0 Dept 0 Date 0 Weekly_Sales 0 IsHoliday 0 Holiday 0 Temperature 0 Fuel_Price 0 MarkDown1 0 MarkDown2 0 MarkDown3 0 MarkDown4 0 MarkDown5 0 CPI 0 Unemployment 0 Size 0 Week 0 Year 0 Type_A 0 Type_B 0 Type_C 0 dtype: int64
3.5. Erstellen und Analysieren eines neuen Datensatzes¶
"kleiner Datensatz": Testdaten, für die wir nur 1 Jahr berücksichtigen, für das wir MarkDown-Werte haben.
# TEST: Create a small dataset that only looks at the times for which we have values for the MarkDowns.
# then we can run a test model with this data set
small_dataset1 = clean_data1[(clean_data1["Year"]==2011) & (clean_data1["Week"]>=45)]
small_dataset2 = clean_data1[(clean_data1["Year"]==2012) & (clean_data1["Week"]<=43)]
small_dataset = pd.concat([small_dataset1, small_dataset2])
small_dataset.head(5)
Store | Dept | Date | Weekly_Sales | IsHoliday | Holiday | Temperature | Fuel_Price | MarkDown1 | MarkDown2 | ... | MarkDown4 | MarkDown5 | CPI | Unemployment | Size | Week | Year | Type_A | Type_B | Type_C | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
92 | 1 | 1 | 2011-11-11 | 18689.54 | 0 | 59.11 | 3.297 | 10382.90 | 6115.67 | ... | 2406.62 | 6551.42 | 217.998085 | 7.866 | 151315 | 45 | 2011 | 1 | 0 | 0 | |
93 | 1 | 1 | 2011-11-18 | 19050.66 | 0 | 62.25 | 3.308 | 6074.12 | 254.39 | ... | 427.39 | 5988.57 | 218.220509 | 7.866 | 151315 | 46 | 2011 | 1 | 0 | 0 | |
94 | 1 | 1 | 2011-11-25 | 20911.25 | 1 | Thanksgiving | 60.14 | 3.236 | 410.31 | 98.00 | ... | 8.00 | 554.92 | 218.467621 | 7.866 | 151315 | 47 | 2011 | 1 | 0 | 0 |
95 | 1 | 1 | 2011-12-02 | 25293.49 | 0 | 48.91 | 3.172 | 5629.51 | 68.00 | ... | 2084.64 | 20475.32 | 218.714733 | 7.866 | 151315 | 48 | 2011 | 1 | 0 | 0 | |
96 | 1 | 1 | 2011-12-09 | 33305.92 | 0 | 43.93 | 3.158 | 4640.65 | 19.00 | ... | 3639.42 | 14461.82 | 218.961846 | 7.866 | 151315 | 49 | 2011 | 1 | 0 | 0 |
5 rows × 21 columns
small_dataset.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 150929 entries, 92 to 421569 Data columns (total 21 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Store 150929 non-null int64 1 Dept 150929 non-null int64 2 Date 150929 non-null datetime64[ns] 3 Weekly_Sales 150929 non-null float64 4 IsHoliday 150929 non-null int64 5 Holiday 150929 non-null object 6 Temperature 150929 non-null float64 7 Fuel_Price 150929 non-null float64 8 MarkDown1 150929 non-null float64 9 MarkDown2 150929 non-null float64 10 MarkDown3 150929 non-null float64 11 MarkDown4 150929 non-null float64 12 MarkDown5 150929 non-null float64 13 CPI 150929 non-null float64 14 Unemployment 150929 non-null float64 15 Size 150929 non-null int64 16 Week 150929 non-null int64 17 Year 150929 non-null int64 18 Type_A 150929 non-null uint8 19 Type_B 150929 non-null uint8 20 Type_C 150929 non-null uint8 dtypes: datetime64[ns](1), float64(10), int64(6), object(1), uint8(3) memory usage: 22.3+ MB
small_dataset.isnull().sum()
Store 0 Dept 0 Date 0 Weekly_Sales 0 IsHoliday 0 Holiday 0 Temperature 0 Fuel_Price 0 MarkDown1 0 MarkDown2 0 MarkDown3 0 MarkDown4 0 MarkDown5 0 CPI 0 Unemployment 0 Size 0 Week 0 Year 0 Type_A 0 Type_B 0 Type_C 0 dtype: int64
sns.heatmap(small_dataset.corr(),cmap="PiYG")
<AxesSubplot:>
plt.figure(figsize=(8, 12))
heatmap = sns.heatmap(small_dataset.corr()[['Weekly_Sales']].sort_values(by='Weekly_Sales', ascending=False), vmin=-1, vmax=1, annot=True, cmap='PiYG')
heatmap.set_title('which features influence Weekly Sales the most ?', fontdict={'fontsize':18}, pad=16);
# We take out the following data:
# Fuel_Price has only a weak correlation to Weekly_Sales but a high correlation to Year
# Unemployment, CPI and Temperature show hardly any correlation
# MarkDown1,2 and 5 and IsHoliday were taken out afterwards, since these have no influence in the model
clean_smalldata = small_dataset.drop(['Fuel_Price', 'Unemployment', 'CPI', 'Temperature', 'Holiday', 'MarkDown1', 'MarkDown2', 'MarkDown5', 'IsHoliday'], axis = 1) #
clean_smalldata.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 150929 entries, 92 to 421569 Data columns (total 12 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Store 150929 non-null int64 1 Dept 150929 non-null int64 2 Date 150929 non-null datetime64[ns] 3 Weekly_Sales 150929 non-null float64 4 MarkDown3 150929 non-null float64 5 MarkDown4 150929 non-null float64 6 Size 150929 non-null int64 7 Week 150929 non-null int64 8 Year 150929 non-null int64 9 Type_A 150929 non-null uint8 10 Type_B 150929 non-null uint8 11 Type_C 150929 non-null uint8 dtypes: datetime64[ns](1), float64(3), int64(5), uint8(3) memory usage: 11.9 MB
3.6. normale (große) Daten¶
# we take out the following data:
# MarkDown1-MarkDown5, since many data are missing, there is hardly any correlation.
# Fuel_Price has only a weak correlation to Weekly_Sales but a high correlation to Year
# Unemployment, CPI and Temperature show hardly correlation
# Holiday - interferes later in modeling
clean_data2 = clean_data1.drop(['MarkDown1', 'MarkDown2', 'MarkDown3', 'MarkDown4', 'MarkDown5', 'Fuel_Price', 'Unemployment', 'CPI', 'Temperature', 'Holiday'], axis = 1) #
clean_data2.isnull().sum()
Store 0 Dept 0 Date 0 Weekly_Sales 0 IsHoliday 0 Size 0 Week 0 Year 0 Type_A 0 Type_B 0 Type_C 0 dtype: int64
clean_data2.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 420212 entries, 0 to 421569 Data columns (total 11 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Store 420212 non-null int64 1 Dept 420212 non-null int64 2 Date 420212 non-null datetime64[ns] 3 Weekly_Sales 420212 non-null float64 4 IsHoliday 420212 non-null int64 5 Size 420212 non-null int64 6 Week 420212 non-null int64 7 Year 420212 non-null int64 8 Type_A 420212 non-null uint8 9 Type_B 420212 non-null uint8 10 Type_C 420212 non-null uint8 dtypes: datetime64[ns](1), float64(1), int64(6), uint8(3) memory usage: 50.1 MB
sns.heatmap(clean_data2.corr(),cmap="PiYG")
<AxesSubplot:>
plt.figure(figsize=(8, 12))
heatmap = sns.heatmap(clean_data2.corr()[['Weekly_Sales']].sort_values(by='Weekly_Sales', ascending=False), vmin=-1, vmax=1, annot=True, cmap='PiYG')
heatmap.set_title('which features influence Weekly Sales the most ?', fontdict={'fontsize':18}, pad=16);
#sns.pairplot(clean_data2, vars=['Weekly_Sales', 'Dept', 'Size', 'Type_A'])
# save data local
clean_data2.to_csv('clean_data2.csv', index = False)
3.7. Testen und Trainieren von Daten¶
# Split Data
inputs = clean_data2.drop(['Weekly_Sales', 'Date'], axis=1)
targets = clean_data2['Weekly_Sales'] # das ist die Zielvariable
#Feature Scaling (transforms our data into standard distributed data)
# the data is transformed in such a way that then a mean of 0 and a standard deviation of 1 is obtained
# ~ (𝜇, 𝜎2) ~ (0,1)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(inputs)
inputs_scaled = scaler.transform(inputs)
# Split data into test and training - test data is 20%.
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(inputs_scaled, targets, test_size=0.2, random_state=365)
4. Modellierung und Evaluation¶
4.1. Random Forest mit normalen (großen) Daten¶
class sklearn.ensemble.RandomForestRegressor(n_estimators=100, *, criterion='mse', max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0. 0, max_features='auto', max_leaf_nodes=Keine, min_impurity_decrease=0.0, min_impurity_split=Eine, bootstrap=True, oob_score=False, n_jobs=Eine, random_state=Eine, verbose=0, warm_start=False, ccp_alpha=0.0, max_samples=Eine)
bootstrap: Ob bei der Erstellung von Bäumen Bootstrap-Stichproben verwendet werden. Wenn False, wird der gesamte Datensatz zur Erstellung jedes Baums verwendet. Voreinstellung ist True.
max_depth: Die Standardwerte für die Parameter, die die Größe der Bäume steuern (z. B. max_depth, min_samples_leaf usw.), führen zu vollständig ausgewachsenen und nicht beschnittenen Bäumen, die bei einigen Datensätzen sehr groß sein können. Um den Speicherverbrauch zu reduzieren, sollte die Komplexität und Größe der Bäume durch die Einstellung dieser Parameterwerte kontrolliert werden. Der Standardwert ist None und bedeutet, dass die Knoten so lange expandiert werden, bis alle Blätter rein sind oder bis alle Blätter weniger als min_samples_split samples enthalten. Der Wert von 70 hat sich in Tests bewährt.
max_features: Der Standardwert max_features="auto" verwendet n_features bei jedem Split. Die Voreinstellung ist "auto".
min_samples_leaf: Die Mindestanzahl von Stichproben, die für einen Blattknoten erforderlich sind. Ein Teilungspunkt in beliebiger Tiefe wird nur berücksichtigt, wenn er mindestens min_samples_leaf Trainingsmuster in jedem der linken und rechten Zweige hinterlässt. Dies kann zu einer Glättung des Modells führen, insbesondere bei Regressionen. Standardwert ist 1. Testreihen haben gezeigt, dass der Wert 4 die besten Ergebnisse liefert.
min_samples_split: Die Mindestanzahl von Stichproben, die erforderlich ist, um einen internen Knoten aufzuteilen. Standardwert ist 2, in Testreihen hat sich der Wert 10 als am sinnvollsten erwiesen.
n_Schätzer: Dies ist die Anzahl der Bäume, die Sie erstellen möchten, bevor Sie die Maximalstimme oder den Durchschnitt der Vorhersagen nehmen. Eine höhere Anzahl von Bäumen führt zu einer besseren Leistung, macht den Code aber auch langsamer. Die Voreinstellung ist 100. Testreihen haben gezeigt: Unter und über 400 nimmt die Genauigkeit ab.
n_jobs: wie viele Jobs parallel laufen können, beeinflusst die Geschwindigkeit des Algorithmus
Die Merkmale werden bei jedem Split immer zufällig permutiert. Daher kann selbst bei gleichen Trainingsdaten, max_features=n_features und bootstrap=False der beste gefundene Split variieren, wenn die Kriteriumverbesserung für mehrere Splits, die bei der Suche nach dem besten Split aufgezählt werden, identisch ist. Um ein deterministisches Verhalten bei der Anpassung zu erhalten, muss random_state in der Testreihe angegeben werden.
from sklearn.ensemble import RandomForestRegressor
rfr = RandomForestRegressor(bootstrap = True, max_depth = 70, max_features = 'auto', min_samples_leaf = 4, min_samples_split = 10, n_estimators = 10)
# rfr = RandomForestRegressor(n_estimators = 400,max_depth=15,n_jobs=5) # 0.955111
rfr.fit(x_train,y_train)
y_predictions=rfr.predict(x_test)
from sklearn.metrics import mean_absolute_error
mean_absolute_error(y_test, y_predictions)
1506.3206109502669
from sklearn.metrics import mean_squared_error
mean_squared_error(y_test, y_predictions)
19103020.45675435
accuracy = rfr.score(x_test, y_test)
print(accuracy)
0.9645552402964631
feature_list = list(inputs.columns)
importances = list(rfr.feature_importances_)
feature_importances = [(feature, round(importance, 2)) for feature, importance in zip(feature_list, importances)]
feature_importances = sorted(feature_importances, key = lambda x: x[1], reverse = True)
[print('Variable: {:20} Importance: {}'.format(*pair)) for pair in feature_importances];
Variable: Dept Importance: 0.64 Variable: Size Importance: 0.2 Variable: Store Importance: 0.08 Variable: Week Importance: 0.05 Variable: Year Importance: 0.01 Variable: IsHoliday Importance: 0.0 Variable: Type_A Importance: 0.0 Variable: Type_B Importance: 0.0 Variable: Type_C Importance: 0.0
4.2. Random Forest mit neuen kleinen Daten¶
inputs_small = clean_smalldata.drop(['Weekly_Sales', 'Date'], axis=1)
targets_small = clean_smalldata['Weekly_Sales'] # das ist die Zielvariable
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(inputs_small)
inputs_scaleds = scaler.transform(inputs_small)
from sklearn.model_selection import train_test_split
x_trains, x_tests, y_trains, y_tests = train_test_split(inputs_scaleds, targets_small, test_size=0.2, random_state=365)
#rfr_small = RandomForestRegressor(n_estimators = 400,max_depth=15,n_jobs=5)
rfr_small = RandomForestRegressor(bootstrap = True, max_depth = 70, max_features = 'auto', min_samples_leaf = 4, min_samples_split = 10, n_estimators = 10)
rfr_small.fit(x_trains,y_trains)
y_predictions_small=rfr_small.predict(x_tests)
MAE - der mittlere absolute Fehler¶
gibt die durchschnittliche absolute Abweichung des Prognosewertes vom tatsächlichen Wert an.
Wert, der tatsächlich eingetreten ist. Über- und Unterschätzungen werden in ungerichteter Weise kumuliert, so dass MAE=0
als perfekte Prognose interpretiert werden kann, d.h. der Prognosewert für jede Periode t entspricht genau dem tatsächlichen Wert.
dem tatsächlich eingetretenen Wert.
mean_absolute_error(y_tests, y_predictions_small)
1766.4411977247057
Wir erhalten einen MAE von 1.716,1138, d.h. die durchschnittliche Abweichung beträgt etwa 1.716 Dollar, mit der unsere Prognose von den tatsächlichen Werten abweicht.
MSE - Mittlerer quadratischer Fehler¶
ist die quadratische Form des ME. Dies hat zur Folge, dass große Abweichungen vom tatsächlichen Wert überproportional stark gewichtet werden im Vergleich zu kleineren Abweichungen, die in der Praxis oft vernachlässigbar sind. Außerdem werden die einzelnen Prognosefehler nun wie beim MAE kumuliert, so dass die perfekte Prognose wieder bei MSE=0 vorliegt.
mean_squared_error(y_tests, y_predictions_small)
17008422.72019644
R² - Bestimmungskoeffizient¶
Das so genannte Bestimmtheitsmaß (R²) drückt aus, wie gut die Regressionsgerade die Beziehung zwischen der unabhängigen und der abhängigen Variable wiedergibt. R² liegt zwischen 0 und 1, wobei der Wert R² = 1 bedeuten würde, dass jeder beobachtete Datenpunkt direkt auf der Regressionsgeraden liegt.
accuracy_small = rfr_small.score(x_tests, y_tests)
print(accuracy_small)
0.9684712372223249
Das bedeutet, dass die vorhergesagten Werte relativ wenig abweichen und zu etwa 97 % auf der Geraden liegen.
# R² = 0.964437 with Markdown 1,2 and 5 included - were thus subsequently removed and R² increases
# R² = 0.9645159264218026 (n_estimators = 400,max_depth=15,n_jobs=5)
# R² = 0.9692695835667837 with new parameters
# R² = 0.9698712189554696 with new parameters and removal of variables with importance 0 each variable since random
#The importance indicates how much the inclusion of a particular variable improves the prediction.
feature_list = list(inputs_small.columns) # in einen numpy array umwandeln
importances = list(rfr_small.feature_importances_)
feature_importances = [(feature, round(importance, 2)) for feature, importance in zip(feature_list, importances)]
feature_importances = sorted(feature_importances, key = lambda x: x[1], reverse = True) # sortieren
[print('Variable: {:20} Importance: {}'.format(*pair)) for pair in feature_importances];
Variable: Dept Importance: 0.63 Variable: Size Importance: 0.14 Variable: Store Importance: 0.08 Variable: Type_A Importance: 0.06 Variable: MarkDown3 Importance: 0.04 Variable: Week Importance: 0.02 Variable: MarkDown4 Importance: 0.01 Variable: Year Importance: 0.0 Variable: Type_B Importance: 0.0 Variable: Type_C Importance: 0.0
predict_data = pd.DataFrame({'Weekly_Sales_predicted': y_predictions_small})
y_test_data = pd.DataFrame(y_tests[0:30186])
predict_data.head(5)
Weekly_Sales_predicted | |
---|---|
0 | 264.884683 |
1 | 7753.860622 |
2 | 26689.840592 |
3 | 7708.035305 |
4 | 6410.364661 |
y_test_data.head(5)
Weekly_Sales | |
---|---|
314427 | 239.81 |
100596 | 7081.39 |
284309 | 21586.81 |
149014 | 9063.87 |
368304 | 5411.37 |
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(y_test_data, predict_data)
w = lr.coef_[0]
plt.scatter(y_test_data, predict_data)
plt.plot(y_test_data, w*y_test_data, c='red')
plt.title("Vorhersage im Vergleich zu Ist-Daten")
plt.xlabel("Ist-Daten")
plt.ylabel("Vorhersage")
plt.show()