Chennai Water Management : A Python Project
By: Aakriti Jain
Tags: Decision Tree, Timeseries, Water, Chennai, Python

An important question that needs to be addressed in the light of low water levels not corresponding to the rainfall levels before jumping onto the conclusion that the scarcity comes from the demand side, is that are the reservoirs even rainfed. There could be several other factors apart rainfall that might affect the water levels. To check upon this fact we aggregate rainfall levels of all years month wise and take mean of water levels for all years month wise and plot them simultaneously. We notice that November is the month which receives highest level of rainfall and correspondingly December is the month with highest water levels. This indicates that the reservoirs are indeed rainfed. But, just to be more sure about this let us conduct a seasonal analysis for water levels and check the correlation between the two.

5. Season-wise Analysis

def read_input_file(filepath):

frame = pd.read_csv(filepath)

frame['Date'] = pd.to_datetime(frame['Date'], format='%d-%m-%Y')

frame = frame.sort_values('Date', ascending=True).reset_index(drop=True)

return frame def assign_temporal_categories(date):

"""Given a date, determines whether to color for 'Monsoon', 'Dry', or 'Regular' season."""

if date.month in [2, 3, 4, 5] or (date.month == 6 and date.day <= 15):

return colors[2]

elif date.month in [10, 11, 12]:

return colors[1]

else:

return colors[0] reservoir_level = read_input_file("/content/gdrive/My Drive/ECO6800_PROJECT/chennai_reservoir_levels.csv")

# Plot the reservoir levels per day summed across all 4 reservoirs

c = reservoir_level['Date'].apply(assign_temporal_categories, colors)

chennai_reservoirs = reservoir_time.iloc[:, 1:].sum(axis=1) plt.figure(figsize=(16, 8))

plt.scatter(reservoir_level['Date'].values, chennai_reservoirs, color=c, alpha=0.3)

plt.ylabel('Reservoir Levels Across Chennai (mcft)', size=14)

plt.xlabel('Date', size=14)

legend_labels = {'Normal Season':colors[0], 'Monsoon Season':colors[1], 'Dry Season':colors[2]}

leg_el = [mpatches.Patch(facecolor=value, edgecolor='k', label=key) for key, value in legend_labels.items()]

plt.legend(handles=leg_el, prop={'size':14})

plt.title('Reservoir Levels in Chennai per Day', size=16) plt.show(renderer="colab")

Above we’ve plotted the total reservoir levels in Chennai per day (by summing the reservoir levels per region) and colored in the regions according to the weather breakdown. The classification of weather breakdown was adapted from Wikipedia, which basically listed various months according to dry, normal and rainy season. As we can see, often during the dry season there is a drop in water levels, although this appears to continue after dry season until monsoon season hits again. This reconfirms our belief that reservoirs are rainfed. Also, the monsoon season in 2017 didn't lead to a significant increase in reservoir levels, and the dry season in 2018 depleted reservoirs to critical levels. The monsoon season in 2018 provided very little relief, which ultimately led to the mass shortage of 2019. Intuitively, we also note that rainfall affects future days as well as the present day, that is if it rained a month ago you would expect that to affect reservoir levels today. Let’s see if we can visualize this correlation by aggregating rainfall across some number of months.

def get_rolling_rainfall_levels(reservoir_levels, rainfall, days): """

Returns a dataframe with the reservoir level per day and the total

accumulated rainfall over the past [days] days.

""" rolling_rainfall = rainfall.iloc[:, 1:].sum(axis=1).rolling(days).sum()

reservoir_sum_per_day = reservoir_levels.iloc[:, 1:].sum(axis=1)

merged_frame = pd.concat([reservoir_sum_per_day, rolling_rainfall], axis=1)

return merged_frame.dropna().reset_index(drop=True) reservoir_levels = read_input_file("/content/gdrive/My Drive/ECO6800_PROJECT/chennai_reservoir_levels.csv")

rainfall = read_input_file("/content/gdrive/My Drive/ECO6800_PROJECT/chennai_reservoir_rainfall.csv") # Try aggregating rainfall from 1 month to 8 months

n_days = 30

n_months = 8

months = [i for i in range(30, (n_months + 1) * 30, 30)]

data = [get_rolling_rainfall_levels(reservoir_levels, rainfall, month) for month in months] # Plot each scatter plot

nrows = math.ceil(len(months) / 2)

