Der Umfang von gene­rier­ten Daten steigt stän­dig durch die zuneh­mende Digi­ta­li­sie­rung nahezu aller Lebens­be­rei­che – von Indus­trie 4.0 über den öffent­li­chen Sek­tor bis hin zu Smar­tHome. Dabei ver­ber­gen sich in den Daten enorme Infor­ma­ti­ons­men­gen, wel­che zur Erfül­lung vie­ler­lei Ziele genutzt wer­den kön­nen, etwa zur Effi­zi­enz­stei­ge­rung in der Wirt­schaft, bei­spiels­weise in Form von kun­den­ori­en­tier­ten Kauf­emp­feh­lun­gen von Amazon. 

Dabei ist die schiere Menge an Daten glei­cher­ma­ßen Fluch und Segen. Denn wäh­rend ein zuneh­men­der Umfang an Mess­punk­ten eine offen­sicht­li­che Infor­ma­ti­ons­zu­nahme bedeu­tet, droht die Flut an beob­ach­te­ten Attri­bu­ten die tat­säch­lich für die Ziel­er­rei­chung rele­van­ten Infor­ma­tio­nen zu ver­wäs­sern. Um schließ­lich die Infor­ma­tio­nen aus den Daten zu extra­hie­ren und nutz­bar zu machen, ist zudem ein geeig­ne­ter Algo­rith­mus zu wäh­len, wel­cher die enorme Daten­menge auch effi­zi­ent ver­ar­bei­ten kann. 

In die­ser drei­tei­li­gen Blog­bei­trag­se­rie soll anhand eines Bei­spiels skiz­ziert wer­den, wie sich große Daten­be­stände nutz­bar machen las­sen. Dabei behan­delt der erste Teil die the­ma­ti­sche Ein­füh­rung in das Bei­spiel sowie die Daten­ex­plo­ra­tion, im zwei­ten Teil wer­den die Daten berei­nigt und auf­be­rei­tet und im drit­ten Teil zum Trai­ning eines Machine-Lear­ning-Modells genutzt, nach­dem die­ses vor­ge­stellt wurde.

Die Pro­blem­stel­lung 

Als Bei­spiel nut­zen wir eine Chall­enge von Kaggle, wel­che hier ver­linkt ist. Dort kön­nen die Infor­ma­tio­nen auch nach­ge­le­sen und her­un­ter­ge­la­den werden. 

Die US-ame­ri­ka­ni­sche Orga­ni­sa­tion ASHRAE (Ame­ri­can Society of Hea­ting, Ref­ri­ge­ra­ting and Air-Con­di­tio­ning Engi­neers) ver­tritt als Berufs­ver­band die Täti­gen aus den Berei­chen Heizungs‑, Lüftungs‑, Klima- und Kühl­an­la­gen­bau. Es gilt eine Vor­her­sage über den Ener­gie­ver­brauch von Gebäu­den zu tref­fen, um unter dem Aspekt der Pay-for-Per­for­mance-Finan­zie­rung ermit­teln zu kön­nen, wie groß die Ein­spa­rung von ener­ge­ti­schen Opti­mie­run­gen eines Gebäu­des ist. Bei die­ser Finan­zie­rung zahlt der Eigen­tü­mer des Gebäu­des einen Teil der tat­säch­lich erreich­ten Ein­spa­run­gen an den­je­ni­gen, der die Bau­maß­nahme zur Ener­gie­ver­brauchs­sen­kung durch­ge­führt hat und finan­ziert diese damit. Doch wäh­rend der tat­säch­li­che Ener­gie­ver­brauch nach einer ener­ge­ti­schen Opti­mie­rungs­maß­nahme mess­bar ist, muss der theo­re­ti­sche Ener­gie­ver­brauch ohne Maß­nahme für die Zeit nach der Opti­mie­rung mit einem Modell berech­net werden.

Die Daten

Der erste Schritt einer Ana­lyse ist stets die Betrach­tung der Roh­da­ten. In die­sem Fall lie­gen Daten zum his­to­ri­sche Ener­gie­ver­bauch von Gebäu­den für ein Jahr vor, wel­che mit einer Gra­nu­la­ri­tät von einer Stunde gemes­sen wur­den. Gege­ben ist in die­sem Fall der his­to­ri­sche Ener­gie­ver­bauch von Gebäu­den in einem Jahr in einer Gra­nu­la­ri­tät von einer Stunde. Es gibt vier ver­schie­dene Ener­gie­for­men, wel­che betrach­tet wer­den: Elec­tri­city, Hot Water, Chil­led Water und Steam, sowie deren abge­le­se­ner Ver­brauch in kWh. Zusätz­lich gibt es Wet­ter­in­for­ma­tio­nen von diver­sen Wet­ter­sta­tio­nen in des­sen Ein­zugs­ge­bie­ten die Gebäude lie­gen. Es gibt einen Trai­nings- und einen Test­da­ten­satz, wobei der Test­da­ten­satz keine Werte für den Ver­brauch beinhal­tet. Im Detail glie­dern sich die Daten wie folgt: 

