"""
Прогноз ціни автомобіля
"""
"""
Постановка проблеми
Китайська автомобільна компанія Geely Auto прагне вийти на американський ринок,
створивши там свою виробничу підрозділ та виробляючи машини на місцевому рівні,
щоб забезпечити конкуренцію своїм американським та європейським колегам.

Вони уклали контракт з автомобільною консалтинговою компанією,
щоб зрозуміти фактори, від яких залежить ціноутворення на автомобілі. 
Зокрема, вони хочуть зрозуміти фактори, що впливають на ціноутворення на автомобілі на американському ринку, 
оскільки вони можуть сильно відрізнятися від китайського. Компанія хоче знати:
    
    - Які змінні є важливими для прогнозування ціни автомобіля
    - Наскільки добре ці змінні описують ціну автомобіля
    
    На основі різноманітних опитувань ринку консалтингова фірма зібрала великий набір
    різних типів автомобілів на американському ринку.
"""
"""
Бізнес-мета
Ми повинні змоделювати ціну автомобілів із наявними незалежними змінними.
Він буде використаний керівництвом, щоб зрозуміти, як саме ціни змінюються залежно від незалежних змінних.
Вони можуть відповідно маніпулювати дизайном автомобілів, бізнес-стратегією тощо для досягнення певного рівня цін.
Далі, ця модель стане гарним способом для керівництва зрозуміти цінову динаміку нового ринку.
"""
import warnings
warnings.filterwarnings('ignore')

#importing the libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
"""
Крок 1: Читання та розуміння даних
Почнемо з наступних кроків:

Імпорт даних за допомогою бібліотеки pandas
Розуміння структури даних
"""
cars = pd.read_csv('../input/car-price-prediction/CarPrice_Assignment.csv')
cars.head()
cars.shape
cars.describe() 
cars.info()
"""
Крок 2: Очищення та підготовка даних
"""
"""
Розділення назви компанії у стовпці Назва автомобіля
"""
CompanyName = cars['CarName'].apply(lambda x : x.split(' ')[0])
cars.insert(3,"CompanyName",CompanyName)
cars.drop(['CarName'],axis=1,inplace=True)
cars.head()
cars.CompanyName.unique()
"""
Виправлення недійсних значень
Є орфографічна помилка у стовпці CompanyName.

maxda = mazda
Nissan = nissan
porsche = porcshce
toyota = тойута
volkswagen = volkswagen = vw
"""
cars.CompanyName = cars.CompanyName.str.lower()

def replace_name(a,b):
    cars.CompanyName.replace(a,b,inplace=True)

replace_name('maxda','mazda')
replace_name('porcshce','porsche')
replace_name('toyouta','toyota')
replace_name('vokswagen','volkswagen')
replace_name('vw','volkswagen')

cars.CompanyName.unique()
# Перевірка на наявність дублікатів
cars.loc[cars.duplicated()]
#виведем назвии стовпців
cars.columns
#Крок 3: Візуалізація даних
plt.figure(figsize=(20,8))

plt.subplot(1,2,1)
plt.title('Car Price Distribution Plot')
sns.distplot(cars.price)

plt.subplot(1,2,2)
plt.title('Car Price Spread')
sns.boxplot(y=cars.price)

plt.show()
print(cars.price.describe(percentiles = [0.25,0.50,0.75,0.85,0.90,1]))
"""
Висновок:
Сюжет здавався неправильним, тобто більшість цін у наборі даних є низькими (нижче 15 000).
Існує суттєва різниця між середнім значенням та медіаною розподілу ціни.
Точки даних далекі від середнього, що вказує на велику різницю в цінах автомобілів 
(85% цін нижчі за 18 500, тоді як решта 15% - від 18 500 до 45 400)
"""
"""
Крок 3.1 : Візуалізація категоріальних даних
- CompanyName
- Symboling
- fueltype
- enginetype
- carbody
- doornumber
- enginelocation
- fuelsystem
- cylindernumber
- aspiration
- drivewheel
"""
plt.figure(figsize=(25, 6))