fig, ax = plt.subplots(nrows, 2, figsize=(16, 16))

for index, frame in enumerate(data):

x_index = math.floor(index / 2)

y_index = math.floor(index % 2)

ax[x_index][y_index].scatter(frame[0], frame[1], alpha=0.2, color=colors[index % len(colors)])

ax[x_index][y_index].set_title('Rainfall (mm) Aggregated Over %d Days' % months[index], size=16) fig.suptitle('Reservoir Levels (mcft) vs Rainfall (mm) Summed Over X Days', size=20)

fig.text(0.05, 0.65, 'Rainfall (mm) Aggregated Over Varying Numbers of Months', rotation=90, size=16)

fig.text(0.45, 0.05, 'Reservoir Levels (mcft)', size=16) plt.show(renderer="colab")

We can see above that indeed the amount of rainfall over the course of the past influences the water level “today”. It also appears that the further back we check, the more of a relationship we see.

6. Per Capita Availability

#population litres per person available (2000 ltr per year) yearly_water = reservoir_time.groupby([(reservoir_time.index.year)]).mean()

yearly_water = yearly_water.reset_index()

yearly_water = yearly_water.melt(id_vars=["Date"], var_name="Reservoir", value_name="Value")

yearly_water = yearly_water.set_index("Date")

yearly_water = yearly_water.sort_index()

yearly_water = yearly_water.groupby([(yearly_water.index)]).sum() population_stats = df_population

years = list(population_stats.index.year)

population_stats["Year"] = years

population_stats = population_stats.reset_index()

population_stats = population_stats.set_index("Year")

population_stats = population_stats.drop(columns=["Date", "Growth", "GrowthRate"])

population_stats = population_stats[(population_stats.index >= 2004) & (population_stats.index <= 2019)] water_avail = pd.DataFrame()

water_avail["Year"] = yearly_water.index

water_quantity = list(yearly_water["Value"])

population = list(population_stats["Population"]) avail = []

for i in range(len(water_quantity)):

avail.append((water_quantity[i] * 28316846.6)/population[i])

water_avail["LitresPerPerson"] = avail fig = px.bar(water_avail, x=water_avail["Year"], y=water_avail["LitresPerPerson"], color="LitresPerPerson", width=800, height=400) fig.show(renderer="colab")

NOTE: This is an interactive plot and values can be seen by hovering over it in python.

To check the role of demand in creating water scarcity, we use population as a proxy for demand for water and calculate per capita availability of water, year wise. In the light of exponentially growing population, our analysis suggest that the limited water resources of the city are burdened with the ever growing demand. 2019 was a year that made it to the headlines for the scarcity of water in Chennai. Our analysis reconfirm the extreme shortage. The per capita availability of water in 2019 stood at less than 3000 litres! According to Wikipedia, average per capita usage of water per day stands at about 100 litres, which makes it 36500 litres per year. Our estimate of roughly 3000 litres falls too short of this. Hence, unless there is miraculously high level of rainfalls, the future of water in the city seems to be doomed. We will further check on this fact by making future predictions by using a time-series approach.

7. Ground Water Analysis

df_Groundwater=df_Groundwater[['YEAR_OBS','TOTAL']]

df_Groundwater=df_Groundwater.groupby('YEAR_OBS').sum()

df_Groundwater.plot(kind='bar')

The persistent decline in the ground water trend further emphasizes the severity of the shortage crisis that the city is facing, and may continue to face. To check the future trends of water availability, we now use MLA algorithms and make predictions.

8. Future Predictions

We now aim to predict the future rainfall so as to see any specific trends or causes of concerns, and thereby try to comment on the future of scarcity of water in the city

8.1 Rainfall Levels

We begin by simply plotting the aggregate rainfall over the years, reservoir wise as shown in the graphs below, and we notice that apart 2015, all years had similar rainfall levels

rainfall_POONDI = rainfall_time["POONDI"].resample('MS').mean()

rainfall_CHOLAVARAM = rainfall_time["CHOLAVARAM"].resample('MS').mean()

rainfall_REDHILLS = rainfall_time["REDHILLS"].resample('MS').mean()

rainfall_CHEMBARAM = rainfall_time["CHEMBARAMBAKKAM"].resample('MS').mean() reservoirs = []

reservoirs.append(rainfall_POONDI)

