자전거 대여 시스템 데이터 분석
RMSLE(Root Mean Squared Logarithmic Error)란?
예측 값과 실제 값의 차이를 로그 변환하여 계산한 후, 그 차이의 제곱 평균의 제곱근을 구한 값. 예측 오차를 측정하는 데 사용되며, 큰 값보다 작은 값의 오차를 더 중요시 하는 경우에 사용된다. 이는 예측 값이 실제 값보다 훨씬 클 때 더 큰 패널티를 부과하므로, 예측 값이 과대평가 되는 것을 방지한다.
| 컬럼명 | 데이터 타입 | 설명 |
|---|---|---|
| datetime | datetime | 자전거 대여 기록의 날짜 및 시간. 예시: 2011-01-01 00:00:00 |
| season | int | 계절 (1: 봄, 2: 여름, 3: 가을, 4: 겨울) |
| holiday | int | 공휴일 여부 (0: 평일, 1: 공휴일) |
| workingday | int | 근무일 여부 (0: 주말/공휴일, 1: 근무일) |
| weather | int | 날씨 상황 (1: 맑음, 2: 구름낌/안개, 3: 약간의 비/눈, 4: 폭우/폭설) |
| temp | float | 실측 온도 (섭씨) |
| atemp | float | 체감 온도 (섭씨) |
| humidity | int | 습도 (%) |
| windspeed | float | 풍속 (m/s) |
| casual | int | 등록되지 않은 사용자의 대여 수 |
| registered | int | 등록된 사용자의 대여 수 |
| count | int | 총 대여 수 (종속 변수) |
category_list = ['season', 'holiday', 'workingday', 'weather']
for var in category_list:
train_df[var] = train_df[var].astype('category')
test_df[var] = test_df[var].astype('category')
# Mapping numbers to understandable text
season_dict = {1:'Spring', 2:'Summer', 3:'Fall', 4:'Winter'}
weather_dict = {1:'Clear', 2:'Misty+Cloudy', 3:'Light Snow/Rain', 4:'Heavy Snow/Rain'}
train_df['season'] = train_df['season'].map(season_dict)
train_df['weather'] = train_df['weather'].map(weather_dict)
test_df['season'] = test_df['season'].map(season_dict)
test_df['weather'] = test_df['weather'].map(weather_dict)
train_df.head(n=3)
# Average values across each of the categorical columns
fig = plt.figure(figsize=(15,10))
axes = fig.add_subplot(2, 2, 1)
group_weather = pd.DataFrame(train_df.groupby(['weather'])['count'].mean()).reset_index()
sns.barplot(data=group_weather, x='weather', y='count', ax=axes)
axes.set(xlabel='Weather', ylabel='Count', title='Average bike rentals across Weather')
axes = fig.add_subplot(2, 2, 2)
group_season = pd.DataFrame(train_df.groupby(['season'])['count'].mean()).reset_index()
sns.barplot(data=group_season, x='season', y='count', ax=axes)
axes.set(xlabel='Season', ylabel='Count', title='Average bike rentals across Seasons')
axes = fig.add_subplot(2, 2, 3)
group_workingday = pd.DataFrame(train_df.groupby(['workingday'])['count'].mean()).reset_index()
sns.barplot(data=group_workingday, x='workingday', y='count', ax=axes)
axes.set(xlabel='Working Day', ylabel='Count', title='Average bike rentals across Working Day')
axes = fig.add_subplot(2, 2, 4)
group_season = pd.DataFrame(train_df.groupby(['holiday'])['count'].mean()).reset_index()
sns.barplot(data=group_season, x='holiday', y='count', ax=axes)
axes.set(xlabel='Holiday', ylabel='Count', title='Average bike rentals across Holiday')
plt.show()

# Seaborn boxplots to get an idea of the distribution/outliers
f, axes = plt.subplots(2, 2, figsize=(15, 12))
hue_order= ['Clear', 'Heavy Snow/Rain', 'Light Snow/Rain', 'Misty+Cloudy']
sns.boxplot(data=train_df, y='count', x='weather', ax=axes[0][0], order=hue_order)
sns.boxplot(data=train_df, y='count', x='workingday', ax=axes[0][1])
hue_order= ['Fall', 'Spring', 'Summer', 'Winter']
sns.boxplot(data=train_df, y='count', x='season', ax=axes[1][0], order=hue_order)
sns.boxplot(data=train_df, y='count', x='holiday', ax=axes[1][1])
plt.show()