plt.subplot(1,3,1)
plt1 = cars.CompanyName.value_counts().plot('bar')
plt.title('Companies Histogram')
plt1.set(xlabel = 'Car company', ylabel='Frequency of company')

plt.subplot(1,3,2)
plt1 = cars.fueltype.value_counts().plot('bar')
plt.title('Fuel Type Histogram')
plt1.set(xlabel = 'Fuel Type', ylabel='Frequency of fuel type')

plt.subplot(1,3,3)
plt1 = cars.carbody.value_counts().plot('bar')
plt.title('Car Type Histogram')
plt1.set(xlabel = 'Car Type', ylabel='Frequency of Car type')

plt.show()
"""
Висновок:
Toyota, здавалося, була улюбленою автомобільною компанією.
Кількість автомобілів на бензині більше, ніж дизельних.
седан - найпопулярніший тип автомобіля.
"""
plt.figure(figsize=(20,8))

plt.subplot(1,2,1)
plt.title('Symboling Histogram')
sns.countplot(cars.symboling, palette=("cubehelix"))

plt.subplot(1,2,2)
plt.title('Symboling vs Price')
sns.boxplot(x=cars.symboling, y=cars.price, palette=("cubehelix"))

plt.show()
"""
Висновок:
Здається, що символізація зі значеннями 0 і 1 має велику кількість рядків (тобто вони продаються найбільше).
Автомобілі з символікою -1 здаються дорогими (оскільки це також має сенс, рейтинг страхового ризику -1 цілком непоганий).
Але, схоже, символізація із значенням 3 має діапазон цін, подібний значенню -2. Існує падіння ціни при позначенні 1.
"""
plt.figure(figsize=(20,8))

plt.subplot(1,2,1)
plt.title('Engine Type Histogram')
sns.countplot(cars.enginetype, palette=("Blues_d"))

plt.subplot(1,2,2)
plt.title('Engine Type vs Price')
sns.boxplot(x=cars.enginetype, y=cars.price, palette=("PuBuGn"))

plt.show()

df = pd.DataFrame(cars.groupby(['enginetype'])['price'].mean().sort_values(ascending = False))
df.plot.bar(figsize=(8,6))
plt.title('Engine Type vs Average Price')
plt.show()
"""
Висновок:
ohc Тип двигуна видається найбільш улюбленим типом.
ohcv має найвищий діапазон цін (в той час як dohcv має лише один рядок), ohc і ohcf мають низький діапазон цін.
"""
plt.figure(figsize=(25, 6))

df = pd.DataFrame(cars.groupby(['CompanyName'])['price'].mean().sort_values(ascending = False))
df.plot.bar()
plt.title('Company Name vs Average Price')
plt.show()

df = pd.DataFrame(cars.groupby(['fueltype'])['price'].mean().sort_values(ascending = False))
df.plot.bar()
plt.title('Fuel Type vs Average Price')
plt.show()

df = pd.DataFrame(cars.groupby(['carbody'])['price'].mean().sort_values(ascending = False))
df.plot.bar()
plt.title('Car Type vs Average Price')
plt.show()
"""
Висновок:
У Jaguar і Buick, здається, найвища середня ціна.
дизель має вищу середню ціну, ніж газ.
твердий дах і кабріолет мають вищу середню ціну.
"""
plt.figure(figsize=(15,5))

plt.subplot(1,2,1)
plt.title('Door Number Histogram')
sns.countplot(cars.doornumber, palette=("plasma"))

plt.subplot(1,2,2)
plt.title('Door Number vs Price')
sns.boxplot(x=cars.doornumber, y=cars.price, palette=("plasma"))

plt.show()

plt.figure(figsize=(15,5))