reservoirs.append(rainfall_CHOLAVARAM)

reservoirs.append(rainfall_REDHILLS)

reservoirs.append(rainfall_CHEMBARAM) rainfall_time.plot(figsize=(20,12), subplots=True, layout=(2,2))

plt.show()

We now attempt to predict the future levels of the rainfall, reservoir wise by adopting a time series approach. The first step in any time series analysis is to check if there are any trends in the data. If there is any trend then the data needs to be detrended and made stationary. To check for stationarity we use the Augmented Dickey–Fuller test as below:

def seasonalDecomposition(df):

rcParams['figure.figsize'] = 15, 6

decomposition = sm.tsa.seasonal_decompose(df, model='additive')

fig = decomposition.plot()

plt.show() # Ho: It is non stationary

# H1: It is stationary def adfuller_test(df):

result=adfuller(df)

labels = ['ADF Test Statistic','p-value','#Lags Used','Number of Observations Used'] for value,label in zip(result,labels):

print(label+' : '+str(value) )

if result[1] <= 0.05:

print("Strong evidence against the null hypothesis(Ho), reject the null hypothesis. Data has no unit root and is stationary")

else:

print("Weak evidence against null hypothesis, time series has a unit root, indicating it is non-stationary ") def initialAnalysis(reservoirs):

for res in reservoirs:

