Skip to main content

Persiapan Data: Missing Values dengan Pandas

Dalam dataset dunia nyata, tidak semua data selalu tersedia dengan lengkap.

Kita mungkin menemukan data seperti:

Make Colour Doors Odometer Price
Toyota Blue 4 150000 5000
Honda NaN 4 120000 6000
BMW Black NaN 80000 12000
NaN Red 4 NaN 7000

Nilai yang tidak tersedia tersebut disebut missing values.

Dalam Pandas, missing values biasanya direpresentasikan sebagai:

NaN

atau bentuk missing lainnya tergantung tipe data.

Sebelum data digunakan untuk Machine Learning, missing values perlu ditangani dengan strategi yang sesuai.

Apa Itu Missing Values?

Missing value adalah kondisi ketika suatu data pada sebuah feature atau target tidak memiliki nilai.

Contoh:

MakeColourDoorsOdometer (KM)Price
ToyotaBlue41500005000
HondaNaN41200006000
BMWBlackNaN8000012000
NaNRed4NaN7000

Pada contoh tersebut terdapat missing values pada:

Colour
Doors
Make
Odometer (KM)

Mengapa Missing Values Harus Ditangani?

Banyak algoritma Machine Learning tidak dapat langsung memproses nilai NaN.

Misalnya kita memiliki:

X = car_sales_missing.drop("Price", axis=1)

Kemudian langsung menjalankan:

model.fit(X, y)

Model tertentu dapat menghasilkan error karena masih terdapat nilai yang hilang.

Selain masalah teknis, missing values juga dapat memengaruhi kualitas model.

Karena itu, sebelum modeling kita perlu menentukan strategi untuk menangani data tersebut.

Dua Pendekatan Utama

Secara umum terdapat dua pendekatan utama:

Missing Values

├───────────────┐
│ │
▼ ▼
Imputation Removal
│ │
▼ ▼
Mengisi nilai Menghapus data
yang hilang yang hilang

Keduanya memiliki kelebihan dan kekurangan.

Pendekatan 1 - Imputation

Imputation adalah proses mengisi missing values menggunakan nilai pengganti.

Contohnya:

NaN

Mean

atau:

NaN

Median

atau:

NaN

Missing

Nilai pengganti dipilih berdasarkan jenis feature dan karakteristik dataset.

Kelebihan Imputation

Salah satu keuntungan utama imputation adalah jumlah sampel dapat dipertahankan.

Misalnya:

Dataset awal = 1000 baris

Jika terdapat missing values pada 100 baris, kita tidak harus langsung menghapus 100 baris tersebut.

Dengan imputation:

1000 baris

1000 baris

Data yang hilang diganti dengan nilai yang dipilih.

Kekurangan Imputation

Nilai yang kita masukkan bukan nilai asli yang sebenarnya.

Misalnya:

Age

20
22
NaN
24
26

Jika NaN diganti dengan mean:

23

kita tidak mengetahui apakah nilai sebenarnya memang 23.

Artinya, imputation menghasilkan nilai estimasi.

Pendekatan 2 - Removal

Pendekatan kedua adalah menghapus data yang memiliki missing values.

Contohnya:

car_sales_missing.dropna()

Jika terdapat baris:

Toyota | Blue | 4 | 150000 | 5000
Honda | NaN | 4 | 120000 | 6000

baris kedua dapat dihapus karena memiliki missing value.

Kelebihan Removal

Data yang tersisa tidak mendapatkan nilai hasil imputasi.

Dengan kata lain, kita tidak memasukkan nilai pengganti ke dalam dataset.

Kekurangan Removal

Jumlah data akan berkurang.

Misalnya:

Dataset awal
1000 samples

dropna()

750 samples

Kita kehilangan:

250 samples

Jika dataset kecil, kehilangan banyak sampel dapat menjadi masalah.

Kapan Menggunakan Imputation atau Removal?

Tidak ada satu metode yang selalu benar.

Pemilihan strategi bergantung pada:

  • jumlah missing values;
  • ukuran dataset;
  • jenis feature;
  • distribusi data;
  • pentingnya feature;
  • apakah target memiliki missing value;
  • kebutuhan model;
  • konteks domain.

