Modul 1 Sains Data: Pengenalan Pandas, Transformasi Data

Pengenalan Series & DataFrame Pandas

Online melalui Zoom
Published

February 26, 2026

Kembali ke Sains Data

Prerequisites

Pada module ini kita akan coba memahami package pandas, yang merupakan package inti dalam sains-data. kita akan coba melakukan beberapa transformasi data menggunakan pandas.

sebelum itu, python module di bawah ini yang akan digunakan selama praktikum.

import numpy as np
import pandas as pd

Apabila ada yang belum terinstal, silakan instal terlebih dahulu menggunakan pip:

!pip install numpy
!pip install pandas

atau conda jika sedang menggunakan Anaconda:

conda install numpy
conda install pandas

Series

pandas.Series sangat mirip dengan array NumPy (bahkan dibangun di atas objek array NumPy). Yang membedakan array NumPy dari sebuah Series adalah bahwa sebuah Series dapat memiliki label index, yang berarti dapat diindeks dengan label, bukan hanya lokasi nomor saja. Selain itu, sebuah Series tidak perlu menyimpan data numerik, ia dapat menyimpan objek Python sembarang.

Membuat pd.Series dengan list

Paling mudah, kitaa dapat membuat pd.Series dengan python list

my_index= ['a','b','c','d','e']
my_data= [1,2,3,4,5]
my_series= pd.Series(data=my_data, index=my_index)
print(my_series)
a    1
b    2
c    3
d    4
e    5
dtype: int64
print(my_series.__class__)
<class 'pandas.core.series.Series'>

Membuat pd.Series dengan dictionary

Kita juga dapat membuat pd.Series dengan dictionary

# creating a series from a dictionary
my_dict= {'a':1, 'b':2, 'c':3, 'd':4, 'e':5}
my_series_dict= pd.Series(my_dict)
print(my_series_dict)
a    1
b    2
c    3
d    4
e    5
dtype: int64
print(my_series_dict.__class__)
<class 'pandas.core.series.Series'>

Operasi pada Series

# Imaginary Sales Data for 1st and 2nd Quarters for Global Company
q1 = {'Japan': 80, 'China': 450, 'India': 200, 'USA': 250}
q2 = {'Brazil': 100,'China': 500, 'India': 210,'USA': 260}


# Creating a Series from a Dictionary q1 and q2
q1_series= pd.Series(q1)
q2_series= pd.Series(q2)
print(q1_series)
Japan     80
China    450
India    200
USA      250
dtype: int64

Kita dapat mengindeks dengan label

# call values of q1_series based on named index
print(q1_series['Japan'])
print(q1_series['China'])
print(q1_series['India'])
80
450
200

kita tetap dapat mengindeks dengan integer

# u can also call values of q1_series based on positional index
print(q1_series[0])
print(q1_series[1])
print(q1_series[2])
80
450
200

hati-hati dalam melakukan indexing dengan label. bisa saja terjadi error jika label tidak ada di dalam pd.series

# remember named index is case sensitive
try:
    print(q1_series['japan'])
except:
    print('something went wrong')
something went wrong

Operasi aritmatik sederhana pada pd.Series bersifat broadcasting, yaitu diterapkan ke masing-masing elemen

# operations with arithmetic on series are broadcasted to all values
print(q1_series*2)
Japan    160
China    900
India    400
USA      500
dtype: int64
print(q1_series+1000)
Japan    1080
China    1450
India    1200
USA      1250
dtype: int64

Untuk penjumlahan antara dua pd.Series, apabila ada label yang hanya muncul di salah satu series, maka label tersebut akan muncul di hasil jumlah dengan data NaN (not a number, di sini artinya tidak ada data).

(Kebetulan, keterangan NaN hanya bisa muncul untuk tipe data float atau koma-komaan, sehingga tipe data terpaksa diubah menjadi float.)

# operation between series are also broadcasted
print(q1_series+q2_series)
Brazil      NaN
China     950.0
India     410.0
Japan       NaN
USA       510.0
dtype: float64

Mengapa tidak nol saja? Ketiadaan label pada salah satu series dianggap sebagai ketidaktahuan data untuk label tersebut, bukan dianggap nol.

Apabila diinginkan agar data yang tiada dianggap nol terlebih dahulu baru dijumlahkan, bisa seperti berikut:

print(q1_series.add(q2_series, fill_value=0))
Brazil    100.0
China     950.0
India     410.0
Japan      80.0
USA       510.0
dtype: float64

Data Frame

Sebuah pd.DataFrame terdiri dari beberapa pd.Series yang berbagi nilai indeks.

Misalkan kita punya data seperti berikut.

my_data = np.array([
    [25, 59, 18],
    [75, 54, 65],
    [29, 21,  7],
    [32, 68, 16]
])
my_data
array([[25, 59, 18],
       [75, 54, 65],
       [29, 21,  7],
       [32, 68, 16]])

Kita akan membuat pd.Dataframe melalui python list. Perhatikan bahwa kita dapat memberikan nama pada kolom dan baris

my_index= ["Toko A", "Toko B", "Toko C", "Toko D"]
my_columns= ["Apel", "Jeruk", "Pisang"]

df= pd.DataFrame(data=my_data, index=my_index, columns=my_columns)
df
Apel Jeruk Pisang
Toko A 25 59 18
Toko B 75 54 65
Toko C 29 21 7
Toko D 32 68 16
df_2 = pd.DataFrame(data=my_data)
df_2
0 1 2
0 25 59 18
1 75 54 65
2 29 21 7
3 32 68 16
df_3 = pd.DataFrame(data=my_data, columns=my_columns)
df_3
Apel Jeruk Pisang
0 25 59 18
1 75 54 65
2 29 21 7
3 32 68 16

Membaca file csv sebagai pd.DataFrame

Jika berkas .py atau .ipynb Anda berada di lokasi folder yang sama persis dengan berkas .csv yang ingin Anda baca, cukup berikan nama berkas sebagai string, misalnya:

df = pd.read_csv('some_file.csv')

Berikan s berkas jika Anda berada di direktori yang berbeda. Jalur berkas harus 100% benar agar ini berfungsi. Misalnya:

df = pd.read_csv("C:\\Users\\myself\\files\\some_file.csv")

sebelum itu, kalian dapat mendownload dataset “Waiter’s Tips Dataset” melalui salah satu link berikut:

catatan : Gunakan Ctrl + S jika menggunakan direct link tidak otomatis tersimpan

df_tips = pd.read_csv('./tips.csv')
df_tips
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID
0 16.99 1.01 Female No Sun Dinner 2 8.49 Christy Cunningham 3560325168603410 Sun2959
1 10.34 1.66 Male No Sun Dinner 3 3.45 Douglas Tucker 4478071379779230 Sun4608
2 21.01 3.50 Male No Sun Dinner 3 7.00 Travis Walters 6011812112971322 Sun4458
3 23.68 3.31 Male No Sun Dinner 2 11.84 Nathaniel Harris 4676137647685994 Sun5260
4 24.59 3.61 Female No Sun Dinner 4 6.15 Tonya Carter 4832732618637221 Sun2251
... ... ... ... ... ... ... ... ... ... ... ...
239 29.03 5.92 Male No Sat Dinner 3 9.68 Michael Avila 5296068606052842 Sat2657
240 27.18 2.00 Female Yes Sat Dinner 2 13.59 Monica Sanders 3506806155565404 Sat1766
241 22.67 2.00 Male Yes Sat Dinner 2 11.34 Keith Wong 6011891618747196 Sat3880
242 17.82 1.75 Male No Sat Dinner 2 8.91 Dennis Dixon 4375220550950 Sat17
243 18.78 3.00 Female No Thur Dinner 2 9.39 Michelle Hardin 3511451626698139 Thur672

244 rows × 11 columns

Operasi sederhana pada DataFrame

# mengecek nama kolom
df_tips.columns
Index(['total_bill', 'tip', 'sex', 'smoker', 'day', 'time', 'size',
       'price_per_person', 'Payer Name', 'CC Number', 'Payment ID'],
      dtype='object')
# mengecek indeks
df_tips.index
RangeIndex(start=0, stop=244, step=1)
# Melihat nilai pada baris teratas (secara default, akan diambil 5 nilai teratas)
df_tips.head()
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID
0 16.99 1.01 Female No Sun Dinner 2 8.49 Christy Cunningham 3560325168603410 Sun2959
1 10.34 1.66 Male No Sun Dinner 3 3.45 Douglas Tucker 4478071379779230 Sun4608
2 21.01 3.50 Male No Sun Dinner 3 7.00 Travis Walters 6011812112971322 Sun4458
3 23.68 3.31 Male No Sun Dinner 2 11.84 Nathaniel Harris 4676137647685994 Sun5260
4 24.59 3.61 Female No Sun Dinner 4 6.15 Tonya Carter 4832732618637221 Sun2251
df_tips.head(10)
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID
0 16.99 1.01 Female No Sun Dinner 2 8.49 Christy Cunningham 3560325168603410 Sun2959
1 10.34 1.66 Male No Sun Dinner 3 3.45 Douglas Tucker 4478071379779230 Sun4608
2 21.01 3.50 Male No Sun Dinner 3 7.00 Travis Walters 6011812112971322 Sun4458
3 23.68 3.31 Male No Sun Dinner 2 11.84 Nathaniel Harris 4676137647685994 Sun5260
4 24.59 3.61 Female No Sun Dinner 4 6.15 Tonya Carter 4832732618637221 Sun2251
5 25.29 4.71 Male No Sun Dinner 4 6.32 Erik Smith 213140353657882 Sun9679
6 8.77 2.00 Male No Sun Dinner 2 4.38 Kristopher Johnson 2223727524230344 Sun5985
7 26.88 3.12 Male No Sun Dinner 4 6.72 Robert Buck 3514785077705092 Sun8157
8 15.04 1.96 Male No Sun Dinner 2 7.52 Joseph Mcdonald 3522866365840377 Sun6820
9 14.78 3.23 Male No Sun Dinner 2 7.39 Jerome Abbott 3532124519049786 Sun3775
# Melihat nilai pada baris terbawah
df_tips.tail()
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID
239 29.03 5.92 Male No Sat Dinner 3 9.68 Michael Avila 5296068606052842 Sat2657
240 27.18 2.00 Female Yes Sat Dinner 2 13.59 Monica Sanders 3506806155565404 Sat1766
241 22.67 2.00 Male Yes Sat Dinner 2 11.34 Keith Wong 6011891618747196 Sat3880
242 17.82 1.75 Male No Sat Dinner 2 8.91 Dennis Dixon 4375220550950 Sat17
243 18.78 3.00 Female No Thur Dinner 2 9.39 Michelle Hardin 3511451626698139 Thur672
df_tips.tail(10)
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID
234 15.53 3.00 Male Yes Sat Dinner 2 7.76 Tracy Douglas 4097938155941930 Sat7220
235 10.07 1.25 Male No Sat Dinner 2 5.04 Sean Gonzalez 3534021246117605 Sat4615
236 12.60 1.00 Male Yes Sat Dinner 2 6.30 Matthew Myers 3543676378973965 Sat5032
237 32.83 1.17 Male Yes Sat Dinner 2 16.42 Thomas Brown 4284722681265508 Sat2929
238 35.83 4.67 Female No Sat Dinner 3 11.94 Kimberly Crane 676184013727 Sat9777
239 29.03 5.92 Male No Sat Dinner 3 9.68 Michael Avila 5296068606052842 Sat2657
240 27.18 2.00 Female Yes Sat Dinner 2 13.59 Monica Sanders 3506806155565404 Sat1766
241 22.67 2.00 Male Yes Sat Dinner 2 11.34 Keith Wong 6011891618747196 Sat3880
242 17.82 1.75 Male No Sat Dinner 2 8.91 Dennis Dixon 4375220550950 Sat17
243 18.78 3.00 Female No Thur Dinner 2 9.39 Michelle Hardin 3511451626698139 Thur672
# Jika ingin melihat tipe data tiap kolom
df_tips.dtypes
total_bill          float64
tip                 float64
sex                  object
smoker               object
day                  object
time                 object
size                  int64
price_per_person    float64
Payer Name           object
CC Number             int64
Payment ID           object
dtype: object
# Jika ingin melihat informasi lebih rinci dataset
df_tips.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 244 entries, 0 to 243
Data columns (total 11 columns):
 #   Column            Non-Null Count  Dtype  
---  ------            --------------  -----  
 0   total_bill        244 non-null    float64
 1   tip               244 non-null    float64
 2   sex               244 non-null    object 
 3   smoker            244 non-null    object 
 4   day               244 non-null    object 
 5   time              244 non-null    object 
 6   size              244 non-null    int64  
 7   price_per_person  244 non-null    float64
 8   Payer Name        244 non-null    object 
 9   CC Number         244 non-null    int64  
 10  Payment ID        244 non-null    object 