# Splitting data into working-day and non-working day
mydata_w = train_df[train_df.workingday==1]
mydata_nw = train_df[train_df.workingday==0]
bin_size = 4
mydata_w['temp_round'] = mydata_w['temp']//bin_size
mydata_nw['temp_round'] = mydata_nw['temp']//bin_size
mean_count_vs_temp_w = mydata_w.groupby('temp_round')['count'].mean()
mean_count_vs_temp_nw = mydata_nw.groupby('temp_round')['count'].mean()
idx_w, idx_nw = range(len(mean_count_vs_temp_w)), range(len(mean_count_vs_temp_nw))
labels_w = [str(bin_size*i)+' to '+str(bin_size*(i+1)) for i in range(len(mean_count_vs_temp_w))]
labels_nw = [str(bin_size*i)+' to '+str(bin_size*(i+1)) for i in range(len(mean_count_vs_temp_nw))]
fig = plt.figure(figsize=(18, 6))
axes = fig.add_subplot(1, 2, 1)
plt.bar(x=idx_w, height=mean_count_vs_temp_w)
plt.xticks(idx_w, labels_w, rotation=90)
plt.xlabel('temp bins')
plt.ylabel('Average Count')
plt.title('Working Days: Average Count given across temperature range')
axes = fig.add_subplot(1, 2, 2)
plt.bar(x=idx_nw, height=mean_count_vs_temp_nw)
plt.xticks(idx_nw, labels_nw, rotation=90)
plt.xlabel('temp bins')
plt.ylabel('Average Count')
plt.title('Non-Working Days: Average Count given across temperature range')
plt.show()
# Convert the 'datetime' column to a proper datetime format
train_df['datetime'] = pd.to_datetime(train_df['datetime'])
test_df['datetime'] = pd.to_datetime(test_df['datetime'])
# Extract components from the 'datetime' column
train_df['month'] = train_df['datetime'].dt.month
train_df['date'] = train_df['datetime'].dt.day
train_df['hour'] = train_df['datetime'].dt.hour
train_df['day'] = train_df['datetime'].dt.weekday
test_df['month'] = test_df['datetime'].dt.month
test_df['date'] = test_df['datetime'].dt.day
test_df['hour'] = test_df['datetime'].dt.hour
test_df['day'] = test_df['datetime'].dt.weekday
# Convert these columns to categorical types
category_list = ['month', 'date', 'hour', 'day']
for var in category_list:
train_df[var] = train_df[var].astype('category')
test_df[var] = test_df[var].astype('category')
# Mapping 0 to 6 day indices to Monday to Saturday
day_dict = {0:'Monday', 1:'Teusday', 2:'Wednesday', 3:'Thursday', 4:'Friday', 5:'Saturday', 6:'Sunday'}
train_df['day'] = train_df['day'].map(day_dict)
test_df['day'] = test_df['day'].map(day_dict)
train_df.head(n=3)
# seaborn boxplots across hours
f, axes = plt.subplots(1, 1, figsize=(15, 6))
sns.boxplot(data=train_df, y='count', x='hour', hue='workingday', ax=axes)
handles, _ = axes.get_legend_handles_labels()
axes.legend(handles, ['Not a Working Day', 'Working Day'])
axes.set(title='Hourly Count based on Working day or not')
plt.show()

# Plots of average count across hour in a day for various categories
f, axes = plt.subplots(nrows=3, ncols=1, figsize=(15, 18))
# Average bike rentals by working day
group_work_hour = pd.DataFrame(train_df.groupby(['workingday', 'hour'])['count'].mean()).reset_index()
sns.pointplot(data=group_work_hour, x='hour', y='count', hue='workingday', ax=axes[0], legend=True)
handles, _ = axes[0].get_legend_handles_labels()
axes[0].legend(handles, ['Not a Working Day', 'Working Day'])
axes[0].set(xlabel='Hour in the day', ylabel='Count', title='Average Bike Rentals by the day if Working day or Not')
# Average bike rentals by weekdays
hue_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
group_day_hour = pd.DataFrame(train_df.groupby(['day', 'hour'])['count'].mean()).reset_index()
sns.pointplot(data=group_day_hour, x='hour', y='count', hue='day', ax=axes[1])
axes[1].set(xlabel='Hour in the day', ylabel='Count', title='Average Bike Rentals by the day across Weekdays')
# Average bike rentals for casual and registered users
df_melt = pd.melt(
frame=train_df,
id_vars='hour',
value_vars=['casual', 'registered'],
value_name='count_value', # Use a different name to avoid conflicts
var_name='casual_or_registered'
)
group_casual_hour = pd.DataFrame(df_melt.groupby(['hour', 'casual_or_registered'])['count_value'].mean()).reset_index()
sns.pointplot(data=group_casual_hour, x='hour', y='count_value', hue='casual_or_registered', ax=axes[2])
axes[2].set(xlabel='Hour in the day', ylabel='Count', title='Average Bike Rentals by the day across Casual/Registered Users')
plt.show()

# Average Monthly Count Distribution plot
f, axes = plt.subplots(nrows=1, ncols=1, figsize=(15, 6))
group_month = pd.DataFrame(train_df.groupby(['month', 'workingday'])['count'].mean()).reset_index()
sns.barplot(data=group_month, x='month', y='count', hue='workingday', ax=axes)
axes.set(xlabel='Month', ylabel='Count', title='Average bike rentals per Month')
handles, _ = axes.get_legend_handles_labels()
axes.legend(handles, ['Not a Working Day', 'Working Day'])
plt.show()

mydata_w = train_df[train_df.workingday==1]
mydata_nw = train_df[train_df.workingday==0]
fig = plt.figure(figsize=(18, 8))
# Working Day
axes = fig.add_subplot(1, 2, 1)
f = axes.scatter(mydata_w.hour, mydata_w['count'], c=mydata_w.temp, cmap = 'RdBu')
axes.set(xticks = range(24), xlabel='Hours in day', ylabel='Count', title='Working Day: Count vs. Day Hour with Temperature Gradient')
cbar = plt.colorbar(f)
cbar.set_label('Temperature in degree C')
# Non Working Day
axes = fig.add_subplot(1, 2, 2)
f = axes.scatter(mydata_nw.hour, mydata_nw['count'], c=mydata_nw.temp, cmap = 'RdBu')
axes.set(xticks = range(24), xlabel='Hours in day', ylabel='Count', title='Non Working Day: Count vs. Day Hour with Temperature Gradient')
cbar = plt.colorbar(f)
cbar.set_label('Temperature in degree C')
plt.show()

# Ensure the 'datetime' column is in datetime format
train_df['datetime'] = pd.to_datetime(train_df['datetime'])
# Set 'datetime' as the index
train_df.set_index('datetime', inplace=True)
# Extract heavy weather data
heavy_weather_data = train_df.loc[train_df['weather'] == 'Heavy Snow/Rain', :]
print(heavy_weather_data.index)
# Slice data for the specified datetime range
time_filtered_data = train_df['2012-01-09 08:00':'2012-01-09 20:00']
# Display the sliced data
print(time_filtered_data)