Secara sederhana:

MetodeKelebihanKekurangan
ImputationMempertahankan jumlah sampelNilai yang dimasukkan merupakan estimasi
RemovalTidak memasukkan nilai hasil estimasiMengurangi jumlah data

Memeriksa Missing Values

Sebelum melakukan apa pun, kita harus mengetahui berapa banyak missing values yang terdapat dalam dataset.

Misalnya:

import pandas as pd

car_sales_missing = pd.read_csv(
"data/car-sales-extended-missing-data.csv"
)

Kemudian:

car_sales_missing.isna().sum()

Contoh hasil:

Make 50
Colour 50
Odometer (KM) 50
Doors 50
Price 50
dtype: int64

Artinya masing-masing kolom memiliki sejumlah missing values.

isna()

Method:

isna()

digunakan untuk mendeteksi missing values.

Contoh:

car_sales_missing.isna()

Hasilnya berupa nilai Boolean:

True
False

True menunjukkan bahwa nilai tersebut missing.

Menghitung Missing Values

Untuk menghitung jumlah missing values:

car_sales_missing.isna().sum()

Penjelasannya:

isna()

Mendeteksi missing value

True / False

sum()

Menghitung jumlah True

Kita juga dapat menggunakan:

car_sales_missing.isnull().sum()

Dalam Pandas, isna() dan isnull() dapat digunakan untuk tujuan yang sama dalam konteks ini.

Memeriksa Persentase Missing Values

Selain jumlah, kita dapat menghitung persentasenya.

Contoh:

missing_percentage = (
car_sales_missing.isna().mean() * 100
)

print(missing_percentage)

Misalnya hasil:

Make 5.0
Colour 5.0
Odometer (KM) 5.0
Doors 5.0
Price 5.0

Artinya sekitar 5% nilai pada masing-masing kolom tersebut missing.

Persentase ini dapat membantu menentukan strategi preprocessing.

Imputation pada Data Kategorikal

Misalnya kolom:

Make
Colour

merupakan categorical features.

Kita dapat mengganti missing value dengan kategori khusus:

Missing

Contoh:

car_sales_missing["Make"] = (
car_sales_missing["Make"]
.fillna("Missing")
)

car_sales_missing["Colour"] = (
car_sales_missing["Colour"]
.fillna("Missing")
)

Sekarang:

NaN

berubah menjadi:

Missing

Mengapa Menggunakan Missing?

Jika kita mengganti:

NaN → Toyota

kita seolah-olah menyatakan bahwa nilai yang hilang adalah Toyota.

Padahal kita tidak mengetahui nilai sebenarnya.

Dengan:

NaN → Missing

kita mempertahankan informasi bahwa data tersebut memang tidak tersedia.

Contoh:

Make
Toyota
Honda
Missing
BMW

Model kemudian dapat mempelajari bahwa:

Missing

merupakan kategori tersendiri.

Imputation pada Data Numerik

Untuk feature numerik, salah satu strategi yang umum adalah menggunakan:

Mean
Median

Misalnya:

Odometer (KM)

100000
120000
NaN
140000
160000

Kita dapat menggunakan mean:

odometer_mean = car_sales_missing["Odometer (KM)"].mean()

car_sales_missing["Odometer (KM)"] = (
car_sales_missing["Odometer (KM)"]
.fillna(odometer_mean)
)

Mean

Mean atau rata-rata dihitung dengan:

Mean = jumlah seluruh nilai / jumlah data

Misalnya:

100
200
300

maka:

Mean = (100 + 200 + 300) / 3
= 200

Jika terdapat missing value:

100
200
NaN
300

mean dihitung dari nilai yang tersedia.

Median

Median adalah nilai tengah setelah data diurutkan.

Contoh:

100
200
300

median:

200

Untuk jumlah data genap:

100
200
300
400

median:

(200 + 300) / 2
= 250

Median sering digunakan ketika data memiliki outlier atau distribusi yang miring karena median lebih tahan terhadap nilai ekstrem dibandingkan mean.

Mean vs Median

Pemilihan mean atau median bergantung pada karakteristik data.

