"""
<div style="color:white;
           display:fill;
           border-radius:5px;
           background-color:SlateBlue;
           font-size:110%;
           font-family:Verdana;
           letter-spacing:0.5px">

<span style='padding: 10px; font-family:Helvetica; font-size:30px' > Seoul Bike Rents </span>
</div>
"""
"""
![Bikes-2.jpg](attachment:8a240da6-f22a-4b05-a431-d7cd1cf16ad8.jpg)
"""
"""
welcome to our nootbook about Seoul Bike Rental dataset

in this nootbook we will try to show some insights about the features in this dataset by visualize them, the Exploration will go into **3** steps:

> **1- Univariate Exploration**

> **2- Bivariate Exploration**

> **3- Multivariate Exploration**

also we will create a **model** that predict the number of bikes rented over 2017 and 2018 in seoul

if you have any suggest,advice or correction please don't hesitate to write it, it will be very helpful for me 
"""
# This Python 3 environment comes with many helpful analytics libraries installed
# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python
# For example, here's several helpful packages to load

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
import matplotlib.pyplot as plt 
import seaborn as sns
# Input data files are available in the read-only "../input/" directory
# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))

# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using "Save & Run All" 
# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session
import warnings
warnings.filterwarnings('ignore')
"""
### First, let's take a quick look on the dataset
"""
train = pd.read_csv('../input/seoul-bike-rental-ai-pro-iti/train.csv')
test = pd.read_csv('../input/seoul-bike-rental-ai-pro-iti/test.csv')
sample_submission = pd.read_csv('../input/seoul-bike-rental-ai-pro-iti/sample_submission.csv')

train.head(2)
train.info()
"""
### now let's preprocess some features before starting
"""
train.Date = pd.to_datetime(train.Date, format='%d/%m/%Y')
train['year'] = train.Date.dt.year
train['Rainfall(cm)'] = train['Rainfall(mm)'] / 10
#------------------------------------------------------------
train['Seasons'] = train['Seasons'].astype('category')
train['Holiday'] = train['Holiday'].astype('category')
train['Functioning Day'] = train['Functioning Day'].astype('category')
train.info()
train.isnull().sum()
"""
### Here, we can detect outlier's with boxplot
"""
train.describe()
plt.figure(figsize=(15, 5))

sns.boxplot(data = train)

plt.tick_params(axis='x', rotation=30)

plt.yscale('symlog')

"""
### as we can see **Rainfall** and **Snowfall** has alot of outliers
"""
"""
### great, all set to go
"""
"""
## 1- Univariate Exploration
"""
"""
### first we will see the distribution of the categorical features 
"""
import plotly.express as px
%matplotlib inline
import cufflinks as cf
# Make Plotly work in your Jupyter Notebook
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
init_notebook_mode(connected=True)
import plotly.graph_objects as go
# Use Plotly locally
cf.go_offline()


px.pie(train, names='Seasons', 
       title='categories of  seasons', 
       color_discrete_sequence=px.colors.sequential.Reds, hole=.3)

 px.pie(train, names='Holiday', 
        title=('categories of Holidays'), hole=0.5)

 px.pie(train, names='Functioning Day', 
        title=('categories of Functioning Day'), hole=0.5)
"""
### now, let's see the numerical features
"""
"""
- we wiil see the distribution of the numerical features 

- as **ID** isn't important we will not consider it as well as the categorical features **Seasons**, **Holiday** and **Functioning Day**
"""
plt.subplots(3, 3,figsize=(18,10))

plt.subplots_adjust(top=1)
plt.suptitle("Distribution of the numerical features", fontsize=25) 


for i, col in enumerate(train[train.columns[3:-5]]):
    plt.subplot(3, 3, i+1)
    sns.distplot(train[col]).set_title("{} distribution".format(col), fontsize=18);
    plt.tight_layout(pad=3.0)
"""
## 2- Bivariate Exploration
"""
"""
### as **y** is the target feature, we will see the correlation between it and all other numerical features by ploting heatmap which represent the **Pearson's Correlation** between each two features
"""
plt.figure(figsize=(15,10))

sns.heatmap(train.corr(),
            vmin=-1,
            vmax=1,
            cmap='RdBu',
            annot=True);