dtypes: float64(3), int64(2), object(6)
memory usage: 21.1+ KB
# Melihat statistik ddeskriptif dari tiap kolom pada data (secara default hanya menampilkan untuk data numerik)
df_tips.describe()
total_bill tip size price_per_person CC Number
count 244.000000 244.000000 244.000000 244.000000 2.440000e+02
mean 19.785943 2.998279 2.569672 7.888197 2.563496e+15
std 8.902412 1.383638 0.951100 2.914234 2.369340e+15
min 3.070000 1.000000 1.000000 2.880000 6.040679e+10
25% 13.347500 2.000000 2.000000 5.800000 3.040731e+13
50% 17.795000 2.900000 2.000000 7.255000 3.525318e+15
75% 24.127500 3.562500 3.000000 9.390000 4.553675e+15
max 50.810000 10.000000 6.000000 20.270000 6.596454e+15
df_tips.describe().transpose()
count mean std min 25% 50% 75% max
total_bill 244.0 1.978594e+01 8.902412e+00 3.070000e+00 1.334750e+01 1.779500e+01 2.412750e+01 5.081000e+01
tip 244.0 2.998279e+00 1.383638e+00 1.000000e+00 2.000000e+00 2.900000e+00 3.562500e+00 1.000000e+01
size 244.0 2.569672e+00 9.510998e-01 1.000000e+00 2.000000e+00 2.000000e+00 3.000000e+00 6.000000e+00
price_per_person 244.0 7.888197e+00 2.914234e+00 2.880000e+00 5.800000e+00 7.255000e+00 9.390000e+00 2.027000e+01
CC Number 244.0 2.563496e+15 2.369340e+15 6.040679e+10 3.040731e+13 3.525318e+15 4.553675e+15 6.596454e+15
# Gunakan include="object" untuk melihat deskripsi kolom bertipe object
df_tips.describe(include="object")
sex smoker day time Payer Name Payment ID
count 244 244 244 244 244 244
unique 2 2 4 2 244 243
top Male No Sat Dinner Christy Cunningham Thur8084
freq 157 151 87 176 1 2
# Jika ingin melihat deskripsi semua kolom sekaligus
df_tips.describe(include="all")
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID
count 244.000000 244.000000 244 244 244 244 244.000000 244.000000 244 2.440000e+02 244
unique NaN NaN 2 2 4 2 NaN NaN 244 NaN 243
top NaN NaN Male No Sat Dinner NaN NaN Christy Cunningham NaN Thur8084
freq NaN NaN 157 151 87 176 NaN NaN 1 NaN 2
mean 19.785943 2.998279 NaN NaN NaN NaN 2.569672 7.888197 NaN 2.563496e+15 NaN
std 8.902412 1.383638 NaN NaN NaN NaN 0.951100 2.914234 NaN 2.369340e+15 NaN
min 3.070000 1.000000 NaN NaN NaN NaN 1.000000 2.880000 NaN 6.040679e+10 NaN
25% 13.347500 2.000000 NaN NaN NaN NaN 2.000000 5.800000 NaN 3.040731e+13 NaN
50% 17.795000 2.900000 NaN NaN NaN NaN 2.000000 7.255000 NaN 3.525318e+15 NaN
75% 24.127500 3.562500 NaN NaN NaN NaN 3.000000 9.390000 NaN 4.553675e+15 NaN
max 50.810000 10.000000 NaN NaN NaN NaN 6.000000 20.270000 NaN 6.596454e+15 NaN

Transformasi Data (Row-Wise)

Filtering

df_tips.head()
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID
0 16.99 1.01 Female No Sun Dinner 2 8.49 Christy Cunningham 3560325168603410 Sun2959
1 10.34 1.66 Male No Sun Dinner 3 3.45 Douglas Tucker 4478071379779230 Sun4608
2 21.01 3.50 Male No Sun Dinner 3 7.00 Travis Walters 6011812112971322 Sun4458
3 23.68 3.31 Male No Sun Dinner 2 11.84 Nathaniel Harris 4676137647685994 Sun5260
4 24.59 3.61 Female No Sun Dinner 4 6.15 Tonya Carter 4832732618637221 Sun2251
print(df_tips["size"] == 3)
0      False
1       True
2       True
3      False
4      False
       ...  
239     True
240    False
241    False
242    False
243    False
Name: size, Length: 244, dtype: bool
conditional_size = (df_tips["size"] == 3)
df_tips[conditional_size]
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID
1 10.34 1.66 Male No Sun Dinner 3 3.45 Douglas Tucker 4478071379779230 Sun4608
2 21.01 3.50 Male No Sun Dinner 3 7.00 Travis Walters 6011812112971322 Sun4458
16 10.33 1.67 Female No Sun Dinner 3 3.44 Elizabeth Foster 4240025044626033 Sun9715
17 16.29 3.71 Male No Sun Dinner 3 5.43 John Pittman 6521340257218708 Sun2998
18 16.97 3.50 Female No Sun Dinner 3 5.66 Laura Martinez 30422275171379 Sun2789
19 20.65 3.35 Male No Sat Dinner 3 6.88 Timothy Oneal 6568069240986485 Sat9213
35 24.06 3.60 Male No Sat Dinner 3 8.02 Joseph Mullins 5519770449260299 Sat632
36 16.31 2.00 Male No Sat Dinner 3 5.44 William Ford 3527691170179398 Sat9139
37 16.93 3.07 Female No Sat Dinner 3 5.64 Erin Lewis 5161695527390786 Sat6406
38 18.69 2.31 Male No Sat Dinner 3 6.23 Brandon Bradley 4427601595688633 Sat4056
39 31.27 5.00 Male No Sat Dinner 3 10.42 Mr. Brandon Berry 6011525851069856 Sat6373
40 16.04 2.24 Male No Sat Dinner 3 5.35 Adam Edwards 3544447755679420 Sat8549
48 28.55 2.05 Male No Sun Dinner 3 9.52 Austin Fisher 6011481668986587 Sun4142
64 17.59 2.64 Male No Sat Dinner 3 5.86 Michael Johnson 2222114458088108 Sat1667
65 20.08 3.15 Male No Sat Dinner 3 6.69 Justin Dixon 180021262464926 Sat6840
71 17.07 3.00 Female No Sat Dinner 3 5.69 Teresa Fisher 5442222963796367 Sat3469
102 44.30 2.50 Female Yes Sat Dinner 3 14.77 Heather Cohen 379771118886604 Sat6240
112 38.07 4.00 Male No Sun Dinner 3 12.69 Jeff Lopez 3572865915176463 Sun591
114 25.71 4.00 Female No Sun Dinner 3 8.57 Katie Smith 5400160161311292 Sun6492
129 22.82 2.18 Male No Thur Lunch 3 7.61 Raymond Torres 4855776744024 Thur9424
146 18.64 1.36 Female No Thur Lunch 3 6.21 Kelly Estrada 60463302327 Thur3941
152 17.26 2.74 Male No Sun Dinner 3 5.75 Gregory Smith 4292362333741 Sun5205
162 16.21 2.00 Female No Sun Dinner 3 5.40 Jennifer Baird 4227834176859693 Sun5521
165 24.52 3.48 Male No Sun Dinner 3 8.17 Jacob Hansen 4031116007387 Sun9043
170 50.81 10.00 Male Yes Sat Dinner 3 16.94 Gregory Clark 5473850968388236 Sat1954
182 45.35 3.50 Male Yes Sun Dinner 3 15.12 Jose Parsons 4112207559459910 Sun2337
186 20.90 3.50 Female Yes Sun Dinner 3 6.97 Heidi Atkinson 4422858423131187 Sun4254
188 18.15 3.50 Female Yes Sun Dinner 3 6.05 Glenda Wiggins 578329325307 Sun430
189 23.10 4.00 Male Yes Sun Dinner 3 7.70 Richard Stevens 3560193117506187 Sun1821
200 18.71 4.00 Male Yes Thur Lunch 3 6.24 Jason Conrad 4581233003487 Thur6048
205 16.47 3.23 Female Yes Thur Lunch 3 5.49 Carly Reyes 4787787236486 Thur8084
206 26.59 3.41 Male Yes Sat Dinner 3 8.86 Daniel Owens 38971087967574 Sat1
210 30.06 2.00 Male Yes Sat Dinner 3 10.02 Shawn Mendoza 30184049218122 Sat8361
214 28.17 6.50 Female Yes Sat Dinner 3 9.39 Marissa Jackson 4922302538691962 Sat3374
223 15.98 3.00 Female No Fri Lunch 3 5.33 Mary Rivera 5343428579353069 Fri6014
231 15.69 3.00 Male Yes Sat Dinner 3 5.23 Jason Parks 4812333796161 Sat6334
238 35.83 4.67 Female No Sat Dinner 3 11.94 Kimberly Crane 676184013727 Sat9777
239 29.03 5.92 Male No Sat Dinner 3 9.68 Michael Avila 5296068606052842 Sat2657
conditional = (df_tips["size"] == 3) & (df_tips["total_bill"] > 20)
print(conditional)
0      False
1      False
2       True
3      False
4      False
       ...  
239     True
240    False
241    False
242    False
243    False
Length: 244, dtype: bool
df_tips[conditional]
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID
2 21.01 3.50 Male No Sun Dinner 3 7.00 Travis Walters 6011812112971322 Sun4458
19 20.65 3.35 Male No Sat Dinner 3 6.88 Timothy Oneal 6568069240986485 Sat9213
35 24.06 3.60 Male No Sat Dinner 3 8.02 Joseph Mullins 5519770449260299 Sat632
39 31.27 5.00 Male No Sat Dinner 3 10.42 Mr. Brandon Berry 6011525851069856 Sat6373
48 28.55 2.05 Male No Sun Dinner 3 9.52 Austin Fisher 6011481668986587 Sun4142
65 20.08 3.15 Male No Sat Dinner 3 6.69 Justin Dixon 180021262464926 Sat6840
102 44.30 2.50 Female Yes Sat Dinner 3 14.77 Heather Cohen 379771118886604 Sat6240
112 38.07 4.00 Male No Sun Dinner 3 12.69 Jeff Lopez 3572865915176463 Sun591
114 25.71 4.00 Female No Sun Dinner 3 8.57 Katie Smith 5400160161311292 Sun6492
129 22.82 2.18 Male No Thur Lunch 3 7.61 Raymond Torres 4855776744024 Thur9424
165 24.52 3.48 Male No Sun Dinner 3 8.17 Jacob Hansen 4031116007387 Sun9043
170 50.81 10.00 Male Yes Sat Dinner 3 16.94 Gregory Clark 5473850968388236 Sat1954
182 45.35 3.50 Male Yes Sun Dinner 3 15.12 Jose Parsons 4112207559459910 Sun2337
186 20.90 3.50 Female Yes Sun Dinner 3 6.97 Heidi Atkinson 4422858423131187 Sun4254
189 23.10 4.00 Male Yes Sun Dinner 3 7.70 Richard Stevens 3560193117506187 Sun1821
206 26.59 3.41 Male Yes Sat Dinner 3 8.86 Daniel Owens 38971087967574 Sat1
210 30.06 2.00 Male Yes Sat Dinner 3 10.02 Shawn Mendoza 30184049218122 Sat8361
214 28.17 6.50 Female Yes Sat Dinner 3 9.39 Marissa Jackson 4922302538691962 Sat3374
238 35.83 4.67 Female No Sat Dinner 3 11.94 Kimberly Crane 676184013727 Sat9777
239 29.03 5.92 Male No Sat Dinner 3 9.68 Michael Avila 5296068606052842 Sat2657
df_tips[(df_tips["size"] == 3) & (df_tips["total_bill"] > 20)]
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID
2 21.01 3.50 Male No Sun Dinner 3 7.00 Travis Walters 6011812112971322 Sun4458
19 20.65 3.35 Male No Sat Dinner 3 6.88 Timothy Oneal 6568069240986485 Sat9213
35 24.06 3.60 Male No Sat Dinner 3 8.02 Joseph Mullins 5519770449260299 Sat632
39 31.27 5.00 Male No Sat Dinner 3 10.42 Mr. Brandon Berry 6011525851069856 Sat6373
48 28.55 2.05 Male No Sun Dinner 3 9.52 Austin Fisher 6011481668986587 Sun4142
65 20.08 3.15 Male No Sat Dinner 3 6.69 Justin Dixon 180021262464926 Sat6840
102 44.30 2.50 Female Yes Sat Dinner 3 14.77 Heather Cohen 379771118886604 Sat6240
112 38.07 4.00 Male No Sun Dinner 3 12.69 Jeff Lopez 3572865915176463 Sun591
114 25.71 4.00 Female No Sun Dinner 3 8.57 Katie Smith 5400160161311292 Sun6492
129 22.82 2.18 Male No Thur Lunch 3 7.61 Raymond Torres 4855776744024 Thur9424
165 24.52 3.48 Male No Sun Dinner 3 8.17 Jacob Hansen 4031116007387 Sun9043
170 50.81 10.00 Male Yes Sat Dinner 3 16.94 Gregory Clark 5473850968388236 Sat1954
182 45.35 3.50 Male Yes Sun Dinner 3 15.12 Jose Parsons 4112207559459910 Sun2337
186 20.90 3.50 Female Yes Sun Dinner 3 6.97 Heidi Atkinson 4422858423131187 Sun4254
189 23.10 4.00 Male Yes Sun Dinner 3 7.70 Richard Stevens 3560193117506187 Sun1821
206 26.59 3.41 Male Yes Sat Dinner 3 8.86 Daniel Owens 38971087967574 Sat1
210 30.06 2.00 Male Yes Sat Dinner 3 10.02 Shawn Mendoza 30184049218122 Sat8361
214 28.17 6.50 Female Yes Sat Dinner 3 9.39 Marissa Jackson 4922302538691962 Sat3374
238 35.83 4.67 Female No Sat Dinner 3 11.94 Kimberly Crane 676184013727 Sat9777
239 29.03 5.92 Male No Sat Dinner 3 9.68 Michael Avila 5296068606052842 Sat2657
conditional_or = (df_tips["tip"] > 4) | (df_tips["total_bill"] > 20)
df_tips[conditional_or]
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID
2 21.01 3.50 Male No Sun Dinner 3 7.00 Travis Walters 6011812112971322 Sun4458
3 23.68 3.31 Male No Sun Dinner 2 11.84 Nathaniel Harris 4676137647685994 Sun5260
4 24.59 3.61 Female No Sun Dinner 4 6.15 Tonya Carter 4832732618637221 Sun2251
5 25.29 4.71 Male No Sun Dinner 4 6.32 Erik Smith 213140353657882 Sun9679
7 26.88 3.12 Male No Sun Dinner 4 6.72 Robert Buck 3514785077705092 Sun8157
... ... ... ... ... ... ... ... ... ... ... ...
237 32.83 1.17 Male Yes Sat Dinner 2 16.42 Thomas Brown 4284722681265508 Sat2929
238 35.83 4.67 Female No Sat Dinner 3 11.94 Kimberly Crane 676184013727 Sat9777
239 29.03 5.92 Male No Sat Dinner 3 9.68 Michael Avila 5296068606052842 Sat2657
240 27.18 2.00 Female Yes Sat Dinner 2 13.59 Monica Sanders 3506806155565404 Sat1766
241 22.67 2.00 Male Yes Sat Dinner 2 11.34 Keith Wong 6011891618747196 Sat3880

