<jupyter_start><jupyter_text>Model Experiments<jupyter_code>%matplotlib inline

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import sqlite3
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import RandomizedSearchCV
from sklearn.metrics import classification_report
conn = sqlite3.connect('../data/fires.sqlite')
cur = conn.cursor()

df_raw = pd.read_sql_query("""
select
	weather_county.fips,
	weather_county.month,
	weather_county.date,
	weather_county.year,
	weather_county.precipitation,
	weather_county.pressure,
	weather_county.humidity_2m,
	weather_county.temp_2m,
	weather_county.temp_dew_point_2m,
	weather_county.temp_wet_bulb_2m,
	weather_county.temp_max_2m,
	weather_county.temp_min_2m,
	weather_county.temp_range_2m,
	weather_county.temp_0m,
	weather_county.wind_10m,
	weather_county.wind_max_10m,
	weather_county.wind_min_10m,
	weather_county.wind_range_10m,
	weather_county.wind_50m,
	weather_county.wind_max_50m,
	weather_county.wind_min_50m,
	weather_county.wind_range_50m,
	weather_county.drought_score,
	soil.elevation,
	soil.slope_005,
	soil.slope_005_02,
	soil.slope_02_05,
	soil.slope_05_10,
	soil.slope_10_15,
	soil.slope_15_30,
	soil.slope_30_45,
	soil.slope_45,
	soil.aspect_north,
	soil.aspect_east,
	soil.aspect_south,
	soil.aspect_west,
	soil.aspect_unknown,
	soil.water_land,
	soil.barren_land,
	soil.urban_land,
	soil.grass_land,
	soil.forest_land,
	soil.partial_cultivated_land,
	soil.irrigated_land,
	soil.cultivated_land,
	soil.nutrient,
	soil.nutrient_retention,
	soil.rooting,
	soil.oxygen,
	soil.excess_salts,
	soil.toxicity,
	soil.workability,
  ifnull(fires.fire_size_class, '') as fire_size_class
from weather_county
inner join soil
  on soil.fips = weather_county.fips
left outer join (
select max(fire_size_class) as fire_size_class, fips, date
	from (
		-- Join date and 1, 2 and 3 days earlier in case the discovery date is delayed
		select fire_size_class, fips, date from fires
		where cause in ('Other causes', 'Natural', 'Power', 'Debris Burning', 'Recreation', 'Missing/Undefined')
		union
		select fire_size_class, fips, date_1d_before as date from fires
		where cause in ('Other causes', 'Natural', 'Power', 'Debris Burning', 'Recreation', 'Missing/Undefined')
		union
		select fire_size_class, fips, date_2d_before as date from fires
		where cause in ('Other causes', 'Natural', 'Power', 'Debris Burning', 'Recreation', 'Missing/Undefined')
		union
		select fire_size_class, fips, date_3d_before as date from fires
		where cause in ('Other causes', 'Natural', 'Power', 'Debris Burning', 'Recreation', 'Missing/Undefined')
	)
	group by fips, date
) as fires on
  fires.fips = weather_county.fips
  and fires.date = weather_county.date
where
	weather_county.fips != 6065
	and weather_county.year in (2005, 2006, 2007, 2008)
""", conn)

# Ignoring FIPS = 6065, Riverside, since it has a disproportionate number of 'Miscellaneous' fires 

conn.close()

df_raw.date = pd.to_datetime(df_raw.date)
df_raw['day_of_week'] = df_raw.date.dt.dayofweek

df_raw.describe().transpose()
df_fire_false = df_raw[df_raw.fire_size_class == '']
df_fire_true = df_raw[df_raw.fire_size_class != '']
print(f'Unbalanced classes of NO fire: {len(df_fire_false):,} and YES fire: {len(df_fire_true):,}')

df_fire_true_balanced = df_fire_true.sample(n=len(df_fire_false), replace=True)

df_fires = pd.concat([df_fire_false, df_fire_true_balanced], axis=0)
df_fire_false = df_fires[df_fires.fire_size_class == '']
df_fire_true = df_fires[df_fires.fire_size_class != '']
print(f'Balanced classes of NO fires: {len(df_fire_false):,} and YES fire: {len(df_fire_true):,}')

