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

import matplotlib.pyplot as plt
import seaborn as sns
import os
from IPython.core.pylabtools import figsize

%matplotlib inline
figsize(10, 8) 
plt.style.use('Solarize_Light2')
os.chdir('D:\Data\Projects\Klassifikation\Klassifikation_West Nile Virus')
pd.set_option('display.max_columns', 500)

Weather einlesen

In [2]:
df = pd.read_csv('weather.csv')
In [3]:
df.columns
Out[3]:
Index(['Station', 'Date', 'Tmax', 'Tmin', 'Tavg', 'Depart', 'DewPoint',
       'WetBulb', 'Heat', 'Cool', 'Sunrise', 'Sunset', 'CodeSum', 'Depth',
       'Water1', 'SnowFall', 'PrecipTotal', 'StnPressure', 'SeaLevel',
       'ResultSpeed', 'ResultDir', 'AvgSpeed'],
      dtype='object')

Datentypen

In [4]:
df.dtypes.sort_values()
Out[4]:
Station          int64
Tmax             int64
Tmin             int64
DewPoint         int64
ResultDir        int64
ResultSpeed    float64
SeaLevel        object
StnPressure     object
PrecipTotal     object
SnowFall        object
Water1          object
Depth           object
Sunrise         object
Sunset          object
Cool            object
Heat            object
WetBulb         object
Depart          object
Tavg            object
Date            object
CodeSum         object
AvgSpeed        object
dtype: object

Datum in datetime umwandeln

In [5]:
df.Date = pd.to_datetime(df.Date)

Zeige alle unbekannten nicht numerischen Werte

Spalten, deren dtype object ist, die aber eigenlich numerisch sein sollten, haben wahrscheinlich strings für fehlende Werte oder andere Bedeutungen, weswegen die Umwandlung nicht funktioniert.

In [8]:
for col in ['Depart', 'Tavg', 'WetBulb', 'Heat', 'Cool', 'PrecipTotal', 'StnPressure', 
            'SeaLevel', 'AvgSpeed', 'Sunrise', 'Sunset']:
    print('***'+col.upper()+'***')
    print(df[col].str.findall('(\D)').value_counts())
***DEPART***
[M]    1472
[ ]     715
[-]     556
[]      201
Name: Depart, dtype: int64
***TAVG***
[]     2933
[M]      11
Name: Tavg, dtype: int64
***WETBULB***
[]     2940
[M]       4
Name: WetBulb, dtype: int64
***HEAT***
[]     2933
[M]      11
Name: Heat, dtype: int64
***COOL***
[ ]    2106
[]      827
[M]      11
Name: Cool, dtype: int64
***PRECIPTOTAL***
[.]          2624
[ ,  , T]     318
[M]             2
Name: PrecipTotal, dtype: int64
***STNPRESSURE***
[.]    2940
[M]       4
Name: StnPressure, dtype: int64
***SEALEVEL***
[.]    2935
[M]       9
Name: SeaLevel, dtype: int64
***AVGSPEED***
[.]    2941
[M]       3
Name: AvgSpeed, dtype: int64
***SUNRISE***
[]     1472
[-]    1472
Name: Sunrise, dtype: int64
***SUNSET***
[]     1472
[-]    1472
Name: Sunset, dtype: int64

Zeige alle Spalten, die einen bekannten nicht numerischen Wert haben

In [9]:
df.columns[df.isin(['T', 'M', '-']).any()]
Out[9]:
Index(['Tavg', 'Depart', 'WetBulb', 'Heat', 'Cool', 'Sunrise', 'Sunset',
       'Depth', 'Water1', 'SnowFall', 'PrecipTotal', 'StnPressure', 'SeaLevel',
       'AvgSpeed'],
      dtype='object')
In [10]:
df.PrecipTotal.str.contains('T').sum()
Out[10]:
318

Umgang mit Ts

Es gibt 318 T und 2 M.
T = Trace, M = Missing Value.
Ersetze T mit 0.0001

T kommt in dieser Form vor: ' , , T' (zwei Leerzeichen, dann das T)
M werden später mit nan ersetzt.
Die 0.0001 muss als string angegeben werden, sonst funktioniert die Methode replace nicht.

In [11]:
df.PrecipTotal = df.PrecipTotal.str.replace('  T', '0.0001')
In [12]:
df.PrecipTotal.value_counts().head()
Out[12]:
0.00      1577
0.0001     318
0.01       127
0.02        63
0.03        46
Name: PrecipTotal, dtype: int64