heavy_weather_data = train_df.loc[train_df['weather']=='Heavy Snow/Rain', :]
print(heavy_weather_data.index)
train_df['2012-01-09 08:00' : '2012-01-09 20:00']

# Function to calculate zscore
def zscore(series):
return (series-series.mean())/series.std()
train_df['count_zscore'] = train_df.groupby(['hour', 'workingday'])['count'].transform(zscore)
outlier_idx = np.abs(train_df['count_zscore'])>4
outlier_data = train_df.loc[outlier_idx, :]
print('Shape of the outlier data entries: ', outlier_data.shape)
outlier_data
# Removing outliers from mydata
traindata_without_outliers = train_df.loc[~outlier_idx, :]
print('Shape of data before outliner pruning: ', train_df.shape)
print('Shape of data after outlier pruning: ', traindata_without_outliers.shape)
# Dropping the zscore column
mydata_without_outliers = traindata_without_outliers.drop('count_zscore', axis=1)
mydata_without_outliers.head(n=3)

# Regression Plots with respect to Temperature, Humidity and Windspeed
fig = plt.figure(figsize=(18, 8))
axes = fig.add_subplot(1, 3, 1)
sns.regplot(data=traindata_without_outliers, x='temp', y='count',ax=axes)
axes.set(title='Reg Plot for Temperature vs. Count')
axes = fig.add_subplot(1, 3, 2)
sns.regplot(data=traindata_without_outliers, x='humidity', y='count',ax=axes, color='r')
axes.set(title='Reg Plot for Humidity vs. Count')
axes = fig.add_subplot(1, 3, 3)
sns.regplot(data=traindata_without_outliers, x='windspeed', y='count',ax=axes, color='g')
axes.set(title='Reg Plot for Windspeed vs. Count')
plt.show()

# Select only numeric columns for the correlation matrix
numeric_data = traindata_without_outliers.select_dtypes(include=[np.number])
# Calculate the correlation matrix
corr_matrix = numeric_data.corr()
# Create a mask for the upper triangle
mask = np.triu(np.ones_like(corr_matrix, dtype=bool))
# Plot the heatmap
fig = plt.figure(figsize=(10, 10))
sns.heatmap(corr_matrix, mask=mask, annot=True, cbar=True, vmax=0.8, vmin=-0.8, cmap='RdYlGn')
plt.title("Heatmap of Numeric Features")
plt.show()

# Using numbers to represent categorical data to transform the categorical columns
season_inv_dict = {'Spring':1, 'Summer':2, 'Fall':3, 'Winter':4}
weather_inv_dict = {'Clear':1, 'Misty+Cloudy':2, 'Light Snow/Rain':3, 'Heavy Snow/Rain':4}
day_inv_dict = {'Monday':0, 'Teusday':1, 'Wednesday':2, 'Thursday':3, 'Friday':4, 'Saturday':5, 'Sunday':6}
mydata_without_outliers['season'] = mydata_without_outliers['season'].map(season_inv_dict)
mydata_without_outliers['weather'] = mydata_without_outliers['weather'].map(weather_inv_dict)
mydata_without_outliers['day'] = mydata_without_outliers['day'].map(day_inv_dict)
test_df['season'] = test_df['season'].map(season_inv_dict)
test_df['weather'] = test_df['weather'].map(weather_inv_dict)
test_df['day'] = test_df['day'].map(day_inv_dict)
# Dropping columns from the provided data set that are either highly correlated with the existing columns:
# season with month, holiday and day with workingday, temp with atemp
# or poorly correlated with the target column: windspeed and date
drop_columns_1 = ['season', 'holiday', 'atemp', 'windspeed', 'date', 'day']
mydata_without_outliers = mydata_without_outliers.drop(drop_columns_1, axis=1)
testdata = test_df.drop(drop_columns_1, axis=1)
mydata_without_outliers.head(n=3)

# Transforming all the categorical columns into binary columns
month=pd.get_dummies(mydata_without_outliers['month'], prefix='month')
weather=pd.get_dummies(mydata_without_outliers['weather'], prefix='weather')
hour=pd.get_dummies(mydata_without_outliers['hour'], prefix='hour')
mydata_train=pd.concat([mydata_without_outliers, weather, month, hour],axis=1)
month=pd.get_dummies(testdata['month'], prefix='month')
weather=pd.get_dummies(testdata['weather'], prefix='weather')
hour=pd.get_dummies(testdata['hour'], prefix='hour')
mydata_test=pd.concat([testdata, weather, month, hour],axis=1)
mydata_train.columns

# Dropping columns and the last binary vector column
drop_columns_2 = ['weather', 'month', 'hour', 'weather_3', 'month_12', 'hour_23']
mydata_train = mydata_train.drop(drop_columns_2+['casual', 'registered'], axis=1)
mydata_test = mydata_test.drop(drop_columns_2, axis=1)
mydata_without_outliers = mydata_without_outliers.drop(['casual', 'registered'], axis=1)
mydata_train.columns