train.csv

FeldBedeu­tung
building_idFremd­schlüs­sel für den Join mit den Gebäudedaten
meterEner­gie­form {0:Electricity, 1:chilledwater, 2:hotwater, 3:steam}
timestampZeit­stem­pel der Messung
meter_readingVer­brauch in kWh

building_metadata.csv

FeldBedeu­tung
site_idFremd­schlüs­sel für den Join mit den Wetterdaten
building_idPri­mär­schlüs­sel
primary_useIndi­ka­tor der pri­mä­ren Nut­zung des Gebäudes
square_feetGebäu­de­flä­che in ft2
year_builtBau­jahr
floor_countAnzahl der Stockwerke

weather_train.csv

FeldBedeu­tung
site_idPri­mär­schlüs­sel
timestampZeit­stem­pel der Messung
ait_temperatureLuft­tem­pe­ra­tur in °C
cloud_coverageAnteil des durch Wol­ken bedeck­ten Himmels
dew_temperatureTem­pe­ra­tur in °C
precip_depth_1_hrNie­der­schlag in mm/m2
sea_level_pressureLuft­druck in mbar
wind_directionWind­rich­tung in Kom­pass­rich­tung (0°-360°)
wind_speedWind­ge­schwin­dig­keit in m/s

Die Explo­ra­tion

Damit sind die Meta­in­for­ma­tio­nen der Daten bereits vor­han­den. Um sich ein bes­se­res Bild der Daten machen zu kön­nen, bie­tet sich eine Visua­li­sie­rung an. Diese wer­den wir wie alle wei­te­ren fol­gen­den Schritte mit Python 3 vor­neh­men. Dazu ist zunächst der Import der Daten (hier als pan­das Data­frame) not­wen­dig. Anschlie­ßend wer­den die Daten­sätze anhand der Fremd­schlüs­sel zusammengeführt: 

train = train.merge(building_metadata, on='building_id', how='left') 
train = train.merge(weather_train, on=['site_id', 'timestamp'], how='left') 

test = test.merge(building_metadata, on='building_id', how='left') 
test = test.merge(weather_test, on=['site_id','timestamp'], how='left') 

Mit die­sen ver­ein­ten Daten­sät­zen kön­nen nun erste ein­fa­che Ana­ly­sen der Daten­struk­tu­ren vor­ge­nom­men wer­den. Zunächst ist es inter­es­sant zu betrach­ten, ob es feh­lende Werte (NAs) in ein­zel­nen Daten­rei­hen bzw. Attri­bu­ten gibt. Dazu lässt sich bei­spiels­weise ein ein­fa­ches Dia­gramm erstellen:

import numpy as np 
import matplotlib.pyplot as plt 

train_data = (train.count() / len(train)).drop('meter_reading').sort_values().values 
ind = np.arange(len(train_data)) 
width = 0.35 

fig, axes = plt.subplots(1,1,figsize=(14, 6), dpi=200) 
tr = axes.bar(ind, train_data, width, color='red') 

test_data = (test.count() / len(test)).drop('row_id').sort_values().values 
tt = axes.bar(ind+width, test_data, width, color='blue') 

axes.set_title('Anteil von NAs an den Daten', fontsize=16) 
axes.set_ylabel('Anteil verfügbarer Daten [%]'); 
axes.set_xticks(ind + width / 2) 
axes.set_xticklabels((train.count() / len(train)).drop('meter_reading')
                      .sort_values().index, rotation=40) 

axes.legend([tr, tt], ['Train', 'Test']); 

Wir erhal­ten bei Aus­füh­rung obi­gen Codes fol­gende Grafik:

Datenanalyse und Machine Learning anhand eines Use Cases – I Bild1
Abbil­dung 1 Anteil von NAs an Datensatz

Es ist ersicht­lich, dass die Daten­ver­füg­bar­keit der Attri­bute floor_count, year_built und cloud_coverage sehr gering ist. Auch die Attri­bute precip_depth_1_hr, wind_direction und sea_level_pressure sind nicht für jeden Daten­punkt ver­füg­bar. Die übri­gen Attri­bute sind für nahezu jeden Daten­punkt vorhanden.