Spalten ohne wesentliche Information entfernen

  • Snowfall. Die Hälfte der Werte fehlen, das heißt, sie wurden nur für Station 1 dokumentiert. T steht für Trace. Da In Station 1 alle Fälle bis auf 13 0 inches Schneefall haben, wird die Spalte entfernt.
  • Water 1 ist ganz leer, Spalte entfernenWater 1 ist ganz leer, Spalte entfernen
  • Alle Werte in Depth sind 0 oder fehlen (wurden nur in einer Station dokumentiert). Spalte entfernen.
In [13]:
df = df.drop(['SnowFall','Water1', 'Depth' ], axis = 1)

Spalten mit dem Datentyp object umwandeln in numeric (außer Codesum, das sind zu Recht strings)

Mit Errors = coerce werden nicht umwandelbare Werte zu nans

In [14]:
cols = ['Depart', 'Tavg','WetBulb', 'Heat', 'Cool', 'PrecipTotal', 'StnPressure', 'SeaLevel', 'AvgSpeed']
df[cols] = df[cols].apply(pd.to_numeric, errors='coerce')

Umgang mit nans

In [15]:
missing = pd.DataFrame(df.isnull().sum()).rename(columns = {0: 'total'})
missing['percent'] = missing['total'] / len(df)*100
missing = missing[missing.total != 0]
missing = missing.sort_values('percent', ascending = False)
missing
Out[15]:
total percent
Depart 1472 50.000000
Tavg 11 0.373641
Heat 11 0.373641
Cool 11 0.373641
SeaLevel 9 0.305707
WetBulb 4 0.135870
StnPressure 4 0.135870
AvgSpeed 3 0.101902
PrecipTotal 2 0.067935

Tavg berechnen

In [16]:
df.Tavg = ((df.Tmax + df.Tmin)/2).astype(int)

Da, wo die Hälfte der Werte fehlen, wurden sie nur in einer Station dokumentiert. Sie können von Station 1 auf Station 2 übertragen werden.
Definition, um Werte von Station 2 mit den Werten aus Station 1 vom gleichen Datum zu ersetzen. (Eigenlich unnötig, da wir später den Wert aus STation 1 nehmen werden, wo die Werte beider Stationen gleich sind und ansonsten den Durchschnitt beider STationen berechnen werden, aber der Vollständigkeit halber wird es gemacht)

In [17]:
# Sunrise und Sunset sind Uhrzeiten. Die Werte aus Station 1 werden auch für Station 2 gesetzt
def sel1(df, col):
    for i, val in enumerate(df[col]):
        if val == '-':
            date = df.loc[i, 'Date']
            hour = df.loc[(df.Date == date) & (df.Station == 1), col]
            hour = hour.values[0]
            df.loc[i, col] = hour
    return(df)
In [18]:
# Für numerische Werte, bei denen ein 'M' für fehlende Werte stand
def sel(df, col):
    for i, val in enumerate(df[col]):
        if np.isnan(val):
            date = df.loc[i, 'Date']
            hour = df.loc[(df.Date == date) & (df.Station == 1), col]
            hour = hour.values[0]
            df.loc[i, col] = hour
    return(df)
In [19]:
for i in ['Sunrise', 'Sunset']:
    sel1(df, i)
In [20]:
df = sel(df, 'Depart')

Da später die Durschnittswerte der beiden Stationen berechnet werden, werden alle Werte benötigt, daher ist ein Entfernen der Beobachtungen mit fehlenden Werten nicht möglich. Die fehlenden Werte werden stattdessen mit Pandas interpoliert.

In [21]:
for column in ['Heat', 'Cool', 'SeaLevel', 'WetBulb', 'StnPressure', 'AvgSpeed', 'PrecipTotal']:
    df[column] = df[column].fillna(df[column].mean(skipna = True))
In [22]:
missing = pd.DataFrame(df.isnull().sum()).rename(columns = {0: 'total'})
missing['percent'] = missing['total'] / len(df)*100
missing = missing[missing.total != 0]
missing = missing.sort_values('percent', ascending = False)
missing
Out[22]:
total percent
In [23]:
df.dtypes.sort_values()
Out[23]:
Tavg                    int32
Station                 int64
Tmax                    int64
Tmin                    int64
DewPoint                int64
ResultDir               int64
ResultSpeed           float64
SeaLevel              float64
StnPressure           float64
PrecipTotal           float64
Cool                  float64
Heat                  float64
WetBulb               float64
Depart                float64
Date           datetime64[ns]
AvgSpeed              float64
Sunset                 object
CodeSum                object
Sunrise                object
dtype: object