model_train, model_test = mydata_train[mydata_train.index.day<15], mydata_train[mydata_train.index.day>=15]
model_train2, model_test2 = mydata_without_outliers[mydata_without_outliers.index.day<15], mydata_without_outliers[mydata_without_outliers.index.day>=15]
# Separating out the working an non-working data from the training set
model_train_w = model_train[model_train['workingday']==1]
model_train_nw = model_train[model_train['workingday']==0]
model_train2_w = model_train2[model_train2['workingday']==1]
model_train2_nw = model_train2[model_train2['workingday']==0]
model_test_w = model_test[model_test['workingday']==1]
model_test_nw = model_test[model_test['workingday']==0]
model_test2_w = model_test2[model_test2['workingday']==1]
model_test2_nw = model_test2[model_test2['workingday']==0]
# Dropping workingday column
model_train_w = model_train_w.drop('workingday', axis=1)
model_train_nw = model_train_nw.drop('workingday', axis=1)
model_train2_w = model_train2_w.drop('workingday', axis=1)
model_train2_nw = model_train2_nw.drop('workingday', axis=1)
model_test_w = model_test_w.drop('workingday', axis=1)
model_test_nw = model_test_nw.drop('workingday', axis=1)
model_test2_w = model_test2_w.drop('workingday', axis=1)
model_test2_nw = model_test2_nw.drop('workingday', axis=1)
# Contains Binary Vector Form of features (Obtained from OneHotEncoder transformed categorical feature)
X, X_w, X_nw = model_train.drop('count', axis=1), model_train_w.drop('count', axis=1), model_train_nw.drop('count', axis=1)
y, y_w, y_nw = model_train['count'], model_train_w['count'], model_train_nw['count']
logy, logy_w, logy_nw = np.log1p(y), np.log1p(y_w), np.log1p(y_nw)
Xtest, Xtest_w, Xtest_nw = model_test.drop('count', axis=1), model_test_w.drop('count', axis=1), model_test_nw.drop('count', axis=1)
ytest, ytest_w, ytest_nw = model_test['count'], model_test_w['count'], model_test_nw['count']
logytest, logytest_w, logytest_nw = np.log1p(y), np.log1p(y_w), np.log1p(y_nw)
# Contains Categorical features instead of the Binary Vector Form
X2, X2_w, X2_nw = model_train2.drop('count', axis=1), model_train2_w.drop('count', axis=1), model_train2_nw.drop('count', axis=1)
y2, y2_w, y2_nw = model_train2['count'], model_train2_w['count'], model_train2_nw['count']
logy2, logy2_w, logy2_nw = np.log1p(y2), np.log1p(y2_w), np.log1p(y2_nw)
Xtest2, Xtest2_w, Xtest2_nw = model_test2.drop('count', axis=1), model_test2_w.drop('count', axis=1), model_test2_nw.drop('count', axis=1)
ytest2, ytest2_w, ytest2_nw = model_test2['count'], model_test2_w['count'], model_test2_nw['count']
logytest2, logytest2_w, logytest2_nw = np.log1p(y), np.log1p(y_w), np.log1p(y_nw)
# Data Frame to store all the RMSLE scores for various algorithms
algo_score = pd.DataFrame()
algo_score.index.name = 'Modelling Algo'
algo_score['Train RMSLE (Working Day)'] = None
algo_score['Train RMSLE (Non Working Day)'] = None
algo_score['Train RMSLE (Average)'] = None
algo_score['Test RMSLE (Working Day)'] = None
algo_score['Test RMSLE (Non Working Day)'] = None
algo_score['Test RMSLE (Average)'] = None
algo_score['Validation RMSLE (Working Day)'] = None
algo_score['Validation RMSLE (Non Working Day)'] = None
algo_score['Validation RMSLE (Average)'] = None
algo_score['Hyperparameters-Working'] = None
algo_score['Hyperparameters-Non Working'] = None
algo_score['Training+Test Time (sec)'] = None
cv_time = []
# Data Frame for second level of prediction. Collect the predicted y values for training and test set of data
ypred_train = pd.DataFrame(index = X.index)
ypred_test = pd.DataFrame(index = Xtest.index)
ypred_train['count'], ypred_test['count'] = y, ytest
from sklearn.metrics import make_scorer
# Metric used to measure the model (Root Mean Square Log Error)
def rmsle(y_actual, y_pred):
log1 = np.nan_to_num(np.array([np.log1p(v) for v in y_pred]))
log2 = np.nan_to_num(np.array([np.log1p(v) for v in y_actual]))
calc = (log1 - log2) ** 2
return np.sqrt(np.mean(calc))
# RMSLE function with inputs in log form. Used for CrossValidation scoring
def rmsle_log(logy_actual, logy_pred):
calc = (logy_actual - logy_pred) ** 2
return np.sqrt(np.mean(calc))
rmsle_cv = make_scorer(rmsle_log, greater_is_better=False)
# Plots True vs. Predictied count values in a particular time interval
def plot_true_vs_pred (y_w_actual, y_nw_actual, y_w_pred, y_nw_pred, algo, t_from, t_to):
fig = plt.figure(figsize=(18, 16))
# Working day plot
axes = fig.add_subplot(2, 1, 1)
axes.plot(y_w_actual[t_from:t_to], label='Actual', marker='.', markersize=15)
axes.plot(y_w_pred[t_from:t_to], label='Predicted', marker='.', markersize=15)
axes.set(xlabel='Time', ylabel='Count', title='{0} Model for Working Day: Count between time {1} and {2}'.format(algo, t_from, t_to))
axes.legend()
# Non working day plot
axes = fig.add_subplot(2, 1, 2)
axes.plot(y_nw_actual[t_from:t_to], label='Actual', marker='.', markersize=15)
axes.plot(y_nw_pred[t_from:t_to], label='Predicted', marker='.', markersize=15)
axes.set(xlabel='Time', ylabel='Count', title='{0} Model for Non Working Day: Count between time {1} and {2}'.format(algo, t_from, t_to))
axes.legend()
plt.show()
def cross_val(model_w, X_in_w, y_in_w, model_nw=None, X_in_nw=None, y_in_nw=None, cv=5):
y_val_pred_w = pd.Series(index=y_in_w.index)
y_val_pred_nw = None if model_nw == None else pd.Series(index=y_in_nw.index)
for idx in range(cv):
from_, to_ = idx*15/cv, (idx+1)*15/cv
val_idx_w = (X_in_w.index.day>from_) & (X_in_w.index.day<=to_)
train_idx_w = ~val_idx_w
X_idx_w, y_idx_w, X_val_idx_w = X_in_w[train_idx_w], y_in_w[train_idx_w], X_in_w[val_idx_w]
model_w.fit(X_idx_w, np.log1p(y_idx_w))
logy_val_pred_idx_w = model_w.predict(X_val_idx_w)
y_val_pred_w[val_idx_w] = np.expm1(logy_val_pred_idx_w)
if model_nw is not None:
val_idx_nw = (X_in_nw.index.day>from_) & (X_in_nw.index.day<=to_)
train_idx_nw = ~val_idx_nw
X_idx_nw, y_idx_nw, X_val_idx_nw = X_in_nw[train_idx_nw], y_in_nw[train_idx_nw], X_in_nw[val_idx_nw]
model_nw.fit(X_idx_nw, np.log1p(y_idx_nw))
logy_val_pred_idx_nw = model_nw.predict(X_val_idx_nw)
y_val_pred_nw[val_idx_nw] = np.expm1(logy_val_pred_idx_nw)
if model_nw is None:
rmsle_avg = rmsle(y_in_w, y_val_pred_w)
rmsle_w = rmsle(y_in_w[X_in_w.workingday==1], y_val_pred_w[X_in_w.workingday==1])
rmsle_nw = rmsle(y_in_w[X_in_w.workingday==0], y_val_pred_w[X_in_w.workingday==0])
else:
rmsle_w = rmsle(y_in_w, y_val_pred_w)
rmsle_nw = rmsle(y_in_nw, y_val_pred_nw)
rmsle_avg = rmsle(np.concatenate([y_in_w, y_in_nw]), np.concatenate([y_val_pred_w, y_val_pred_nw]))
rmsle_all = [rmsle_w, rmsle_nw, rmsle_avg]
y_pred_all =[y_val_pred_w, y_val_pred_nw]
return(rmsle_all, y_pred_all)
# Linear Regressor Ensemble for the above 3 models
def stack_model_fit (model, X_tr, X_t, y_tr, y_t):
model.fit(X_tr, y_tr)
y_tr_pred = model.predict(X_tr)
y_t_pred = model.predict(X_t)
[rmsle_avg_tr, rmsle_avg_t] = rmsle(y_tr, y_tr_pred), rmsle(y_t, y_t_pred)
y_tr_w_pred, y_tr_nw_pred = y_tr_pred[X.workingday==1], y_tr_pred[X.workingday==0]
y_t_w_pred, y_t_nw_pred = y_t_pred[Xtest.workingday==1], y_t_pred[Xtest.workingday==0]
y_tr_w, y_tr_nw = y_tr[X.workingday==1], y_tr[X.workingday==0]
y_t_w, y_t_nw = y_t[Xtest.workingday==1], y_t[Xtest.workingday==0]
rmsle_w_tr, rmsle_nw_tr = rmsle(y_tr_w, y_tr_w_pred), rmsle(y_tr_nw, y_tr_nw_pred)
rmsle_w_t, rmsle_nw_t = rmsle(y_t_w, y_t_w_pred), rmsle(y_t_nw, y_t_nw_pred)
rmsle_all = [rmsle_w_tr, rmsle_nw_tr, rmsle_avg_tr, rmsle_w_t, rmsle_nw_t, rmsle_avg_t]
y_pred_all = [y_tr_pred, y_t_pred]
return(rmsle_all, y_pred_all)
stack_model_fit : 스태킹 모델을 위한 데이터 세트를 적합하고 예측하는데 사용. 입력/핏처라는 개별모델에서부터 새로운 모델을 학습시키는데 사용된다.
from sklearn.linear_model import LinearRegression
lreg_w, lreg_nw = LinearRegression(), LinearRegression()
param_summary = ['', '', '']
rmsle_summary, y_predict_summary = model_fit(lreg_w, X_w, Xtest_w, y_w, ytest_w, lreg_nw, X_nw, Xtest_nw, y_nw, ytest_nw)
ypred_test.loc[Xtest.workingday==1,'LR'], ypred_test.loc[Xtest.workingday==0,'LR'] = y_predict_summary[1], y_predict_summary[3]
rmsle_val_summary, y_predict_val_summary = cross_val(lreg_w, X_w, y_w, lreg_nw, X_nw, y_nw)
ypred_train.loc[X.workingday==1,'LR'], ypred_train.loc[X.workingday==0,'LR'] = y_predict_val_summary[0], y_predict_val_summary[1]
algo_score.loc['Linear Regression'] = rmsle_summary+rmsle_val_summary+param_summary
algo_score.loc[['Linear Regression']]