Ein hoher Anteil feh­len­der Werte eines Attri­buts dis­qua­li­fi­ziert die­ses Attri­but oft­mals für wei­tere Ana­ly­sen. Zusam­men­hänge zwi­schen die­sem Attri­but und der vor­her­zu­sa­gen­den Größe, in unse­rem Fall der Ener­gie­ver­brauch, las­sen sich dann nur für einen klei­nen Teil der Daten unter­su­chen. Dies führt dann beim spä­te­ren Trai­ning des Machine-Lear­ning-Modells zu Problemen.

Auch eine Betrach­tung des Ener­gie­ver­brauchs im Laufe der Zeit erscheint sinn­voll. Dazu erstel­len wir eine wei­tere Grafik: 

fig, axes = plt.subplots(1, 1, figsize=(14, 6), dpi=200) 

train[['timestamp', 'meter_reading']]
.set_index(pd.DatetimeIndex(train['timestamp'])).resample('H').mean()

['meter_reading'].plot(ax=axes, label='By hour', alpha=0.8, color='red').set_ylabel('Energieverbrauch', fontsize=14); 

train[['timestamp', 'meter_reading']]
.set_index(pd.DatetimeIndex(train['timestamp'])).resample('D').mean()

['meter_reading'].plot(ax=axes, label='By day', alpha=1, color='blue').set_ylabel('Energieverbrauch', fontsize=14); 

axes.set_title('Gemittelter Energieverbrauch pro Stunde und Tag', fontsize=16); 
axes.legend(); 
Datenanalyse und Machine Learning anhand eines Use Cases – I Bild2
Abbil­dung 2 Gemit­tel­ter Ener­gie­ver­brauch pro Stunde und Tag

Es sind gleich meh­rere inter­es­sante Dinge zu beob­ach­ten. Zunächst ist der Ener­gie­ver­brauch über das Jahr ver­teilt sehr unter­schied­lich. Diese enor­men Unter­schiede las­sen sich nicht mit den unter­schied­li­chen Wet­ter­be­din­gun­gen wäh­rend des Jah­res erklären. 

Dar­über hin­aus lässt sich erken­nen, dass die stünd­li­chen Werte sehr viel stär­ker schwan­ken, als die täg­li­chen Werte. Die stünd­li­chen Werte beinhal­ten dem­nach ein­zelne grö­ßere Aus­rei­ßer, wel­che wei­tere Ana­ly­sen stark ver­fäl­schen können. 

Was kann man nun tun, um den Daten­satz genauer zu ana­ly­sie­ren? Anhand der bekann­ten Attri­bute wirkt es plau­si­bel, den Daten­satz anhand der kate­go­ri­schen Attri­bute auf­zu­tei­len, also bei­spiels­weise anhand von meter_type, primary_use oder site_id. 

Im Fol­gen­den betrach­ten wir den Daten­satz für den meter_type chil­led water. Für die übri­gen Werte des Attri­buts meter_type kann ana­log vor­ge­gan­gen werden. 

Ein­ge­schränkt auf den meter_type chil­led water ergibt sich fol­gen­der Graph:

Datenanalyse und Machine Learning anhand eines Use Cases – I Bild3
Abbil­dung 3 Gemit­tel­ter Ener­gie­ver­brauch begrenzt auf chilled_water

Der Ver­lauf des Ener­gie­ver­brauchs über das Jahr wirkt nun schon homo­ge­ner als zuvor. Eine wei­tere Auf­tei­lung anhand des site_id Attri­buts ist denk­bar. Dabei fällt auf, dass nicht zu jeder site_id Ein­träge mit dem meter_type chilled_water vor­han­den sind. 

for i in range(train_combined['site_id'].nunique()): 
    print(str(i) + " " + 
    str(len(train_combined[train_combined['site_id'] == i]))) 
site_idAnzahl Ein­träge
0168253
10
2863845
30
40
50
6164107
7130956
80
9833113

Es gibt also nur Ein­träge zu den site_ids 0, 2, 6, 7 und 9. Plot­tet man nun die Daten­sätze für diese site_ids erhält man fol­gen­des Bild: 

Datenanalyse und Machine Learning anhand eines Use Cases – I Bild4.1
Abbil­dung 4 Gemit­tel­ter Ener­gie­ver­brauch für Sites 0 und 2
Datenanalyse und Machine Learning anhand eines Use Cases – I Bild4.2
Abbil­dung 5 Gemit­tel­ter Ener­gie­ver­brauch für Sites 6 und 7
Datenanalyse und Machine Learning anhand eines Use Cases – I Bild4.3
Abbil­dung 6 Gemit­tel­ter Ener­gie­ver­brauch für Site 9