Allgemeine Infos

In [24]:
df.describe()
Out[24]:
Station Tmax Tmin Tavg Depart DewPoint WetBulb Heat Cool PrecipTotal StnPressure SeaLevel ResultSpeed ResultDir AvgSpeed
count 2944.000000 2944.000000 2944.000000 2944.000000 2944.000000 2944.000000 2944.000000 2944.000000 2944.000000 2944.00000 2944.000000 2944.000000 2944.000000 2944.000000 2944.000000
mean 1.500000 76.166101 57.810462 66.736073 1.954484 53.457880 59.310884 3.407092 5.647119 0.13134 29.284429 29.968129 6.960666 17.494905 8.580449
std 0.500085 11.461970 10.381939 10.534346 6.839947 10.675181 9.280428 5.942067 6.093667 0.39319 0.158436 0.158354 3.587527 10.063609 3.145696
min 1.000000 41.000000 29.000000 35.000000 -17.000000 22.000000 32.000000 0.000000 0.000000 0.00000 28.550000 29.230000 0.100000 1.000000 1.700000
25% 1.000000 69.000000 50.000000 60.000000 -3.000000 46.000000 53.000000 0.000000 0.000000 0.00000 29.190000 29.870000 4.300000 7.000000 6.300000
50% 1.500000 78.000000 59.000000 68.000000 2.000000 54.000000 61.000000 0.000000 4.000000 0.00000 29.280000 29.970000 6.400000 19.000000 8.100000
75% 2.000000 85.000000 66.000000 75.000000 7.000000 62.000000 67.000000 5.000000 10.000000 0.06000 29.390000 30.060000 9.200000 25.000000 10.400000
max 2.000000 104.000000 83.000000 93.000000 23.000000 75.000000 78.000000 29.000000 29.000000 6.86000 29.860000 30.530000 24.100000 36.000000 26.300000

Visualisierung

In [33]:
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)
In [34]:
df.Station.value_counts()
Out[34]:
1    1472
2    1472
Name: Station, dtype: int64
In [35]:
a = df.loc[df.Date.dt.year == 2007]
In [36]:
figsize(10, 8)
ax = plt.subplot()
a.groupby(a.Date.dt.month)['Tavg'].mean().plot()
a.groupby(a.Date.dt.month)['Tmin'].mean().plot()
a.groupby(a.Date.dt.month)['Tmax'].mean().plot()
ax.set_xlabel('Date', fontsize=18);
ax.set_ylabel('Temperature (°F)', fontsize=18); 
ax.set_title('Temperatures in 2007', fontsize=20); 
In [38]:
ax = plt.subplot()
ax.plot_date(a.Date, a.Tavg)
ax.plot_date(a.Date, a.Tmax);
ax.plot_date(a.Date, a.Tmin);
ax.set_xlabel('Date', fontsize=18);
ax.set_ylabel('Temperature (°F)', fontsize=18); 
ax.set_title('Weather in 2007'); 
In [39]:
a[['Date','Tavg']].set_index('Date').plot()
Out[39]:
<matplotlib.axes._subplots.AxesSubplot at 0xee206d8>
In [40]:
trace_high = go.Scatter(
                x=a.Date,
                y=a['Tmax'],
                name = "Tmax",
                line = dict(color = 'pink'),
                opacity = 0.9)

trace_low = go.Scatter(
                x=a.Date,
                y=a['Tmin'],
                name = "Tmin",
                line = dict(color = 'lightblue'),
                opacity = 0.9)

data = [trace_high,trace_low]


