import pandas as pd
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
df = pd.read_csv('../input/california-housing-prices/housing.csv')
df
df['ocean_proximity'].value_counts()
df.info()
df.isnull().sum()
"""
### Total no of bedrooms has null values, we will fill them using mean
"""
"""
### We wont be doing spatial analysis so we will remove the latitude and longitude columns
"""
df.drop(['longitude','latitude'],axis=1,inplace =True)
df.info()
df['total_bedrooms'].fillna(df['total_bedrooms'].mean(),inplace=True)
df.info()
"""
### We have filled in the null values
"""
df.head()
"""
# EDA
"""
df.corr()
sns.heatmap(df.corr(),annot=True);
df.hist(figsize=(20,15));
"""
### We can see that only median house age is normally distributed rest all are right skewed
"""
sns.scatterplot(data=df,x='median_income',y='median_house_value')
plt.title('Median Income vs Median House value');
"""
#### Above plot tells us that there is a positive relation b/w the median income and median house value telling us that more the income of the houseolds , they buy expensive property
"""
sns.boxplot(data=df,y='median_house_value'); #let's see the box plot to see the mean house price 
df['median_house_value'].mean()
sns.scatterplot(data=df,x='total_rooms',y='median_house_value')
plt.title('Total no of rooms vs Median House value');
sns.scatterplot(data=df,x='total_bedrooms',y='median_house_value')
plt.title('Total no of rooms vs Median House value');
# Let's see the distribution of of the Median house age
sns.displot(data=df,x='housing_median_age',kde=True);
sns.countplot(data=df,x='ocean_proximity');
df['ocean_proximity'].value_counts()
house_near_onehocean = df[df['ocean_proximity'] == '<1H OCEAN']
house_near_onehocean.describe()
df.groupby('ocean_proximity').mean()
"""
### Let's seperate the data by ocean proxmity and then see detailed information to gain insight
"""
house_near_inland = df[df['ocean_proximity'] == 'INLAND']
house_near_inland.describe()
house_near_bay = df[df['ocean_proximity'] == 'NEAR BAY']
house_near_bay.describe()
house_near_ocean = df[df['ocean_proximity'] == 'NEAR OCEAN']
house_near_ocean.describe()
house_near_island = df[df['ocean_proximity'] == 'ISLAND']
house_near_island.describe()
"""
#### 1.After seperating the category by the ocean proximity we can see the average housel value and we can tell that houses located near Island are expensive which also justifies because island mostly being a vacation place has higher value of houses. But we cant rely on this data as we have information just 5 houses in this category, so to have clear idea of thus we might need more data.

#### 2.Other than this we can see that houses located at the Bay area are expensive than houses of other areas.

#### 3.So we can say that the location of house from nearby oceans plays vital role in prices of the houses.

#### 4.There is very little positive relation as seen from plots, between the total no of rooms and bedrooms within a block and the price of the houses.
"""
"""
## Feature Selection 
"""
X = df.drop('median_house_value',axis=1)
X = pd.get_dummies(data=X)
X.head()
X['ocean_proximity_<1H_OCEAN'] = X['ocean_proximity_<1H OCEAN']
X = X.drop('ocean_proximity_<1H OCEAN',axis=1)
X.head()
print('Shape of input features',X.shape) # 2D array
y = df['median_house_value']
y.head()
print('Shape of input features',y.shape)
y.values # 1D array
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42 )
"""
## Model Creation
"""
from sklearn.linear_model import LinearRegression
house_price_model = LinearRegression()
"""
#### Training model on training data
"""
house_price_model.fit(X_train,y_train)
"""
### Predicting the house values on test data
"""
y_predictions = house_price_model.predict(X_test)
y_predictions
"""
### Now that we have predicted the test data using the model we need to find out the accuracy of this model and find out whether Linear Regerssion was best algo for this data
"""
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
y_test.mean()
#df['median_house_value'].mean()
y_predictions.mean()
mean_absolute_error(y_test,y_predictions)
def mape(actual, pred): 
    actual, pred = np.array(actual), np.array(pred)
    return np.mean(np.abs((actual - pred) / actual)) * 100

mape(y_test,y_predictions)
"""
#### So we after calculating MAPE we found out that we are off by or we have 28.9% i.e. 29% as error rate

#### So is that error acceptable - it depends on context and consider our scenario - 29 % error is surely not  completely acceptable but it can give us a fair idea about the prices of the houses
"""
np.sqrt(mean_squared_error(y_test,y_predictions))
"""
##### We can that we have a high RMSE than our MAE, whcih suggests that for most of the data the predicted value is having fair accuracy but for few points/data features the predicted house value is havng very high errors
"""
#### Accuracy of the model
r2_score(y_test,y_predictions)
X.columns
"""
So our model accuracy is 63%
"""
house_price_model.coef_
house_price_model.intercept_
"""
#### Now we want to know if the underlying dataset was a valid dataset for Linear Regression , by checking the residual plots if there was a sttrange pattern that we coul not see in some multidimensional level

#### Let's visualize the residual plots to see that 
"""
test_residuals = y_test - y_predictions
test_residuals
sns.scatterplot(x=y_test,y=test_residuals)
plt.axhline(y=0,color='red')
plt.title('Residual Plot');
"""
### We can see that the data residual plot shows us that the points are normally distributed along the line, so we can conclude that the underlying dataset a valid choice for Linear Regression.

### As for the low model accuracy, it might be because of less data and also in model coefficients we have some negative coefficients values which we can remove to get better accuracy.
"""
sns.displot(test_residuals,bins=30,kde=True);
"""
### Even the distribution tells us that the errors/residuals are somewhat distributed normally and also the kde shows us that the mean is pretty close to zero

### Also we can see there is undershoot of a little bit but having a little bit skewed on one way or the other is not too bad.
"""
"""
### Saving the model
"""
from joblib import dump
dump(house_price_model,'House_prediction_model.joblib')