"""
### let's see the relation between 'y' and the same features with plots
"""
plt.subplots(2, 4,figsize=(18,10))

plt.subplots_adjust(top=1)
plt.suptitle("investigate relation between 'y' and numerical features", fontsize=25) 


for i, col in enumerate(train[train.columns[3:-5]]):
    plt.subplot(3, 3, i+1)
    sns.regplot(x = train['y'], y = train[col], line_kws = {'color': 'red'}).set_title("{} vs y distribution".format(col), fontsize=18);
    plt.tight_layout(pad=3.0)
   
"""
### now let's see the relation between **y** and the categorical features
"""
from plotly.subplots import make_subplots

fig = make_subplots(rows=1, cols=2, specs=[[{"type": "pie"}, {"type": "pie"}]])


fig.add_trace(go.Pie(labels=train['Holiday'], values=train['y']),
    row=1, col=1)

fig.add_trace(go.Pie(labels=train['Functioning Day'], values=train['y']),
    row=1, col=2)

fig.update_traces(hole=.5)

fig.update_layout(height=600, width=800, title_text="The count of <b>y</b> according to <b>Holidays</b> and <b>Functioning days</b>"
                  ,annotations=[dict(text='Holidays', x=0.17, y=0.5, font_size=15, showarrow=False),
                                dict(text='Functioning day', x=0.87, y=0.5, font_size=14, showarrow=False)])
fig.show()

"""
### it looks like there is no rented bikes when there is no functioning day, to make sure that it's not a bug in the plot we can see the table using **Groupby**
"""
train[train['Functioning Day'] == 'No'].head()
train[train['Functioning Day'] == 'No']['y'].sum()
plt.figure(figsize=(15, 5))

sns.barplot(data = train, x = 'Seasons', y = 'y', palette = 'Blues').set_title("number of bikes rented in each season", fontsize=18);

"""
### we can see the change of some features over time with **Date** feature as follows :
"""
features_for_lp = ['Rainfall(mm)','y','Snowfall (cm)','Solar Radiation (MJ/m2)','Humidity(%)','Wind speed (m/s)']

plt.subplots(3, 3,figsize=(18,10))
plt.subplots_adjust(top=1)
#plt.suptitle("investigate relation between 'y' and numerical features", fontsize=25) 

for i, col in enumerate(features_for_lp):
    plt.subplot(3, 2, i+1)
    sns.lineplot(data = train , x = 'Date', y=train[col]).set_title("change of the count of {} over provided years".format(col), fontsize=18);

    plt.tight_layout(pad=3.0)


"""
## 3- Multivariate Exploration
"""
sns.barplot(data = train, x = 'year', y = 'y', hue = 'Seasons', palette = 'Reds').set_title("Season's rental bikes Frequency for each year", fontsize=18);
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=[20, 5])

sns.barplot(data = train, x = 'Seasons', y = 'y', hue = 'Functioning Day', palette = 'Reds', ax = axes[0]).set_title("Season's rental bikes Frequency for each year", fontsize=18);
sns.barplot(data = train, x = 'Seasons', y = 'y', hue = 'Holiday', palette = 'Blues', ax = axes[1]).set_title("Season's rental bikes Frequency for each year", fontsize=18);
"""
### for each season we can see the change of the number of rented bikes based on the variety of some features like **Rainfall** , **Snowfall** , **Solar Radiation** , **Humidity** and **Wind speed**
"""
features_for_fp = ['Rainfall(mm)','Snowfall (cm)','Solar Radiation (MJ/m2)','Humidity(%)','Wind speed (m/s)']

for i, col in enumerate(features_for_fp):
    g = sns.FacetGrid(data = train, col = 'Seasons', height = 4.5,
            margin_titles = True);
    g.map(sns.regplot, col, 'y',scatter = True, fit_reg = True, line_kws = {'color': 'red'});
"""
## 4- ML Implementation
"""
train.isnull().sum()
"""
so there is no null values, let's continue
"""
train.info()
"""
now we will do **feature engineering** 
"""
train.Date = pd.to_datetime(train.Date, format='%d/%m/%Y')
train['day'] = train['Date'].dt.day_name()
train['month'] = train['Date'].dt.month_name()
train['year'] = pd.DatetimeIndex(train['Date']).year
train['day_night'] = train['Hour'].apply(lambda x: 'Night' if (x >= 20) or (x<=5) else "Day")
#-------------------------------------------
train["weather"] = train.Seasons.map({'Spring': " Clear",\
                                        'Summer' : " Mist ", \
                                        'Autumn' : " Light Rain", \
                                        'Winter' :" Heavy Rain" })