layout = go.Layout(
    template= 'plotly_dark',
    title = "Temperatures from May to October 2007",
    xaxis=dict(
        range = ['2007-04-01','2007-11-30'],
        title='Date',
        titlefont=dict(
            family='Arial, sans-serif',
            size=18,
            color='lightgrey'),
        showticklabels=True,
        tickangle=0,
        tickfont=dict(
            family='Old Standard TT, serif',
            size=14,
            color='white'),
        exponentformat='e',
        showexponent='all'),
    
    yaxis=dict(
        title='Degrees Fahrenheit',
        titlefont=dict(
            family='Arial, sans-serif',
            size=18,
            color='lightgrey'
        ),
        showticklabels=True,
        tickangle=0,
        tickfont=dict(
            family='Old Standard TT, serif',
            size=14,
            color='white'
        ),
        exponentformat='e',
        showexponent='all'
    )
)
fig = dict(data=data, layout=layout)
iplot(fig)
In [41]:
trace1 = go.Bar(x=a['Date'],
                y=a['Tmax'],
                marker = dict(color = 'red'),
                name = 'Tmax')

trace2 = go.Bar(x=a['Date'],
                y=a['Tmin'],
                marker = dict(color = 'blue'),
                name = 'Tmin')
                

data1 = [trace1, trace2]

layout1 = go.Layout(title = "Temperatures of Summer/Autumn 2007",
                    barmode='group')

fig = dict(data=data1, layout=layout1)
iplot(fig)

Kombination beider Wetterstation mit Durchschnitt in neuem DF

In [42]:
# Zwei DF für je station 1 und 2
# reset index, weil der sonst nur aus jedem 2. besteht
station_1 = df[df['Station'] == 1].reset_index()
station_2 = df[df['Station'] == 2].reset_index()
In [43]:
# Df mit dem Datum von Station 1 und Daten, die nur in Station 1 dokumentiert worden waren

weather_new = pd.DataFrame()
weather_new['Date'] = station_1.Date
weather_new['Sunrise'] = station_1.Sunrise
weather_new['Sunset'] = station_1.Sunset
weather_new['Depart'] = station_1.Depart
In [44]:
# Für jede Spalte den Durchschnitt der beiden Stationen hinzufügen
for col in ['Tmax', 'Tmin', 'Tavg', 'DewPoint',
       'WetBulb', 'Heat', 'Cool', 
       'PrecipTotal', 'StnPressure', 'SeaLevel', 'ResultSpeed', 'ResultDir',
       'AvgSpeed']:
        weather_new[col] = (station_1[col] + station_2[col])*0.5
In [45]:
weather_new.head()
Out[45]:
Date Sunrise Sunset Depart Tmax Tmin Tavg DewPoint WetBulb Heat Cool PrecipTotal StnPressure SeaLevel ResultSpeed ResultDir AvgSpeed
0 2007-05-01 0448 1849 14.0 83.5 51.0 67.0 51.0 56.5 0.000000 2.500000 0.00000 29.140 29.820 2.20 26.0 9.40
1 2007-05-02 0447 1850 -3.0 59.5 42.5 50.5 42.0 47.0 13.500000 0.000000 0.00000 29.410 30.085 13.15 3.0 13.40
2 2007-05-03 0446 1851 2.0 66.5 47.0 56.5 40.0 49.0 8.000000 0.000000 0.00000 29.425 30.120 12.30 6.5 12.55
3 2007-05-04 0444 1852 4.0 72.0 50.0 60.5 41.5 50.0 5.203546 2.823559 0.00005 29.335 30.045 10.25 7.5 10.60
4 2007-05-05 0443 1853 5.0 66.0 53.5 59.5 38.5 49.5 5.000000 0.000000 0.00010 29.430 30.095 11.45 7.0 11.75
In [46]:
df.Date.dt.year.value_counts().sort_index()
Out[46]:
2007    368
2008    368
2009    368
2010    368
2011    368
2012    368
2013    368
2014    368
Name: Date, dtype: int64

Feature Engineering

Da Moskitos einen Zyklus haben, und von bestimmten Wetterfaktoren beeinflusst werden, aber mit einem Lag, sollten diese Lags aufgenommen werden.
Regen-Pfützen-Eiablage-Schlüpfen
Warmes Wetter- mehr
Windrichtung- driften der Population
Windstärke- Aktivität der Mücken Druckunterschiede- Flughöhe

Gesamtregenfall im Jahr davor

In [47]:
weather_new['Rain_prev']= 0
In [48]:
weather_new.loc[weather_new.Date.dt.year == 2009, 'Rain_prev'] = weather_new.loc[weather_new.Date.dt.year == 2008, 'PrecipTotal'].sum()
In [49]:
weather_new.loc[weather_new.Date.dt.year == 2011, 'Rain_prev'] = weather_new.loc[weather_new.Date.dt.year == 2010, 'PrecipTotal'].sum()
In [50]:
weather_new.loc[weather_new.Date.dt.year == 2013, 'Rain_prev'] = weather_new.loc[weather_new.Date.dt.year == 2012, 'PrecipTotal'].sum()