X_trim = df_fires.drop(['date', 'fire_size_class'], axis=1)
plt.figure(figsize=(20, 20))
sns.heatmap(X_trim.corr())
X_encoded = pd.get_dummies(X_trim, columns=['fips', 'day_of_week', 'year', 'month'])

scaler = StandardScaler()
scaler.fit(X_encoded)
X = scaler.transform(X_encoded)<jupyter_output><empty_output><jupyter_text>All Fire Sizes (A-G)<jupyter_code>y = (df_fires.fire_size_class != '').values

num_fires = (y == 1).sum()
num_data_pt = len(y)
print(f'{num_fires} fires out of {len(y)} data points')
print(f'No fires {(1 - round(num_fires/num_data_pt, 2)) * 100}% of the time (ie Balanced)')

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

params={
  'C': [0.1, 1, 2],
}

log_reg = LogisticRegression(solver='liblinear', max_iter=1000)
log_reg = GridSearchCV(estimator=log_reg, param_grid=params, scoring='recall', cv=5)

log_reg.fit(X_train, y_train)

print(f'All size fires estimator', log_reg.best_estimator_)
print('--------- Log Regression Accuracy ---------')
print(accuracy_score(log_reg.predict(X_test), y_test))

y_pred_log_reg = log_reg.predict(X_train)

print('--------- Log Regression Classification ---------')
print(classification_report(y_train, y_pred_log_reg))
from sklearn.ensemble import RandomForestClassifier

params = {
  'n_estimators': np.arange(40, 100, 20),
  # 'splitter': ['best', 'random'],
  'max_features': np.arange(4, 12, 1),
  'max_depth': np.arange(10, 20, 2),
  'min_samples_split': np.arange(4, 12, 2),
  'min_samples_leaf': np.arange(2, 10, 2),
  'max_leaf_nodes': np.arange(4, 10, 2),
}

rand_forest = RandomizedSearchCV(RandomForestClassifier(), params, scoring="recall")
rand_forest.fit(X_train, y_train)

print(f'Best Params', rand_forest.best_params_)
print(f'Best Score', rand_forest.best_score_)

print('--------- RandomForestClassifier Classification ---------')
y_pred_rand_forest = rand_forest.predict(X_test)
print(classification_report(y_test, y_pred_rand_forest))
importances = rand_forest.best_estimator_.feature_importances_
forest_importances = pd.Series(importances, index=X_encoded.columns.values)
std = np.std([tree.feature_importances_ for tree in rand_forest.best_estimator_], axis=0)

fig, ax = plt.subplots()
forest_importances.plot.bar(yerr=std, ax=ax)
ax.set_title('Feature importances using MDI')
ax.set_ylabel('Mean decrease in impurity')
fig.set_figheight(8)
fig.set_figwidth(25)
import xgboost as xgb

params={
  'objective': ['binary:logistic'],
  'max_depth': [4, 5, 6, 7],
  'gamma':[0.1, 0.5, 1],
  'subsample': [0.5],
  'colsample_bytree': [0.1],
  'colsample_bylevel': [1.0],
  'learning_rate': [0.1, 0.5],
  'n_estimators': [500, 600, 700],
  'eval_metric': ['logloss']
}

xgb_clf = xgb.XGBClassifier(seed=42, use_label_encoder=False)

xgb_clf = RandomizedSearchCV(estimator=xgb_clf, param_distributions=params)

xgb_clf.fit(X_train, y_train)

print("Best Params:", xgb_clf.best_params_)

y_pred_xgb = xgb_clf.predict(X_test)

print('--------- XGBoost Classification Report ---------')
print(classification_report(y_test, y_pred_xgb))<jupyter_output>--------- XGBoost Classification Report ---------
              precision    recall  f1-score   support

       False       0.84      0.81      0.83     14674
        True       0.82      0.85      0.83     14432

    accuracy                           0.83     29106
   macro avg       0.83      0.83      0.83     29106
