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%.
- Несмотря на многообещающие результаты, ясно, что точное предсказание любого типа климатического явления является очень сложной задачей, и предлагается включить еще более влиятельные переменные (которые недоступны в данном случае), чтобы построить более точную модель. модель в будущем.