MetodeKarakteristik
MeanCocok ketika rata-rata representatif dan tidak terlalu dipengaruhi outlier
MedianSering lebih robust terhadap outlier

Contoh data:

10
11
12
13
1000

Mean akan sangat dipengaruhi oleh:

1000

Sedangkan median tetap berada di sekitar nilai tengah data.

Imputation pada Doors

Misalnya:

Doors
-----
4
4
NaN
4
5

Jika berdasarkan konteks dataset jumlah pintu yang paling umum adalah:

4

kita dapat mengisinya dengan:

car_sales_missing["Doors"] = (
car_sales_missing["Doors"]
.fillna(4)
)

Namun, nilai 4 bukan aturan universal.

Dalam dataset lain, nilai yang paling sesuai dapat berbeda.

Menggunakan Modus

Untuk data kategorikal atau discrete data, kita juga dapat menggunakan nilai yang paling sering muncul.

Pandas menyediakan:

car_sales_missing["Doors"].mode()

Hasilnya dapat berupa Series.

Untuk mengambil nilai pertama:

car_sales_missing["Doors"].mode()[0]

Kemudian:

doors_mode = car_sales_missing["Doors"].mode()[0]

car_sales_missing["Doors"] = (
car_sales_missing["Doors"]
.fillna(doors_mode)
)

Pendekatan ini lebih fleksibel dibandingkan menetapkan angka tertentu secara manual.

Imputation Harus Memperhatikan Data Leakage

Ada satu konsep penting yang harus diperhatikan.

Jangan menghitung nilai imputasi dari seluruh dataset sebelum train-test split dalam workflow Machine Learning.

Misalnya:

mean_value = X["Odometer (KM)"].mean()

Jika X masih berisi training dan test data, maka informasi dari test set ikut digunakan untuk menentukan mean.

Hal tersebut dapat menyebabkan data leakage.

Workflow yang Lebih Aman

Gunakan workflow:

Dataset


Train-Test Split

├──────────────┐
│ │
Training Test
│ │
▼ │
Fit Imputer │
│ │
▼ │
Transform │
Training │


Transform
Test

Artinya:

Training → fit + transform
Test → transform

Bukan:

Training + Test → fit

Menghapus Baris dengan dropna()

Jika kita ingin menghapus baris yang memiliki missing value:

car_sales_missing = car_sales_missing.dropna()

Setelah itu kita dapat memeriksa kembali:

car_sales_missing.isna().sum()

Jika semua missing values sudah dihapus, hasilnya dapat menjadi:

Make 0
Colour 0
Odometer (KM) 0
Doors 0
Price 0

Menghapus Hanya Baris dengan Target Missing

Target memiliki perlakuan yang sedikit berbeda.

Misalnya:

Price

merupakan target yang ingin diprediksi.

Jika:

Price = NaN

kita tidak memiliki label untuk baris tersebut.

Contohnya:

MakeColourDoorsOdometerPrice
ToyotaBlue41000005000
HondaRed4120000NaN

Untuk supervised learning, baris kedua tidak memiliki target yang diketahui.

Jika tujuan kita adalah melatih model supervised learning menggunakan data berlabel, baris tersebut biasanya dikeluarkan dari dataset training.

Contoh:

car_sales_missing = car_sales_missing.dropna(
subset=["Price"]
)

Dengan:

subset=["Price"]

kita hanya meminta Pandas memeriksa missing value pada kolom Price.

Mengapa Target Missing Biasanya Dihapus?

Misalnya kita ingin melatih model:

Features → Price

Model membutuhkan pasangan:

X → y

Contoh:

Mobil A → 5000
Mobil B → 6000
Mobil C → NaN

Untuk Mobil C, kita memiliki feature tetapi tidak memiliki target.

Model supervised learning tidak dapat menggunakan baris tersebut sebagai contoh training biasa karena labelnya tidak tersedia.

Karena itu, baris dengan target missing sering dikeluarkan dari dataset training.

Memisahkan Features dan Target

Setelah target missing ditangani:

X = car_sales_missing.drop(
"Price",
axis=1
)

y = car_sales_missing["Price"]

Sekarang:

X → features
y → target