weighted avg       0.83      0.83      0.83     29106<jupyter_text>Only Larger Fire Sizes (B-G)<jupyter_code>df_non_small_false = df_raw[df_raw.fire_size_class <= 'A']
df_non_small_true = df_raw[df_raw.fire_size_class > 'A']
print(f'Unbalanced classes of NO fire: {len(df_non_small_false):,} and YES fire: {len(df_non_small_true):,}')

df_non_small_true_balanced = df_non_small_true.sample(n=len(df_non_small_false), replace=True)

df_non_small = pd.concat([df_non_small_false, df_non_small_true_balanced], axis=0)
df_non_small_false = df_non_small[df_non_small.fire_size_class <= 'A']
df_non_small_true = df_non_small[df_non_small.fire_size_class > 'A']
print(f'Balanced classes of NO fires: {len(df_non_small_false):,} and YES fire: {len(df_non_small_true):,}')

X_non_small_trim = df_non_small.drop(['date', 'fire_size_class'], axis=1)

X_non_small_encoded = pd.get_dummies(X_non_small_trim, columns=['fips', 'day_of_week', 'year', 'month'])

scaler = StandardScaler()
scaler.fit(X_non_small_encoded)
X_non_small = scaler.transform(X_non_small_encoded)
y_non_small = (df_non_small.fire_size_class > 'A').values # Ignore small fires ('' or 'A') for now

X_non_small_train, X_non_small_test, y_non_small_train, y_non_small_test = train_test_split(X_non_small, y_non_small, random_state=42)

params={
  'C': [0.1, 1, 2],
}

log_reg_non_small = LogisticRegression(solver='liblinear', max_iter=1000)
log_reg_non_small = GridSearchCV(estimator=log_reg_non_small, param_grid=params, scoring='recall', cv=5)

log_reg_non_small.fit(X_non_small_train, y_non_small_train)

print(f'Non-small fires estimator', log_reg_non_small.best_estimator_)
print('--------- Log Regression Accuracy (non-small fires) ---------')
print(accuracy_score(log_reg_non_small.predict(X_non_small_test), y_non_small_test))

y_non_small_pred_log_reg = log_reg_non_small.predict(X_non_small_train)

print('--------- Log Regression Classification (non-small fires) ---------')
print(classification_report(y_non_small_train, y_non_small_pred_log_reg))
import xgboost as xgb

params={
  'objective': ['binary:logistic'],
  'max_depth': [3],
  'gamma':[5],
  'subsample': [0.5],
  'colsample_bytree': [0.1],
  'colsample_bylevel': [1.0],
  'learning_rate': [0.1],
  'n_estimators': [500],
  'eval_metric': ['logloss']
}

xgb_clf_non_small = xgb.XGBClassifier(seed=42, use_label_encoder=False)

xgb_clf_non_small = RandomizedSearchCV(estimator=xgb_clf_non_small, param_distributions=params)

xgb_clf_non_small.fit(X_non_small_train, y_non_small_train)

print("Non-small best Params:", xgb_clf_non_small.best_params_)
y_non_small_pred_xgb = xgb_clf_non_small.predict(X_non_small_test)

print('--------- XGBoost Classification Report (non-small) ---------')
print(classification_report(y_non_small_test, y_non_small_pred_xgb))<jupyter_output>--------- XGBoost Classification Report (non-small) ---------
              precision    recall  f1-score   support

       False       0.83      0.78      0.80     16677
        True       0.79      0.84      0.81     16670

    accuracy                           0.81     33347
   macro avg       0.81      0.81      0.81     33347
weighted avg       0.81      0.81      0.81     33347
{'fraction_non_alphanumeric': 0.13204292832502876, 'fraction_numerical': 0.03152548869298582, 'mean_word_length': 5.121407624633431, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 14, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '331132', 'license': 'CC0-1.0', 'n_tokens_mistral': 4304, 'n_tokens_neox': 3989, 'n_words': 732, 'path': 'experiments/3_trees_county.ipynb', 'repo_name': 'eerichmond/ml-wildfire-prediction', 'size': '10436'}