Auswahl Wetterdaten

Es wird nur das Wetter der Jahre verwendet, die auch in train sind. Schön wäre ein Jahr vor dem ersten Jahr im train Satz, da dies eine Population beeinflussen kann. Flag, falls das Jahr davor verregnet war.

In [51]:
wny = weather_new.loc[(weather_new.Date.dt.year == 2007)|
                     (weather_new.Date.dt.year == 2009) |
                     (weather_new.Date.dt.year == 2011) |
                     (weather_new.Date.dt.year == 2013)]
In [52]:
wny.Date.dt.year.value_counts()
Out[52]:
2013    184
2011    184
2009    184
2007    184
Name: Date, dtype: int64

Summe der Tage in der Periode mit Temperaturen 77 <Tavg< 86° F

nein! Damit wüßte ich dann schon im Voraus etwas, das ich nicht wissen kann, wenn ich Mitten im Sommer die Moskitozahl vorhersagen soll. Nimm stattdessen die Summe der Tage bis zum Zeitpunkt der Beobachtung!

In [53]:
d = 0
hot = []

for i in wny.Tavg:
    if 77 <i< 86:
        d+=1
        hot.append(d)
    else:
        d=0
        hot.append(d)
In [54]:
wny['Hot_Days'] = hot
In [55]:
wny.Hot_Days.value_counts()
Out[55]:
0     630
1      42
2      26
3      15
4       8
7       3
6       3
5       3
13      1
12      1
11      1
10      1
9       1
8       1
Name: Hot_Days, dtype: int64

Timelag Regentage

In [56]:
wny['Rain_lag_3'] = wny.PrecipTotal.shift(3)
In [57]:
wny['Rain_lag_8'] = wny.PrecipTotal.shift(8)
wny['Rain_lag_15'] = wny.PrecipTotal.shift(15)

Timelag Windstärke

In [58]:
wny['Wind_lag_3'] = wny.AvgSpeed.shift(3)
wny['Wind_lag_8'] = wny.AvgSpeed.shift(8)
wny['Wind_lag_15'] = wny.AvgSpeed.shift(15)

Tageslänge aus Sunrise und Sunset

In [73]:
wny.Sunrise = wny.Sunrise.astype(int)
wny.Sunset = wny.Sunset.astype(int)
In [74]:
wny['Daylen'] = wny.Sunset - wny.Sunrise
In [75]:
wny.head()
Out[75]:
Date Sunrise Sunset Depart Tmax Tmin Tavg DewPoint WetBulb Heat Cool PrecipTotal StnPressure SeaLevel ResultSpeed ResultDir AvgSpeed Rain_prev Hot_Days Rain_lag_3 Rain_lag_8 Rain_lag_15 Wind_lag_3 Wind_lag_8 Wind_lag_15 Daylen
0 2007-05-01 448 1849 14.0 83.5 51.0 67.0 51.0 56.5 0.000000 2.500000 0.00000 29.140 29.820 2.20 26.0 9.40 0.0 0 NaN NaN NaN NaN NaN NaN 1401
1 2007-05-02 447 1850 -3.0 59.5 42.5 50.5 42.0 47.0 13.500000 0.000000 0.00000 29.410 30.085 13.15 3.0 13.40 0.0 0 NaN NaN NaN NaN NaN NaN 1403
2 2007-05-03 446 1851 2.0 66.5 47.0 56.5 40.0 49.0 8.000000 0.000000 0.00000 29.425 30.120 12.30 6.5 12.55 0.0 0 NaN NaN NaN NaN NaN NaN 1405
3 2007-05-04 444 1852 4.0 72.0 50.0 60.5 41.5 50.0 5.203546 2.823559 0.00005 29.335 30.045 10.25 7.5 10.60 0.0 0 0.0 NaN NaN 9.4 NaN NaN 1408
4 2007-05-05 443 1853 5.0 66.0 53.5 59.5 38.5 49.5 5.000000 0.000000 0.00010 29.430 30.095 11.45 7.0 11.75 0.0 0 0.0 NaN NaN 13.4 NaN NaN 1410

Datensatz speichern

In [76]:
wny.to_csv('weather_final.csv', sep=',', encoding='utf-8', index=False)