plt.subplot(1,2,1)
plt.title('Aspiration Histogram')
sns.countplot(cars.aspiration, palette=("plasma"))

plt.subplot(1,2,2)
plt.title('Aspiration vs Price')
sns.boxplot(x=cars.aspiration, y=cars.price, palette=("plasma"))

plt.show()

"""
Висновок:
Inference :
door number не дуже впливає на ціну. Значної різниці між категоріями в ньому немає.
Здається aspiration з турбонаддувом мають вищий діапазон цін, ніж std(хоча він має деякі високі значення)
"""
def plot_count(x,fig):
    plt.subplot(4,2,fig)
    plt.title(x+' Histogram')
    sns.countplot(cars[x],palette=("magma"))
    plt.subplot(4,2,(fig+1))
    plt.title(x+' vs Price')
    sns.boxplot(x=cars[x], y=cars.price, palette=("magma"))
    
plt.figure(figsize=(15,20))

plot_count('enginelocation', 1)
plot_count('cylindernumber', 3)
plot_count('fuelsystem', 5)
plot_count('drivewheel', 7)

plt.tight_layout()
"""
Висновок:
Дуже мало точок даних для категорій розташування двигунів, щоб зробити висновок.
Найбільш поширеною кількістю циліндрів є чотири, шість та п’ять. Хоча вісім циліндрів мають найвищий діапазон цін.
mpfi та 2bbl - найпоширеніший тип паливних систем. mpfi та idi, що мають найвищий діапазон цін. Але даних для інших категорій, щоб отримати будь-який значущий висновок, мало
Дуже значна різниця в категорії приводного колеса. Більшість автомобілів високого діапазону, мабуть, віддають перевагу приводному колесу з повним приводом.
"""
"""
Крок 3.2: Візуалізація числових даних
"""
def scatter(x,fig):
    plt.subplot(5,2,fig)
    plt.scatter(cars[x],cars['price'])
    plt.title(x+' vs Price')
    plt.ylabel('Price')
    plt.xlabel(x)

plt.figure(figsize=(10,20))

scatter('carlength', 1)
scatter('carwidth', 2)
scatter('carheight', 3)
scatter('curbweight', 4)

plt.tight_layout()
"""
Висновок:
ширина автомобіля, довжина автомобіля та споряджена вага, схоже, мають позитивну кореляцію з ціною.
carheight не демонструє суттєвих тенденцій щодо ціни.
"""
def pp(x,y,z):
    sns.pairplot(cars, x_vars=[x,y,z], y_vars='price',size=4, aspect=1, kind='scatter')
    plt.show()

pp('enginesize', 'boreratio', 'stroke')
pp('compressionratio', 'horsepower', 'peakrpm')
pp('wheelbase', 'citympg', 'highwaympg')
"""
Inference :
enginesize, boreratio, horsepower, wheelbase - здається, існує суттєва позитивна кореляція з ціною.
citympg, highwaympg - здається, існує суттєва негативна кореляція з ціною.
"""
np.corrcoef(cars['carlength'], cars['carwidth'])[0, 1]
"""
Крок 4: Застосування нових функцій
"""
#Fuel economy
cars['fueleconomy'] = (0.55 * cars['citympg']) + (0.45 * cars['highwaympg'])
#Binning the Car Companies based on avg prices of each Company.
cars['price'] = cars['price'].astype('int')
temp = cars.copy()
table = temp.groupby(['CompanyName'])['price'].mean()
temp = temp.merge(table.reset_index(), how='left',on='CompanyName')
bins = [0,10000,20000,40000]
cars_bin=['Budget','Medium','Highend']
cars['carsrange'] = pd.cut(temp['price_y'],bins,right=False,labels=cars_bin)
cars.head()
"""
Крок 5: Двовимірний аналіз
"""
plt.figure(figsize=(8,6))

plt.title('Fuel economy vs Price')
sns.scatterplot(x=cars['fueleconomy'],y=cars['price'],hue=cars['drivewheel'])
plt.xlabel('Fuel Economy')
plt.ylabel('Price')