Melakukan Encoding

Dataset masih dapat memiliki categorical features seperti:

Make
Colour
Doors

Kita dapat menggunakan:

from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer

Tentukan categorical features:

categorical_features = [
"Make",
"Colour",
"Doors"
]

Kemudian:

one_hot = OneHotEncoder(
handle_unknown="ignore"
)

Buat transformer:

transformer = ColumnTransformer(
transformers=[
(
"one_hot",
one_hot,
categorical_features
)
],
remainder="passthrough"
)

Transformasi Data

Setelah transformer dibuat:

transformed_X = transformer.fit_transform(X)

Data kategorikal akan diubah menjadi representasi numerik.

Namun, untuk workflow Machine Learning yang benar, kita perlu memperhatikan kapan fit_transform() dilakukan.

Jika data belum dibagi menjadi training dan test set, melakukan fit_transform() pada seluruh dataset dapat menyebabkan data leakage.

Workflow yang Lebih Aman dengan Pipeline

Pendekatan yang lebih baik adalah menggabungkan imputation dan encoding dalam Pipeline.

Misalnya kita memiliki:

Categorical Features


SimpleImputer


OneHotEncoder


Model

Sedangkan numeric features dapat memiliki pipeline sendiri:

Numerical Features


SimpleImputer


Model

Kemudian keduanya dapat digabungkan menggunakan:

ColumnTransformer

SimpleImputer

Scikit-Learn menyediakan:

SimpleImputer

yang dapat digunakan untuk melakukan imputasi secara otomatis.

Import:

from sklearn.impute import SimpleImputer

Contoh untuk numerical data:

imputer = SimpleImputer(
strategy="mean"
)

Strategi lain:

SimpleImputer(strategy="median")

atau:

SimpleImputer(strategy="most_frequent")

atau:

SimpleImputer(
strategy="constant",
fill_value="Missing"
)

Strategi SimpleImputer

Beberapa strategi umum:

StrategyFungsi
meanMenggunakan rata-rata
medianMenggunakan median
most_frequentMenggunakan nilai yang paling sering muncul
constantMenggunakan nilai yang ditentukan

mean dan median terutama digunakan untuk data numerik.

most_frequent dan constant dapat digunakan pada berbagai jenis data sesuai kebutuhan.

Contoh SimpleImputer untuk Data Numerik

Misalnya:

from sklearn.impute import SimpleImputer

imputer = SimpleImputer(
strategy="median"
)

Kemudian imputer dipelajari dari training data:

imputer.fit(X_train)

Dan digunakan untuk transform:

X_train_imputed = imputer.transform(X_train)
X_test_imputed = imputer.transform(X_test)

Perhatikan bahwa:

fit → hanya training
transform → training dan test

Mengapa Bukan fit_transform() pada Test?

Jangan melakukan:

X_test_imputed = imputer.fit_transform(X_test)

Karena berarti imputer mempelajari parameter baru dari test set.

Workflow yang benar:

X_train_imputed = imputer.fit_transform(X_train)

X_test_imputed = imputer.transform(X_test)

Dengan demikian test set tetap berfungsi sebagai data yang tidak digunakan untuk mempelajari parameter preprocessing.

Menggabungkan Imputation dan Encoding

Untuk categorical features, kita dapat membuat Pipeline:

from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder

categorical_pipeline = Pipeline(
steps=[
(
"imputer",
SimpleImputer(
strategy="constant",
fill_value="Missing"
)
),
(
"onehot",
OneHotEncoder(
handle_unknown="ignore"
)
)
]
)

Workflow tersebut berarti:

Categorical Data


SimpleImputer


Missing → "Missing"


OneHotEncoder


Numerical Features

Pipeline untuk Numerical Features

Untuk numerical features kita dapat menggunakan:

numeric_pipeline = Pipeline(
steps=[
(
"imputer",
SimpleImputer(
strategy="median"
)
)
]
)

Workflow:

Numerical Data


SimpleImputer


Missing → Median

Menggabungkan dengan ColumnTransformer

Misalnya:

from sklearn.compose import ColumnTransformer

categorical_features = [
"Make",
"Colour",
"Doors"
]