# Linear Regression Plot: True vs. Predicted for one week
t_from, t_to = '2012-08-15', '2012-08-19'
ytest_w_predict, ytest_nw_predict = y_predict_summary[1], y_predict_summary[3]
ytest_w_predict = pd.Series(ytest_w_predict, index = ytest_w.index)
ytest_nw_predict = pd.Series(ytest_nw_predict, index = ytest_nw.index)
plot_true_vs_pred(ytest_w, ytest_nw, ytest_w_predict, ytest_nw_predict, 'Linear Regression', t_from, t_to)

# Features and the Estimated Linear Regression Coefficients obtained for Working day and Non-working day models
df_coeff = pd.DataFrame({'features': X_w.columns, 'Lin_Coeff_Working': lreg_w.coef_, 'Lin_Coeff_Non_Working': lreg_nw.coef_})
from sklearn.linear_model import Ridge
from sklearn.model_selection import GridSearchCV
# Hyperparameter Tuning
param_grid = {'alpha': [0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, 100]}
ridge_w = GridSearchCV(Ridge(random_state=42), param_grid, cv=5, scoring=rmsle_cv)
ridge_w.fit(X_w, logy_w)
print('Best alpha for Working Day Ridge Regression Model: {}'.format(ridge_w.best_params_))
ridge_nw = GridSearchCV(Ridge(random_state=42), param_grid, cv=5, scoring=rmsle_cv)
ridge_nw.fit(X_nw, logy_nw)
print('Best alpha for Non Working Day Ridge Regression Model: {}'.format(ridge_nw.best_params_))
param_summary = [ridge_w.best_params_, ridge_nw.best_params_,'']
rmsle_summary, y_predict_summary = model_fit(ridge_w, X_w, Xtest_w, y_w, ytest_w, ridge_nw, X_nw, Xtest_nw, y_nw, ytest_nw)
ypred_test.loc[Xtest.workingday==1,'Ridge'], ypred_test.loc[Xtest.workingday==0,'Ridge'] = y_predict_summary[1], y_predict_summary[3]
rmsle_val_summary, y_predict_val_summary = cross_val(ridge_w, X_w, y_w, ridge_nw, X_nw, y_nw)
ypred_train.loc[X.workingday==1,'Ridge'], ypred_train.loc[X.workingday==0,'Ridge'] = y_predict_val_summary[0], y_predict_val_summary[1]
algo_score.loc['Ridge Regression'] = rmsle_summary+rmsle_val_summary+param_summary
algo_score.loc[['Ridge Regression']]