plt.show()
plt.tight_layout()
"""
Висновок:
економія палива має очевидну негативну кореляцію з ціною і є значною.
"""
df = pd.DataFrame(cars.groupby(['fuelsystem','drivewheel','carsrange'])['price'].mean().unstack(fill_value=0))
df.plot.bar()
plt.title('Car Range vs Average Price')
plt.show()
"""
Висновок:
Автомобілі великого діапазону віддають перевагу ведучому колесу з повним приводом та паливною системою gdi або mpfi.
"""
"""
Список значущих змінних після візуального аналізу:

    - Car Range 
    - Engine Type 
    - Fuel type 
    - Car Body 
    - Aspiration 
    - Cylinder Number 
    - Drivewheel 
    - Curbweight 
    - Car Length
    - Car width
    - Engine Size 
    - Boreratio 
    - Horse Power 
    - Wheel base 
    - Fuel Economy 
"""
cars_lr = cars[['price', 'fueltype', 'aspiration','carbody', 'drivewheel','wheelbase',
                  'curbweight', 'enginetype', 'cylindernumber', 'enginesize', 'boreratio','horsepower', 
                    'fueleconomy', 'carlength','carwidth', 'carsrange']]
cars_lr.head()
sns.pairplot(cars_lr)
plt.show()
"""
Крок 6: Фіктивні змінні
"""
# Defining the map function
def dummies(x,df):
    temp = pd.get_dummies(df[x], drop_first = True)
    df = pd.concat([df, temp], axis = 1)
    df.drop([x], axis = 1, inplace = True)
    return df
# Applying the function to the cars_lr

cars_lr = dummies('fueltype',cars_lr)
cars_lr = dummies('aspiration',cars_lr)
cars_lr = dummies('carbody',cars_lr)
cars_lr = dummies('drivewheel',cars_lr)
cars_lr = dummies('enginetype',cars_lr)
cars_lr = dummies('cylindernumber',cars_lr)
cars_lr = dummies('carsrange',cars_lr)
cars_lr.head()
cars_lr.shape
"""
Крок 7: Спліт поїзд-тест і масштабування функцій
"""
from sklearn.model_selection import train_test_split

np.random.seed(0)
df_train, df_test = train_test_split(cars_lr, train_size = 0.7, test_size = 0.3, random_state = 100)
from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()
num_vars = ['wheelbase', 'curbweight', 'enginesize', 'boreratio', 'horsepower','fueleconomy','carlength','carwidth','price']
df_train[num_vars] = scaler.fit_transform(df_train[num_vars])
df_train.head()
df_train.describe()
#Correlation using heatmap
plt.figure(figsize = (30, 25))
sns.heatmap(df_train.corr(), annot = True, cmap="YlGnBu")
plt.show()
"""
Сильно корелюють змінні ціни - curbweight, enginesize, horsepower,carwidth і highend.
"""
#Dividing data into X and y variables
y_train = df_train.pop('price')
X_train = df_train
"""
Крок 8: Створення моделі
"""
#RFE
from sklearn.feature_selection import RFE
from sklearn.linear_model import LinearRegression
import statsmodels.api as sm 
from statsmodels.stats.outliers_influence import variance_inflation_factor
lm = LinearRegression()
lm.fit(X_train,y_train)
rfe = RFE(lm, 10)
rfe = rfe.fit(X_train, y_train)
list(zip(X_train.columns,rfe.support_,rfe.ranking_))
X_train.columns[rfe.support_]
"""
Побудова моделі за допомогою statsmodel для детальної статистики
"""
X_train_rfe = X_train[X_train.columns[rfe.support_]]
X_train_rfe.head()
def build_model(X,y):
    X = sm.add_constant(X) #Adding the constant
    lm = sm.OLS(y,X).fit() # fitting the model
    print(lm.summary()) # model summary
    return X
    
