<jupyter_start><jupyter_text>Exercise November, 4th 2019 : Prediksi dan Rekomendasi Pembelian Tiket Pesawat untuk Waktu dan Tujuan Tertentu Berdasarkan Waktu DelayAuthor : <NAME>ama Tujuan akhir dari workflow ini adalah untuk menyediakan rekomendasi kepada *customer* untuk memilih *flight* yang tidak terlambat untuk bulan Desember, 2018.Tahapan-tahapan dalam workflow ini adalah 1. Tahap Persiapan2. EDA dan Feature Engineering3. Membuat Algoritma Rekomendasi4. Pemodelan Delay untuk Bulan Desember5. Algoritma Rekomendasi berdasarkan Model  Tahap PersiapanTahap persiapan terdiri dari a. Memasukkan modul dan datab. Overview datac. Mempersiapkan data  Memasukkan Modul dan Data<jupyter_code># memasukkan modul yang diperlukan

# modul dasar untuk rekayasa data, perhitungan, dan pengaturan direktori

import pandas as pd
import numpy as np
from scipy import stats
import os
os.chdir('D:/Titip')
from sklearn.utils.extmath import cartesian

# modul untuk visualisasi data

import matplotlib.pyplot as plt
import seaborn as sns

# modul untuk scrapping data

from bs4 import BeautifulSoup as bs
import requests
import time
import lxml.html as lh

# modul untuk manipulasi data text

import re
from collections import defaultdict

# modul untuk manipulasi data tanggal

import datetime
from datetime import datetime, timedelta

# modul untuk scaling data dan clustering

from sklearn.preprocessing import MinMaxScaler
from sklearn.cluster import KMeans

# modul untuk pemodelan

from sklearn.linear_model import LogisticRegression
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.tree import DecisionTreeRegressor
from sklearn import metrics
from sklearn.metrics import roc_auc_score
from sklearn.metrics import accuracy_score
import statsmodels.formula.api as smf
# memasukkan data

df_train = pd.read_csv('./training_dataset.csv')
df_test = pd.read_csv('./test_dataset.csv')<jupyter_output><empty_output><jupyter_text>Overview Data<jupyter_code>def resumetable(df):
    print(f"Dataset Shape: {df.shape}")
    summary = pd.DataFrame(df.dtypes,columns=['dtypes'])
    summary = summary.reset_index()
    summary['Name'] = summary['index']
    summary = summary[['Name','dtypes']]
    summary['Perc Missing'] = df.isnull().sum().values / len(df) * 100   
    summary['Uniques'] = df.nunique().values
    summary['First Value'] = df.loc[0].values
    summary['Second Value'] = df.loc[1].values
    summary['Third Value'] = df.loc[2].values

    for name in summary['Name'].value_counts().index:
        summary.loc[summary['Name'] == name, 'Entropy'] = round(stats.entropy(df[name].value_counts(normalize=True), base=2),2) 

    return summary

print('Resume dari data train adalah :')
display(resumetable(df_train))
print('Resume dari data_test adalah :')
display(resumetable(df_test))<jupyter_output>Resume dari data train adalah :
Dataset Shape: (111068, 26)<jupyter_text>Mempersiapkan Data<jupyter_code># menggabungkan data untuk training dan data untuk testing

df_train['status'] = 'train'
df_test['status'] = 'test'
df_comb = pd.concat([df_train,df_test], axis = 0)
del df_comb['id']

# menghilangkan kolom arrival_airport gate karena missing value sekitar 90 %

del df_comb['arrival_airport_gate']

# menghilangkan data yang nilai unik-nya hanya 1

df_comb.drop(['departure_airport_country','departure_airport_region','departure_airport_timezone'], axis = 1, inplace = True)

# mengganti nama kolom flight_equipment_iata

df_comb = df_comb.rename(columns = {'flight_equipment_iata' : 'flight_equipment_data'})

# airline sudah diwakili oleh airline_name sehingga bisa dihilangkan

del df_comb['airline']

# departure dan arrival airport name sudah terwakilkan oleh departure dan arrival airport code maupun city  
# sehingga bisa dibuang

del df_comb['departure_airport_name']
del df_comb['arrival_airport_name']

# menghapus flight duration karena tidak make sense untuk digunakan (lebih make sense memakai scheduled flight duration)

del df_comb['flight_duration']

# mengubah kota tujuan menjadi huruf kecil untuk menghindari case-sensitive

df_comb['arrival_airport_city'] = df_comb['arrival_airport_city'].str.lower()<jupyter_output><empty_output><jupyter_text>Exploratory Data Analysis dan Feature Engineering Fitur yang akan dibuat di sini adalah :1. Jenis penerbangan (premium / regular)2. Penggolongan airline_name menjadi "Others"3. Tanggal, hari, dan jam penerbangan serta rencana durasi penerbangan4. Jarak penerbangan (scrapping)5. Memisahkan gate menjadi huruf dan angkanya6. Menghapus data terminal7. Region secara spesifik8. Jenis pesawat9. Liburan / tidak10. Pembagian waktu (pagi, siang, sore, malam, dinihari)11. Kluster jarak dan durasi penerbangan12. Penerbangan di dalam Malaysia atau keluar Malaysia13. Kecepatan pesawat14. Jumlah dan densitas penerbangan setiap waktu untuk setiap bandara (scrapping)15. Cuaca (data eksternal)<jupyter_code># menurut https://en.wikipedia.org/wiki/Flight_number, 
# flight number mengandung arti sehingga akan diambil hanya angka saja
# lalu akan digolongkan tipe flight dari angkanya

df_comb['number'] = df_comb.number.str.extract('(\d+)')
df_comb['number'] = df_comb.number.astype(int)