101 rows × 11 columns

weekend = ["Sun", "Sat"]
conditional_in = df_tips["day"].isin(weekend)
df_tips[conditional_in]
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID
0 16.99 1.01 Female No Sun Dinner 2 8.49 Christy Cunningham 3560325168603410 Sun2959
1 10.34 1.66 Male No Sun Dinner 3 3.45 Douglas Tucker 4478071379779230 Sun4608
2 21.01 3.50 Male No Sun Dinner 3 7.00 Travis Walters 6011812112971322 Sun4458
3 23.68 3.31 Male No Sun Dinner 2 11.84 Nathaniel Harris 4676137647685994 Sun5260
4 24.59 3.61 Female No Sun Dinner 4 6.15 Tonya Carter 4832732618637221 Sun2251
... ... ... ... ... ... ... ... ... ... ... ...
238 35.83 4.67 Female No Sat Dinner 3 11.94 Kimberly Crane 676184013727 Sat9777
239 29.03 5.92 Male No Sat Dinner 3 9.68 Michael Avila 5296068606052842 Sat2657
240 27.18 2.00 Female Yes Sat Dinner 2 13.59 Monica Sanders 3506806155565404 Sat1766
241 22.67 2.00 Male Yes Sat Dinner 2 11.34 Keith Wong 6011891618747196 Sat3880
242 17.82 1.75 Male No Sat Dinner 2 8.91 Dennis Dixon 4375220550950 Sat17

163 rows × 11 columns

df_tips.head()
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID
0 16.99 1.01 Female No Sun Dinner 2 8.49 Christy Cunningham 3560325168603410 Sun2959
1 10.34 1.66 Male No Sun Dinner 3 3.45 Douglas Tucker 4478071379779230 Sun4608
2 21.01 3.50 Male No Sun Dinner 3 7.00 Travis Walters 6011812112971322 Sun4458
3 23.68 3.31 Male No Sun Dinner 2 11.84 Nathaniel Harris 4676137647685994 Sun5260
4 24.59 3.61 Female No Sun Dinner 4 6.15 Tonya Carter 4832732618637221 Sun2251

Mencari Nilai Unik

df_tips["day"].unique()
array(['Sun', 'Sat', 'Thur', 'Fri'], dtype=object)
df_tips[["day","time"]]
day time
0 Sun Dinner
1 Sun Dinner
2 Sun Dinner
3 Sun Dinner
4 Sun Dinner
... ... ...
239 Sat Dinner
240 Sat Dinner
241 Sat Dinner
242 Sat Dinner
243 Thur Dinner

244 rows × 2 columns

df_tips.drop_duplicates(["day","time"])[["day","time"]]
day time
0 Sun Dinner
19 Sat Dinner
77 Thur Lunch
90 Fri Dinner
220 Fri Lunch
243 Thur Dinner

Transforming Data (Column-Wise)

Selecting Columns

print(df_tips["day"])
0       Sun
1       Sun
2       Sun
3       Sun
4       Sun
       ... 
239     Sat
240     Sat
241     Sat
242     Sat
243    Thur
Name: day, Length: 244, dtype: object
print(df_tips.day)
0       Sun
1       Sun
2       Sun
3       Sun
4       Sun
       ... 
239     Sat
240     Sat
241     Sat
242     Sat
243    Thur
Name: day, Length: 244, dtype: object
df_tips[["day","time"]]
day time
0 Sun Dinner
1 Sun Dinner
2 Sun Dinner
3 Sun Dinner
4 Sun Dinner
... ... ...
239 Sat Dinner
240 Sat Dinner
241 Sat Dinner
242 Sat Dinner
243 Thur Dinner

244 rows × 2 columns

Mutating (membuat kolom baru)

df_tips["tips_percentage"]= df_tips["tip"]/df_tips["total_bill"]*100

df_tips.head()
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID tips_percentage
0 16.99 1.01 Female No Sun Dinner 2 8.49 Christy Cunningham 3560325168603410 Sun2959 5.944673
1 10.34 1.66 Male No Sun Dinner 3 3.45 Douglas Tucker 4478071379779230 Sun4608 16.054159
2 21.01 3.50 Male No Sun Dinner 3 7.00 Travis Walters 6011812112971322 Sun4458 16.658734
3 23.68 3.31 Male No Sun Dinner 2 11.84 Nathaniel Harris 4676137647685994 Sun5260 13.978041
4 24.59 3.61 Female No Sun Dinner 4 6.15 Tonya Carter 4832732618637221 Sun2251 14.680765

Renaming Column

df_tips.rename(columns={"tips_percentage": "tips_%"}, inplace=True)
df_tips.head()
total_bill tip sex smoker day time size price_per_person Payer Name CC Number Payment ID tips_%
0 16.99 1.01 Female No Sun Dinner 2 8.49 Christy Cunningham 3560325168603410 Sun2959 5.944673
1 10.34 1.66 Male No Sun Dinner 3 3.45 Douglas Tucker 4478071379779230 Sun4608 16.054159
2 21.01 3.50 Male No Sun Dinner 3 7.00 Travis Walters 6011812112971322 Sun4458 16.658734
3 23.68 3.31 Male No Sun Dinner 2 11.84 Nathaniel Harris 4676137647685994 Sun5260 13.978041
4 24.59 3.61 Female No Sun Dinner 4 6.15 Tonya Carter 4832732618637221 Sun2251 14.680765

Relocate Columns

#relocate tips_percentage_% column to the rightmost
cols = list(df_tips.columns)
cols = [cols[-1]]+ cols[:-2]

df_tips = df_tips[cols]
df_tips
tips_% total_bill tip sex smoker day time size price_per_person Payer Name CC Number
0 5.944673 16.99 1.01 Female No Sun Dinner 2 8.49 Christy Cunningham 3560325168603410
1 16.054159 10.34 1.66 Male No Sun Dinner 3 3.45 Douglas Tucker 4478071379779230
2 16.658734 21.01 3.50 Male No Sun Dinner 3 7.00 Travis Walters 6011812112971322
3 13.978041 23.68 3.31 Male No Sun Dinner 2 11.84 Nathaniel Harris 4676137647685994
4 14.680765 24.59 3.61 Female No Sun Dinner 4 6.15 Tonya Carter 4832732618637221
... ... ... ... ... ... ... ... ... ... ... ...
239 20.392697 29.03 5.92 Male No Sat Dinner 3 9.68 Michael Avila 5296068606052842
240 7.358352 27.18 2.00 Female Yes Sat Dinner 2 13.59 Monica Sanders 3506806155565404
241 8.822232 22.67 2.00 Male Yes Sat Dinner 2 11.34 Keith Wong 6011891618747196
242 9.820426 17.82 1.75 Male No Sat Dinner 2 8.91 Dennis Dixon 4375220550950
243 15.974441 18.78 3.00 Female No Thur Dinner 2 9.39 Michelle Hardin 3511451626698139

244 rows × 11 columns

Export DataFrame ke CSV

df_tips.to_csv("tips_modified.csv")

Explorasi dan Visualisasi Data

Prerequisites

Import Module

Sebelum memulai, mari kita import terlebih dahulu module - module yang diperlukan.

import matplotlib.pyplot as plt
import seaborn as sns

Import Data

Pada module kali ini, akan digunakan tiga data csv yang berbeda untuk mempermudah kebutuhan visualisasi, yaitu:

  1. Spotify Dataset (spotify.csv), bisa di-download dari

  2. Flight Delays Dataset (flight_delays.csv), bisa di-download dari

  3. Insurance Dataset (insurance.csv), bisa di-download dari

atau langsung download ketiganya sekaligus, bisa dari:

Kemudian, baca tiap CSV sebagai dataframe:

spotify_df = pd.read_csv("./spotify.csv",
                         index_col='Date',
                         parse_dates=['Date'])
flight_df = pd.read_csv("./flight_delays.csv")
insurance_df = pd.read_csv("./insurance.csv")

(Review) Matplotlib

Seperti yang sudah dipelajari pada Algoritma dan Pemrograman, visualisasi data dapat dilakukan dengan module matplotlib, antara lain untuk membuat line plot dan scatter plot.

spotify_df
Shape of You Despacito Something Just Like This HUMBLE. Unforgettable
Date
2017-01-06 12287078 NaN NaN NaN NaN
2017-01-07 13190270 NaN NaN NaN NaN
2017-01-08 13099919 NaN NaN NaN NaN
2017-01-09 14506351 NaN NaN NaN NaN
2017-01-10 14275628 NaN NaN NaN NaN
... ... ... ... ... ...
2018-01-05 4492978 3450315.0 2408365.0 2685857.0 2869783.0
2018-01-06 4416476 3394284.0 2188035.0 2559044.0 2743748.0
2018-01-07 4009104 3020789.0 1908129.0 2350985.0 2441045.0
2018-01-08 4135505 2755266.0 2023251.0 2523265.0 2622693.0
2018-01-09 4168506 2791601.0 2058016.0 2727678.0 2627334.0

366 rows × 5 columns

Catatan:

  • Shape of You dirilis tanggal 6 Januari 2017.
  • Despacito dirilis tanggal 13 Januari 2017.
  • Something Just Like This dirilis tanggal 22 Februari 2017.
  • HUMBLE. dirilis tanggal 30 Maret 2017.
  • Unforgettable dirilis tanggal 7 April 2017.

Perhatikan bahwa ada beberapa data NaN (not a number), artinya tidak ada data (missing data).

spotify_df.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 366 entries, 2017-01-06 to 2018-01-09
Data columns (total 5 columns):
 #   Column                    Non-Null Count  Dtype  
---  ------                    --------------  -----  
 0   Shape of You              366 non-null    int64  
 1   Despacito                 359 non-null    float64
 2   Something Just Like This  319 non-null    float64
 3   HUMBLE.                   282 non-null    float64
 4   Unforgettable             275 non-null    float64
dtypes: float64(4), int64(1)
memory usage: 17.2 KB
spotify_df.isna()
Shape of You Despacito Something Just Like This HUMBLE. Unforgettable
Date
2017-01-06 False True True True True
2017-01-07 False True True True True
2017-01-08 False True True True True
2017-01-09 False True True True True
2017-01-10 False True True True True
... ... ... ... ... ...
2018-01-05 False False False False False
2018-01-06 False False False False False
2018-01-07 False False False False False
2018-01-08 False False False False False
2018-01-09 False False False False False

366 rows × 5 columns

spotify_df.isna().sum()
Shape of You                 0
Despacito                    7
Something Just Like This    47
HUMBLE.                     84
Unforgettable               91
dtype: int64
"""
Membuat line plot untuk lagu Shape of You menggunakan matplotlib
"""

# Mengatur besar figur plot
plt.subplots(figsize=(8,6))

# Membuat line plot
plt.plot(spotify_df['Shape of You'], 'b')
# Membuat label sumbu-x dan sumbu-y
plt.xlabel('Date')
plt.ylabel('Shape of You Total Daily Streams')
# Menampilkan plot
plt.show()

Apabila kita ingin menampilkan fitur-fitur lain dalam figur yang sama, kita dapat memanfaatkan loop

"""
Membuat line plot untuk semua lagu dalam spotify_df menggunakan loop
"""

plt.subplots(figsize=(8,6))

# Loop setiap nama kolom pada dataframe, lalu plot
for column in spotify_df.columns:
    plt.plot(spotify_df[column])

plt.legend(spotify_df.columns)
plt.show()

Namun, terdapat cara yang lebih mudah selain menggunakan looping. pandas dataframe memiliki method yang dapat secara langsung memvisualisasikan keseluruhan fiturnya, yaitu .plot().