algo_score.loc['Ridge Regression', 'Training+Test Time (sec)'] = 1.3
cv_time.append(4.64)
# Ridge Regression Plot: True vs. Predicted for one month
t_from, t_to = '2012-08-15', '2012-08-19'
ytest_w_predict, ytest_nw_predict = y_predict_summary[1], y_predict_summary[3]
ytest_w_predict = pd.Series(ytest_w_predict, index = ytest_w.index)
ytest_nw_predict = pd.Series(ytest_nw_predict, index = ytest_nw.index)
plot_true_vs_pred(ytest_w, ytest_nw, ytest_w_predict, ytest_nw_predict, 'Ridge Regularization Regression', t_from, t_to)

# Lasso Regression
from sklearn.linear_model import Lasso
from sklearn.model_selection import GridSearchCV
# Hyperparameter Tuning
param_grid = {'alpha': [0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, 100]}
lasso_w = GridSearchCV(Lasso(random_state=42), param_grid, cv=5, scoring=rmsle_cv)
lasso_w.fit(X_w, logy_w)
print('Best alpha for Working Day Lasso Regression Model: {}'.format(lasso_w.best_params_))
lasso_nw = GridSearchCV(Lasso(random_state=42), param_grid, cv=5, scoring=rmsle_cv)
lasso_nw.fit(X_nw, logy_nw)
print('Best alpha for Non Working Day Lasso Regression Model: {}'.format(lasso_nw.best_params_))
param_summary = [lasso_w.best_params_, lasso_nw.best_params_,'']
rmsle_summary, y_predict_summary = model_fit(lasso_w, X_w, Xtest_w, y_w, ytest_w, lasso_nw, X_nw, Xtest_nw, y_nw, ytest_nw)
ypred_test.loc[Xtest.workingday==1,'Lasso'], ypred_test.loc[Xtest.workingday==0,'Lasso'] = y_predict_summary[1], y_predict_summary[3]
rmsle_val_summary, y_predict_val_summary = cross_val(lasso_w, X_w, y_w, lasso_nw, X_nw, y_nw)
ypred_train.loc[X.workingday==1,'Lasso'], ypred_train.loc[X.workingday==0,'Lasso'] = y_predict_val_summary[0], y_predict_val_summary[1]
algo_score.loc['Lasso Regression'] = rmsle_summary+rmsle_val_summary+param_summary
algo_score.loc[['Lasso Regression']]

algo_score.loc['Lasso Regression', 'Training+Test Time (sec)'] = 1.3
cv_time.append(4.57)
# Lasso Regression Plot: True vs. Predicted for one month
t_from, t_to = '2012-08-15', '2012-08-19'
ytest_w_predict, ytest_nw_predict = y_predict_summary[1], y_predict_summary[3]
ytest_w_predict = pd.Series(ytest_w_predict, index = ytest_w.index)
ytest_nw_predict = pd.Series(ytest_nw_predict, index = ytest_nw.index)
plot_true_vs_pred(ytest_w, ytest_nw, ytest_w_predict, ytest_nw_predict, 'Lasso Regularization Regression', t_from, t_to)