def label(number) :
    if number < 1000 :
        value = 'Premium'
    else :
        value = 'Regular'
    return value

df_comb['number'] = df_comb['number'].apply(label)
df_comb = df_comb.rename(columns = {'number':'type'})
df_comb['type'] = df_comb['type'].str.lower()
# terdapat banyak nama airline dan akan divisualisasi tingkat rata-rata delay setiap airline name

d1 = df_comb[(df_comb['status'] == 'train')].groupby('airline_name')['delay'].agg('mean').reset_index()
d2 = df_comb[(df_comb['status'] == 'train')]['airline_name'].value_counts().reset_index().rename(columns = {'index' : 'airline_name','airline_name':'count'})
d3 = pd.merge(d1,d2, on = 'airline_name', how = 'inner')
d3
# membuat scatter plot dari count dan delay

plt.figure(figsize = (10,6))
sns.scatterplot(x = 'count', y = 'delay', data=d3)
display(d3[d3['delay'] > 25].sort_values(by = 'count', ascending = False))<jupyter_output><empty_output><jupyter_text>Bisa dilihat bahwa semakin rendah jumlah data dengan nama pesawat tertentu, delaynya makin tinggi. Kita akan set threshold dan mengubah yang termasuk dalam data airline_name tersebut<jupyter_code># merubah nama pesawat pada data di atas menjadi Others

others = d3[d3['delay'] > 25].sort_values(by = 'count', ascending = False)['airline_name'].tolist()
df_comb.loc[df_comb['airline_name'].isin(others),'airline_name'] = 'Others'
# mengambil tanggal, hari (dalam seminggu), dan jam (dalam menit) pada scheduled_departure_time dan
# scheduled_arrival_time
# juga mengambil selisih scheduled arrival dan departure untuk mengetahui durasi asli dari penerbangan

df_comb['date_dept'] = df_comb.scheduled_departure_time.apply(lambda x : x.split()[0])
df_comb['day_dept'] = [t.dayofweek for t in pd.DatetimeIndex(df_comb.scheduled_departure_time)]
df_comb['hour_dept'] = [t.hour for t in pd.DatetimeIndex(df_comb.scheduled_departure_time)]

df_comb['day_arr'] = [t.dayofweek for t in pd.DatetimeIndex(df_comb.scheduled_arrival_time)]
df_comb['hour_arr'] = [t.hour for t in pd.DatetimeIndex(df_comb.scheduled_arrival_time)]

df_comb['scheduled_duration'] = (pd.to_datetime(df_comb['scheduled_arrival_time']) - pd.to_datetime(df_comb['scheduled_departure_time']))
df_comb['scheduled_duration'] = df_comb['scheduled_duration'].dt.components['hours']*60 + df_comb['scheduled_duration'].dt.components['minutes']

df_comb.drop(['scheduled_departure_time','scheduled_arrival_time'], axis = 1, inplace = True)
# mengambil data jarak dari kombinasi tempat yang ada di data dari https://www.prokerala.com/ (web-scrapping)

df_comb['departure_airport_code'] = df_comb['departure_airport_code'].str.lower()
df_comb['arrival_airport_code'] = df_comb['arrival_airport_code'].str.lower()
df_dist = df_comb[['departure_airport_code','arrival_airport_code']].drop_duplicates().reset_index(drop = True)
df_dept = df_dist['departure_airport_code'].tolist()
df_arr = df_dist['arrival_airport_code'].tolist()
distances = []

for i in range(len(df_dept)):
    dept_code = str(df_dept[i])
    arr_code = str(df_arr[i])
    url = f"https://www.prokerala.com/travel/airports/distance/from-{dept_code}/to-{arr_code}/"
    response = requests.get(url)
    soup = bs(response.content, 'html.parser')
    imp = [element.text for element in soup.find_all('div', {'class': 'tc'})]
    if imp :
        imp_inf = imp[1]
        distance = re.findall(r"(\d+(\.\d+)?)", imp[1])[1][0]
        distances.append(float(distance))
    else :
        distances.append(np.nan)
    time.sleep(2)
df_dist['distances'] = pd.DataFrame(distances)
df_comb = pd.merge(df_comb, df_dist, on = ['departure_airport_code','arrival_airport_code'], how = 'inner')
df_comb = df_comb.drop(['departure_airport_code','arrival_airport_code'], axis = 1)
# memisahkan gate menjadi huruf dan angka pada departure_airport_gate

df_comb['dept_gate_num'] = df_comb['departure_airport_gate'].str.replace('([A-Z]+)','')
df_comb['dept_gate_alpha'] =  df_comb['departure_airport_gate'].str.extract('([A-Z]+)')
del df_comb['departure_airport_gate'] 
# menghilangkan departure_airport_terminal dan arrival_airport_terminal karena inputnya tidak berpola dan informasinya 
# dapat diwakilkan oleh prediktor lain

del df_comb['departure_airport_terminal']
del df_comb['arrival_airport_terminal']
# mengambil region spesifik dari arrival_airport_timezone dan menghapus, region, serta timezone-nya 
# karena sudah terwakilkan oleh region spesifik tersebut

df_comb['arrival_specific_region'] = df_comb['arrival_airport_timezone'].apply(lambda x : x.split("/")[1])
del df_comb['arrival_airport_region']
del df_comb['arrival_airport_timezone']
# mengambil jenis pesawat dari flight_equipment_name 

df_comb['type_of_plane'] = df_comb.flight_equipment_name.apply(lambda x : str(x).split(" ")[0])
del df_comb['flight_equipment_name']
# membuat flag apakah tanggal keberangkatan ada di sekitar tanggal liburan
# berikut adalah data tanggal liburan di Malaysia