# #-------------------------------------------
hours_binning = []
for i in train['Hour']:
    if i < 8 :
        hours_binning.append(1)
    elif i >= 22 :
        hours_binning.append(2)
    elif i > 9 and i<18 :
        hours_binning.append(3)
    elif i == 8 :
        hours_binning.append(4)
    elif i == 9 :
        hours_binning.append(5)
    elif i == 20 or i == 21 :
        hours_binning.append(6)
    elif i == 19 or i == 18 :
        hours_binning.append(7)
        
train['hours_binning'] = hours_binning

# #-------------------------------------------
day_condition = []
for i in train['Humidity(%)']:
    if i >= 0 and i<= 20 :
        day_condition.append("uncomfortably dry")
    elif i >= 21 and i<=60 :
        day_condition.append("comfort")
    elif i >= 61 and i<=100 :
        day_condition.append("uncomfortably wet")
        
train['day_condition'] = day_condition

# #-------------------------------------------
bins = [-20,0,25.7,40]
labels = ['cold', 'normal', 'hot']
train['temp_binned'] = pd.cut(train['Temperature(�C)'], bins=bins, labels=labels).astype('object')

# #-------------------------------------------
bins = [-28,9,13,15.5,17.5,28]
labels = ['A bit dry', 'Very comfortable', 'Comfortable','Ok','Very humid']
train['dew_point_binned'] = pd.cut(train['Dew point temperature(�C)'], bins=bins, labels=labels).astype('object')
pd.set_option('display.max_columns', None)
train.head()
"""
now we will calculate correlation between **numerical**, **categorical features** and **y**
"""
# train_modified = train.drop(columns = ['y','ID','day_condition','Rainfall(cm)'],axis=1)

train_modified = train[['Date', 'Hour', 'Temperature(�C)', 'Humidity(%)', 'Wind speed (m/s)','Visibility (10m)', 'Dew point temperature(�C)',
        'Solar Radiation (MJ/m2)', 'Rainfall(mm)', 'Snowfall (cm)', 'Seasons','Holiday', 'Functioning Day', 'day', 'month', 'year', 'day_night',
        'weather', 'hours_binning', 'temp_binned', 'dew_point_binned']]

y = train['y'].astype('float')
y =  np.log1p(y)

# define numerical features
numerical = train_modified[['Hour', 'Temperature(�C)', 'Humidity(%)', 'Wind speed (m/s)','Visibility (10m)', 'Dew point temperature(�C)',
                            'Solar Radiation (MJ/m2)', 'Rainfall(mm)', 'Snowfall (cm)', 'year', 'hours_binning']]
from sklearn.ensemble import ExtraTreesRegressor

ETR = ExtraTreesRegressor()
ETR.fit(numerical,y)
print(ETR.feature_importances_) #use inbuilt class feature_importances of tree based classifiers
#plot graph of feature importances for better visualization
feat_importances = pd.Series(ETR.feature_importances_, index=numerical.columns)
feat_importances.nlargest(15).plot(kind='barh')
plt.show()
colormap = plt.cm.RdBu
plt.figure(figsize=(22,11))
plt.title('Pearson Correlation of Features', y=1.05, size=20)
sns.heatmap(numerical.corr(),linewidths=0.1,vmin= -1.0,vmax=1.0, 
            square=True, cmap=colormap, linecolor='white', annot=True);
"""
now, to the categorical we will calcaulate **anova** 
"""
categorical = train[['Seasons','Holiday', 'Functioning Day', 'day', 'month', 'day_night','weather', 'temp_binned', 'dew_point_binned','y']]

from scipy.stats import f_oneway
 
# Running the one-way anova test between CarPrice and FuelTypes
# Assumption(H0) is that FuelType and CarPrices are NOT correlated
 