numeric_features = [
"Odometer (KM)"
]

Kemudian:

preprocessor = ColumnTransformer(
transformers=[
(
"categorical",
categorical_pipeline,
categorical_features
),
(
"numeric",
numeric_pipeline,
numeric_features
)
]
)

Sekarang kita memiliki preprocessing terstruktur:

Dataset

┌─────────┴─────────┐
│ │
Categorical Numerical
│ │
▼ ▼
SimpleImputer SimpleImputer
│ │
▼ │
OneHotEncoder │
│ │
└─────────┬─────────┘


Preprocessed Data

Menggabungkan Preprocessing dengan Model

Kita dapat memasukkan preprocessor ke dalam Pipeline bersama model.

Contoh:

from sklearn.ensemble import RandomForestRegressor
from sklearn.pipeline import Pipeline

model = RandomForestRegressor(
random_state=42
)

pipeline = Pipeline(
steps=[
(
"preprocessor",
preprocessor
),
(
"model",
model
)
]
)

Kemudian:

pipeline.fit(
X_train,
y_train
)

Untuk prediksi:

y_preds = pipeline.predict(X_test)

Pipeline akan menjalankan preprocessing dan model secara berurutan.

Mengapa Pipeline Lebih Aman?

Pipeline membantu menjaga urutan:

Training Data


Fit Preprocessor


Transform Training


Fit Model

Ketika melakukan prediksi:

Test Data


Transform menggunakan preprocessor yang sudah di-fit


Model


Prediction

Hal ini membantu menghindari kesalahan preprocessing dan data leakage.

Contoh Workflow Lengkap

Berikut contoh lengkap penanganan missing values menggunakan Pipeline:

import pandas as pd

from sklearn.model_selection import train_test_split
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder
from sklearn.ensemble import RandomForestRegressor

# Membaca dataset
car_sales = pd.read_csv(
"data/car-sales-extended-missing-data.csv"
)

# Menghapus baris yang tidak memiliki target
car_sales = car_sales.dropna(
subset=["Price"]
)

# Features dan target
X = car_sales.drop(
"Price",
axis=1
)

y = car_sales["Price"]

# Menentukan kolom
categorical_features = [
"Make",
"Colour",
"Doors"
]

numeric_features = [
"Odometer (KM)"
]

# Pipeline categorical
categorical_pipeline = Pipeline(
steps=[
(
"imputer",
SimpleImputer(
strategy="constant",
fill_value="Missing"
)
),
(
"onehot",
OneHotEncoder(
handle_unknown="ignore"
)
)
]
)

# Pipeline numerical
numeric_pipeline = Pipeline(
steps=[
(
"imputer",
SimpleImputer(
strategy="median"
)
)
]
)

# Menggabungkan preprocessing
preprocessor = ColumnTransformer(
transformers=[
(
"categorical",
categorical_pipeline,
categorical_features
),
(
"numeric",
numeric_pipeline,
numeric_features
)
]
)

# Model
model = RandomForestRegressor(
random_state=42
)

# Pipeline utama
pipeline = Pipeline(
steps=[
(
"preprocessor",
preprocessor
),
(
"model",
model
)
]
)

# Train-test split
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42
)

# Training
pipeline.fit(
X_train,
y_train
)

# Evaluasi
score = pipeline.score(
X_test,
y_test
)

print("Model score:", score)

Perbedaan Pendekatan Manual dan Pipeline

Ada dua pendekatan yang dapat kita gunakan.

Pendekatan Manual

Pandas

├── fillna()
├── dropna()
└── Encoding


Model

Pendekatan ini mudah dipahami ketika pertama kali belajar.

Pendekatan Scikit-Learn Pipeline

Raw Data


ColumnTransformer

├── Numerical Pipeline
│ └── SimpleImputer

└── Categorical Pipeline
├── SimpleImputer
└── OneHotEncoder


Model

Pendekatan Pipeline lebih cocok untuk workflow Machine Learning yang lebih terstruktur.

Kapan Menggunakan Pandas?

Pandas sangat berguna ketika kita sedang:

  • melakukan eksplorasi data;
  • memahami dataset;
  • membersihkan data secara manual;
  • melakukan analisis awal;
  • memvisualisasikan data.