holiday = pd.read_csv('C:/Users/rangga.pertama/Downloads/holiday_2018.csv')
holiday['month'] =  pd.DatetimeIndex(holiday['Date']).month
holiday = holiday[holiday['month'].isin([10,11,12])]
# melanjutkan membuat flag sekitar liburan

holiday['Date'] = pd.to_datetime(holiday['Date'])
holiday['date_before'] = holiday['Date'] - timedelta(days = 1)
holiday['date_after'] = holiday['Date'] + timedelta(days = 1)
list_date = holiday['Date'].astype(str).tolist()
list_date_before = holiday['date_before'].astype(str).tolist()
list_date_after = holiday['date_after'].astype(str).tolist()

def holiday(date) :
    if (date in list_date) or (date in list_date_before) or (date in list_date_after):
        value = 'yes'
    else :
        value = 'no'
    return value

df_comb['around_holiday'] = df_comb["date_dept"].apply(holiday)
# membuat pembagian waktu keberangkatan menjadi pagi, siang, sore, malam, dan dinihari

def time(x) :
    if 0 <= x <= 4 :
        value = 'dinihari'
    elif 5 <= x <= 10 :
        value = 'pagi'
    elif 11 <= x <= 15 :
        value = 'siang'
    elif 16 <= x <= 20 :
        value = 'sore'
    elif 21 <= x <= 23 :
        value = 'malam'
    return value

df_comb['cat_time_dept'] = df_comb['hour_dept'].apply(time)
# membuat clustering distance dengan k-means

# karena data distance masih terdapat nan maka akan diimputasi terlebih dahulu dengan mean jarak dari arrival_specific_region

display(df_comb.loc[df_comb['distances'].isnull()]['arrival_specific_region'].value_counts())

display(df_comb.groupby(['arrival_specific_region']).agg('mean')['distances'])
# melanjutkan imputasi lalu clustering

df_comb.loc[(df_comb['distances'].isnull()) & (df_comb['arrival_specific_region'] == 'Kuala_Lumpur'),'distances'] = 677.677027
df_comb.loc[(df_comb['distances'].isnull()) & (df_comb['arrival_specific_region'] == 'Makassar'), 'distances'] = 1969.265502
df_comb.loc[(df_comb['distances'].isnull()) & (df_comb['arrival_specific_region'] == 'Tehran'),'distances'] = 5875.555776
df_comb.loc[(df_comb['distances'].isnull()) & (df_comb['arrival_specific_region'] == 'Jakarta'),'distances'] = 2940.378251

df_comb['distances'] = MinMaxScaler().fit_transform(df_comb[['distances']])
kmeans = KMeans(n_clusters=3, max_iter=600, algorithm = 'auto')
kmeans.fit(df_comb[['distances']])
idx = np.argsort(kmeans.cluster_centers_.sum(axis=1))
lut = np.zeros_like(idx)
lut[idx] = np.arange(3)
df_comb['distances_scaled'] = pd.DataFrame(lut[kmeans.labels_])
# membuat clustering scheduled_duration dengan k-means

# karena data scheduled_duration masih terdapat nan maka akan diimputasi terlebih dahulu 

df_comb.loc[(df_comb['scheduled_duration'].isnull()) & (df_comb['arrival_airport_city'] == 'penang'),'scheduled_duration'] = df_comb[df_comb['arrival_airport_city']=='penang']['scheduled_duration'].mean()
df_comb.loc[(df_comb['scheduled_duration'].isnull()) & (df_comb['arrival_airport_city'] == 'cairns'), 'scheduled_duration'] = df_comb[df_comb['arrival_airport_city']=='cairns']['scheduled_duration'].mean()
df_comb.loc[(df_comb['scheduled_duration'].isnull()) & (df_comb['arrival_airport_city'] == 'guam'), 'scheduled_duration'] = df_comb['scheduled_duration'].median()

df_comb['scheduled_duration'] = MinMaxScaler().fit_transform(df_comb[['scheduled_duration']])
kmeans = KMeans(n_clusters=3, max_iter=600, algorithm = 'auto')
kmeans.fit(df_comb[['scheduled_duration']])
idx = np.argsort(kmeans.cluster_centers_.sum(axis=1))
lut = np.zeros_like(idx)
lut[idx] = np.arange(3)
df_comb['duration_scaled'] = pd.DataFrame(lut[kmeans.labels_])
# membaut kecepatan yang seharusnya dari setiap pesawat

df_comb['speed'] = df_comb['distances'] / df_comb['scheduled_duration']
# membuat jumlah dan densitas penerbangan setiap kategori waktu pada suatu hari

flight = df_comb.groupby(['date_dept','departure_airport_city','cat_time_dept'])['airline_name'].count().reset_index().rename(columns = {'airline_name':'cnt_flight'})

def dens(flight) :
    if flight['cat_time_dept'] == 'dinihari' :
        values = flight['cnt_flight'] / 4
    elif flight['cat_time_dept'] == 'pagi' :
        values = flight['cnt_flight'] / 5 
    elif flight['cat_time_dept'] == 'siang' :
        values = flight['cnt_flight'] / 5
    elif flight['cat_time_dept'] == 'sore' :
        values = flight['cnt_flight'] / 4
    elif flight['cat_time_dept'] == 'malam' :
        values = flight['cnt_flight'] / 2
    return values
        
flight['dens_flight'] = flight.apply(dens,axis = 1)

df_comb = pd.merge(df_comb, flight, on = ['date_dept','departure_airport_city','cat_time_dept'], how = 'inner')
# membuat pengkategorian penerbangan keluar dari malaysia atau di dalam malaysia

def inout(df_comb) :
    if df_comb['arrival_airport_country'] == 'MY' :
        value = 1
    elif df_comb['arrival_airport_country'] != 'MY' :
        value = 0
    return value