# Finds out the Prices data for each FuelType as a list
for i in categorical.columns:
    CategoryGroupLists=categorical.groupby(i)['y'].apply(list)

    # Performing the ANOVA test
    # We accept the Assumption(H0) only when P-Value > 0.05
    AnovaResults = f_oneway(*CategoryGroupLists)
    print('P-Value for Anova {} is: '.format(i), AnovaResults[1], '\ncorrelated = {} \n'.format(AnovaResults[1] < 0.05))
train_modified.head(2)
train_modified.info()
train_modified['weather'] = train_modified['weather'].astype(np.object)
train_modified['Functioning Day'] = train_modified['Functioning Day'].astype(np.object)
train_modified['Holiday'] = train_modified['Holiday'].astype(np.object)
train_modified['Seasons'] = train_modified['Seasons'].astype(np.object)

from sklearn.model_selection import train_test_split
from catboost import CatBoostRegressor

X_train, X_val, y_train, y_val = train_test_split(train_modified, y, test_size=0.2, random_state=42) 


categorical_features_indices = np.where(train_modified.dtypes == np.object)[0]

model= CatBoostRegressor(iterations = 4998,
                         loss_function='RMSE',
                         learning_rate = 0.01, 
                         depth = 8, 
                         l2_leaf_reg = 2)
                         #,early_stopping_rounds = 10 )


model.fit(X_train, y_train,cat_features=categorical_features_indices,eval_set=(X_val, y_val),plot=True)
print(model.score(X_train, y_train))
print(model.score(X_val, y_val))
from sklearn.metrics import r2_score

#model_predicted = np.exp(model.predict(X_val))
model_predicted = model.predict(X_val)

r2 = r2_score(y_val, model_predicted)
print('R2: {:.6f}'.format(r2))
"""
now preparing test set
"""
testing = test.copy()
testing.columns
testing.Date = pd.to_datetime(testing.Date, format='%d/%m/%Y')
testing['day'] = testing['Date'].dt.day_name()
testing['month'] = testing['Date'].dt.month_name()
testing['year'] = pd.DatetimeIndex(testing['Date']).year
testing['day_night'] = testing['Hour'].apply(lambda x: 'Night' if (x >= 20) or (x<=5) else "Day")
#-------------------------------------------
testing["weather"] = testing.Seasons.map({'Spring': " Clear",\
                                        'Summer' : " Mist ", \
                                        'Autumn' : " Light Rain", \
                                        'Winter' :" Heavy Rain" })

# #-------------------------------------------
day_condition = []
for i in testing['Humidity(%)']:
    if i >= 0 and i<= 20 :
        day_condition.append("uncomfortably dry")
    elif i >= 21 and i<=60 :
        day_condition.append("comfort")
    elif i >= 61 and i<=100 :
        day_condition.append("uncomfortably wet")
        
testing['day_condition'] = day_condition
# #-------------------------------------------

hours_binning = []
for i in testing['Hour']:
    if i < 8 :
        hours_binning.append(1)
    elif i >= 22 :
        hours_binning.append(2)
    elif i > 9 and i<18 :
        hours_binning.append(3)
    elif i == 8 :
        hours_binning.append(4)
    elif i == 9 :
        hours_binning.append(5)
    elif i == 20 or i == 21 :
        hours_binning.append(6)
    elif i == 19 or i == 18 :
        hours_binning.append(7)

testing['hours_binning'] = hours_binning

# #-------------------------------------------
bins = [-20,0,25.7,40]
labels = ['cold', 'normal', 'hot']
testing['temp_binned'] = pd.cut(testing['Temperature(�C)'], bins=bins, labels=labels).astype('object')

# #-------------------------------------------
bins = [-35,9,13,15.5,17.5,35]
labels = ['A bit dry', 'Very comfortable', 'Comfortable','Ok','Very humid']
testing['dew_point_binned'] = pd.cut(testing['Dew point temperature(�C)'], bins=bins, labels=labels).astype('object')

testing = testing.drop(columns = ['ID','day_condition'],axis=1)

CB_predicted = model.predict(testing)
test['y'] = np.exp(CB_predicted)
test[['ID', 'y']].to_csv('/kaggle/working/CB_submission.csv', index=False)


"""
### well, that's it for now, hope this notebook would be helpful 
"""
plt.savefig('facetgrid.jpg')
plt.show()