Contohnya:

car_sales_missing.isna().sum()

atau:

car_sales_missing.describe()

Kapan Menggunakan Scikit-Learn?

Scikit-Learn lebih cocok ketika preprocessing menjadi bagian dari workflow Machine Learning.

Contohnya:

Train-Test Split

Preprocessing

Model

Cross Validation

Hyperparameter Tuning

Dalam workflow seperti ini, Pipeline sangat membantu.

Kesalahan Umum dalam Missing Values

Kesalahan 1 - Menghapus Semua Baris Secara Sembarangan

Contoh:

car_sales.dropna()

Tidak selalu merupakan solusi terbaik.

Jika jumlah missing values besar, kita dapat kehilangan banyak data.

Kesalahan 2 - Mengisi Semua Kolom dengan Nilai yang Sama

Contoh:

car_sales.fillna(0)

Ini dapat bermasalah karena:

0

belum tentu memiliki arti yang sama untuk setiap feature.

Kesalahan 3 - Menggunakan Mean untuk Semua Kolom

Mean tidak cocok untuk semua jenis data.

Contohnya:

Make = "Toyota"

tidak dapat diisi menggunakan mean.

Kesalahan 4 - Mengisi Target Missing Secara Sembarangan

Jika target:

Price = NaN

mengisinya secara sembarangan dapat menghasilkan label yang tidak benar.

Untuk supervised learning, baris tanpa target sering kali lebih tepat dikeluarkan dari data training.

Kesalahan 5 - Data Leakage

Kesalahan yang sangat penting:

Seluruh Dataset

Fit Imputer

Train-Test Split

Sebaiknya:

Dataset

Train-Test Split

Training → Fit Imputer

Test → Transform

Checklist Missing Values

Sebelum melanjutkan ke modeling, periksa:

  • Sudah memeriksa jumlah missing values.
  • Sudah menghitung persentase missing values jika diperlukan.
  • Sudah membedakan categorical dan numerical features.
  • Sudah menentukan strategi imputation atau removal.
  • Target dengan missing value sudah ditangani.
  • Tidak mengisi semua kolom dengan nilai yang sama tanpa alasan.
  • Imputation dilakukan tanpa menggunakan informasi test set.
  • SimpleImputer dipertimbangkan untuk workflow Machine Learning.
  • Pipeline digunakan untuk preprocessing yang lebih kompleks.

Ringkasan

Missing values merupakan masalah umum dalam dataset dunia nyata.

Dua strategi utama adalah:

Missing Values

├── Imputation
│ └── Mengisi nilai yang hilang

└── Removal
└── Menghapus data

Untuk categorical features, kita dapat menggunakan:

SimpleImputer(
strategy="constant",
fill_value="Missing"
)

Untuk numerical features, kita dapat menggunakan:

SimpleImputer(
strategy="median"
)

atau:

SimpleImputer(
strategy="mean"
)

Untuk supervised learning, missing pada target sering ditangani dengan menghapus baris tersebut:

data.dropna(
subset=["target"]
)

Dalam workflow Machine Learning modern, pendekatan yang lebih terstruktur adalah:

SimpleImputer
+
OneHotEncoder
+
ColumnTransformer
+
Pipeline
+
Machine Learning Model

Yang paling penting adalah memastikan parameter preprocessing dipelajari dari training data, bukan dari seluruh dataset, sehingga kita dapat mengurangi risiko data leakage.

Cheat Sheet

KebutuhanKode
Cek missing valuesdf.isna().sum()
Cek missing valuesdf.isnull().sum()
Hapus baris missingdf.dropna()
Hapus jika target missingdf.dropna(subset=["Price"])
Isi dengan nilai tertentudf["Column"].fillna(value)
Meandf["Column"].mean()
Mediandf["Column"].median()
Modedf["Column"].mode()[0]
SimpleImputerSimpleImputer(strategy="median")
Categorical constantSimpleImputer(strategy="constant", fill_value="Missing")
PipelinePipeline(steps=[...])
ColumnTransformerColumnTransformer(transformers=[...])