df_comb['in_MY'] = df_comb.apply(inout, axis = 1)
# scrap data flight_equipment_iata dari http://www.flugzeuginfo.net/table_accodes_iata_en.php

url = "http://www.flugzeuginfo.net/table_accodes_iata_en.php"
page = requests.get(url)
doc = lh.fromstring(page.content)
tr_elements = doc.xpath('//tr')
# cek banyaknya kolom di tiap tr_elements

display([len(T) for T in tr_elements[:12]])

# mengambil data tiap row-nya

col = []
i = 0
for t in tr_elements[0]:
    i+=1
    name=t.text_content()
    col.append((name,[]))
display(col)

for j in range(1,len(tr_elements)):
    T=tr_elements[j]
    if len(T)!=4:
        break
    i=0
    for t in T.iterchildren(): 
        data=t.text_content()
        if i>0:
            try:
                data=int(data)
            except:
                pass
        col[i][1].append(data)
        i+=1
display([len(C) for (title,C) in col])

Dict={title:column for (title,column) in col}
df_iata=pd.DataFrame(Dict)
df_iata = df_iata.rename(columns = {'IATA':'flight_equipment_data'})
df_comb = pd.merge(df_comb, df_iata[['flight_equipment_data','Wake']], on =  'flight_equipment_data', how = 'inner')
# mengambil data cuaca https://rp5.ru/Weather_in_the_world

df_weather = pd.read_csv('D:/Titip/weather_data_comp.csv', )
df_weather['WW'] = df_weather['WW'].str.strip()
df_weather['WW'] = df_weather['WW'].map({'Haze.' : 'Haze', 'Clouds generally dissolving or becoming less developed.' : 'Cloudy',
                                        'State of sky on the whole unchanged.' : 'Normal',
                                         'Clouds generally forming or developing.' : 'Cloudy',
                                        'Lightning visible, no thunder heard.' : 'Lightning',
                                        'Mist.' : 'Fog',
                                        'Rain, not freezing, continuous, slight at time of observation.' : 'Rain',
                                        'Thunderstorm, slight or moderate, without hail, but with rain and/or snow at time of observation.' : 'Thunderstorm',
                                        'Precipitation within sight, reaching the ground or the surface of the sea, but distant, i.e. estimated to be more than 5 km from the station.' : 'Fog',
                                        'Rain (not freezing) not falling as shower(s).' : 'Rain',
                                        'Thunderstorm, but no precipitation at the time of observation.' : 'Thunderstorm',
                                        'Rain, not freezing, intermittent, slight at time of observation.' : 'Rain',
                                        'Thunderstorm (with or without precipitation).' : 'Thunderstorm',
                                        'Slight rain at time of observation. Thunderstorm during the preceding hour but not at time of observation.' : 'Rain',
                                        'Shower(s) of rain.' : 'Rain',
                                        'Rain shower(s), slight.' : 'Rain',
                                        'Rain, not freezing, continuous, moderate at time of observation.' : 'Rain',
                                        'Rain shower(s), moderate or heavy.' : 'Rain',
                                        'Fog or ice fog.' : 'Fog',
                                        'Rain, not freezing, continuous, heavy at time of observation.' : 'Rain',
                                        'Rain, not freezing, intermittent, moderate at time of observation.' : 'Rain',
                                        'Moderate or heavy rain at time of observation. Thunderstorm during the preceding hour but not at time of observation.' : 'Rain',
                                        'Thunderstorm, heavy, without hail, but with rain and/or snow at time of observation.' : 'Thunderstorm',
                                        'Fog or ice fog, sky visible, has begun or has become thicker during the preceding hour.' : "Fog",
                                        'Rain, not freezing, intermittent, heavy at time of observation.' : 'Rain',
                                        'Fog or ice fog, sky visible (has become thinner during the preceding hour).' : 'Fog',
                                        'Fog or ice fog, sky visible, no appreciable change during the preceding hour.' : 'Fog',
                                        'Drizzle, not freezing, intermittent, slight at time of observation.' : 'Fog',
                                        'Rain shower(s), violent.' : 'Rain'})
# mengkategorikan nilai kolom bertipe object

df_weather['N'] = df_weather['N'].str.rstrip()
df_weather['N'] = df_weather['N'].map({'90  or more, but not 100%' : 'high', '100%.' : 'very high', '70 – 80%.' : 'medium',
                                      '60%.' : 'low'})
df_weather['DD'] = df_weather['DD'].str[22:]
del df_weather['Cl']
df_weather['H'] = df_weather['H'].str.rstrip()
df_weather['H'] = df_weather['H'].map({'Less than  50' : 'very_low', '50-100' : 'low','100-200' : 'medium',
                                      '200-300' : 'high', '300-600' : 'very_high', '600-1000' : 'extremely_high'})
del df_weather['Nh']
def time(x) :
    if x in [23,22,21] :
        value = 23
    elif x in [20,19,18] :
        value = 20
    elif x in [17,16,15] :
        value = 17
    elif x in [14,13,12] :
        value = 14
    elif x in [11,10,9] :
        value = 11
    elif x in [8,7,6] :
        value = 8
    elif x in [5,4,3] :
        value = 5
    elif x in [2,1,0] :
        value = 2
    return value