def checkVIF(X):
    vif = pd.DataFrame()
    vif['Features'] = X.columns
    vif['VIF'] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
    vif['VIF'] = round(vif['VIF'], 2)
    vif = vif.sort_values(by = "VIF", ascending = False)
    return(vif)
"""
Модель 1
"""
X_train_new = build_model(X_train_rfe,y_train)
"""
p-vale з дванадцяти, здається, перевищує значення значимості 0,05,
отже, знижує його, оскільки він незначний за наявності інших змінних.
"""
X_train_new = X_train_rfe.drop(["twelve"], axis = 1)
"""
Модель 2
"""
X_train_new = build_model(X_train_new,y_train)
X_train_new = X_train_new.drop(["fueleconomy"], axis = 1)
"""
Модель 3
"""
X_train_new = build_model(X_train_new,y_train)
#Calculating the Variance Inflation Factor
checkVIF(X_train_new)
"""
зниження власної ваги через високе значення VIF. (показує, що власна вага має високу мультиколінеарність.)
"""
X_train_new = X_train_new.drop(["curbweight"], axis = 1)
"""
Модель 4
"""
X_train_new = build_model(X_train_new,y_train)
checkVIF(X_train_new)
"""
скидання седана через високе значення VIF 
"""
X_train_new = X_train_new.drop(["sedan"], axis = 1)
"""
Модель 5
"""
X_train_new = build_model(X_train_new,y_train)
checkVIF(X_train_new)
"""
скидання вагона через велике значення p.
"""
X_train_new = X_train_new.drop(["wagon"], axis = 1)
"""
Модель 6
"""
X_train_new = build_model(X_train_new,y_train)
checkVIF(X_train_new)
"""
Модель 7
"""
#Dropping dohcv to see the changes in model statistics
X_train_new = X_train_new.drop(["dohcv"], axis = 1)
X_train_new = build_model(X_train_new,y_train)
checkVIF(X_train_new)
Крок 9: Залишковий аналіз моделі
lm = sm.OLS(y_train,X_train_new).fit()
y_train_price = lm.predict(X_train_new)
# Plot the histogram of the error terms
fig = plt.figure()
sns.distplot((y_train - y_train_price), bins = 20)
fig.suptitle('Error Terms', fontsize = 20)                  # Plot heading 
plt.xlabel('Errors', fontsize = 18)   
"""
Умови помилок здаються приблизно нормально розподіленими, тому припущення про лінійне моделювання, здається, виконується.
"""
"""
Крок 10: Прогнозування та оцінка
"""
#масштабування тестового набору
num_vars = ['wheelbase', 'curbweight', 'enginesize', 'boreratio', 'horsepower','fueleconomy','carlength','carwidth','price']
df_test[num_vars] = scaler.fit_transform(df_test[num_vars])
# Поділ на X та y
y_test = df_test.pop ('price')
X_test = df_test
# Тепер давайте використаємо нашу модель для прогнозування.
X_train_new = X_train_new.drop('const',axis=1)
# Створення кадру даних X_test_new, скинувши змінні з X_test
X_test_new = X_test[X_train_new.columns]

# Додавання константи змінної
X_test_new = sm.add_constant(X_test_new)
# Складання прогнозів
y_pred = lm.predict(X_test_new)
"""
Оцінка тесту шляхом порівняння y_pred та y_test
"""
from sklearn.metrics import r2_score 
r2_score(y_test, y_pred)
#EVALUATION OF THE MODEL
# Plotting y_test and y_pred to understand the spread.
fig = plt.figure()
plt.scatter(y_test,y_pred)
fig.suptitle('y_test vs y_pred', fontsize=20)              # Plot heading 
plt.xlabel('y_test', fontsize=18)                          # X-label
plt.ylabel('y_pred', fontsize=16)   
"""
Оцінка моделі за допомогою статистики
"""
print(lm.summary())