Pada .plot() kita memiliki beberapa parameter yang dapat diatur, antara lain kind dan figsize. kind berfungsi untuk mengatur jenis plot yang ingin kita buat, sedangkan figsize berfungsi untuk mengatur besar figur yang dihasilkan.

Parameter lainnya dapat dilihat pada:

https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.plot.html

"""
Membuat line plot untuk semua lagu dalam spotify_df menggunakan pandas .plot()
"""

spotify_df.plot(kind='line', figsize=(8,6))
plt.xlabel('Date')
plt.ylabel('Total Daily Streams')
plt.show()

Selain line plot, terdapat banyak macam kind yang bisa digunakan. Pada code cell dibawah terlihat bahwa pandas .plot() dapat menghasilkan histogram (perlu diperhatikan bahwa jenis plot perlu menyesuaikan tipe data yang dimiliki, terlihat bahwa menggunakan data spotify, histogram tidak menghasilkan insight yang cukup berguna).

spotify_df.plot(kind='hist', figsize=(8,6), alpha=.7)

plt.show()

Pada praktikum Algoritma dan Pemrograman kita juga telah mempelajari cara untuk membuat scatter plot. Berikut code untuk membuat scatter plot menggunakan matplotlib, untuk melihat korelasi antara daily streams lagu Shape of You dengan Something Just Like This.

"""
Membuat scatter plot untuk melihat korelasi antara lagu
Shape of You dengan Something Just Like This menggunakan
matplotlib
"""

plt.subplots(figsize=(8,6))

plt.scatter(x=spotify_df['Shape of You'], 
            y=spotify_df['Something Just Like This'],
            alpha=.5)
plt.xlabel('"Shape of You" Total Daily Streams')
plt.ylabel('"Something Just Like This" Total Daily Streams')
plt.show()

Pengenalan Seaborn

Walaupun matplotlib cukup fleksibel dalam menghasilkan plot, tetapi tipe plot yang disediakan cenderung terbatas. Oleh karena itu, kita dapat menggunakan Seaborn karena tipe plot yang disediakan sangat banyak sesuai kebutuhan kita, antara lain line, bar, heatmap, scatter, box, swarm, histogram, density, dan masih banyak lagi.

Line Plot

Line plot biasa digunakan untuk melihat trend data dalam jangka waktu tertentu.

Untuk membuat line plot pada seaborn, kita dapat menggunakan sns.lineplot(). Jika data yang ingin kita visualisasikan adalah dataframe, kita dapat memasukkan variabel dataframe tersebut pada parameter data, seperti code di bawah ini.

"""
Membuat line plot dengan module seaborn
"""

plt.subplots(figsize=(8,6))
sns.lineplot(data=spotify_df)
plt.show()

Fleksibilitas Seaborn membuat kita dapat memilih color palette yang sesuai dengan keinginan kita. Kita dapat memilih palette yang sudah disediakan oleh seaborn (antara lain: bright, deep, pastel, dan masih banyak lagi) atau kita dapat mengatur sendiri palette yang ingin kita gunakan.

Untuk memilih palette yang akan digunakan untuk plot selanjutnya pada seaborn, kita dapat menggunakan sns.set_palette().

Jenis palette yang disediakan seaborn serta cara membuat color palette secara mandiri dapat dilihat pada:

https://seaborn.pydata.org/tutorial/color_palettes.html#tools-for-choosing-color-palettes

# Mengganti color palette menjadi "bright"
sns.set_palette('bright')
"""
Membuat line plot setelah color palette diubah menjadi "bright"
"""

# Mengatur besar figur yang ingin ditampilkan
plt.figure(figsize=(14,6))

# Membuat line plot
sns.lineplot(data=spotify_df)
# Membuat judul figur
plt.title("Daily Global Streams of Popular Songs in 2017-2018")
# Menampilkan plot
plt.show()

Apabila tidak semua fitur pada data ingin kita visualisasikan, kita dapat menggunakan sns.lineplot() beberapa kali, sesuai dengan banyaknya fitur yang ingin kita tampilkan, seperti pada code di bawah.

plt.figure(figsize=(14,6))

# Membuat line plot hanya dengan lagu Shape of You
sns.lineplot(data=spotify_df['Shape of You'], label="Shape of You")
# Menambahkan line plot pada figur dengan lagu Despacito
sns.lineplot(data=spotify_df['Despacito'], label="Despacito")

plt.title("Daily Global Streams of Popular Songs in 2017-2018")
plt.xlabel("Date")
plt.ylabel('')
plt.show()

Bar Plot

Bar plot biasa digunakan untuk membandingkan kuantitas/nilai pada data bertipe kategori.

Selanjutnya, kita akan menggunakan data flight_delays.csv, yaitu data rata-rata keterlambatan beberapa maskapai pesawat pada setiap bulannya.

flight_df
Month AA AS B6 DL EV F9 HA MQ NK OO UA US VX WN
0 January 6.955843 -0.320888 7.347281 -2.043847 8.537497 18.357238 3.512640 18.164974 11.398054 10.889894 6.352729 3.107457 1.420702 3.389466
1 February 7.530204 -0.782923 18.657673 5.614745 10.417236 27.424179 6.029967 21.301627 16.474466 9.588895 7.260662 7.114455 7.784410 3.501363
2 March 6.693587 -0.544731 10.741317 2.077965 6.730101 20.074855 3.468383 11.018418 10.039118 3.181693 4.892212 3.330787 5.348207 3.263341
3 April 4.931778 -3.009003 2.780105 0.083343 4.821253 12.640440 0.011022 5.131228 8.766224 3.223796 4.376092 2.660290 0.995507 2.996399
4 May 5.173878 -1.716398 -0.709019 0.149333 7.724290 13.007554 0.826426 5.466790 22.397347 4.141162 6.827695 0.681605 7.102021 5.680777
5 June 8.191017 -0.220621 5.047155 4.419594 13.952793 19.712951 0.882786 9.639323 35.561501 8.338477 16.932663 5.766296 5.779415 10.743462
6 July 3.870440 0.377408 5.841454 1.204862 6.926421 14.464543 2.001586 3.980289 14.352382 6.790333 10.262551 NaN 7.135773 10.504942
7 August 3.193907 2.503899 9.280950 0.653114 5.154422 9.175737 7.448029 1.896565 20.519018 5.606689 5.014041 NaN 5.106221 5.532108
8 September -1.432732 -1.813800 3.539154 -3.703377 0.851062 0.978460 3.696915 -2.167268 8.000101 1.530896 -1.794265 NaN 0.070998 -1.336260
9 October -0.580930 -2.993617 3.676787 -5.011516 2.303760 0.082127 0.467074 -3.735054 6.810736 1.750897 -2.456542 NaN 2.254278 -0.688851
10 November 0.772630 -1.916516 1.418299 -3.175414 4.415930 11.164527 -2.719894 0.220061 7.543881 4.925548 0.281064 NaN 0.116370 0.995684
11 December 4.149684 -1.846681 13.839290 2.504595 6.685176 9.346221 -1.706475 0.662486 12.733123 10.947612 7.012079 NaN 13.498720 6.720893

Untuk membuat bar plot pada seaborn dengan dataframe, kita dapat menggunakan sns.barplot() dengan tiga parameter yang wajib kita set, yaitu:

  • data: dataframe yang ingin kita visualisasikan

  • x: nama fitur pada dataframe yang ingin kita jadikan sumbu-x

  • y: nama fitur pada dataframe yang ingin kita jadikan sumbu-y

Pada kode di bawah, juga digunakan satu parameter opsional, yaitu palette yang merupakan cara lain untuk mengatur color palette yang ingin kita gunakan

"""
Membuat bar plot keterlambatan maskapai EV setiap 
bulannya menggunakan seaborn
"""

plt.figure(figsize=(14,6))

sns.barplot(data=flight_df, x='Month', y='EV',hue="Month",
            palette=sns.color_palette('deep', n_colors=12))
plt.ylabel('EV Flight Delays (minute)')
plt.title('Average EV Flight Delays per Month')
plt.show()

Berdasarkan hasil plot di atas, terlihat bahwa maskapai EV memiliki rata-rata keterlambatan terlama pada bulan Juni, serta tercepat pada bulan September.

Selanjutnya, mari kita coba lihat urutan rata-rata keterlambatan semua maskapai dalam satu tahun (maskapai mana yang memiliki rata-rata keterlambatan terlama, serta maskapai mana yang tercepat).

Hal pertama yang perlu kita lakukan adalah, jadikan fitur Month sebagai index dataframe.

# Set fitur "Month" menjadi index dataframe
flight_df = flight_df.set_index('Month')
flight_df.head(2)
AA AS B6 DL EV F9 HA MQ NK OO UA US VX WN
Month
January 6.955843 -0.320888 7.347281 -2.043847 8.537497 18.357238 3.512640 18.164974 11.398054 10.889894 6.352729 3.107457 1.420702 3.389466
February 7.530204 -0.782923 18.657673 5.614745 10.417236 27.424179 6.029967 21.301627 16.474466 9.588895 7.260662 7.114455 7.784410 3.501363

Selanjutnya, kita perlu hitung rata-rata keterlambatan tiap maskapai dalam satu tahun, yaitu hitung rata-rata tiap kolom pada dataframe menggunakan .mean() (Tambahan: apabila kita ingin menghitung rata-rata tiap barisnya, kita dapat menggunakan parameter axis=1 pada .mean()). .mean() akan menghasilkan pandas Series.

Lalu, agar mempermudah kita dalam melihat visualisasi bar plotnya, kita dapat menggunakan .sort_values().

# Simpan rata-rata keterlambatan semua maskapai dalam satu tahun pada variabel flight_mean_inyear
flight_mean_inyear = flight_df.mean()
# Urutkan flight_mean_inyear secara ascending
flight_mean_inyear = flight_mean_inyear.sort_values()

flight_mean_inyear
AS    -1.023656
DL     0.231116
HA     1.993205
US     3.776815
AA     4.120776
WN     4.275277
VX     4.717718
UA     5.413415
OO     5.909658
MQ     5.964953
EV     6.543328
B6     6.788370
F9    13.035736
NK    14.549663
dtype: float64

Terakhir, visualisasikan bar plot menggunakan cara seperti sebelumnya.

Kita dapat lihat pada code dibawah bahwa tidak digunakan parameter data, karena flight_mean_inyear merupakan pandas Series (bukan dataframe) sehingga lebih mudah jika kita langsung menggunakan parameter x dan y saja.

plt.subplots(figsize=(14,6))
sns.barplot(x=flight_mean_inyear.index, hue=flight_mean_inyear.index,
            y=flight_mean_inyear.values,
            palette=sns.color_palette('deep', n_colors=14))
plt.title('Average Delay per Flight in a Year')
plt.show()