df_weather
df_comb['time_merg'] = df_comb['hour_dept'].apply(time)
df_weather = df_weather.rename(columns = {'time' : 'time_merg','city' : 'departure_airport_city','date' : 'date_dept'})
df_weather
df_comb = pd.merge(df_comb, df_weather, on = ['date_dept','time_merg','departure_airport_city'], how = 'inner')
del df_comb['time_merg']<jupyter_output><empty_output><jupyter_text>Membuat Algoritma RekomendasiDi sini akan dibuat algoritma rekomendasi untuk memilih pesawat dengan delay paling sebentar untuk setiap tanggal keberangkatan, jam keberangkatan, kota tujuan, dan jenis penerbangan. Data yang digunakan adalah data train (Oktober dan November) dengan inputnya adalah tanggal keberangkatan, jam keberangkatan, kota tujuan, dan jenis. Teknik dalam membuat algoritma ini pada dasarnya menggunakan agregasi berupa rata-rata delay yang terjadi dengan kondisi input yang telah dimasukkan. Namun bila kita hanya mengambil data tepat untuk kondisi seperti pada input, maka agregasi yang dihasilkan akan sedikit dan mungkin tidak mewakilkan. Maka akan dicari terlebih dahulu variabel lain yang berhubungan dengan tanggal, jam, dan kota tujuan dan akan digambarkan hubungannya dengan tingkat delay yang terjadi.<jupyter_code>df_train = df_comb[df_comb['status'] == 'train']
df_test = df_comb[df_comb['status'] == 'test']
# menghilangkan terlebih dahulu nilai delay yang termasuk extreme outliers (lebih dari 5 kali iqr)

iqr_1 = np.quantile(df_train['delay'], 0.25)
iqr_3 = np.quantile(df_train['delay'], 0.75)
df_train_wo = df_train[(df_train['delay'] < iqr_3 + 6*(iqr_3 - iqr_1)) & (df_train['delay'] > iqr_1 - 6*(iqr_3 - iqr_1))]

del df_train_wo['status']
del df_test['status']
# variabel yang berhubungan dengan tanggal adalah around_holiday
# akan dilihat perbedaan tingkat delay untuk 'yes' dan 'no' pada around_holiday untuk setiap jenis penerbangan 

plt.figure(figsize = (10,6))
ax = sns.boxplot(x = 'around_holiday', y = 'delay', hue = 'type',
                 data = df_train_wo)
# tanggal juga berhubungan dengan hari dalam seminggu jadi akan dilihat juga bagaimana hubungannya terhadap delay