# Add Ridge and Lasso coefficients for working day models
df_coeff['Ridge_Coeff_Working'] = ridge_w.best_estimator_.coef_
df_coeff['Lasso_Coeff_Working'] = lasso_w.best_estimator_.coef_
# Add Ridge and Lasso coefficients for non-working day models
df_coeff['Ridge_Coeff_Non_Working'] = ridge_nw.best_estimator_.coef_
df_coeff['Lasso_Coeff_Non_Working'] = lasso_nw.best_estimator_.coef_
import matplotlib.pyplot as plt
# Plotting the feature coefficients for Linear, Ridge, and Lasso Regression Models
fig = plt.figure(figsize=(18, 18))
# Working day plot
axes = fig.add_subplot(2, 1, 1)
axes.plot(df_coeff['Lin_Coeff_Working'], label='Linear Regression', marker='.', markersize=15)
axes.plot(df_coeff['Ridge_Coeff_Working'], label='Ridge Regression, alpha={}'.format(ridge_w.best_params_['alpha']), marker='.', markersize=15)
axes.plot(df_coeff['Lasso_Coeff_Working'], label='Lasso Regression, alpha={}'.format(lasso_w.best_params_['alpha']), marker='.', markersize=15)
axes.axvline(2-0.5, c='k', ls='--')
axes.axvline(4-0.5, c='k', ls='--')
axes.axvline(15-0.5, c='k', ls='--')
plt.xticks(range(len(df_coeff['Lin_Coeff_Working'])), df_coeff['features'], rotation=90)
axes.set(ylabel='Estimated Regression Coefficients', title='Coefficients for Working Day Model')
axes.set(xlim=[-1, len(df_coeff['Lin_Coeff_Working'])])
axes.legend()
# Non-working day plot
axes = fig.add_subplot(2, 1, 2)
axes.plot(df_coeff['Lin_Coeff_Non_Working'], label='Linear Regression', marker='.', markersize=15)
axes.plot(df_coeff['Ridge_Coeff_Non_Working'], label='Ridge Regression, alpha={}'.format(ridge_nw.best_params_['alpha']), marker='.', markersize=15)
axes.plot(df_coeff['Lasso_Coeff_Non_Working'], label='Lasso Regression, alpha={}'.format(lasso_nw.best_params_['alpha']), marker='.', markersize=15)
axes.axvline(2-0.5, c='k', ls='--')
axes.axvline(4-0.5, c='k', ls='--')
axes.axvline(15-0.5, c='k', ls='--')
plt.xticks(range(len(df_coeff['Lin_Coeff_Working'])), df_coeff['features'], rotation=90)
axes.set(ylabel='Estimated Regression Coefficients', title='Coefficients for Non-Working Day Model')
axes.set(xlim=[-1, len(df_coeff['Lin_Coeff_Working'])])
axes.legend()
plt.show()

X2.head(n=3)

## Random Forest Regression Hyperparameter tuning using Grid Search to obtain the best parameters.
## Commented it out since it takes a lot of time to run. Using the best parameters obtained via the below search
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import GridSearchCV
#param_grid = {'n_estimators': [50, 100, 200, 500, 1000, 2000, 5000]}
#rf_main = GridSearchCV(RandomForestRegressor(random_state=42), param_grid, cv=5, scoring=rmsle_cv)
#rf_main.fit(X2, logy2)
#param_grid = {'n_estimators': [500], 'max_features':['auto', 'sqrt', 'log2']}
#rf_main = GridSearchCV(RandomForestRegressor(random_state=42), param_grid, cv=5, scoring=rmsle_cv)
#rf_main.fit(X2, logy2)
#param_grid = {'n_estimators': [500], 'max_features':['auto'], 'min_samples_leaf':[1, 3, 7, 10, 20, 50]}
#rf_main = GridSearchCV(RandomForestRegressor(random_state=42), param_grid, cv=5, scoring=rmsle_cv)
#rf_main.fit(X2, logy2)
#param_grid = {'n_estimators': [500], 'max_features':['auto'], 'min_samples_leaf':[7], 'max_depth':[5, 8, 10, 20, 30, 40, 50, 70]}
#rf_main = GridSearchCV(RandomForestRegressor(random_state=42), param_grid, cv=5, scoring=rmsle_cv)
#rf_main.fit(X2, logy2)
#param_grid = {'n_estimators': [500], 'max_features':['auto'], 'min_samples_leaf':[7], 'max_depth':[10], 'min_samples_split':[0.0001, 0.001, 0.002, 0.005, 0.01]}
#rf_main = GridSearchCV(RandomForestRegressor(random_state=42), param_grid, cv=5, scoring=rmsle_cv)
#rf_main.fit(X2, logy2)
#print('Best parameters for Random Forest Regression Model: {}'.format(rf_main.best_params_))
# All the below results are obtained from the above GridSearchCV hyperparamter tuning
fig=plt.figure(figsize=(18, 12))
n_est_array = [50, 100, 200, 500, 1000, 2000, 5000]
n_est_cv_score = [-0.59522862, -0.59345376, -0.59307606, -0.59260841, -0.59292831,-0.59274538, -0.59279494]
axes=fig.add_subplot(2, 3, 1)
axes.plot(n_est_array, n_est_cv_score, marker='.')
axes.set(xlabel='n_estimators', ylabel='Mean CV Test Score', title='n_estimators vs. Score (best_n_estimator = 500)')
max_feature_array = ['auto', 'sqrt', 'log2']
max_feature_cv_score = [-0.59260841, -0.60178303, -0.60178303]
axes=fig.add_subplot(2, 3, 2)
axes.plot(range(3), max_feature_cv_score, marker='.')
plt.xticks(range(3), max_feature_array)
axes.set(xlabel='max_feature', ylabel='Mean CV Test Score', title='max_feature vs. Score (best_max_feature = auto)')
min_samples_leaf_array = [1, 3, 7, 10, 20, 50]
min_samples_leaf_cv_score = [-0.59260841, -0.58788348, -0.58764415, -0.59060404, -0.60340495,-0.63367843]
axes=fig.add_subplot(2, 3, 3)
axes.plot(min_samples_leaf_array, min_samples_leaf_cv_score, marker='.')
axes.set(xlabel='min_samples_leaf', ylabel='Mean CV Test Score', title='min_samples_leaf vs. Score (best_min_samples_leaf = 7)')
max_depth_array = [5, 8, 10, 15, 20, 30, 40, 50, 70]
max_depth_cv_score = [-0.71808668, -0.60209726, -0.58727382, -0.58785334, -0.58764068, -0.58764415, -0.58764415, -0.58764415, -0.58764415]
axes=fig.add_subplot(2, 3, 4)
axes.plot(max_depth_array, max_depth_cv_score, marker='.')
axes.set(xlabel='max_depth', ylabel='Mean CV Test Score', title='max_depth vs. Score (best_max_depth = 10)')
min_samples_split_array = [0.0001, 0.001, 0.002, 0.005, 0.01]
min_samples_split_cv_score = [-0.58727382, -0.58727382, -0.58796633, -0.59735637, -0.61832167]
axes=fig.add_subplot(2, 3, 5)
axes.plot(min_samples_split_array, min_samples_split_cv_score, marker='.')
axes.set(xlabel='min_samples_split', ylabel='Mean CV Test Score', title='min_samples_split vs. Score (best_min_samples_split = 0.0001)')
plt.show()