Es ist ersicht­lich, dass die Unter­schei­dung nach der site_id wei­tere Inho­mo­ge­ni­tä­ten offen­bart. Man könnte nun noch wei­tere Unter­schei­dun­gen vor­neh­men: nach meter_type und site_id und primary_use etwa. Und selbst dann ließe sich noch wei­ter dif­fe­ren­zie­ren, etwa nach der building_id. Man sieht: oft ist eine belie­big feine Gra­nu­la­ri­tät möglich. 

Cor­re­la­tion-Heat-Maps

Eine wei­tere Mög­lich­keit zur Visua­li­sie­rung von Zusam­men­hän­gen bie­tet die Cor­re­la­tion-Heat-Map. In die­ser wer­den die Kor­re­la­tio­nen der aus­ge­wähl­ten Attri­bute visu­ell dar­ge­stellt. Bei­spiel­haft für unse­ren Daten­satz könnte das so aussehen: 

train_values = train_combined.drop(['building_id', 'meter', 'site_id'], axis = 1) 
correlation_values = train_values.corr() 

axis_heatmap = sns.heatmap(correlation_values, vmin = -1, vmax=1, center=0, 
                           cmap = sns.diverging_palette(20, 220, n = 200), 
                           square = True) 

axis_heatmap.set_xticklabels(axis_heatmap.get_xticklabels(), rotation = 45,  
                             horizontalalignment = 'right') 
Datenanalyse und Machine Learning anhand eines Use Cases – I Bild5
Abbil­dung 7 Cor­re­la­tion-Heat-Map für Beispieldatensatz

Dabei wur­den die Attri­bute building_id, meter und site_id aus­sor­tiert, da diese als kate­go­ri­sche Attri­bute keine sinn­volle Kor­re­la­tion mit ande­ren Attri­bu­ten auf­wei­sen können. 

Box­plots

Zuletzt sei die Mög­lich­keit von Box­Plots erwähnt. Diese ermög­li­chen die Visua­li­sie­rung der Ver­tei­lung der ein­zel­nen Daten­punkte und kann so dabei unter­stüt­zen Aus­rei­ßer zu erken­nen. Hier am Bei­spiel von den Ein­trä­gen mit primary_use Edu­ca­tion, wobei die Werte der Attri­bute alle­samt auf den Wer­te­be­reich von 0 bis 1 nor­ma­li­siert wurden. 

columns_with_values = ['meter_reading', 'square_feet', 'age', 'air_temperature',  
                       'cloud_coverage', 'dew_temperature', 'sea_level_pressure', 
                       'wind_speed'] 

plt.figure(figsize=(16, 7)) 
sns.boxplot(data = train_combined[train_combined['primary_use'] == 'Education'], order = columns_with_values) 
sns.despine(offset = 10, trim = True) 
Datenanalyse und Machine Learning anhand eines Use Cases – I Bild6
Abbil­dung 8 Box­Plots für Beispieldatensatz

Die „Box“ der Box­plots wird durch die unte­ren und obe­ren Quar­tile begrenzt, die Linie im Innern einer Box mar­kiert den Median. Die „Füh­ler“ an der Box rei­chen maxi­mal bis zum 1,5‑fachen des Inter­quar­tils­ab­stands. Alle Punkte außer­halb die­ser Füh­ler wer­den als „Aus­rei­ßer“ bezeich­net. Nähere Infos dazu gibt es u.A. hier.

Aus­blick

Wir haben nun diverse Mög­lich­kei­ten gese­hen, um die Daten zu visua­li­sie­ren und sich einen bes­se­ren Über­blick zu ver­schaf­fen. Die­ser Über­blick ist nötig, da die Daten oft­mals Inho­mo­ge­ni­tä­ten auf­wei­sen, ver­ur­sacht zum Bei­spiel durch Mess­feh­ler. Auch kate­go­ri­sche Unter­schiede in den Daten kön­nen so erkannt und berück­sich­tigt wer­den, genauso wie sai­so­nale Komponenten. 

Wie mit Inho­mo­ge­ni­tä­ten umge­gan­gen wer­den kann, wel­che in die­sem Bei­spiel deut­lich zu erken­nen waren, wird im nächs­ten Teil die­ser Blog­bei­trag­se­rie beschrieben.