plt.figure(figsize=(10,6))
ax = sns.violinplot(x="day_dept", y="delay", hue="type",
                   data=df_train_wo, palette="Set2", split=True, scale="count")<jupyter_output>C:\ProgramData\Anaconda3\lib\site-packages\scipy\stats\stats.py:1713: FutureWarning: Using a non-tuple sequence for multidimensional indexing is deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`. In the future this will be interpreted as an array index, `arr[np.array(seq)]`, which will result either in an error or a different result.
  return np.add.reduce(sorted[indexer] * weights, axis=axis) / sumval<jupyter_text>Dapat dilihat bahwa hari dalam minggu dan apakah tanggal tersebut di sekitar hari libur tidak berpengaruh signifikan kepada tingkat delay. Jadi sebenarnya untuk suatu tanggal, tidak perlu mengambil data hari dalam seminggu yang sama seperti tanggal tersebut atau status sekitar hari libur yang mirip seperti tanggal tersebut.<jupyter_code># sekarang akan dicek untuk variabel yang berhubungan dengan jam yaitu kategori waktu

plt.figure(figsize = (10,6))
sns.pointplot(x="cat_time_dept", y="delay", hue="type", data=df_train_wo,
              palette={"regular": "green", "premium": "red"},
              markers=["*", "o"], linestyles=["-", "--"])<jupyter_output><empty_output><jupyter_text>Ternyata cukup signifikan perbedaan untuk setiap kategori waktu dan kita bisa mengambil data semua kategori waktu dengan kategori waktu yang sama seperti data input.<jupyter_code># akan diperiksa variabel-variabel yang berhubungan dengan kota tujuan
# misalnya arrival_specific_region

plt.figure(figsize = (20,6))
ax = sns.boxplot(x = 'arrival_specific_region', y = 'delay', hue = 'type',
                data = df_train_wo.sort_values(by = ['delay'], ascending = False).head(50000))<jupyter_output><empty_output><jupyter_text>Ternyata setiap specific region berbeda-beda secara signifikan (dari visualisasi) sehingga kita bisa mengambil specific region yang sama seperti data kota tujuan yang diinput.<jupyter_code># akan dibandingkan delay antar jarak (distances)

plt.figure(figsize = (10,6))
ax = sns.boxenplot(x = 'distances_scaled', y = 'delay', hue = 'type',
                data = df_train_wo)
plt.figure(figsize = (10,6))
ax = sns.boxenplot(x = 'duration_scaled', y = 'delay', hue = 'type',
                data = df_train_wo)<jupyter_output><empty_output><jupyter_text>Ternyata diketahui bahwa keterlambatan terjadi banyak pada penerbangan jarak dekat dan dengan durasi yang lama sehingga pengkategorian jarak dan durasi yang sama dengan data yang diinput bisa dimanfaatkan juga.<jupyter_code># membuat hubungan antara dept_gate_num dan dept_gate_alpha

plt.figure(figsize = (10,6))
ax = sns.scatterplot(x="dept_gate_num", y="delay", hue="type",
                    data=df_train_wo)
plt.figure(figsize = (10,6))
ax = sns.catplot(x="dept_gate_alpha", y="delay", hue="type",
                    data=df_train_wo)<jupyter_output><empty_output><jupyter_text>Ternyata tidak ada pola antara gate dan delay. Maka kita hanya akan mengambil kategori waktu, region spesifik, jarak, dan durasi yang sama dengan input data yang diambil untuk mencari agregasi rata-rata delay yang paling kecil untuk direkomendasikan.<jupyter_code># input

berangkat = '2018-10-06'       # masukkan tanggal dengan format tahun-bulan-tanggal
jam = 6                      # masukkan jam keberangkatan
kota_tujuan = 'jakarta'        # masukkan nama kota tujuan
jenis = 'regular'              # masukkan jenis penerbangan ('regular' atau 'premium')
# menunjukkan rekomendasi nama pesawat untuk input seperti di atas 

# menunjukkan pesawat dengan delay tersedikit dengan pengelompokan jam, region, jarak, dan waktu yang sesuai dengan input

df_dum = df_train_wo[(df_train_wo['date_dept'] == berangkat) & (df_train_wo['hour_dept'] == jam) & (df_train_wo['arrival_airport_city'] == kota_tujuan) & (df_train_wo['type'] == jenis)]
cat_jam = df_dum['cat_time_dept'].values[0]
reg_spes = df_dum['arrival_specific_region'].values[0]
jar = df_dum['distances_scaled'].values[0]
dur = df_dum['duration_scaled'].values[0]
df_dum = df_train_wo[(df_train_wo['date_dept'] == berangkat) & (df_train_wo['cat_time_dept'] == cat_jam) & (df_train_wo['distances_scaled'] == jar) & \
                (df_train_wo['duration_scaled'] == dur) & (df_train_wo['type'] == jenis)].groupby('airline_name')['delay'].agg('mean').reset_index().sort_values(by = ['delay'], ascending = True).reset_index(drop = True)

# menunjukkan pesawat dari daftar pesawat dengan delay tercepat yang punya jadwal penerbangan sesuai input

list_air = list(df_dum['airline_name'])
list_air_inp = []
for i in range(len(list_air)) :
    name = list_air[i]
    list_dum_dest = list(df_train_wo[(df_train_wo['airline_name'] == name) & (df_train_wo['date_dept'] == berangkat) & (df_train_wo['type'] == jenis) & (df_train_wo['hour_dept'] == jam)]['arrival_airport_city'])
    if kota_tujuan in list_dum_dest :
        list_air_inp.append(name)
df_rec = df_dum[df_dum.airline_name.isin(list_air_inp)].reset_index(drop = True)

# bila error, maka tidak ada data keberangkatan pada tanggal, jam, kota tujuan, dan jenis seperti pada input
# menampilkan hasil rekomendasinya

df_rec<jupyter_output><empty_output><jupyter_text>Pada contoh di atas, terlihat bahwa bila kita ingin pergi ke Jakarta dari Kuala Lumpur pada tanggal 6 Oktober 2018 jam 5 pagi, pesawat yang direkomendasikan pertama sesuai waktu delay-nya adalah Etihad Airways, lalu Garuda Indonesia, dan terakhir Qatar Airways.  Membuat Model Prediksi Model yang akan digunakan adalah regresi linear, logistik, dan decision tree sementara metrics yang akan digunakan adalah RMSE dan Adjusted R-squared untuk regresi serta akurasi untuk klasifikasi  Mempersiapkan Data<jupyter_code># dari hasil pengamatan di atas terlihat bahwa data yang berhubungan dengan gate tidak berhubungan dengan delay

df_train_wo.drop(['dept_gate_alpha','dept_gate_num'], axis = 1, inplace = True)
df_test.drop(['dept_gate_alpha','dept_gate_num'], axis = 1, inplace = True)

# date_dept juga lebih baik tidak diikutkan karena formatnya tanggal dan terlalu banyak dan tidak make sense

del df_train_wo['date_dept']
del df_test['date_dept']

# membuat kategori delay lebih dari 60 menit

def more(delay) :
    if delay > 60 : 
        value = 1
    else :
        value = 0
    return value

df_train_wo['del_more'] = df_train_wo['delay'].apply(more)
df_test['del_more'] = df_test['delay'].apply(more)

# merubah day_dept, day_arr, distances_scaled, dan duration_scaled menjadi kategorikal

list = ['day_dept','day_arr', 'distances_scaled', 'duration_scaled']

df_train_wo['day_dept'] = df_train_wo['day_dept'].astype(str)
df_train_wo['distances_scaled'] = df_train_wo['distances_scaled'].astype(str)
df_train_wo['duration_scaled'] = df_train_wo['duration_scaled'].astype(str)
df_train_wo['day_arr'] = df_train_wo['day_arr'].astype(str)
df_test['day_dept'] = df_test['day_dept'].astype(str)
df_test['distances_scaled'] = df_test['distances_scaled'].astype(str)
df_test['duration_scaled'] = df_test['duration_scaled'].astype(str)
df_test['day_arr'] = df_test['day_arr'].astype(str)


# menskala variabel delay

max_delay = max(df_train_wo['delay'])
min_delay = min(df_train_wo['delay'])
df_train_wo['delay'] = (df_train_wo['delay'] - min_delay) / (max_delay - min_delay)
# imputasi kecepatan yang masih nan

df_train_wo.loc[df_train_wo['speed'].isnull(), 'speed'] = df_train_wo[(df_train_wo['airline_name'] == 'Malaysia Airlines') & (df_train_wo['type'] == 'regular')]['speed'].mean()
df_test.loc[df_test['speed'].isnull(), 'speed'] = df_test[(df_test['airline_name'] == 'Malaysia Airlines') & (df_test['type'] == 'regular')]['speed'].mean()
# untuk hour_arr yang masih nan tidak akan diimputasi dan akan dibuang

df_train_wo = df_train_wo.dropna(subset = ['hour_arr'])
df_test = df_test.dropna(subset = ['hour_arr'])
# # membuat dummy untuk variabel bertipe object

df_train_wo = pd.get_dummies(df_train_wo, drop_first=True)
df_test = pd.get_dummies(df_test, drop_first = True)
# membuang data yang tidak ada di data_test karena tidak akan bisa membuat model dari sana

list = [x for x in df_train_wo.columns.tolist() if x not in df_test.columns.tolist()]
col = [x for x in df_train_wo.columns if x not in list]
df_train_wo = df_train_wo[col]

list2 = [x for x in df_test.columns.tolist() if x not in df_train_wo.columns.tolist()]
col2 = [x for x in df_test.columns if x not in list2]
df_test = df_test[col2]
# memisahkan data train dan split serta membagi prediktor dan respons

y_train_delay = df_train_wo['delay']
y_train_more = df_train_wo['del_more']
y_test_delay = df_test['delay']
y_test_more = df_test['del_more']
X_train = df_train_wo.drop(['delay','del_more'], axis = 1)
X_test = df_test.drop(['delay','del_more'], axis = 1)<jupyter_output><empty_output><jupyter_text>Membuat Model<jupyter_code># model regresi untuk variabel delay

lr = LinearRegression()
model_delay = lr.fit(X_train, y_train_delay)
y_pred_delay = model_delay.predict(X_test)
y_pred_delay = y_pred_delay * (max_delay - min_delay) + min_delay
np.sqrt(metrics.mean_squared_error(y_pred_delay,y_test_delay))<jupyter_output><empty_output><jupyter_text>Dapat dilihat bahwa base model regresi linear untuk delay menghasilkan rata-rata error sebesar 28.79 menit.<jupyter_code>base_train = pd.concat([X_train, y_train_delay], axis = 1)
base_train.columns = base_train.columns.str.strip()
base_train.columns = base_train.columns.str.replace(' ', '_')
base_train.columns = base_train.columns.str.replace('.0','')
base_train.columns = base_train.columns.str.replace('-','_')
base_train.columns = base_train.columns.str.replace('(','')
base_train.columns = base_train.columns.str.replace(')','')
features = " + ".join(base_train.drop("delay", axis=1).columns)
res = smf.ols(formula = f"delay ~ {features}", data = base_train).fit()
res.summary()<jupyter_output><empty_output><jupyter_text>Adjusted R-squared dari regresi linear menggunakan data aslinya adalah 0.218. Dapat dilihat bahwa terdapat beberapa fitur yang tidak signifikan dari p-value (lebih dari 0.05). Kemungkinan ini terjadi karena efek multikolinearitas sehingga akan dicoba untuk dihilangkan.<jupyter_code># menghilangkan data yang berkorelasi tinggi

def correlation(dataset, threshold):
    dataset_no_high_corr = dataset.copy()
    col_corr = set() # Set of all the names of deleted columns
    corr_matrix = dataset_no_high_corr.corr()
    for i in range(len(corr_matrix.columns)):
        for j in range(i):
            if (corr_matrix.iloc[i, j] >= threshold) and (corr_matrix.columns[j] not in col_corr):
                colname = corr_matrix.columns[i] # getting the name of column
                col_corr.add(colname)
                if colname in dataset_no_high_corr.columns:
                    del dataset_no_high_corr[colname] # deleting the column from the dataset
    dataset = dataset_no_high_corr.copy()
correlation(X_train, 0.4)
# menyesuaikan hasil pembuangan data

list = [x for x in X_test.columns.tolist() if x not in X_train.columns.tolist()]
col = [x for x in X_test.columns if x not in list]
X_test = X_test[col]
df_dum1 = pd.concat([X_train,y_train_delay], axis = 1)
# cek korelasi tertinggi terhadap variabel delay

# Plot korelasi tertinggi terstandardisasi terhadap variabel target yang bernilai yes (membeli)

corr1 = df_dum1[df_dum1.columns[1:]].corr()['delay'][:].reset_index()
corr1 = corr1.sort_values(by = ['delay'], ascending = False).reset_index(drop = True)[1:].head(5)
corr2 = df_dum1[df_dum1.columns[1:]].corr()['delay'][:].reset_index()
corr2 = corr2.sort_values(by = ['delay'], ascending = True).reset_index(drop = True)[1:].head(5)
corr = pd.concat([corr1,corr2], axis = 0).reset_index(drop = True)
corr = corr.rename(columns = {'index':'variable'})

x = corr.loc[:, ['delay']]
corr['delay_z'] = (x - x.mean())/x.std()
corr['colors'] = ['red' if x < 0 else 'darkgreen' for x in corr['delay_z']]
corr.sort_values('delay_z', inplace=True)
corr.reset_index(inplace=True)

plt.figure(figsize=(14,16), dpi= 80)
plt.scatter(corr.delay_z, corr.index, s=450, alpha=.6, color=corr.colors)
for x, y, tex in zip(corr.delay_z, corr.index, corr.delay_z):
    t = plt.text(x, y, round(tex, 1), horizontalalignment='center', 
                 verticalalignment='center', fontdict={'color':'white'})

plt.gca().spines["top"].set_alpha(.3)
plt.gca().spines["bottom"].set_alpha(.3)
plt.gca().spines["right"].set_alpha(.3)
plt.gca().spines["left"].set_alpha(.3)

plt.yticks(corr.index, corr.variable)
plt.title('Korelasi Tertinggi Terhadap Delay', fontdict={'size':20})
plt.xlabel('$Correlation$')
plt.grid(linestyle='--', alpha=0.5)
plt.xlim(-1, 1)
plt.show()
# model regresi untuk variabel delay dengan kolom yang berkorelasi telah dibuang

lr = LinearRegression()
model_delay = lr.fit(X_train, y_train_delay)
y_pred_delay_reg = model_delay.predict(X_test)
y_pred_delay_reg = y_pred_delay * (max_delay - min_delay) + min_delay
np.sqrt(metrics.mean_squared_error(y_pred_delay_reg,y_test_delay))<jupyter_output><empty_output><jupyter_text>Dapat dilihat bahwa ternyata saat data-data yang berkorelasi lebih dari 0.4 dibuang maka menghasilkan RMSE yang semakin tinggi sehingga model regresi dengan data awal masihlah yang terbaik.<jupyter_code># Membuat model decision tree 

decision_tree = DecisionTreeRegressor()
decision_tree.fit(X_train, y_train_delay)
y_pred = decision_tree.predict(X_test)
y_pred_delay = y_pred_delay * (max_delay - min_delay) + min_delay
np.sqrt(metrics.mean_squared_error(y_pred_delay,y_test_delay))<jupyter_output><empty_output><jupyter_text>Ternyata regresi menggunakan regresi linear sederhana dengan data awal lebih baik daripada regresi menggunakan decision tree. Maka algoritma rekomendasi akan menggunakan hasil prediksi dari regresi linear dengan data awal.<jupyter_code># membuat model regresi logistik untuk prediksi klasifikasi delay > 60 menit

logreg = LogisticRegression()
logreg.fit(X_train, y_train_more)
y_pred = logreg.predict(X_test)
acc_log = round(logreg.score(X_train, y_train_more) * 100, 2)
display(acc_log)
display(roc_auc_score(y_test_more, y_pred))<jupyter_output>C:\ProgramData\Anaconda3\lib\site-packages\sklearn\linear_model\logistic.py:433: FutureWarning: Default solver will be changed to 'lbfgs' in 0.22. Specify a solver to silence this warning.
  FutureWarning)<jupyter_text>Akurasi model logistik dengan data yang telah dibuang variabel yang berkorelasi tinggi adalah 98.5 % namun AUC-nya masih buruk yaitu 0.5<jupyter_code>coeff_df = pd.DataFrame(X_train.columns)
coeff_df.columns = ['Prediktor']
coeff_df["Koefisien"] = pd.Series(logreg.coef_[0])
coeff_df.sort_values(by='Koefisien', ascending=False)
# membuat model decision tree untuk prediksi klasifikasi delay > 60 menit

decision_tree = DecisionTreeClassifier()
decision_tree.fit(X_train, y_train_more)
y_pred = decision_tree.predict(X_test)
acc_decision_tree = round(decision_tree.score(X_train, y_train_more) * 100, 2)
acc_decision_tree
roc_auc_score(y_test_more, y_pred)<jupyter_output><empty_output><jupyter_text>Dapat dilihat bahwa akurasi untuk model training mencapai 100 % dengan decision tree dan terdapat sedikit peningkatan di AUC. Dapat disimpulkan bahwa model ini terlalu overfit pada hasil di data training. Ini kemungkinan disebabkan oleh distribusi data training yang tidak sama dengan data testing.  Membuat Rekomendasi Delay sesuai Hasil Prediksi untuk Bulan Desember<jupyter_code>df_test2 = df_comb[df_comb['status'] == 'test'].reset_index(drop = True)
df_test2 = pd.concat([df_test2,pd.DataFrame(y_pred_delay)], axis = 1).rename(columns = {0 : 'delay'})
# input

berangkat = '2018-12-02'       # masukkan tanggal dengan format tahun-bulan-tanggal
jam = 6                      # masukkan jam keberangkatan
kota_tujuan = 'kota bharu'        # masukkan nama kota tujuan
jenis = 'regular'              # masukkan jenis penerbangan ('regular' atau 'premium')
# menunjukkan rekomendasi nama pesawat untuk input seperti di atas 

# menunjukkan pesawat dengan delay tersedikit dengan pengelompokan jam, region, jarak, dan waktu yang sesuai dengan input

df_dum = df_test2[(df_test2['date_dept'] == berangkat) & (df_test2['hour_dept'] == jam) & (df_test2['arrival_airport_city'] == kota_tujuan) & (df_test2['type'] == jenis)]
cat_jam = df_dum['cat_time_dept'].values[0]
reg_spes = df_dum['arrival_specific_region'].values[0]
df_dum = df_test2[(df_test2['date_dept'] == berangkat) & (df_test2['cat_time_dept'] == cat_jam) & \
         (df_test2['type'] == jenis)].groupby('airline_name')['delay'].agg('mean').reset_index().sort_values(by = ['delay'], ascending = True).reset_index(drop = True)

del df_dum['level_1']

# menunjukkan pesawat dari daftar pesawat dengan delay tercepat yang punya jadwal penerbangan sesuai input

list_air = df_dum['airline_name'].tolist()
list_air_inp = []
for i in range(len(list_air)) :
    name = list_air[i]
    list_dum_dest = df_test2[(df_test2['airline_name'] == name) & (df_test2['date_dept'] == berangkat) & (df_test2['type'] == jenis) & (df_test2['hour_dept'] == jam)]['arrival_airport_city'].tolist()
    if kota_tujuan in list_dum_dest :
        list_air_inp.append(name)
df_rec = df_dum[df_dum.airline_name.isin(list_air_inp)].reset_index(drop = True).groupby('airline_name')['delay'].agg('mean').reset_index().sort_values(by = 'delay', ascending = True).reset_index(drop = True)

df_rec<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12251937015746063, 'fraction_numerical': 0.012421894526368408, 'mean_word_length': 4.7495329788762755, 'pattern_counts': {'":': 2, '<': 88, '<?xml version=': 0, '>': 83, 'https://': 4, 'lorem ipsum': 0, 'www.': 4, 'xml': 1}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 35, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '19', 'dir': 'jupyter-structured-clean-dedup', 'id': '495268', 'license': 'Apache-2.0', 'n_tokens_mistral': 15014, 'n_tokens_neox': 14188, 'n_words': 3249, 'path': 'Exercise November, 4th 2019.ipynb', 'repo_name': 'ranggarppb/Exercise-Foundation-of-Data-Analysis-Using-Python', 'size': '40021'}