from sklearn.ensemble import RandomForestRegressor
# Best parameters obtained via GridSearchCV or defined manually
best_n_estimators = 500
best_max_features = 'sqrt' # Replace 'auto' with 'sqrt', 'log2', or a valid value
best_min_samples_leaf = 7
best_max_depth = 10
# Update param_summary to reflect the new max_features value
param_summary = [
f'n_estimators: {best_n_estimators}, max_features: {best_max_features}, '
f'min_samples_leaf: {best_min_samples_leaf}, max_depth: {best_max_depth}',
f'n_estimators: {best_n_estimators}, max_features: {best_max_features}, '
f'min_samples_leaf: {best_min_samples_leaf}, max_depth: {best_max_depth}',
''
]
# Initialize the RandomForestRegressor with the corrected parameter
rfa = RandomForestRegressor(
n_estimators=best_n_estimators,
max_features=best_max_features,
min_samples_leaf=best_min_samples_leaf,
max_depth=best_max_depth,
random_state=42
)
# Fit the model using the helper function
rmsle_summary, y_predict_summary = model_fit(rfa, X2, Xtest2, y2, ytest2)
# Add predictions to the DataFrame
ypred_test['RF1'] = y_predict_summary[1]
rmsle_val_summary, y_predict_val_summary = cross_val(rfa, X2, y2)
ypred_train['RF1'] = y_predict_val_summary[0]
algo_score.loc['Random Forest-Categorical+Single'] = rmsle_summary+rmsle_val_summary+param_summary
algo_score.loc[['Random Forest-Categorical+Single']]

algo_score.loc['Random Forest-Categorical+Single', 'Training+Test Time (sec)'] = 5.48
cv_time.append(19.9)
# Random Forest Regression Plot: True vs. Predicted for one month
t_from, t_to = '2012-08-15', '2012-08-19'
y_test_predict = pd.Series(y_predict_summary[1], index = ytest2.index)
ytest_w_predict, ytest_nw_predict = y_test_predict[Xtest2.workingday==1], y_test_predict[Xtest2.workingday==0]
plot_true_vs_pred(ytest2_w, ytest2_nw, ytest_w_predict, ytest_nw_predict, 'Random Forest Regression', t_from, t_to)

Xtest2['2012-08-19 09':'2012-08-19 15']

# Plotting the Feature Importance
fig = plt.figure(figsize=(8, 6))
axes = fig.add_subplot(1, 1, 1)
axes.plot(rfa.feature_importances_, marker='.', markersize=15)
plt.xticks(range(len(rfa.feature_importances_)), X2.columns)
axes.set(ylabel='Feature Importance', title='Feature Importance for Random Forest Regression using Categorical Data')
axes.set(xlim=[-1, len(X2.columns)], ylim=[0, 1])
plt.show()

특정 시간대의 자전거 대여 패턴은 어떻게 나올까?
-> 출근시간에 자전거 대여 수요가 급증. 이는 직장인이나 학생들이 통근 및 통학 목적으로 자전거를 이용하고, 퇴근시간에는 퇴근 후 집으로 돌아가는 이동에 자전거를 활용하는 경우가 많음. 주중 저녁 및 주말 낮 시간대에는 수요가 완만하며, 여가 활동이나 레저 목적으로 이용하는 경향이 강하다.
날씨 변수와 자전거 대여 수요 간의 상관관계는 무엇인가요?
-> 기온(temp), 체감온도(atemp), 습도(humidity), 풍속(windspeed), 날씨 상태(weather)
계절별 자전거 대여 패턴의 차이는 무엇인가요?
-> 봄에는 수요가 점차 증가하는 시기. 여름에는 수요가 가장 높은 계절(적정 기온과 긴 낮 시간). 가을에는 여름과 비슷하게 높은 수치를 보이며, 날씨가 선선한 경우에 대여 활발. 겨울에는 수요가 가장 낮은 계절
주말과 평일의 자전거 대여 수요 차이는 무엇인가요?
-> 주말에는 여가 및 레저 목적의 대여가 많아지며, 수요가 낮 시간대(오전10시-오후4시)에 분포. 평일 출퇴근 시간에 집중(오전8시9시, 오후5시7시)
자전거 대여 수요를 예측하기 위해 사용할 수 있는 가장 중요한 변수는 무엇인가요?
-> 모델 분석 결과와 변수 주요도에 따르면 시간, 기온, 체감 온도, 습도, 날씨 상태, 요일 및 주말이 될 수 있다.
자전거 대여 수요 예측 모델을 구축하고, 이를 기반으로 한 운영 전략을 제안해보세요.
-> 랜덤포레스트를 이용하여 자전거 대여 수요를 예측한 결과, RMSLE를 기준으로 중요변수를 고려하여 높은 정확도를 달성했다. 시간대별 자전거 배치 최적화, 계절별 운영 계획, 날씨 기반 예측 활용, 가격 및 프로모션 전략이 있을 수 있다.
- os.path -> pathlib
지금까지 os 모듈을 사용했지만, pathlib 모듈에 더 익숙해지려 한다. 외우자!!!
# os
def read_csv(file_name):
df = pd.read_csv(f'./interim/{file_name}.csv')
return df
# Pathlib
def read_csv(file_name):
file_path = Path('/Users/jaewon/Desktop/mission') / f"{file_name}.csv"
df = pd.read_csv(file_path)
return df