Berdasarkan plot diatas, NK merupakan maskapai dengan rata-rata keterlambatan terlama dalam satu tahun, sedangkan AS adalah yang tercepat (AS bernilai negatif yang berarti rata-rata kedatangan pesawat lebih cepat dari yang dijadwalkan dalam satu tahun.

Heatmap

Heatmap biasa digunakan untuk mempermudah melihat pola pada data berdasarkan warna yang dihasilkan.

Pada seaborn, kita dapat menggunakan heatmap dengan sns.heatmap() seperti pada kode dibawah. Parameter annot berfungsi untuk menampilkan nilai data (jika True) atau tidak (jika False).

Bar sebelah kanan heatmap menunjukkan bahwa, semakin lama keterlambatan pesawat, maka warna yang dihasilkan semakin terang. Sebaliknya, semakin gelap warna yang dihasilkan berarti semakin cepat pesawat datang tersebut.

"""
Membuat heatmap menggunakan Seaborn
"""
plt.figure(figsize=(14,10))

sns.heatmap(data=flight_df, annot=True)
plt.title("Average Arrival Delay for Each Airline, by Month")
plt.xlabel("Airline")
plt.show()

"""
Membuat heatmap menggunakan Seaborn
"""
plt.figure(figsize=(14,10))

sns.heatmap(data=flight_df, annot=True)
plt.title("Average Arrival Delay for Each Airline, by Month")
plt.xlabel("Airline")
plt.show()

Berdasarkan heatmap di atas, kita dapat melihat dengan mudah pada bulan apa suatu maskapai sangat terlambat (contoh: maskapai NK pada bulan Juni).

Heatmap sangat sering digunakan untuk melihat korelasi antar fitur pada dataset agar kita dapat mengerti lebih jauh tentang fitur-fitur pada data, atau juga dapat dimanfaatkan untuk melakukan feature selection sebelum membuat sebuat model Machine Learning.

Untuk melakukan hal tersebut, kita perlu menghitung dahulu korelasi antar fitur menggunakan pandas .corr(), yaitu fungsi yang akan menghitung korelasi antar dua fitur menggunakan korelasi Pearson.

Notes: Metode korelasi dapat diubah dengan menggunakan parameter method pada .corr(), contoh: .corr(method='spearman'). Metode lainnya dapat dilihat pada:

https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.corr.html

# Hitung korelasi antar dua fitur pada flight_df
flight_corr = flight_df.corr()

flight_corr
AA AS B6 DL EV F9 HA MQ NK OO UA US VX WN
AA 1.000000 0.334980 0.429854 0.805229 0.896523 0.903986 0.220065 0.842701 0.573716 0.620477 0.809874 0.823713 0.425237 0.615664
AS 0.334980 1.000000 0.340359 0.394359 0.356608 0.336791 0.684979 0.283977 0.480863 0.350657 0.457414 0.489025 0.229571 0.519228
B6 0.429854 0.340359 1.000000 0.643313 0.342627 0.510718 0.467905 0.529724 0.032038 0.591115 0.233021 0.788345 0.579750 0.151750
DL 0.805229 0.394359 0.643313 1.000000 0.796951 0.783265 0.262251 0.598765 0.625277 0.569073 0.797339 0.821757 0.700605 0.691805
EV 0.896523 0.356608 0.342627 0.796951 1.000000 0.828515 0.099369 0.721468 0.784026 0.692697 0.911499 0.669736 0.462638 0.730115
F9 0.903986 0.336791 0.510718 0.783265 0.828515 1.000000 0.273878 0.912984 0.414064 0.582509 0.671986 0.878874 0.308397 0.465765
HA 0.220065 0.684979 0.467905 0.262251 0.099369 0.273878 1.000000 0.436015 0.176485 0.056941 0.066821 0.586160 -0.008439 -0.007296
MQ 0.842701 0.283977 0.529724 0.598765 0.721468 0.912984 0.436015 1.000000 0.281890 0.586963 0.503575 0.660181 0.150111 0.239744
NK 0.573716 0.480863 0.032038 0.625277 0.784026 0.414064 0.176485 0.281890 1.000000 0.365273 0.827455 0.293515 0.395419 0.742869
OO 0.620477 0.350657 0.591115 0.569073 0.692697 0.582509 0.056941 0.586963 0.365273 1.000000 0.626051 0.590313 0.561515 0.548304
UA 0.809874 0.457414 0.233021 0.797339 0.911499 0.671986 0.066821 0.503575 0.827455 0.626051 1.000000 0.477816 0.536968 0.926800
US 0.823713 0.489025 0.788345 0.821757 0.669736 0.878874 0.586160 0.660181 0.293515 0.590313 0.477816 1.000000 0.333396 0.242344
VX 0.425237 0.229571 0.579750 0.700605 0.462638 0.308397 -0.008439 0.150111 0.395419 0.561515 0.536968 0.333396 1.000000 0.630278
WN 0.615664 0.519228 0.151750 0.691805 0.730115 0.465765 -0.007296 0.239744 0.742869 0.548304 0.926800 0.242344 0.630278 1.000000

Pandas .corr() menghasilkan dataframe dengan nama baris dan kolom yang sama, serta berisi nilai korelasi antara baris dan kolom yang ditinjau (contoh: korelasi antara maskapai AA dan AS adalah 0,334980). Serta, dataframe yang dihasilkan adalah sebuat matriks simetris.

Tentu dengan hanya melihat dataframe di atas, tidak terlihat begitu jelas mana fitur yang memiliki korelasi tinggi dan mana yang yang memiliki korelasi rendah. Oleh karena itu, kita dapat memanfaatkan heatmap.

Pada code di bawah, untuk mempermudah pembacaan heatmap, kita menggunakan parameter vmin, vmax, dan center pada sns.heatmap(). vmin berfungsi untuk mengatur nilai terendah, vmax berfungsi untuk mengatur nilai tertinggi, dan center berfungsi untuk mengatur nilai tengah pada heatmap. Korelasi Pearson menghasilkan nilai antara -1 hingga 1, sehingga kita dapat set ketiga parameter tersebut seperti pada code di bawah.

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

sns.heatmap(data=flight_corr, vmin=-1, vmax=1, center=0, annot=True)
plt.title("Pearson Correlation of Each Airline Flight Delays")
plt.xlabel("Airline")
plt.show()

Dengan menggunakan heatmap, sekarang terlihat bahwa mana maskapai yang keterlambatannya berkorelasi tinggi dan mana yang rendah. Misal, AA dan EV menghasilkan korelasi yang cukup tinggi positif, yaitu 0.9, yang artinya jika keterlambatan maskapai AA tinggi, begitu juga maskapai EV, dan sebaliknya jika keterlambatan maskapai AA rendah, begitu juga maskapai EV.

Untuk meyakinkan kita dengan hal tersebut, kita dapat lihat pada materi selanjutnya, yaitu Scatter Plot.

Scatter Plot

Scatter plot biasa digunakan untuk melihat korelasi antara dua fitur bertipe numerik.

Untuk menggunakan scatter plot pada seaborn, kita dapat menggunakan sns.scatterplot(), dengan parameter yang sama seperti kita membuat bar plot.

"""
Membuat scatter plot untuk melihat 
keterkaitan pada keterlambatan pesawat
maskapai EV dan AA
"""

sns.scatterplot(data=flight_df, x='EV', y='AA')
plt.show()

Melalui scatter plot di atas, kita dapat semakin yakin bahwa kesimpulan yang kita ambil dengan melihat heatmap sebelumnya benar.

"""
Tambahan scatter plot pada maskapai lain yang
memiliki korelasi tinggi
"""

sns.scatterplot(data=flight_df, x='EV', y='UA')
plt.show()

"""
Scatter plot pada maskapai yang memiliki
korelasi rendah (mendekati 0)
"""

sns.scatterplot(data=flight_df, x='UA', y='HA')
plt.show()

Pada heatmap, terlihat bahwa maskapai UA dan HA memiliki korelasi yang rendah, yaitu 0.067. Sehingga, jika kita buat scatter plotnya, menghasilkan plot seperti di atas.

Sekarang kita akan menggunakan dataset lainnya, yaitu insurance.csv yang merupakan data berisi biaya asuransi (charges) beberapa orang.

insurance_df.head()
age sex bmi children smoker region charges
0 19 female 27.900 0 yes southwest 16884.92400
1 18 male 33.770 1 no southeast 1725.55230
2 28 male 33.000 3 no southeast 4449.46200
3 33 male 22.705 0 no northwest 21984.47061
4 32 male 28.880 0 no northwest 3866.85520

Misal, kita ingin melihat keterkaitan indeks massa tubuh (bmi) seseorang dengan biaya asuransi (charges) orang tersebut. Sama seperti sebelumnya, kita dapat melakukannya seperti pada code di bawah.

# Mengubah palette menjadi default
sns.set_palette('tab10')
# Membuat scatter plot antara fitur bmi dan charges
sns.scatterplot(data=insurance_df, x='bmi', y='charges')

plt.show()

Scatter plot di atas menunjukkan bahwa korelasi antara bmi dan charges adalah cenderung positif, tetapi tidak terlalu tinggi. Yang artinya, orang dengan BMI tinggi, cenderung akan membayar biaya asuransi lebih tinggi.

Agar kita semakin yakin dengan kesimpulan tersebut, kita dapat menambahakn garis regresi pada scatter plot tersebut dengan menggunakan sns.regplot().

sns.regplot(data=insurance_df, x='bmi', y='charges', line_kws=dict(color="r"))
plt.show()

Berdasarkan scatter plot dan garis regresi dihasilkan, terlihat bahwa kesimpulan yang kita ambil benar. Agar semakin yakin lagi, kita juga dapat menghitung langsung korelasi Pearsonnya menggunakan cara sebelumnya, yaitu pandas .corr().

insurance_df[['bmi', 'charges']].corr()
bmi charges
bmi 1.000000 0.198341
charges 0.198341 1.000000

Dengan menggunakan seaborn, kita juga dapat memvisualisasikan scatter plot berdasarkan dengan pewarnaan yang berbeda berdasarkan fitur lainnya yang bertipe kategorik.

Misal, kita ingin membuat scatter plot antara fitur bmi dan charges dengan pewarnaannya berdasarkan nilai dari fitur smoker, yaitu yes atau no. Kita dapat set parameter hue='smoker' pada sns.scatterplot() seperti pada code di bawah.

sns.scatterplot(data=insurance_df, x='bmi', y='charges', hue='smoker')
plt.show()

Sehingga dihasilkan pewarnaan yang berbeda untuk seseorang yang merupakan perokok (biru) dan yang tidak (orange). Berdasarkan scatter plot di atas, terlihat bahwa korelasi antara bmi dan charges untuk perokok cendering tinggi positif (semakin besar bmi, semakin besar juga charges). Sedangkan, untuk bukan perokok, korelasinya cenderung rendah (semakin besar bmi, tidak terlalu berpengaruh terhadap charges).

Seperti cara sebelumnya, kita dapat menambahkan garis regresi. Namun, karena kita disini menggunakan hue, terdapat dua cara untuk menambahkan garis regresi, yaitu yang pertama adalah menggunakan sns.regplot() seperti di bawah ini.

sns.regplot(data=insurance_df.query('smoker == "yes"'), x='bmi', y='charges', line_kws=dict(color="g")) # axes 1
sns.regplot(data=insurance_df.query('smoker == "no"'), x='bmi', y='charges', line_kws=dict(color="g")) # axes 2
plt.show()

Perhatikan bahwa sns.regplot() dipanggil dua kali karena fungsi tersebut tidak memiliki parameter hue.

Untuk mempermudah, kita dapat menggunakan cara kedua, yaitu menggunakan sns.lmplot(). Cara kerja sns.lmplot() yaitu menggabungkan dua (atau lebih) sns.regplot() dalam satu figur.

sns.lmplot(data=insurance_df, x='bmi', y='charges', hue='smoker', line_kws=dict(color="g"))
plt.show()

(Pengayaan) Plot lainnnya menggunakan Seaborn

Box Plot dan Swarm Plot

Box plot dan swarm plot biasa digunakan untuk melihat keterkaitan antara data kategorik dan data numerik. Swarm plot biasa disebut sebagai “categorical scatter plot”, karena plot yang dihasilkan mirip seperti scatter plot, tetapi untuk data kategorik.

Untuk menggunakan box plot pada seaborn kita dapat menggunakan sns.boxplot().

Untuk menggunakan swarm plot pada seaborn kita dapat menggunakan sns.swarmplot().

Misal, kita ingin melihat keterkaitan antara fitur smoker dan charges menggunakan swarm plot. Maka, kita dapat menggunakan code seperti di bawah ini.

plt.subplots(figsize=(10,6))

sns.swarmplot(data=insurance_df, x='smoker', y='charges', size=3)
plt.show()

Berdasarkan swarm plot di atas, terlihat bahwa perokok cenderung memiliki biaya asuransi yang lebih tinggi dibandingkan yang bukan perokok. Selain itu, semakin lebar “swarm” pada suatu kategori berarti semakin banyak seseorang dengan nilai charges tersebut.

Apabila kita ingin menggunakan box plot, maka dapat digunakan code seperti di bawah ini.

sns.boxplot(data=insurance_df, x='smoker', y='charges')
plt.show()

Pada box plot, terdapat dua istilah yang umum digunakan, yaitu “box” dan “whiskers”. Pada box plot di atas, “box” merupakan persegi panjang berwarna biru dan orange. Garis di tengah box merupakan nilai mediannya, serta garis bawah dan garis atas box merupakan kuartil bawah (Q1) dan kuartil atas (Q3) secara berurutan. “Whiskers” adalah garis yang merupakan perpanjangan dari box. Ujung dari whiskers atas adalah Q3 + (1.5 x IQR) data, sedangkan ujung whiskers bawah adalah Q1 - (1.5 x IQR) data.

Titik di luar box dan whiskers tersebut adalah titik yang biasa dijadikan sebagai outlier (penentuan outlier diserahkan ke diri masing-masing, apakah hanya dengan melihat box plot atau dengan menggunakan metode lain, tetapi untuk mempermudah dapat menggunakan box plot).

Histogram dan Density Plot

Selain box plot dan swarm plot, kita juga dapat melihat persebaran data menggunakan histogram dan density plot. Histogram biasa digunakan untuk melihat persebaran data secara diskrit, sedangkan density plot untuk melihat persebaran data secara kontinu.

Untuk membuat histogram pada seaborn, kita dapat menggunakan sns.histplot().

Untuk membuat density plot pada seaborn, kita dapat menggunakan sns.kdeplot().

Misal, kita ingin melihat persebaran dari fitur charges pada insurance_df. Maka dapat digunakan code seperti di bawah.

plt.subplots(figsize=(12,6))

sns.histplot(data=insurance_df, x='charges')
plt.show()

Berdasarkan histogram di atas, terlihat bahwa distribusi charges cenderung “skew” atau miring ke kanan. “Skewness” atau tingkat kecondongan merupakan aspek yang penting untuk diperhatikan ketika kita ingin membuat model Machine Learning.

Seperti scatter plot, kita juga dapat menentukan pewarnaan histogram berdasarkan fitur lainnya dengan menggunakan parameter hue seperti di bawah ini/

plt.subplots(figsize=(12,6))
sns.histplot(data=insurance_df, x='charges', hue='smoker')
plt.show()

Jika ingin membuat density plot dari fitur charges, kita dapat menggunakan kode seperti di bawah ini. Parameter shade berfungsi untuk memberikan warna di bawah kurva.

plt.subplots(figsize=(12,6))
sns.kdeplot(data=insurance_df, x='charges', fill=True)
plt.show()

sns.kdeplot() juga dapat menggunakan parameter hue.

plt.subplots(figsize=(12,6))
sns.kdeplot(data=insurance_df, x='charges',
            hue='smoker', fill=True)
plt.show()

Apabila kita ingin menggabungkan histogram dan density plot dalam satu figur, kita dapat menggunakan sns.histplot() dengan parameter kde=True.

plt.subplots(figsize=(12,6))
sns.histplot(data=insurance_df, x='charges', hue='smoker', kde=True)
plt.show()

Joint Plot

Pada seaborn, kita juga dapat membuat dua plot yang berbeda dari dua fitur dalam satu figur yang sama menggunakan sns.jointplot().

Jenis plot yang dihasilkan dapat diatur pada parameter kind. Pilihan jenis kind yang disediakan dapat dilihat pada:

https://seaborn.pydata.org/generated/seaborn.jointplot.html

sns.jointplot(data=insurance_df, x='charges', y='bmi', hue='smoker', kind="scatter")

plt.show()

sns.jointplot(data=insurance_df, x='charges', y='bmi', hue='smoker', kind="hist")

plt.show()

sns.jointplot(data=insurance_df, x='charges', y='bmi', hue='smoker', kind="kde")

plt.show()

Panduan Pemilihan Plot

image.png

source: https://www.kaggle.com/code/alexisbcook/choosing-plot-types-and-custom-styles

Encoding Data Kategorik

Banyak metode sains data / machine learning yang hanya bisa digunakan dengan data numerik. Oleh karena itu, data kategorik perlu diubah terlebih dahulu menjadi data numerik, melakukan yang namanya categorical data encoding

Ingat kembali, data kategorik adalah suatu tipe data yang merepresentasikan kelompok atau kategori. Data bertipe ini dapat diklasifikasikan menjadi dua jenis utama: nominal dan ordinal.

  1. Data nominal adalah tipe data kategorik di mana kategori tidak memiliki urutan atau hierarki yang bermakna. Setiap kategori hanya mewakili suatu label tanpa adanya perbedaan kuantitatif atau tingkatan di antara kategori tersebut.

    Ciri-ciri Data Nominal:

    • Tidak memiliki urutan atau skala tertentu.
    • Hanya sebagai label atau nama tanpa makna numerik.
    • Bisa berupa teks atau angka, tetapi angka hanya digunakan sebagai kode, bukan untuk perhitungan.
  2. Data ordinal adalah tipe data kategorik di mana kategori memiliki urutan atau hierarki yang jelas. Meskipun memiliki urutan, jarak antar kategori tidak selalu sama.

    Ciri-ciri Data Ordinal:

    • Memiliki urutan yang jelas (ada tingkatan).
    • Tidak bisa dihitung perbedaan antar tingkatan secara numerik.
    • Bisa diurutkan tetapi tidak bisa dioperasikan secara matematis (misalnya, tidak bisa dijumlahkan atau dikurangi).

Kedua jenis data kategorik ini memiliki cara encoding yang berbeda.

Dataset yang akan digunakan adalah “California Housing Prices” (housing.csv) yang bisa didownload dari salah satu sumber berikut:

df_housing = pd.read_csv("./housing.csv")

mari kita lihat isinya

df_housing
longitude latitude housing_median_age total_rooms total_bedrooms population households median_income median_house_value ocean_proximity
0 -122.23 37.88 41.0 880.0 129.0 322.0 126.0 8.3252 452600.0 NEAR BAY
1 -122.22 37.86 21.0 7099.0 1106.0 2401.0 1138.0 8.3014 358500.0 NEAR BAY
2 -122.24 37.85 52.0 1467.0 190.0 496.0 177.0 7.2574 352100.0 NEAR BAY
3 -122.25 37.85 52.0 1274.0 235.0 558.0 219.0 5.6431 341300.0 NEAR BAY
4 -122.25 37.85 52.0 1627.0 280.0 565.0 259.0 3.8462 342200.0 NEAR BAY
... ... ... ... ... ... ... ... ... ... ...
20635 -121.09 39.48 25.0 1665.0 374.0 845.0 330.0 1.5603 78100.0 INLAND
20636 -121.21 39.49 18.0 697.0 150.0 356.0 114.0 2.5568 77100.0 INLAND
20637 -121.22 39.43 17.0 2254.0 485.0 1007.0 433.0 1.7000 92300.0 INLAND
20638 -121.32 39.43 18.0 1860.0 409.0 741.0 349.0 1.8672 84700.0 INLAND
20639 -121.24 39.37 16.0 2785.0 616.0 1387.0 530.0 2.3886 89400.0 INLAND

20640 rows × 10 columns

df_housing.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20640 entries, 0 to 20639
Data columns (total 10 columns):
 #   Column              Non-Null Count  Dtype  
---  ------              --------------  -----  
 0   longitude           20640 non-null  float64
 1   latitude            20640 non-null  float64
 2   housing_median_age  20640 non-null  float64
 3   total_rooms         20640 non-null  float64
 4   total_bedrooms      20433 non-null  float64
 5   population          20640 non-null  float64
 6   households          20640 non-null  float64
 7   median_income       20640 non-null  float64
 8   median_house_value  20640 non-null  float64
 9   ocean_proximity     20640 non-null  object 
dtypes: float64(9), object(1)
memory usage: 1.6+ MB

Ada satu data kategorik, yaitu ocean_proximity. Mari kita liat jenis-jenisnya (kategorinya):

df_housing["ocean_proximity"].value_counts()
ocean_proximity
<1H OCEAN     9136
INLAND        6551
NEAR OCEAN    2658
NEAR BAY      2290
ISLAND           5
Name: count, dtype: int64

Perhatikan bahwa data kategorik tersebut berisi nilai-nilai yang tidak memiliki urutan atau skala, sehingga data kategorik tersebut merupakan data nominal

Selanjutnya cek, apakah ada missing value?

df_housing.isna().sum()
longitude               0
latitude                0
housing_median_age      0
total_rooms             0
total_bedrooms        207
population              0
households              0
median_income           0
median_house_value      0
ocean_proximity         0
dtype: int64
df_housing[df_housing["total_bedrooms"].isna()]
longitude latitude housing_median_age total_rooms total_bedrooms population households median_income median_house_value ocean_proximity
290 -122.16 37.77 47.0 1256.0 NaN 570.0 218.0 4.3750 161900.0 NEAR BAY
341 -122.17 37.75 38.0 992.0 NaN 732.0 259.0 1.6196 85100.0 NEAR BAY
538 -122.28 37.78 29.0 5154.0 NaN 3741.0 1273.0 2.5762 173400.0 NEAR BAY
563 -122.24 37.75 45.0 891.0 NaN 384.0 146.0 4.9489 247100.0 NEAR BAY
696 -122.10 37.69 41.0 746.0 NaN 387.0 161.0 3.9063 178400.0 NEAR BAY
... ... ... ... ... ... ... ... ... ... ...
20267 -119.19 34.20 18.0 3620.0 NaN 3171.0 779.0 3.3409 220500.0 NEAR OCEAN
20268 -119.18 34.19 19.0 2393.0 NaN 1938.0 762.0 1.6953 167400.0 NEAR OCEAN
20372 -118.88 34.17 15.0 4260.0 NaN 1701.0 669.0 5.1033 410700.0 <1H OCEAN
20460 -118.75 34.29 17.0 5512.0 NaN 2734.0 814.0 6.6073 258100.0 <1H OCEAN
20484 -118.72 34.28 17.0 3051.0 NaN 1705.0 495.0 5.7376 218600.0 <1H OCEAN

207 rows × 10 columns

Perhatikan bahwa tipe datanya adalah int64 atau bilangan bulat.

Dari 20640 baris, ada satu kolom/fitur (total_bedrooms) dengan 207 missing value.

Encoding Data Nominal dengan One-Hot Encoding

Metode yang sering digunakan untuk melakukan encoding data nominal adalah one hot encoding. Misalnya ada satu fitur kategorik dengan \(n\) kemungkinan data, bernama \(D_i\) untuk \(i = 1, 2, \dots, n\). Maka fitur tersebut diganti dengan \(n\) kolom baru, misal bernama \(K_i\) untuk \(i = 1, 2, \dots, n\), di mana pada kolom ke-i, isinya adalah

  • \(1\), apabila data aslinya pada baris tersebut adalah \(D_i\)
  • \(0\) apabila bukan \(D_i\)
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder()
hasil_onehot = encoder.fit_transform(df_housing[["ocean_proximity"]])
print(encoder.categories_)
[array(['<1H OCEAN', 'INLAND', 'ISLAND', 'NEAR BAY', 'NEAR OCEAN'],
      dtype=object)]
print(encoder.categories_[0])
['<1H OCEAN' 'INLAND' 'ISLAND' 'NEAR BAY' 'NEAR OCEAN']
kolom_encoding = list(encoder.categories_[0])
onehot_array = hasil_onehot.toarray()
print(onehot_array)
[[0. 0. 0. 1. 0.]
 [0. 0. 0. 1. 0.]
 [0. 0. 0. 1. 0.]
 ...
 [0. 1. 0. 0. 0.]
 [0. 1. 0. 0. 0.]
 [0. 1. 0. 0. 0.]]
onehot_df = pd.DataFrame(onehot_array, columns=kolom_encoding)
onehot_df
<1H OCEAN INLAND ISLAND NEAR BAY NEAR OCEAN
0 0.0 0.0 0.0 1.0 0.0
1 0.0 0.0 0.0 1.0 0.0
2 0.0 0.0 0.0 1.0 0.0
3 0.0 0.0 0.0 1.0 0.0
4 0.0 0.0 0.0 1.0 0.0
... ... ... ... ... ...
20635 0.0 1.0 0.0 0.0 0.0
20636 0.0 1.0 0.0 0.0 0.0
20637 0.0 1.0 0.0 0.0 0.0
20638 0.0 1.0 0.0 0.0 0.0
20639 0.0 1.0 0.0 0.0 0.0

20640 rows × 5 columns

Setelah dilakukan one-hot encoding, gabungkan dataframenya dengan dataframe awal

df_housing = pd.concat([df_housing, onehot_df], axis=1)
df_housing
longitude latitude housing_median_age total_rooms total_bedrooms population households median_income median_house_value ocean_proximity <1H OCEAN INLAND ISLAND NEAR BAY NEAR OCEAN
0 -122.23 37.88 41.0 880.0 129.0 322.0 126.0 8.3252 452600.0 NEAR BAY 0.0 0.0 0.0 1.0 0.0
1 -122.22 37.86 21.0 7099.0 1106.0 2401.0 1138.0 8.3014 358500.0 NEAR BAY 0.0 0.0 0.0 1.0 0.0
2 -122.24 37.85 52.0 1467.0 190.0 496.0 177.0 7.2574 352100.0 NEAR BAY 0.0 0.0 0.0 1.0 0.0
3 -122.25 37.85 52.0 1274.0 235.0 558.0 219.0 5.6431 341300.0 NEAR BAY 0.0 0.0 0.0 1.0 0.0
4 -122.25 37.85 52.0 1627.0 280.0 565.0 259.0 3.8462 342200.0 NEAR BAY 0.0 0.0 0.0 1.0 0.0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
20635 -121.09 39.48 25.0 1665.0 374.0 845.0 330.0 1.5603 78100.0 INLAND 0.0 1.0 0.0 0.0 0.0
20636 -121.21 39.49 18.0 697.0 150.0 356.0 114.0 2.5568 77100.0 INLAND 0.0 1.0 0.0 0.0 0.0
20637 -121.22 39.43 17.0 2254.0 485.0 1007.0 433.0 1.7000 92300.0 INLAND 0.0 1.0 0.0 0.0 0.0
20638 -121.32 39.43 18.0 1860.0 409.0 741.0 349.0 1.8672 84700.0 INLAND 0.0 1.0 0.0 0.0 0.0
20639 -121.24 39.37 16.0 2785.0 616.0 1387.0 530.0 2.3886 89400.0 INLAND 0.0 1.0 0.0 0.0 0.0

20640 rows × 15 columns

df_housing = df_housing.drop(["ocean_proximity"], axis=1)
df_housing
longitude latitude housing_median_age total_rooms total_bedrooms population households median_income median_house_value <1H OCEAN INLAND ISLAND NEAR BAY NEAR OCEAN
0 -122.23 37.88 41.0 880.0 129.0 322.0 126.0 8.3252 452600.0 0.0 0.0 0.0 1.0 0.0
1 -122.22 37.86 21.0 7099.0 1106.0 2401.0 1138.0 8.3014 358500.0 0.0 0.0 0.0 1.0 0.0
2 -122.24 37.85 52.0 1467.0 190.0 496.0 177.0 7.2574 352100.0 0.0 0.0 0.0 1.0 0.0
3 -122.25 37.85 52.0 1274.0 235.0 558.0 219.0 5.6431 341300.0 0.0 0.0 0.0 1.0 0.0
4 -122.25 37.85 52.0 1627.0 280.0 565.0 259.0 3.8462 342200.0 0.0 0.0 0.0 1.0 0.0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
20635 -121.09 39.48 25.0 1665.0 374.0 845.0 330.0 1.5603 78100.0 0.0 1.0 0.0 0.0 0.0
20636 -121.21 39.49 18.0 697.0 150.0 356.0 114.0 2.5568 77100.0 0.0 1.0 0.0 0.0 0.0
20637 -121.22 39.43 17.0 2254.0 485.0 1007.0 433.0 1.7000 92300.0 0.0 1.0 0.0 0.0 0.0
20638 -121.32 39.43 18.0 1860.0 409.0 741.0 349.0 1.8672 84700.0 0.0 1.0 0.0 0.0 0.0
20639 -121.24 39.37 16.0 2785.0 616.0 1387.0 530.0 2.3886 89400.0 0.0 1.0 0.0 0.0 0.0

20640 rows × 14 columns

Encoding Data Ordinal dengan Label Encoding & Ordinal Encoding

Untuk melakukan encoding data ordinal, terdapat dua metode, yaitu menggunakan label encoding dan ordinal encoding. Secara matematis, keduanya cukup serupa. Misalkan ada satu fitur kategorik \(D\) dengan \(n\) data, atau dapat ditulis sebagai himpunan $C={c_1, c_2, …, c_n} $. Kemudian misalkan \(D=\{d_1, d_2, ..., d_k\}\) subhimpunan dari \(C\) yang hanya berisi data unik. Label encoding dan ordinal encoding akan memetakan setiap elemen di \(C\) ke bilangan asli jika label data ke-\(i\) di \(C\) sama dengan label data ke-\(i\) di \(D\), atau didefinisikan fungsi \(f:D\rightarrow \mathbb{N}\) dengan

\[ f(c_i)=f(d_i)=i, \hspace{5mm}\text{ jika } c_i=d_i, i=0,1,...,n \]

Namun perbedaan dari keduanya adalah:

  • label Encoding: tidak memerhatikan urutan tiap label. Sehingga sebenarnya data nominal bisa saya dilakukan label encoding.
  • Ordinal Encoding: memerhatikan urutan tiap label. yaitu jika \(c_i<c_j\) maka \(f(c_i)<f(c_j)\).
from sklearn.preprocessing import LabelEncoder, OrdinalEncoder

Disini kita akan menggunakan dataset flight delay sebelumnya

flight_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 12 entries, 0 to 11
Data columns (total 15 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   Month   12 non-null     object 
 1   AA      12 non-null     float64
 2   AS      12 non-null     float64
 3   B6      12 non-null     float64
 4   DL      12 non-null     float64
 5   EV      12 non-null     float64
 6   F9      12 non-null     float64
 7   HA      12 non-null     float64
 8   MQ      12 non-null     float64
 9   NK      12 non-null     float64
 10  OO      12 non-null     float64
 11  UA      12 non-null     float64
 12  US      6 non-null      float64
 13  VX      12 non-null     float64
 14  WN      12 non-null     float64
dtypes: float64(14), object(1)
memory usage: 1.5+ KB

Perhatikan bahwa fitur Month merupakan fitur yang kategorik. Bulan bisa kita anggap memiliki keterurutan atau tidak tergantung konteks datanya. Disini kita coba saja lakukan encoding untuk mempraktikan cara menggunakan encoder

urutan_bulan = [list(flight_df["Month"])]
print(urutan_bulan)
[['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']]
label_encoder = LabelEncoder()
ordinal_encoder = OrdinalEncoder(categories=urutan_bulan)
flight_df["Month_Label"] = label_encoder.fit_transform(flight_df["Month"])
flight_df["Month_ordinal"] = ordinal_encoder.fit_transform(flight_df[["Month"]])
flight_df
Month AA AS B6 DL EV F9 HA MQ NK OO UA US VX WN Month_Label Month_ordinal
0 January 6.955843 -0.320888 7.347281 -2.043847 8.537497 18.357238 3.512640 18.164974 11.398054 10.889894 6.352729 3.107457 1.420702 3.389466 4 0.0
1 February 7.530204 -0.782923 18.657673 5.614745 10.417236 27.424179 6.029967 21.301627 16.474466 9.588895 7.260662 7.114455 7.784410 3.501363 3 1.0
2 March 6.693587 -0.544731 10.741317 2.077965 6.730101 20.074855 3.468383 11.018418 10.039118 3.181693 4.892212 3.330787 5.348207 3.263341 7 2.0
3 April 4.931778 -3.009003 2.780105 0.083343 4.821253 12.640440 0.011022 5.131228 8.766224 3.223796 4.376092 2.660290 0.995507 2.996399 0 3.0
4 May 5.173878 -1.716398 -0.709019 0.149333 7.724290 13.007554 0.826426 5.466790 22.397347 4.141162 6.827695 0.681605 7.102021 5.680777 8 4.0
5 June 8.191017 -0.220621 5.047155 4.419594 13.952793 19.712951 0.882786 9.639323 35.561501 8.338477 16.932663 5.766296 5.779415 10.743462 6 5.0
6 July 3.870440 0.377408 5.841454 1.204862 6.926421 14.464543 2.001586 3.980289 14.352382 6.790333 10.262551 NaN 7.135773 10.504942 5 6.0
7 August 3.193907 2.503899 9.280950 0.653114 5.154422 9.175737 7.448029 1.896565 20.519018 5.606689 5.014041 NaN 5.106221 5.532108 1 7.0
8 September -1.432732 -1.813800 3.539154 -3.703377 0.851062 0.978460 3.696915 -2.167268 8.000101 1.530896 -1.794265 NaN 0.070998 -1.336260 11 8.0
9 October -0.580930 -2.993617 3.676787 -5.011516 2.303760 0.082127 0.467074 -3.735054 6.810736 1.750897 -2.456542 NaN 2.254278 -0.688851 10 9.0
10 November 0.772630 -1.916516 1.418299 -3.175414 4.415930 11.164527 -2.719894 0.220061 7.543881 4.925548 0.281064 NaN 0.116370 0.995684 9 10.0
11 December 4.149684 -1.846681 13.839290 2.504595 6.685176 9.346221 -1.706475 0.662486 12.733123 10.947612 7.012079 NaN 13.498720 6.720893 2 11.0

Dapat dilihat hasil dari label encoding dan ordinal encoding berbeda. Pada label encoding, kita tidak bisa mengatur urutan secara manual. Urutan angka hasil label encoding ditentukan berdasarkan urutan alphabet dari nilai pada fitur yang di encoding. Contohnya bulan “April” dipetakan ke nilai 0, “August” dipetakan ke nilai 1, dsb. Sedangkan pada ordinal encoding, kita bisa menentukan urutannya terlebih dahulu menggunakan list, namun perlu diperhatikan bahwa hasil petanya dimulai dari nol, sehingga bulan “January” = 0, “February” = 1, dst.

Bisa dibilang kedua metode tersebut tidak cocok untuk melakukan encoding pada kolom bulan yang indeksnya dimulai dari satu, namun jika data yang dimiliki tidak peduli indeksnya (hanya perlu diubah ke bentuk numerik berdasarkan urutan), metode ordinal encoding sudah sangat cocok untuk digunakan.

Jika kita ingin melakukan pelabelan secara manual bisa dilakukan dengan menggunakan method .map()

month_mapping = {
    "January": 1, "February": 2, "March": 3, "April": 4, "May": 5, "June": 6,
    "July": 7, "August": 8, "September": 9, "October": 10, "November": 11, "December": 12
}

flight_df["Month_Map"] = flight_df["Month"].map(month_mapping)
flight_df
Month AA AS B6 DL EV F9 HA MQ NK OO UA US VX WN Month_Label Month_ordinal Month_Map
0 January 6.955843 -0.320888 7.347281 -2.043847 8.537497 18.357238 3.512640 18.164974 11.398054 10.889894 6.352729 3.107457 1.420702 3.389466 4 0.0 1
1 February 7.530204 -0.782923 18.657673 5.614745 10.417236 27.424179 6.029967 21.301627 16.474466 9.588895 7.260662 7.114455 7.784410 3.501363 3 1.0 2
2 March 6.693587 -0.544731 10.741317 2.077965 6.730101 20.074855 3.468383 11.018418 10.039118 3.181693 4.892212 3.330787 5.348207 3.263341 7 2.0 3
3 April 4.931778 -3.009003 2.780105 0.083343 4.821253 12.640440 0.011022 5.131228 8.766224 3.223796 4.376092 2.660290 0.995507 2.996399 0 3.0 4
4 May 5.173878 -1.716398 -0.709019 0.149333 7.724290 13.007554 0.826426 5.466790 22.397347 4.141162 6.827695 0.681605 7.102021 5.680777 8 4.0 5
5 June 8.191017 -0.220621 5.047155 4.419594 13.952793 19.712951 0.882786 9.639323 35.561501 8.338477 16.932663 5.766296 5.779415 10.743462 6 5.0 6
6 July 3.870440 0.377408 5.841454 1.204862 6.926421 14.464543 2.001586 3.980289 14.352382 6.790333 10.262551 NaN 7.135773 10.504942 5 6.0 7
7 August 3.193907 2.503899 9.280950 0.653114 5.154422 9.175737 7.448029 1.896565 20.519018 5.606689 5.014041 NaN 5.106221 5.532108 1 7.0 8
8 September -1.432732 -1.813800 3.539154 -3.703377 0.851062 0.978460 3.696915 -2.167268 8.000101 1.530896 -1.794265 NaN 0.070998 -1.336260 11 8.0 9
9 October -0.580930 -2.993617 3.676787 -5.011516 2.303760 0.082127 0.467074 -3.735054 6.810736 1.750897 -2.456542 NaN 2.254278 -0.688851 10 9.0 10
10 November 0.772630 -1.916516 1.418299 -3.175414 4.415930 11.164527 -2.719894 0.220061 7.543881 4.925548 0.281064 NaN 0.116370 0.995684 9 10.0 11
11 December 4.149684 -1.846681 13.839290 2.504595 6.685176 9.346221 -1.706475 0.662486 12.733123 10.947612 7.012079 NaN 13.498720 6.720893 2 11.0 12

Mengatasi Missing Value dengan Metode Imputasi

Pada dataset df_housing, banyaknya missing value relatif sedikit, sebenarnya tidak masalah apabila baris-baris tersebut cukup dihapus saja. Namun, kita akan gunakan untuk mempelajari metode imputasi.

Median

df_fill_median = df_housing.copy()
df_housing["total_bedrooms"].median()
435.0
bedrooms_median = df_housing["total_bedrooms"].median()
print(bedrooms_median)
435.0
df_fill_median["total_bedrooms"] = df_fill_median["total_bedrooms"].fillna(bedrooms_median)
df_fill_median.isna().sum()
longitude             0
latitude              0
housing_median_age    0
total_rooms           0
total_bedrooms        0
population            0
households            0
median_income         0
median_house_value    0
<1H OCEAN             0
INLAND                0
ISLAND                0
NEAR BAY              0
NEAR OCEAN            0
dtype: int64

Cara lain, menggunakan scikit-learn:

df_fill_median2 = df_housing.copy()
from sklearn.impute import SimpleImputer
median_imputer = SimpleImputer(
    missing_values=np.nan, strategy='median'
)
df_fill_median2[["total_bedrooms"]] = median_imputer.fit_transform(
    df_fill_median2[["total_bedrooms"]]
)
df_fill_median2.isna().sum()
longitude             0
latitude              0
housing_median_age    0
total_rooms           0
total_bedrooms        0
population            0
households            0
median_income         0
median_house_value    0
<1H OCEAN             0
INLAND                0
ISLAND                0
NEAR BAY              0
NEAR OCEAN            0
dtype: int64

Modus

df_fill_mode = df_housing.copy()
df_housing["total_bedrooms"].mode()
0    280.0
Name: total_bedrooms, dtype: float64
df_housing["total_bedrooms"].mode()[0]
280.0
bedrooms_mode = df_housing["total_bedrooms"].mode()[0]
print(bedrooms_mode)
280.0
df_fill_mode["total_bedrooms"] = df_fill_mode["total_bedrooms"].fillna(bedrooms_mode)
df_fill_mode.isna().sum()
longitude             0
latitude              0
housing_median_age    0
total_rooms           0
total_bedrooms        0
population            0
households            0
median_income         0
median_house_value    0
<1H OCEAN             0
INLAND                0
ISLAND                0
NEAR BAY              0
NEAR OCEAN            0
dtype: int64

Cara lain, menggunakan scikit-learn:

df_fill_mode2 = df_housing.copy()
mode_imputer = SimpleImputer(
    missing_values=np.nan, strategy='most_frequent'
)
df_fill_mode2[["total_bedrooms"]] = mode_imputer.fit_transform(
    df_fill_mode2[["total_bedrooms"]]
)
df_fill_mode2.isna().sum()
longitude             0
latitude              0
housing_median_age    0
total_rooms           0
total_bedrooms        0
population            0
households            0
median_income         0
median_house_value    0
<1H OCEAN             0
INLAND                0
ISLAND                0
NEAR BAY              0
NEAR OCEAN            0
dtype: int64

Mean

df_fill_mean = df_housing.copy()
df_fill_mean["total_bedrooms"].mean()
537.8705525375618
np.round(df_fill_mean["total_bedrooms"].mean())
538.0
bedrooms_mean = np.round(df_fill_mean["total_bedrooms"].mean())
print(bedrooms_mean)
538.0
df_fill_mean["total_bedrooms"] = df_fill_mean["total_bedrooms"].fillna(bedrooms_mean)
df_fill_mean.isna().sum()
longitude             0
latitude              0
housing_median_age    0
total_rooms           0
total_bedrooms        0
population            0
households            0
median_income         0
median_house_value    0
<1H OCEAN             0
INLAND                0
ISLAND                0
NEAR BAY              0
NEAR OCEAN            0
dtype: int64

Cara lain, menggunakan scikit-learn:

df_fill_mean2 = df_housing.copy()
mean_imputer = SimpleImputer(
    missing_values=np.nan, strategy='mean'
)
df_fill_mean2[["total_bedrooms"]] = mean_imputer.fit_transform(
    df_fill_mean2[["total_bedrooms"]]
)
df_fill_mean2.isna().sum()
longitude             0
latitude              0
housing_median_age    0
total_rooms           0
total_bedrooms        0
population            0
households            0
median_income         0
median_house_value    0
<1H OCEAN             0
INLAND                0
ISLAND                0
NEAR BAY              0
NEAR OCEAN            0
dtype: int64

KNN Imputer

from sklearn.impute import KNNImputer
knn_imputer = KNNImputer(n_neighbors=3)
df_fill_knn = df_housing.copy()

KNN Imputer memerlukan kolom-kolom lainnya sebagai acuan, dan hanya bisa bekerja dengan data numerik. Sehingga, kita perlu mem-filter terlebih dahulu kolom-kolom numerik dari dataset kita.

df_fill_knn.select_dtypes(include='number')
longitude latitude housing_median_age total_rooms total_bedrooms population households median_income median_house_value <1H OCEAN INLAND ISLAND NEAR BAY NEAR OCEAN
0 -122.23 37.88 41.0 880.0 129.0 322.0 126.0 8.3252 452600.0 0.0 0.0 0.0 1.0 0.0
1 -122.22 37.86 21.0 7099.0 1106.0 2401.0 1138.0 8.3014 358500.0 0.0 0.0 0.0 1.0 0.0
2 -122.24 37.85 52.0 1467.0 190.0 496.0 177.0 7.2574 352100.0 0.0 0.0 0.0 1.0 0.0
3 -122.25 37.85 52.0 1274.0 235.0 558.0 219.0 5.6431 341300.0 0.0 0.0 0.0 1.0 0.0
4 -122.25 37.85 52.0 1627.0 280.0 565.0 259.0 3.8462 342200.0 0.0 0.0 0.0 1.0 0.0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
20635 -121.09 39.48 25.0 1665.0 374.0 845.0 330.0 1.5603 78100.0 0.0 1.0 0.0 0.0 0.0
20636 -121.21 39.49 18.0 697.0 150.0 356.0 114.0 2.5568 77100.0 0.0 1.0 0.0 0.0 0.0
20637 -121.22 39.43 17.0 2254.0 485.0 1007.0 433.0 1.7000 92300.0 0.0 1.0 0.0 0.0 0.0
20638 -121.32 39.43 18.0 1860.0 409.0 741.0 349.0 1.8672 84700.0 0.0 1.0 0.0 0.0 0.0
20639 -121.24 39.37 16.0 2785.0 616.0 1387.0 530.0 2.3886 89400.0 0.0 1.0 0.0 0.0 0.0

20640 rows × 14 columns

num_col = df_fill_knn.select_dtypes(include='number').columns
print(num_col)
Index(['longitude', 'latitude', 'housing_median_age', 'total_rooms',
       'total_bedrooms', 'population', 'households', 'median_income',
       'median_house_value', '<1H OCEAN', 'INLAND', 'ISLAND', 'NEAR BAY',
       'NEAR OCEAN'],
      dtype='object')
df_fill_knn[num_col] = knn_imputer.fit_transform(df_fill_knn[num_col])
df_fill_knn.isna().sum()
longitude             0
latitude              0
housing_median_age    0
total_rooms           0
total_bedrooms        0
population            0
households            0
median_income         0
median_house_value    0
<1H OCEAN             0
INLAND                0
ISLAND                0
NEAR BAY              0
NEAR OCEAN            0
dtype: int64

(Pengayaan) Perbandingan Metode Imputasi

Kita bisa membandingkan beberapa metode imputasi (dan memilih yang mana yang terbaik) dengan langkah-langkah berikut.

  1. Gunakan sample dari dataset awal yang tidak mengandung missing data (Sample data ini akan menjadi ground truth).
  2. Perkenalkan proporsi data hilang yang meningkat secara acak (contoh 5–50 % dengan kenaikan data hilang 5%).
  3. Lakukan metode imputasi dengan metode-metode yang ingin di bandingkan.
  4. Hitung jumlahan error kuadrat antara data yang dilakukan imputasi dan data original, untuk setiap metode, dan setiap proporsi data yang hilang.

Langkah pertama, kita perlu memperoleh sample dari dataset kita yang tidak mengandung missing value, yang bisa disebut ground truth. Cara termudah adalah dengan menghapus baris-baris yang memiliki missing value (biasanya dipilih lagi sample hanya sebagian baris, tapi di sini tidak kita lakukan):

df_ground_truth = df_housing.dropna()
df_ground_truth.info()
<class 'pandas.core.frame.DataFrame'>
Index: 20433 entries, 0 to 20639
Data columns (total 14 columns):
 #   Column              Non-Null Count  Dtype  
---  ------              --------------  -----  
 0   longitude           20433 non-null  float64
 1   latitude            20433 non-null  float64
 2   housing_median_age  20433 non-null  float64
 3   total_rooms         20433 non-null  float64
 4   total_bedrooms      20433 non-null  float64
 5   population          20433 non-null  float64
 6   households          20433 non-null  float64
 7   median_income       20433 non-null  float64
 8   median_house_value  20433 non-null  float64
 9   <1H OCEAN           20433 non-null  float64
 10  INLAND              20433 non-null  float64
 11  ISLAND              20433 non-null  float64
 12  NEAR BAY            20433 non-null  float64
 13  NEAR OCEAN          20433 non-null  float64
dtypes: float64(14)
memory usage: 2.3 MB

Selanjutnya, kita perlu membolong-bolongi dataset ini, agar sekian persen diisi missing value.

import random
def missing_value_generator(df_original, percentage):
    df_miss = df_original.copy()
    baris, kolom = df_miss.shape
    n_total = baris*kolom

    permutasi = list(range(n_total))
    random.shuffle(permutasi)

    n_pilih = int(percentage * n_total)
    pilihan = permutasi[0 : n_pilih]

    for p in pilihan:
        df_miss.iloc[ int(p/kolom), p%kolom ] = np.nan
    
    return df_miss
df_miss_5 = missing_value_generator(df_housing, 0.05)
df_miss_5.isna().sum()
longitude             1112
latitude              1032
housing_median_age    1051
total_rooms           1005
total_bedrooms        1170
population            1000
households            1026
median_income         1052
median_house_value    1021
<1H OCEAN             1041
INLAND                1066
ISLAND                1036
NEAR BAY              1020
NEAR OCEAN            1018
dtype: int64
def compare_imputation(df_ground_truth, methods, percentages):
    list_missing_df = []
    for percent in percentages:
        df_miss = missing_value_generator(df_ground_truth, percent)
        list_missing_df.append(df_miss)

    all_results = []
    for method in methods:
        method_results = []
        for df_miss in list_missing_df:
            df_imputed = method.fit_transform(df_miss)
            SSE = ((df_ground_truth - df_imputed)**2).sum().sum()
            method_results.append(SSE)
        all_results.append(method_results)

    return all_results
median_imputer = SimpleImputer(missing_values=np.nan, strategy='median')
mean_imputer = SimpleImputer(missing_values=np.nan, strategy='mean')
mode_imputer = SimpleImputer(missing_values=np.nan, strategy="most_frequent")
list_persen = [0.05, 0.10, 0.15, 0.20, 0.25]
all_results = compare_imputation(
    df_ground_truth,
    [median_imputer, mean_imputer, mode_imputer],
    list_persen
)
all_results
[[13472556117040.742,
  29200125925218.32,
  44888219322432.08,
  58760057941823.1,
  71218852004932.02],
 [12893394505105.559,
  27585393139970.81,
  42959166785142.76,
  55771678195422.18,
  67446611118587.484],
 [98136114329477.44,
  203833066224326.94,
  320455690937897.0,
  406196187048687.2,
  505507295952070.3]]
print("Median:", all_results[0])
print("Mean:", all_results[1])
print("Mode:", all_results[2])
Median: [13472556117040.742, 29200125925218.32, 44888219322432.08, 58760057941823.1, 71218852004932.02]
Mean: [12893394505105.559, 27585393139970.81, 42959166785142.76, 55771678195422.18, 67446611118587.484]
Mode: [98136114329477.44, 203833066224326.94, 320455690937897.0, 406196187048687.2, 505507295952070.3]
plt.plot(list_persen, all_results[0])
plt.plot(list_persen, all_results[1])
plt.plot(list_persen, all_results[2])
plt.legend(["Median", "Mean", "Mode"])
plt.show()

Dari hasil tersebut, didapat bahwa secara keseluruhan, untuk setiap persentase missing values, metode imputasi dengan menggunakan mean menghasilkan SSE yang terkecil dibandingkan imputasi dengan median dan modus. Oleh karena itu, kita akan menggunakan metode imputasi menggunakan mean untuk mengisi missing value pada kolom “total_bedrooms” dari df asli.

# melihat kembali df awal
df_housing
longitude latitude housing_median_age total_rooms total_bedrooms population households median_income median_house_value <1H OCEAN INLAND ISLAND NEAR BAY NEAR OCEAN
0 -122.23 37.88 41.0 880.0 129.0 322.0 126.0 8.3252 452600.0 0.0 0.0 0.0 1.0 0.0
1 -122.22 37.86 21.0 7099.0 1106.0 2401.0 1138.0 8.3014 358500.0 0.0 0.0 0.0 1.0 0.0
2 -122.24 37.85 52.0 1467.0 190.0 496.0 177.0 7.2574 352100.0 0.0 0.0 0.0 1.0 0.0
3 -122.25 37.85 52.0 1274.0 235.0 558.0 219.0 5.6431 341300.0 0.0 0.0 0.0 1.0 0.0
4 -122.25 37.85 52.0 1627.0 280.0 565.0 259.0 3.8462 342200.0 0.0 0.0 0.0 1.0 0.0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
20635 -121.09 39.48 25.0 1665.0 374.0 845.0 330.0 1.5603 78100.0 0.0 1.0 0.0 0.0 0.0
20636 -121.21 39.49 18.0 697.0 150.0 356.0 114.0 2.5568 77100.0 0.0 1.0 0.0 0.0 0.0
20637 -121.22 39.43 17.0 2254.0 485.0 1007.0 433.0 1.7000 92300.0 0.0 1.0 0.0 0.0 0.0
20638 -121.32 39.43 18.0 1860.0 409.0 741.0 349.0 1.8672 84700.0 0.0 1.0 0.0 0.0 0.0
20639 -121.24 39.37 16.0 2785.0 616.0 1387.0 530.0 2.3886 89400.0 0.0 1.0 0.0 0.0 0.0

20640 rows × 14 columns

df_housing.isna().sum()
longitude               0
latitude                0
housing_median_age      0
total_rooms             0
total_bedrooms        207
population              0
households              0
median_income           0
median_house_value      0
<1H OCEAN               0
INLAND                  0
ISLAND                  0
NEAR BAY                0
NEAR OCEAN              0
dtype: int64
# lakukan imputasi dengan metode terbaik yg telah didapat, yaitu dengan mean

df_housing[['total_bedrooms']] = mean_imputer.fit_transform(df_housing[['total_bedrooms']] )
df_housing
longitude latitude housing_median_age total_rooms total_bedrooms population households median_income median_house_value <1H OCEAN INLAND ISLAND NEAR BAY NEAR OCEAN
0 -122.23 37.88 41.0 880.0 129.0 322.0 126.0 8.3252 452600.0 0.0 0.0 0.0 1.0 0.0
1 -122.22 37.86 21.0 7099.0 1106.0 2401.0 1138.0 8.3014 358500.0 0.0 0.0 0.0 1.0 0.0
2 -122.24 37.85 52.0 1467.0 190.0 496.0 177.0 7.2574 352100.0 0.0 0.0 0.0 1.0 0.0
3 -122.25 37.85 52.0 1274.0 235.0 558.0 219.0 5.6431 341300.0 0.0 0.0 0.0 1.0 0.0
4 -122.25 37.85 52.0 1627.0 280.0 565.0 259.0 3.8462 342200.0 0.0 0.0 0.0 1.0 0.0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
20635 -121.09 39.48 25.0 1665.0 374.0 845.0 330.0 1.5603 78100.0 0.0 1.0 0.0 0.0 0.0
20636 -121.21 39.49 18.0 697.0 150.0 356.0 114.0 2.5568 77100.0 0.0 1.0 0.0 0.0 0.0
20637 -121.22 39.43 17.0 2254.0 485.0 1007.0 433.0 1.7000 92300.0 0.0 1.0 0.0 0.0 0.0
20638 -121.32 39.43 18.0 1860.0 409.0 741.0 349.0 1.8672 84700.0 0.0 1.0 0.0 0.0 0.0
20639 -121.24 39.37 16.0 2785.0 616.0 1387.0 530.0 2.3886 89400.0 0.0 1.0 0.0 0.0 0.0

20640 rows × 14 columns

df_housing.isna().sum()
longitude             0
latitude              0
housing_median_age    0
total_rooms           0
total_bedrooms        0
population            0
households            0
median_income         0
median_house_value    0
<1H OCEAN             0
INLAND                0
ISLAND                0
NEAR BAY              0
NEAR OCEAN            0
dtype: int64

Export Dataframe yang telah diimputasi ke CSV

df_housing.to_csv("housing_modified.csv")