print("Seasonal Decompostion Graphs for Reservoir - " + str(res.name) + "

")

seasonalDecomposition(res)

print("Adfuller Test for Reservoir - " + str(res.name) + "

")

adfuller_test(res)

print("

----------------------------------------------------------------

") initialAnalysis(reservoirs)

We notice that the rainfall data of all the four reservoirs satisfy the Augmented Dickey-Fuller test, and thus we can move ahead with our time series analysis. We also note that rainfall in all four reservoirs display seasonality. We use the famous ARIMA modelling (or SARIMA) technique for our predictions.

def ARIMA(df):

""" This Function is Used to Do Complete Analysis using ARIMA """ # Finding AIC

paramList = []

seasonalParamList = []

AICList = []

finalParam = ()

finalSeasonalParam = ()

finalAIC = 0 p = d = q = range(0, 2)

pdq = list(itertools.product(p, d, q))

seasonal_pdq = [(x[0], x[1], x[2], 12) for x in list(itertools.product(p, d, q))] print("AIC Values For for Reservoir - " + str(df.name) + "

")

for param in pdq:

for param_seasonal in seasonal_pdq:

try:

mod = sm.tsa.statespace.SARIMAX(df, order=param, seasonal_order=param_seasonal, enforce_stationarity=False, enforce_invertibility=False)

results = mod.fit()

print("ARIMA {} x {} 12 - AIC : {}".format(param, param_seasonal,results.aic)) paramList.append(param)

seasonalParamList.append(param_seasonal)

AICList.append(results.aic) except:

continue

print("

----------------------------------------------------------------

")

AIC_Sorted = []

Param_Sorted = []

SeasonalParam_Sorted = [] for a, p, ps in sorted(zip(AICList, paramList, seasonalParamList)):

AIC_Sorted.append(a)

Param_Sorted.append(p)

SeasonalParam_Sorted.append(ps)

for idx in range(len(AIC_Sorted)):

cur_AIC = AIC_Sorted[idx]

cur_Param = Param_Sorted[idx]

cur_SeasonalParam = SeasonalParam_Sorted[idx]

mod = sm.tsa.statespace.SARIMAX(df, order=cur_Param, seasonal_order=cur_SeasonalParam,

enforce_stationarity=False, enforce_invertibility=False)

results = mod.fit()

print("

AIC : ", cur_AIC, "

Params : ", cur_Param, "

Seasonal Params : ", cur_SeasonalParam) print(results.summary().tables[1]) results_as_html = results.summary().tables[1].as_html()

dfr = pd.read_html(results_as_html, header=0, index_col=0)[0]

pzVal = list(dfr["P>|z|"])

allLess = 1

for pz in pzVal:

if pz > 0.05:

allLess = 0

if allLess == 1:

finalResults = results

finalParam = cur_Param

finalSeasonalParam = cur_SeasonalParam

finalAIC = cur_AIC

break print("

Finally Selected Parameters")

print("

AIC : ", finalAIC, "

Params : ", finalParam, "

Seasonal Params : ", finalSeasonalParam)

finalResults.plot_diagnostics(figsize=(16, 8))

plt.show() pred = finalResults.get_prediction(start=pd.to_datetime('2017-01-01'), dynamic=False)

pred_ci = pred.conf_int()

ax = df['2008' : ].plot(label = 'observed')

pred.predicted_mean.plot(ax=ax, label='One-Step Ahead Forecast', alpha=.7, figsize=(14,7))

ax.fill_between(pred_ci.index, pred_ci.iloc[:,0], pred_ci.iloc[:,1],color='k',alpha=.2) ax.set_xlabel('Date')

ax.set_ylabel('Rainfall')

plt.legend()

plt.show() y_forecasted = pred.predicted_mean

y_truth = df['2017-01-01':] mse = ((y_forecasted - y_truth)**2).mean()

print("The Mean Squared Error of our Forecasts is {}".format(round(mse,2)))

print("The Root Mean Squared Error of our Forecasts is {}".format(round(np.sqrt(mse), 2))) pred_uc = finalResults.get_forecast(steps=100)

pred_ci = pred_uc.conf_int()

ax = df.plot(label="Observed", figsize=(14, 7))

pred_uc.predicted_mean.plot(ax=ax, label='Forecast')

ax.fill_between(pred_ci.index, pred_ci.iloc[:,0], pred_ci.iloc[:,1], color='k', alpha=.25) ax.set_xlabel('Date')

ax.set_ylabel('Rainfall')

plt.legend()

plt.show()

In the cell above, we have created a function for the entire process of ARIMA modelling so as to increase efficiency and reduce manual labor. The steps of the model are as follows:

First, we calculate AIC for all possible combinations of the calibration parameters. The calibrating parameters can be described as below:

p — the number of autoregressive

d — degree of differencing

q — the number of moving average terms

m — refers to the number of periods in each season

(P, D, Q ) — represents the (p,d,q) for the seasonal part of the time series.

We then define SARIMA as ARIMA(p,d,q)(P, D, Q)m

AIC (Akaike Information Criterion) is an estimate of out-of-sample prediction error and thereby relative quality of statistical model for a given set of data. A good model is the one that has min AIC among all other models. So, as our next step we select that combination of calibrating parameters which has min AIC.

One must note that min AIC is a necessary, but not a sufficient condition. A more important and desired characteristics is the significance of coeffetionts of parameters of the model. Hence, in our next step, we select that combination of callibrating parameters which gives all significant coefficient with the minimum possible AIC.

After the selection of final combination of calibrating parameters, we use this combination to run our SARIMA model and obtain results.

We have also calculated the Mean square Errors (MSE) and the Root Mean Square Errors (RSME) to see the variations in predictions.

def ARIMA_Analysis(reservoirs):

for res in reservoirs:

print("ARIMA Analysis for Reservoir - " + str(res.name) + "

")

ARIMA(res)

print("

----------------------------------------------------------------

") ARIMA_Analysis(reservoirs)

ARIMA Analysis for Reservoir — Poondi

ARIMA Analysis for Reservoir — Poondi

ARIMA Analysis for Reservoir — Poondi

ARIMA Analysis for Reservoir — Poondi

ARIMA Analysis for Reservoir — Poondi

ARIMA Analysis for Reservoir — Cholavaram

ARIMA Analysis for Reservoir — Cholavaram

ARIMA Analysis for Reservoir — Cholavaram

ARIMA Analysis for Reservoir — Cholavaram

ARIMA Analysis for Reservoir — Cholavaram

ARIMA Analysis for Reservoir — Redhills

ARIMA Analysis for Reservoir — Redhills

ARIMA Analysis for Reservoir — Redhills

ARIMA Analysis for Reservoir — Redhills

ARIMA Analysis for Reservoir — Redhills

ARIMA Analysis for Reservoir — Chembarambakkam

ARIMA Analysis for Reservoir — Chembarambakkam

ARIMA Analysis for Reservoir — Chembarambakkam

ARIMA Analysis for Reservoir — Chembarambakkam

ARIMA Analysis for Reservoir — Chembarambakkam

We have now run our function on all the four rainfall datasets. We notice that results of our model are almost normally distributed in all the four rainfall datasets. To, check the predictive ability of our model we have first made predictions about the recent years using our model and then compared it with original values. We find that our model does a fairly good job fitting the actually observed values, and thus we use it to make future predictions.

8.2 Water Levels

Although we have established above that all reservoirs are rainfed, we still carry on to predict the future levels of all the reservoirs using a similar time series approach to feed our curiosity.

We begin by plotting average levels across years for all the four reservoirs and as earlier, we notice a declining trend in the recent years.

level_POONDI = reservoir_time["POONDI"].resample('MS').mean()

level_CHOLAVARAM = reservoir_time["CHOLAVARAM"].resample('MS').mean()

level_REDHILLS = reservoir_time["REDHILLS"].resample('MS').mean()

level_CHEMBARAM = reservoir_time["CHEMBARAMBAKKAM"].resample('MS').mean() levels = []

levels.append(level_POONDI)

levels.append(level_CHOLAVARAM)

levels.append(level_REDHILLS)

levels.append(level_CHEMBARAM) reservoir_time.plot(figsize=(20,12), subplots=True, layout=(2,2))

plt.show()

initialAnalysis(levels)

Using the function we created for Augmented Dickey-Fuller Test, we check stationarity for water levels of all four reservoirs. We find that three of them are non stationary and Chembarambakkam is the only stationary dataset. We Proceed by using our earlier function of ARIMA modeling for Chembarambakkam.

ARIMA(level_CHEMBARAM)

ARIMA Analysis for Reservoir — Chembarambakkam

ARIMA Analysis for Reservoir — Chembarambakkam

ARIMA Analysis for Reservoir — Chembarambakkam

ARIMA Analysis for Reservoir — Chembarambakkam

ARIMA Analysis for Reservoir — Chembarambakkam

For the other three reservoirs, we will first detrend the data using the method of differencing and check the stationarity of the detrended data.

POONDI_Difference = level_POONDI - level_POONDI .shift(12)

REDHILLS_Difference = level_REDHILLS - level_REDHILLS .shift(12)

CHOLAVARAM_Difference = level_CHOLAVARAM - level_CHOLAVARAM .shift(12) adfuller_test(POONDI_Difference.dropna())

adfuller_test(REDHILLS_Difference.dropna())

adfuller_test(CHOLAVARAM_Difference.dropna())

The detrended data now satisfies the stationarity test and we can use our function for ARIMA modelling to predict results now.

ARIMA(POONDI_Difference.dropna())

ARIMA Analysis for Reservoir — Poondi

ARIMA Analysis for Reservoir — Poondi

ARIMA Analysis for Reservoir — Poondi

ARIMA Analysis for Reservoir — Poondi

ARIMA Analysis for Reservoir — Poondi

ARIMA(REDHILLS_Difference.dropna())

ARIMA Analysis for Reservoir — Redhills

ARIMA Analysis for Reservoir — Redhills

ARIMA Analysis for Reservoir — Redhills

ARIMA Analysis for Reservoir — Redhills

ARIMA Analysis for Reservoir — Redhills

ARIMA(CHOLAVARAM_Difference.dropna())

ARIMA Analysis for Reservoir — Cholavaram

ARIMA Analysis for Reservoir — Cholavaram

ARIMA Analysis for Reservoir — Cholavaram

ARIMA Analysis for Reservoir — Cholavaram

ARIMA Analysis for Reservoir — Cholavaram

The results are somewhat stable and similar to that of rainfall datasets which makes sense due to presence of correlation between the two.

The Almost similar and comparatively lower trend of rainfall and water levels, coupled with declining ground water levels the exponentially growing population suggest that the city should not expect any major relief in its water scarcity problems. It is high time that the city must invest in rainwater harvesting techniques and also explore alternate sources of water supply and conservation.

We could not include many features in the model due to unavailability of data and thus, we do not see any major ups and downs in the future predictions. However, our model will be capable of predicting any abnormalities in rainfall and/or levels approximately one month before and not a year before. As and when we get data on other features we can incorporate those to get better predictions. We have tried to build an alternative model including more parameters in the next section.

9. Alternative Approaches

9.1 Exceedance Probability and Return Years

The total rainfall received in a given period at a location is highly variable from one year to another. The variability depends on the type of climate and the length of the considered period. In general it can be stated that the shorter the period the higher the variability of rainfall in that period.

Because of the strong variability of rainfall in time, the design and management of irrigation water supply and flood control systems are not based on the long-term average of rainfall records but on particular rainfall depths that can be expected for a specific probability or return period. These rainfall depths can only be obtained by a thoroughly analysis of long time series of historic rainfall data.

Although time series of historic rainfall data are characterized by their average and standard deviations, these values cannot be blindly used to estimate design rainfall depths.

Probability of exceedance : It is defined as the likelihood that the actual rainfall during that period will be equal to or higher than the estimated rainfall.

Since the rainfall depth is the amount of rain that can be expected or might be exceeded in a given period for a specific probability, it refers to the minimum amount of rain one can rely on during the reference period, and therefore is often denoted as ‘dependable rainfall’ in irrigation sciences. As the rainfall amount increases, its probability of exceedance decreases.

Return Period : The return period (also called the recurrence interval) is simply the reciprocal of exceedance probability when expressed as a fraction.

# Sorting data smallest to largest

rain_com_sorted = rain_com.sort_values(by="Combined") # Count total obervations

n = rain_com_sorted.shape[0] # Add a numbered column 1 -> n to use in return calculation for rank

rain_com_sorted.insert(0, 'rank', range(1, 1 + n)) # Calculate probability

rain_com_sorted["probability"] = ((n - rain_com_sorted["rank"] + 1) / (n + 1))

rain_com_sorted["return-years"] = (1 / rain_com_sorted["probability"]) print(rain_com_sorted)

We can interpret from the above table that , say in 2011, the probability of exceedance comes out to be around 99% or the return period is one year meaning that we expect the rainfall levels in March 2011 to return after one year. Similarly, we can say that it will take around 193 years to get the rainfall levels as in November 2015.

The results meet our expectation that during floods (say 2015) the return period will be higher and probability of occurrence of such high levels of rainfall will be lower.

plt.plot(rain_com_sorted['Combined'], rain_com_sorted["probability"], 'o') plt.xlabel('Levels of Rainfall') plt.ylabel('Probability')

From the above graph, we can infer that low levels of rainfall occur with high probability and high levels occur with lower probability.

9.2 Training a Prediction Model

In this section we train a model using an ML algorithm called Decision Tree Classifier to predict the level of precipitation, but not on a time series basis. The model rather works by taking into account various other parameter like temperature, vapor pressure, cloud cover, evaporization, etc.

We collected monthly data for these various parameters separately for a period 0f 1901–2002. We complied and cleaned the data, and removed entries of initial years due to the problem of same entries repeating year after year which we suspected was due to error in data collection. We have then used the clean version of this data for training our Model.

df = pd.read_csv("/content/gdrive/My Drive/ECO6800_PROJECT/weather.csv") df = df.set_index(["Year", "Month"]) #Splitting data into features and labels(into X and Y)

colList = list(df.columns) yL = []

xL = []

for c in colList:

if c == "precipitation":

yL.append(c)

else:

xL.append(c) X = df

Y = df

X = X.drop(columns=yL)

Y = Y.drop(columns=xL)

Here we say that our prediction parameter, i.e. precipitation is Y, and all the other variables playing a role in explaining Y are X. We then scale the values of Y between 0 and 1 and divide them into classes in the next step, as below:

#Converting Label Values into Proper Classes

scaler = MinMaxScaler()

Y[Y.columns] = scaler.fit_transform(Y[Y.columns]) def part(x):

if x >= 0.00 and x < 0.10:

return 1

if x >= 0.10 and x < 0.20:

return 2

if x >= 0.20 and x < 0.40:

return 3

if x >= 0.40 and x <= 1.00:

return 4 Y = Y.applymap(part) #Making Train and Test Splits and Scaling the Training Data

X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2) scaler = StandardScaler() X_train = scaler.fit_transform(X_train)

X_test = scaler.transform(X_test)

We train our model on 80% of the available data and then test it on the remaining 20%

#Defining Our Decision Tree Classifier Model (Hyperparameters Tuned As Well) classifier = DecisionTreeClassifier(criterion='entropy', splitter="random", max_depth = 12, min_samples_split=8, min_samples_leaf = 8, max_features = "auto", random_state=42) #Fitting Training Data on our Model

classifier.fit(X_train, Y_train)

#Checking Training and Testing Accuracies print("Training Accuracy => ", str(100 * round(classifier.score(X_train, Y_train), 4)), "%") print("Testing Accuracy => ", str(100 * round(classifier.score(X_test, Y_test), 4)), "%")