import pandas as pd
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
import dexplot as dxp
import datetime as dt
from imblearn.over_sampling import RandomOverSampler
from scipy.stats import chi2_contingency,ttest_ind,bartlett
import warnings


warnings.filterwarnings('ignore')


%matplotlib inline

Загрузка данных

В этом случае мы будем работать с набором данных различных переменных, связанных с дождем в Австралии.

Эти переменные включают в себя такую ​​информацию, как скорость ветра, температура, давление, влажность, солнечный свет и т. д. А также переменная под названием «RainTomorrow», которая указывает, был ли дождь на следующий день или нет. Это та переменная, которую мы пытаемся предсказать.

Чтобы получить более подробную информацию об используемом наборе данных, перейдите на эту страницу kaggle (https://www.kaggle.com/jsphyg/weather-dataset-rattle-package), где подробно описаны все переменные.

weather = pd.read_csv("weatherAUS.csv")

Описательный анализ

Прежде чем приступить к созданию желаемой прогностической модели, необходимо выполнить первоначальный описательный анализ, чтобы найти отношения между различными возможными прогностическими переменными и переменной отклика (RainTomorrow), чтобы мы могли найти, как каждая из этих первых упомянутых переменных влияет на окончательный результат завтрашнего дождя.

weather.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 145460 entries, 0 to 145459
Data columns (total 23 columns):
 #   Column         Non-Null Count   Dtype  
---  ------         --------------   -----  
 0   Date           145460 non-null  object 
 1   Location       145460 non-null  object 
 2   MinTemp        143975 non-null  float64
 3   MaxTemp        144199 non-null  float64
 4   Rainfall       142199 non-null  float64
 5   Evaporation    82670 non-null   float64
 6   Sunshine       75625 non-null   float64
 7   WindGustDir    135134 non-null  object 
 8   WindGustSpeed  135197 non-null  float64
 9   WindDir9am     134894 non-null  object 
 10  WindDir3pm     141232 non-null  object 
 11  WindSpeed9am   143693 non-null  float64
 12  WindSpeed3pm   142398 non-null  float64
 13  Humidity9am    142806 non-null  float64
 14  Humidity3pm    140953 non-null  float64
 15  Pressure9am    130395 non-null  float64
 16  Pressure3pm    130432 non-null  float64
 17  Cloud9am       89572 non-null   float64
 18  Cloud3pm       86102 non-null   float64
 19  Temp9am        143693 non-null  float64
 20  Temp3pm        141851 non-null  float64
 21  RainToday      142199 non-null  object 
 22  RainTomorrow   142193 non-null  object 
dtypes: float64(16), object(7)
memory usage: 25.5+ MB

Мы видим, что во многих переменных в этом наборе данных много значений NA.

weather.head()

weather.describe()

Работа со значениями NA

Во-первых, мы хотим проверить, важны ли переменные с большим количеством нулевых значений для предсказания, будет ли завтра дождь или нет, чтобы решить, правдоподобно ли избавиться от них, не влияя на окончательную модель. Этими переменными являются: «Солнечный свет», «Испарение», «Облако3pm» и «Облако9am».

plt.figure(figsize = (10,6))
plt.subplot(2,2,1)

sns.boxplot(data = weather, x = "RainTomorrow",y = "Sunshine",palette = "Set1")

plt.subplot(2,2,2)

sns.boxplot(data = weather, x = "RainTomorrow",y = "Evaporation",palette = "Set1")

plt.subplot(2,2,3)

sns.boxplot(data = weather, x = "RainTomorrow",y = "Cloud3pm",palette = "Set1")


plt.subplot(2,2,4)

sns.boxplot(data = weather, x = "RainTomorrow",y = "Cloud9am",palette = "Set1")

plt.tight_layout()

Во-первых, мы можем видеть тесную связь, которая существует между «RainTomorrow» и «Sunshine», где, как и следовало ожидать, уровень солнечного света, как правило, намного ниже, когда на следующий день будет дождь.

Точно так же мы можем сделать вывод, что в дни с более высоким уровнем облачности как утром, так и днем ​​вероятность дождя на следующий день выше, как мы можем видеть на двух нижних графиках.

Однако переменная «Испарение», похоже, не следует той же схеме. Вместо этого кажется, что нет никакой разницы в уровне испарения, если на следующий день идет дождь или нет. По этой причине допустимо отбросить эту переменную, поскольку ее прогнозируемое значение не кажется очень высоким и имеет много нулевых значений.

weather.drop("Evaporation",axis = 1,inplace = True)
weather.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 145460 entries, 0 to 145459
Data columns (total 22 columns):
 #   Column         Non-Null Count   Dtype  
---  ------         --------------   -----  
 0   Date           145460 non-null  object 
 1   Location       145460 non-null  object 
 2   MinTemp        143975 non-null  float64
 3   MaxTemp        144199 non-null  float64
 4   Rainfall       142199 non-null  float64
 5   Sunshine       75625 non-null   float64
 6   WindGustDir    135134 non-null  object 
 7   WindGustSpeed  135197 non-null  float64
 8   WindDir9am     134894 non-null  object 
 9   WindDir3pm     141232 non-null  object 
 10  WindSpeed9am   143693 non-null  float64
 11  WindSpeed3pm   142398 non-null  float64
 12  Humidity9am    142806 non-null  float64
 13  Humidity3pm    140953 non-null  float64
 14  Pressure9am    130395 non-null  float64
 15  Pressure3pm    130432 non-null  float64
 16  Cloud9am       89572 non-null   float64
 17  Cloud3pm       86102 non-null   float64
 18  Temp9am        143693 non-null  float64
 19  Temp3pm        141851 non-null  float64
 20  RainToday      142199 non-null  object 
 21  RainTomorrow   142193 non-null  object 
dtypes: float64(15), object(7)
memory usage: 24.4+ MB

Затем строится тепловая карта, включающая все значения NA для каждого значения.

sns.heatmap(weather.isna(),yticklabels=False,cbar=False,cmap='viridis')
<AxesSubplot:>

Совершенно очевидно, как много нулевых значений почти в каждой из присутствующих переменных.

Эти значения NA проблематичны, поскольку наша модель не может использовать их в качестве входных данных. По этой причине было принято решение удалить все наблюдения, содержащие хотя бы одно значение NA, поскольку использование методов условного исчисления может быть контрпродуктивным из-за огромного количества неизвестных значений.

weather2 = weather.dropna()
weather2.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 58278 entries, 6049 to 142302
Data columns (total 22 columns):
 #   Column         Non-Null Count  Dtype  
---  ------         --------------  -----  
 0   Date           58278 non-null  object 
 1   Location       58278 non-null  object 
 2   MinTemp        58278 non-null  float64
 3   MaxTemp        58278 non-null  float64
 4   Rainfall       58278 non-null  float64
 5   Sunshine       58278 non-null  float64
 6   WindGustDir    58278 non-null  object 
 7   WindGustSpeed  58278 non-null  float64
 8   WindDir9am     58278 non-null  object 
 9   WindDir3pm     58278 non-null  object 
 10  WindSpeed9am   58278 non-null  float64
 11  WindSpeed3pm   58278 non-null  float64
 12  Humidity9am    58278 non-null  float64
 13  Humidity3pm    58278 non-null  float64
 14  Pressure9am    58278 non-null  float64
 15  Pressure3pm    58278 non-null  float64
 16  Cloud9am       58278 non-null  float64
 17  Cloud3pm       58278 non-null  float64
 18  Temp9am        58278 non-null  float64
 19  Temp3pm        58278 non-null  float64
 20  RainToday      58278 non-null  object 
 21  RainTomorrow   58278 non-null  object 
dtypes: float64(15), object(7)
memory usage: 10.2+ MB

Наконец, мы получаем сокращенный набор данных, который содержит 58278 точек данных, относящихся к переменным, связанным с дождем, и не имеет нулевых значений, как мы и предполагали.

weather2["RainTomorrow"].value_counts()/58278
No     0.77712
Yes    0.22288
Name: RainTomorrow, dtype: float64

Частота дождливых дней для каждого местоположения

weather2["Location"].value_counts()
Darwin              3114
Perth               3026
Brisbane            2970
MelbourneAirport    2932
PerthAirport        2914
SydneyAirport       2910
Watsonia            2733
Mildura             2630
NorfolkIsland       2494
Cairns              2482
MountGambier        2476
Townsville          2474
WaggaWagga          2420
AliceSprings        2225
Nuriootpa           2023
Hobart              1944
Moree               1924
Melbourne           1898
Portland            1871
Woomera             1753
Sydney              1716
Sale                1688
CoffsHarbour        1412
PearceRAAF          1399
Williamtown         1214
Canberra            1100
Cobar                536
Name: Location, dtype: int64
plt.figure(figsize = (12,8))

dxp.count("Location",data = weather2,split = "RainTomorrow",normalize = "Location")
findfont: Font family ['Helvetica'] not found. Falling back to DejaVu Sans.

<Figure size 864x576 with 0 Axes>

предыдущий график показывает долю дождливых дней для каждого из зарегистрированных мест в Австралии.

Несмотря на то, что это может выглядеть немного грязно из-за большого количества разных мест, заметно, что в некоторых местах больше дождливых дней, чем в других. На самом деле, мы можем видеть, что местом с более высокой долей дождливых дней является Портленд, где около 40% дождливых дней, а местом с наименьшим количеством дождливых дней будет Вумера, где не более 8% дождливых дней. дни дождливые.

Несмотря на то, что кажется очевидным, что существует разница в вероятности дождя в зависимости от местоположения, мы хотим выполнить критерий хи-квадрат для таблицы непредвиденных обстоятельств, чтобы достоверно сделать вывод об этой вероятности.

counts = pd.DataFrame(weather2.value_counts(["Location","RainTomorrow"]))
counts.columns = ["Count"]

counts.reset_index(level = [0,1],inplace = True)

counts

rainyes_count = [counts["Count"][counts["RainTomorrow"]=="Yes"]]

rainno_count = [counts["Count"][counts["RainTomorrow"]=="No"]]

freq_table = np.array([rainyes_count,rainno_count])
print(freq_table)
chi2_contingency(freq_table)
[[[ 815  778  761  751  741  738  685  646  638  617  554  471  471  455
    433  430  428  391  359  330  328  298  261  229  187  130   64]]

 [[2409 2360 2332 2324 2299 2294 2169 2048 2041 2038 1992 1738 1733 1704
   1663 1632 1623 1473 1427 1329 1286 1120 1071  957  884  871  472]]]





(213.95845633297688,
 1.8275731733485535e-31,
 26,
 array([[[ 718.56508459,  699.39740554,  689.36780603,  685.35596623,
           677.55516662,  675.7721267 ,  636.09948866,  600.43869041,
           597.09549058,  591.74637084,  567.45245204,  492.34189574,
           491.2274958 ,  481.19789629,  467.15645698,  459.57853736,
           457.12685748,  415.44829953,  398.06366039,  369.75790178,
           359.72830228,  316.04382443,  296.87614537,  264.33566698,
           238.70446824,  223.10286901,  119.46367411]],
 
        [[2505.43491541, 2438.60259446, 2403.63219397, 2389.64403377,
          2362.44483338, 2356.2278733 , 2217.90051134, 2093.56130959,
          2081.90450942, 2063.25362916, 1978.54754796, 1716.65810426,
          1712.7725042 , 1677.80210371, 1628.84354302, 1602.42146264,
          1593.87314252, 1448.55170047, 1387.93633961, 1289.24209822,
          1254.27169772, 1101.95617557, 1035.12385463,  921.66433302,
           832.29553176,  777.89713099,  416.53632589]]]))

Как мы и подозревали, критерий хи-квадрат возвращает значение p, очень близкое к нулю (1,8275731733485535e-31), что означает, что гипотеза о том, что вероятность дождя не меняется в зависимости от местоположения, отвергается, а это означает, что влияние местоположения значительно для вероятности дождя

Сравнение температуры в дождливые и не дождливые дни

Мы могли бы быть склонны полагать, что температура оказывает прямое влияние на частоту дождливых дней. Где мы могли бы предположить, что в дождливые дни температура предыдущего дня, как правило, ниже, чем в обычные дни.

Далее мы графически проверим, верна ли эта гипотеза.

plt.figure(figsize = (14,6))
plt.subplot(2,2,1)
sns.set_style("whitegrid")

sns.boxplot(data = weather2,x = "RainTomorrow",y = "MinTemp",palette = "Set1")
plt.title("Minimum temperature by rain tomorrow")
plt.ylabel("Temperature (Mininum)")

plt.subplot(2,2,2)
sns.set_style("whitegrid")

sns.boxplot(data = weather2,x = "RainTomorrow",y = "MaxTemp",palette ="Set1")
plt.title("Maximum temperature by rain tomorrow")
plt.ylabel("Temperature (Maximum)")

plt.subplot(2,2,3)
sns.set_style("whitegrid")

weather2[weather2["RainTomorrow"] == "No"]["MinTemp"].hist(bins = 20,color = "red")
weather2[weather2["RainTomorrow"] == "Yes"]["MinTemp"].hist(bins = 20)

plt.subplot(2,2,4)
sns.set_style("whitegrid")

weather2[weather2["RainTomorrow"] == "No"]["MaxTemp"].hist(bins = 20,color = "red")
weather2[weather2["RainTomorrow"] == "Yes"]["MaxTemp"].hist(bins = 20)
<AxesSubplot:>



findfont: Font family ['Helvetica'] not found. Falling back to DejaVu Sans.
findfont: Font family ['Helvetica'] not found. Falling back to DejaVu Sans.

В отличие от наших первых представлений о температуре и дожде, похоже, что минимальная и максимальная температура дня практически не связаны с вероятностью дождя на следующий день. Это ясно, поскольку графики показывают, как дождливые и не дождливые даты, как правило, имеют очень похожее распределение температуры накануне.

Однако это только визуальный результат, чтобы уверенно проверить разницу температур, если на следующий день пойдет дождь, будет выполнен t-тест для разницы средних значений, но с использованием средней температуры, рассчитанной из минимальной и максимальной температур.

weather2["AverageTemp"] = (weather2["MaxTemp"]+weather2["MinTemp"])/2
plt.figure(figsize = (12,8))

sns.set_style("whitegrid")
sns.boxplot(data = weather2,x = "RainTomorrow",
            y = "AverageTemp",palette = "Set1")
plt.title("Average temperature by rain tomorrow")
plt.ylabel("Temperature (Average)")
Text(0, 0.5, 'Temperature (Average)')

print(bartlett(weather2["MinTemp"][weather2["RainTomorrow"] == "Yes"],
               weather2["MinTemp"][weather2["RainTomorrow"] == "No"]))

ttest_ind(weather2["MinTemp"][weather2["RainTomorrow"] == "Yes"],
          weather2["MinTemp"][weather2["RainTomorrow"] == "No"],equal_var=False)
BartlettResult(statistic=3.941141671440655, pvalue=0.04711878945991635)





Ttest_indResult(statistic=21.270002327445827, pvalue=2.464930571367529e-99)

Несмотря на то, что на первый взгляд кажется, что нет существенной разницы в средней температуре между дождливыми и не дождливыми днями, t-тест с разными дисперсиями (поскольку возвращаемое значение p для теста Бартлетта было меньше 0,05) возвращает очень маленькое значение. p (2,464930571367529e-99), что заставляет нас отвергнуть гипотезу о том, что среднее значение температуры одинаково в оба типа дней.

weather2.shape
(58278, 23)

Сравнение уровней осадков и солнечного сияния (за предыдущий день) между дождливыми и не дождливыми днями

Также разумно полагать, что дни с более высоким уровнем осадков, как правило, приводят к дождю на следующий день чаще, чем дни, когда уровень осадков в целом ниже.

Нечто подобное происходит с уровнем солнечного света, который, как мы видели ранее, может указывать на вероятность того, что завтра будет дождливый день. Это уже кратко обсуждалось ранее; однако желательно повторить анализ с оставшимися данными.

rainfallWeather = weather2[weather2["Rainfall"]<(weather2["Rainfall"].quantile(0.75)-weather2["Rainfall"].quantile(0.25))*1.5+weather2["Rainfall"].quantile(0.75)]



plt.figure(figsize = (14,6))
plt.subplot(2,2,1)
sns.set_style("whitegrid")

sns.boxplot(data = rainfallWeather,x = "RainTomorrow",
            y = "Rainfall",palette = "coolwarm")
plt.title("Rainfall level by rain tomorrow")
plt.ylabel("Rainfall")

plt.subplot(2,2,2)
sns.set_style("whitegrid")

sns.boxplot(data = weather2,x = "RainTomorrow",
            y = "Sunshine",palette ="coolwarm")
plt.title("Sunshine level by rain tomorrow")
plt.ylabel("Sunshine")

plt.subplot(2,2,3)
sns.set_style("whitegrid")

rainfallWeather[rainfallWeather["RainTomorrow"] == "No"]["Rainfall"].hist(bins = 10)
rainfallWeather[rainfallWeather["RainTomorrow"] == "Yes"]["Rainfall"].hist(bins = 10)

plt.subplot(2,2,4)
sns.set_style("whitegrid")

weather2[weather2["RainTomorrow"] == "No"]["Sunshine"].hist(bins = 20)
weather2[weather2["RainTomorrow"] == "Yes"]["Sunshine"].hist(bins = 20)
<AxesSubplot:>

Как и ожидалось, мы можем видеть, как диаграмма солнечного света и гистограмма дождя завтра показывают четкую разницу для дней, когда был дождь, и дней, когда его не было. В частности, мы можем сказать, что, как правило, день перед дождливой датой имеет более высокий уровень солнечного света, чем за день до не дождливой даты.

С другой стороны, похоже, что количество осадков за предыдущий день не так важно, как можно было бы ожидать при прогнозировании дождя на следующий день. В основном это относится к данному случаю, потому что большинство измеренных уровней осадков имеют нулевое значение, что означает, что переменная не добавляет много информации для прогнозирования дождя на следующий день. Однако это может быть связано с тем, как измерялись осадки для этого набора данных.

Сравнение поведения ветра (в предыдущий день) в дождливые и бездождливые дни

В этом разделе мы рассмотрим, как различные сведения о ветре в определенный день могут повлиять на вероятность дождя на следующий день.

Разумно полагать, что скорость ветра, а также его направление могут быть связаны с вероятностью дождя, может быть, более высокие скорости ветра представляют большую вероятность дождя, или, может быть, определенные направления ветра являются признаками дождя на следующий день.

plt.figure(figsize = (14,6))

plt.subplot(1,3,1)

sns.countplot(data = weather2, hue= "RainTomorrow",
              x="WindGustDir",palette = "GnBu",
              order = weather2["WindGustDir"].unique())

plt.subplot(1,3,2)

sns.countplot(data = weather2, hue= "RainTomorrow",
              x="WindDir9am",palette = "GnBu",
              order = weather2["WindGustDir"].unique())


plt.subplot(1,3,3)

sns.countplot(data = weather2, hue= "RainTomorrow",
              x="WindDir3pm",palette = "GnBu",
              order = weather2["WindGustDir"].unique())

plt.tight_layout()

Прежде всего, направление ветра действительно трудно определить, глядя только на графики, поэтому было бы предложено выполнить формальный статистический тест (например, критерий хи-квадрат), чтобы решить, является ли направление ветра на самом деле влияет на вероятность дождя. В любом случае разница если и есть, то не очень существенная. Однако, чтобы правильно проверить, изменяется ли вероятность дождливого дня в зависимости от направления ветра, будет выполнен тест хи-квадрат для таблиц непредвиденных обстоятельств.

counts = pd.DataFrame(weather2.value_counts(["WindDir3pm","RainTomorrow"]))
counts.columns = ["Count"]

counts.reset_index(level = [0,1],inplace = True)

counts

rainyes_count = [counts["Count"][counts["RainTomorrow"]=="Yes"]]

rainno_count = [counts["Count"][counts["RainTomorrow"]=="No"]]

freq_table = np.array([rainyes_count,rainno_count])
print(freq_table)

print("\nChi-square test for wind direction at 3pm")

chi2_contingency(freq_table)
[[[1132 1060 1056  871  852  850  836  792  735  733  702  685  682  677
    677  649]]

 [[3502 3433 3368 3360 3199 3196 3142 2975 2905 2769 2707 2644 2249 2051
   1943 1846]]]

Chi-square test for wind direction at 3pm





(119.83420933490862,
 2.037983915670458e-18,
 15,
 array([[[1032.82586911, 1001.39979066,  986.02107142,  943.00523354,
           902.88683551,  901.77243557,  886.61659631,  839.58891863,
           811.28316003,  780.52572154,  759.79788256,  741.96748344,
           653.26124781,  608.01661004,  583.94557123,  556.0855726 ]],
 
        [[3601.17413089, 3491.60020934, 3437.97892858, 3287.99476646,
          3148.11316449, 3144.22756443, 3091.38340369, 2927.41108137,
          2828.71683997, 2721.47427846, 2649.20211744, 2587.03251656,
          2277.73875219, 2119.98338996, 2036.05442877, 1938.9144274 ]]]))

Хотя визуально казалось, что нет четкой разницы в вероятности дождливого дня в зависимости от направления ветра, после выполнения соответствующего теста хи-квадрат для таблиц непредвиденных обстоятельств мы вычисляем значение ap, близкое к нулю (2,037983915670458e-18), что означает, что гипотеза о том, что вероятность одинакова для каждого направления в 3 часа дня, отвергается.

plt.figure(figsize = (14,6))

plt.subplot(2,3,1)

sns.boxplot(data = weather2, x= "RainTomorrow",
            y="WindGustSpeed",palette = "GnBu")
plt.title("Wind speed by rain tomorrow")

plt.subplot(2,3,2)

sns.boxplot(data = weather2, x= "RainTomorrow",
            y="WindSpeed9am",palette = "GnBu")
plt.title("Wind speed at 9am by rain tomorrow")

plt.subplot(2,3,3)

sns.boxplot(data = weather2, x= "RainTomorrow",
            y="WindSpeed3pm",palette = "GnBu")
plt.title("Wind speed at 3pm by rain tomorrow")

plt.subplot(2,3,4)
sns.set_style("whitegrid")

weather2[weather2["RainTomorrow"] == "No"]["WindGustSpeed"].hist(bins = 20,color = "darkseagreen")
weather2[weather2["RainTomorrow"] == "Yes"]["WindGustSpeed"].hist(bins = 20,color = "steelblue")

plt.subplot(2,3,5)
sns.set_style("whitegrid")

weather2[weather2["RainTomorrow"] == "No"]["WindSpeed9am"].hist(bins = 20,color ="darkseagreen")
weather2[weather2["RainTomorrow"] == "Yes"]["WindSpeed9am"].hist(bins = 20,color = "steelblue")

plt.subplot(2,3,6)
sns.set_style("whitegrid")

weather2[weather2["RainTomorrow"] == "No"]["WindSpeed3pm"].hist(bins = 20,color = "darkseagreen")
weather2[weather2["RainTomorrow"] == "Yes"]["WindSpeed3pm"].hist(bins = 20,color = "steelblue")


plt.tight_layout()

Похоже, что может быть небольшая разница между скоростью ветра в дождливые дни и дни без дождя. Эта разница кажется более очевидной для переменных «WindGustSpeed» и «WindSpeed3pm», чего и следовало ожидать, поскольку мы можем предположить, что первая объясняет большую часть общего поведения ветра, а вторая объясняет ветер ближе к следующему дню. .

У нас может возникнуть соблазн сделать вывод, что эта разница не кажется существенной для вероятности дождя. Однако, как мы уже представили ранее, для этих близких случаев мы хотим выполнить t-критерий, чтобы сравнить средние значения скорости ветра между дождливыми и не дождливыми днями.

print("Variance test for wind gust speed")
print(bartlett(weather2["WindGustSpeed"][weather2["RainTomorrow"] == "Yes"],
               weather2["WindGustSpeed"][weather2["RainTomorrow"] == "No"]))

print("\nT-test for wind gust speed")
print(ttest_ind(weather2["WindGustSpeed"][weather2["RainTomorrow"] == "Yes"],
                weather2["WindGustSpeed"][weather2["RainTomorrow"] == "No"],
                equal_var=False))

print("\nVariance test for Wind speed at 3pm")
print(bartlett(weather2["WindSpeed3pm"][weather2["RainTomorrow"] == "Yes"],
               weather2["WindSpeed3pm"][weather2["RainTomorrow"] == "No"]))

print("\nT-test for wind speed at 3pm")
print(ttest_ind(weather2["WindSpeed3pm"][weather2["RainTomorrow"] == "Yes"],
          weather2["WindSpeed3pm"][weather2["RainTomorrow"] == "No"],
                equal_var=False))
Variance test for wind gust speed
BartlettResult(statistic=1298.759370377031, pvalue=2.1029455429358744e-284)

T-test for wind gust speed
Ttest_indResult(statistic=51.39628376904462, pvalue=0.0)

Variance test for Wind speed at 3pm
BartlettResult(statistic=346.7448089098752, pvalue=2.167768606820148e-77)

T-test for wind speed at 3pm
Ttest_indResult(statistic=21.233577413955743, pvalue=6.474745141578774e-99)

Выполняя t-тест с неравными дисперсиями для двух переменных, мы получаем чрезвычайно маленькое значение p в обоих случаях. Это означает, что можно с большой долей уверенности сказать, что скорость в дождливые дни отличается от скорости в обычные дни, и с помощью диаграммы мы можем подтвердить, что в дождливые дни ветер, как правило, сильнее.

Сравнение влажности (в предыдущий день) между дождливыми и не дождливыми днями

Мы могли бы ожидать увидеть более высокие уровни влажности в дни перед дождливыми днями, чем перед не дождливыми днями, поскольку влажность можно описать как концентрацию водяного пара, присутствующего в воздухе в определенное время, поэтому, если мы увидим более высокие уровни влажности, особенно в 3 часа дня, на следующий день может быть более вероятно увидеть дождь.

plt.figure(figsize = (14,6))
plt.subplot(2,2,1)
sns.set_style("whitegrid")

sns.boxplot(data = weather2,x = "RainTomorrow",
            y = "Humidity9am",palette = "BuPu")
plt.title("Humidity at 9am by rain tomorrow")

plt.subplot(2,2,2)
sns.set_style("whitegrid")

sns.boxplot(data = weather2,x = "RainTomorrow",
            y = "Humidity3pm",palette ="BuPu")
plt.title("Humidity at 3pm by rain tomorrow")

plt.subplot(2,2,3)
sns.set_style("whitegrid")

weather2[weather2["RainTomorrow"] == "No"]["Humidity9am"].hist(bins = 20,
                                                               color = "lightblue")
weather2[weather2["RainTomorrow"] == "Yes"]["Humidity9am"].hist(bins = 20,
                                                                color = "purple")

plt.subplot(2,2,4)
sns.set_style("whitegrid")

weather2[weather2["RainTomorrow"] == "No"]["Humidity3pm"].hist(bins = 20,
                                                               color = "lightblue")
weather2[weather2["RainTomorrow"] == "Yes"]["Humidity3pm"].hist(bins = 20,
                                                                color = "purple")
<AxesSubplot:>

Как мы и подозревали, разница уровней влажности между днями с дождем и без дождя кажется весьма существенной. В частности, как упоминалось ранее, для уровня влажности в 15:00, где с помощью графиков мы можем ясно видеть, как дождливые дни имели более высокие уровни влажности за несколько дней до этого.

Сравнение давления (за предыдущий день) между дождливыми и не дождливыми днями.

plt.figure(figsize = (14,6))
plt.subplot(2,2,1)
sns.set_style("whitegrid")

sns.boxplot(data = weather2,x = "RainTomorrow",
            y = "Pressure9am",palette = "RdPu")
plt.title("Pressure at 9 am by rain tomorrow")

plt.subplot(2,2,2)
sns.set_style("whitegrid")

sns.boxplot(data = weather2,x = "RainTomorrow",
            y = "Pressure3pm",palette ="RdPu")
plt.title("Pressure at 3 pm by rain tomorrow")

plt.subplot(2,2,3)
sns.set_style("whitegrid")

weather2[weather2["RainTomorrow"] == "No"]["Pressure9am"].hist(bins = 20,
                                                               color = "pink")
weather2[weather2["RainTomorrow"] == "Yes"]["Pressure9am"].hist(bins = 20,
                                                                color = "mediumvioletred")

plt.subplot(2,2,4)
sns.set_style("whitegrid")

weather2[weather2["RainTomorrow"] == "No"]["Pressure3pm"].hist(bins = 20,
                                                               color = "pink")
weather2[weather2["RainTomorrow"] == "Yes"]["Pressure3pm"].hist(bins = 20,
                                                                color = "mediumvioletred")
<AxesSubplot:>

Нас интересует давление в 15:00, так как оно ближе всего к следующему дню. Визуально мы можем видеть, что дни дождя обычно имеют более низкие уровни давления накануне. Однако графически разница может показаться незначительной, поэтому в заключение мы проведем t-тест.

print("\nVariance test for pressure level at 3pm")
print(bartlett(weather2["Pressure3pm"][weather2["RainTomorrow"] == "Yes"],
               weather2["Pressure3pm"][weather2["RainTomorrow"] == "No"]))

print("\nT-test for pressure level at 3pm")
print(ttest_ind(weather2["Pressure3pm"][weather2["RainTomorrow"] == "Yes"],
          weather2["Pressure3pm"][weather2["RainTomorrow"] == "No"],
                equal_var=False))
Variance test for pressure level at 3pm
BartlettResult(statistic=156.9796122547408, pvalue=5.171361726553795e-36)

T-test for pressure level at 3pm
Ttest_indResult(statistic=-55.17534328218922, pvalue=0.0)

Опять же, как и предполагалось, разница значительна, поскольку результирующее значение p практически равно нулю.

Облака (в предыдущий день) сравнение дождливых и не дождливых дней.

Мы ожидаем найти больше облаков в дни, предшествующие дождливым дням, поскольку дождь накапливается в облаках, прежде чем попасть на землю.

plt.figure(figsize = (14,6))
plt.subplot(2,2,1)
sns.set_style("whitegrid")

sns.boxplot(data = weather2,x = "RainTomorrow",y = "Cloud9am",palette = "Greys")
plt.title("Cloud level at 9am by rain tomorrow")


plt.subplot(2,2,2)
sns.set_style("whitegrid")

sns.boxplot(data = weather2,x = "RainTomorrow",y = "Cloud3pm",palette ="Greys")
plt.title("Cloud level at 3pm by rain tomorrow")

plt.subplot(2,2,3)
sns.set_style("whitegrid")

weather2[weather2["RainTomorrow"] == "No"]["Cloud9am"].hist(bins = 10,color = "lightgrey")
weather2[weather2["RainTomorrow"] == "Yes"]["Cloud9am"].hist(bins = 10,color = "grey")

plt.subplot(2,2,4)
sns.set_style("whitegrid")

weather2[weather2["RainTomorrow"] == "No"]["Cloud3pm"].hist(bins = 10,color = "lightgrey")
weather2[weather2["RainTomorrow"] == "Yes"]["Cloud3pm"].hist(bins = 10,color = "grey")
<AxesSubplot:>

И, как подсказывает нам наш здравый смысл, мы можем ясно увидеть и сделать вывод, что количество облаков, обнаруженных за день до дождливой даты, как правило, намного выше, чем в дни перед не дождливым днем. Это особенно верно для уровня облаков, обнаруженного в 15:00, где разница кажется еще более значительной.

Влияние даты на вероятность дождя.

weather2["Month"] = pd.DatetimeIndex(weather2["Date"]).month
weather2["Year"] = pd.DatetimeIndex(weather2["Date"]).year
plt.figure(figsize = (14,8))

sns.set_style("whitegrid")
sns.countplot(data = weather2,x = "Month",hue ="RainTomorrow",palette = "Set2")
plt.title("Frequency of days with rain tomorrow by month")
Text(0.5, 1.0, 'Frequency of days with rain tomorrow by month')

Визуально не похоже, что месяц года оказывает какое-либо существенное влияние на вероятность дождя в определенный день.

Однако, поскольку количество наблюдений очень велико, небольшая разница в вероятностях по месяцам может быть значительной. По этой причине мы еще раз проведем тест хи-квадрат для таблиц непредвиденных обстоятельств.

counts = pd.DataFrame(weather2.value_counts(["Month","RainTomorrow"]))
counts.columns = ["Count"]

counts.reset_index(level = [0,1],inplace = True)

counts

rainyes_count = [counts["Count"][counts["RainTomorrow"]=="Yes"]]

rainno_count = [counts["Count"][counts["RainTomorrow"]=="No"]]

freq_table = np.array([rainyes_count,rainno_count])
print(freq_table)

print("\nChi-square test for month")
chi2_contingency(freq_table)
[[[1215 1200 1181 1178 1178 1100 1037 1034 1003  967  961  935]]

 [[4311 4196 4022 3872 3829 3742 3715 3645 3575 3522 3464 3396]]]

Chi-square test for month





(13.473250782640017,
 0.26352170013905174,
 11,
 array([[[1231.63481931, 1202.66042074, 1159.64458286, 1125.54394454,
          1115.96010501, 1079.18490683, 1059.12570781, 1042.85546862,
          1020.34458973, 1000.5082707 ,  986.24395141,  965.29323244]],
 
        [[4294.36518069, 4193.33957926, 4043.35541714, 3924.45605546,
          3891.03989499, 3762.81509317, 3692.87429219, 3636.14453138,
          3557.65541027, 3488.4917293 , 3438.75604859, 3365.70676756]]]))

В этом случае, как мы упоминали в предыдущем абзаце, высокий уровень p-значения для теста хи-квадрат (0,26352170013905174) означает, что нет существенной разницы в вероятности дождя в зависимости от месяца года ( каждый месяц, как вероятно, будут дождливые дни).

plt.figure(figsize = (14,8))

sns.set_style("whitegrid")
sns.countplot(data = weather2,x = "Year",hue ="RainTomorrow",palette = "Set2")
plt.title("Frequency of days with rain tomorrow by Year")
Text(0.5, 1.0, 'Frequency of days with rain tomorrow by Year')

Теперь, взглянув на график частоты дождливых дней по годам, мы можем увидеть очень небольшую разницу в некоторые годы, когда кажется, что доля дождливых дат выше, чем в другие годы.

Чтобы подтвердить наше мнение, мы проведем еще один тест хи-квадрат, но на этот раз для года.

counts = pd.DataFrame(weather2.value_counts(["Year","RainTomorrow"]))
counts.columns = ["Count"]

counts.reset_index(level = [0,1],inplace = True)

counts

rainyes_count = [counts["Count"][counts["RainTomorrow"]=="Yes"]]

rainno_count = [counts["Count"][counts["RainTomorrow"]=="No"]]

freq_table = np.array([rainyes_count,rainno_count])
print(freq_table)

print("\nChi-square test for year")
chi2_contingency(freq_table)
[[[2038 1739 1625 1492 1467 1410 1383 1143  390  283   19]]

 [[6776 6339 5460 5219 5200 5135 5086 3599 1395 1038   42]]]

Chi-square test for year





(26.021321940028816,
 0.003711614689867482,
 10,
 array([[[1964.46422321, 1800.42455129, 1579.10472219, 1495.7476063 ,
          1485.94088678, 1458.74952812, 1441.81064896, 1056.89690792,
           397.8407804 ,  294.42446549,   13.59567933]],
 
        [[6849.53577679, 6277.57544871, 5505.89527781, 5215.2523937 ,
          5181.05911322, 5086.25047188, 5027.18935104, 3685.10309208,
          1387.1592196 , 1026.57553451,   47.40432067]]]))

В отличие от теста для месяца, этот возвращает небольшое значение p-значения (0,003711614689867482), что приводит нас к выводу, что год даты имеет существенное значение для вероятности дождя.

Несмотря на то, что эта переменная оказалась значимой, необходимо понимать, что в зависимости от использования модели год может быть не очень информативной переменной. Если мы пытаемся предсказать прошедший день, мы могли бы улучшить производительность, используя год. Однако для предсказания будущих дней было бы нецелесообразно использовать год, так как влияние будущих лет не учитывалось при обучении модели.

Вероятность дождя, если накануне был дождь

Следует ожидать, что если в определенный день был дождь, то и следующий день будет иметь больше шансов быть дождливым.

plt.figure(figsize = (14,8))

sns.set_style("whitegrid")
sns.countplot(data = weather2,x = "RainToday",hue ="RainTomorrow",palette = "Set1")
plt.title("Frequency of days with rain tomorrow if there was rain today")
Text(0.5, 1.0, 'Frequency of days with rain tomorrow if there was rain today')

Как мы и ожидали, очевидна разница в вероятности дождя в зависимости от того, был ли дождь в предыдущий день или нет. Заметно, что дни, которые накануне были дождливыми, также с гораздо большей вероятностью будут дождливыми.

Корреляция переменных

plt.figure(figsize = (14,10))
sns.heatmap(weather2.drop(["Location","WindGustDir","WindDir9am",
                           "WindDir3pm","Date","RainToday",
                           "RainTomorrow"],axis = 1).corr())
<AxesSubplot:>

Как мы видим, некоторые переменные сильно коррелируют друг с другом. Это связано со схожей природой некоторых из этих переменных; например, есть 4 различных переменных, связанных с измерениями температуры.

Это может вызвать проблемы при определении нашей модели прогнозирования. По этой причине нас интересует использование только одного из них (возможно, тех, что были записаны в 15:00) или среднего приближения.

Выбор переменной

Для целей этого проекта мы выберем только те переменные, которые были измерены в 15:00 (в случае, если есть несколько переменных, измеряющих одно и то же значение), и только те переменные, которые оказали влияние на решение, будет ли завтра дождь или нет.

weatherFinal = weather2[["WindDir3pm","Location","RainToday","RainTomorrow",
                         "Sunshine","Humidity3pm","Cloud3pm","Temp3pm",
                        "Pressure3pm","WindSpeed3pm"]]
weatherFinal.head()

Создание фиктивных переменных

Теперь, когда мы выбрали наиболее важные переменные для прогнозирования желаемого результата (завтра дождь), мы приступим к созданию раздела, включающего наборы данных для обучения и тестирования. Однако перед этим мы хотим предварительно обработать данные, чтобы привести их к пригодному для использования формату, создав числовые данные из категориальных переменных и создав фиктивные переменные.

def rain (x):
    if x == "Yes": 
        return 1
    elif x == "No":
        return 0
weatherFinal["RainTomorrow"] = weatherFinal["RainTomorrow"].apply(rain)

weatherFinal["RainToday"] = weatherFinal["RainToday"].apply(rain)
weatherFinal["RainTomorrow"].value_counts()
0    45289
1    12989
Name: RainTomorrow, dtype: int64
WindDir = pd.get_dummies(weatherFinal["WindDir3pm"],drop_first=True)

Location = pd.get_dummies(weatherFinal["Location"],drop_first=True)
weatherFinal.drop(["WindDir3pm","Location"],axis = 1,inplace = True)
weatherFinal = pd.concat([weatherFinal,WindDir,Location],axis = 1)
X = weatherFinal.drop("RainTomorrow",axis = 1)

Y = weatherFinal["RainTomorrow"]
print(X.shape)

X.iloc[:,7:17]
(58278, 48)

После выполнения необходимых преобразований основной набор данных делится на два: один набор данных, который содержит все предикторы (X), а другой содержит только переменную ответа (Y).

Затем результирующий набор данных X имеет 58278 строк, как и раньше, но теперь содержит 48 столбцов, включая числовые переменные, которые были выбраны из исходного набора данных, а также новые фиктивные переменные для категориальных. Они принимают значение 1, если наблюдение соответствует соответствующим критериям, или 0, если нет.

Разделение данных на обучение и тестирование

Чтобы обучить модель и точно рассчитать метрики, описывающие ее производительность, необходимо разделить исходные данные на данные обучения и тестирования. Первый будет использоваться, как следует из его названия, для обучения модели; а второй будет использоваться для расчета производительности полученной модели на данных, которые она никогда не видела.

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(X,Y,test_size=0.3,random_state=34)

Построение модели

Модель логистической регрессии

from sklearn.linear_model import LogisticRegression
logModel = LogisticRegression(max_iter=500)
logModel.fit(x_train,y_train)
LogisticRegression(max_iter=500)
logPred = logModel.predict(x_test)
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
print(confusion_matrix(y_test,logPred))
print(classification_report(y_test,logPred))
[[12748   842]
 [ 1905  1989]]
              precision    recall  f1-score   support

           0       0.87      0.94      0.90     13590
           1       0.70      0.51      0.59      3894

    accuracy                           0.84     17484
   macro avg       0.79      0.72      0.75     17484
weighted avg       0.83      0.84      0.83     17484

После построения логистической регрессии и вычисления соответствующих прогнозов для набора тестовых данных возвращаются матрица путаницы и отчет о классификации модели.

Метрики, представляющие интерес для этого случая:

  • Точность, которая в основном измеряет процент правильных прогнозов; логистическая модель возвращает и точность примерно 84%.
  • Особый интерес для модели представляет правильное предсказание, будет ли дождь и не очень, если дождя не будет. По этой причине точность для класса «1» очень важна, эта модель вернула точность около 70%, что означает, что модель правильно предсказывает дождь в 70% случаев.

Модель случайного леса

from sklearn.ensemble import RandomForestClassifier
rfModel = RandomForestClassifier(n_estimators=200)
rfModel.fit(x_train,y_train)
RandomForestClassifier(n_estimators=200)
rfPred = rfModel.predict(x_test)
print(confusion_matrix(y_test,rfPred))
print(classification_report(y_test,rfPred))
[[12889   701]
 [ 1810  2084]]
              precision    recall  f1-score   support

           0       0.88      0.95      0.91     13590
           1       0.75      0.54      0.62      3894

    accuracy                           0.86     17484
   macro avg       0.81      0.74      0.77     17484
weighted avg       0.85      0.86      0.85     17484

Для этой модели возвращаемые интересующие метрики:

  • Точность приняла значение 86%, что означает, что в глобальном масштабе случайный лес дает правильный прогноз примерно в 86% случаев.
  • Точность для класса «1» приняла значение 75%, что означает, что модель правильно предсказывает дождь на следующий день примерно в 75% случаев.

Заключение

  • Модель случайного леса оказалась лучше в прогнозировании дождливых дней, чем логистическая модель, с точностью 86% против точности 84% и, что более важно, точностью дождливых дней 75% против только 70%.
  • Несмотря на многообещающие результаты, ясно, что точное предсказание любого типа климатического явления является очень сложной задачей, и предлагается включить еще более влиятельные переменные (которые недоступны в данном случае), чтобы построить более точную модель. модель в будущем.