Saya suka mengeksplorasi data dan menemukan pola-pola tak terduga dari data. Menurut saya, sebagai seorang data scientist kita perlu memiliki sifat curious (rasa ingin tahu) agar bisa sukses di bidang ini. Cara mengeksplorasi data tidak hanya terbatas pada teknik dasar seperti memvisualisasikan data dan mendapatkan angka-angka statistik; salah satu cara lain adalah dengan menerapkan machine learning.
Machine learning adalah teknik untuk mengeksplorasi data, bukan hanya untuk tujuan prediksi seperti yang sering dikatakan orang. Inilah mengapa saya sering fokus pada pemahaman konsep model untuk mengetahui bagaimana data saya diproses; untuk lebih memahami apa yang terjadi pada data kita.
Pada artikel ini, saya ingin memperkenalkan informasi apa yang bisa kita dapatkan dari angka-angka statistik dan teknik data mining melalui unsupervised learning untuk mengeksplorasi data. Di sini, karena salah satu hobi saya adalah memasak, saya akan menggunakan dataset dari Kaggle tentang nilai nutrisi dari makanan. Saya ingin mengeksplorasi data ini baik untuk belajar maupun memenuhi rasa penasaran saya sendiri. Artinya, tujuan saya di sini hanyalah untuk mengetahui apa yang terjadi pada data dan jenis informasi apa yang bisa saya dapatkan tanpa tujuan spesifik. Mari kita mulai.
Eksplorasi Data
Data Cleaning
Pertama, kita perlu membaca data dan memahami bagaimana data kita. Ini adalah langkah pertama yang sangat penting.
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
data = pd.read_csv('nutrition.csv')
data.head()
data.info()
Kita memiliki 76 kolom yang tidak saya tampilkan semua di sini (daftarnya akan sangat panjang) dengan contoh data yang ditunjukkan pada tabel di atas.
Sebagian besar data kita terdiri dari nilai nutrisi (kalori, lemak, gula, karbohidrat, dll.) dengan nama makanannya. Kolom nilai nutrisi memiliki ukuran yang berbeda seperti g (gram), mg (miligram), dan mcg (mikrogram). Dalam hal ini, kita juga bisa mengabaikan fitur serving_size karena tidak memberikan informasi tambahan selain semua data didasarkan pada 100 gram makanan. Beberapa kolom juga mengandung nilai NaN, yang saya percaya nilai Null ini berarti sama dengan 0. Sekarang, mari kita lakukan beberapa data cleaning.
data.drop('serving_size', axis = 1, inplace = True)
data.fillna(0, inplace = True)
Dalam hal ini, saya ingin semua fitur kecuali fitur nama menjadi kolom numerik. Artinya, kita perlu menghapus semua teks non-numerik dalam data. Saya juga ingin mengubah semua data numerik kecuali kalori agar memiliki ukuran yang sama (gram). Mari kita mulai.
import re
for col in data.drop('name',axis = 1).select_dtypes(exclude = 'number').columns:
for i in data[col]:
if i == '0' or i == 0:
pass
else:
point = re.findall('[a-zA-Z]+',i)[0]
replace = []
if point == 'mg':
for j in data[col]:
if j == '0' or j == 0:
replace.append(float(j))
else:
replace.append(float(re.sub('[a-zA-Z]','',j))/1000)
elif point == 'mcg':
for j in data[col]:
if j == '0' or j == 0:
replace.append(float(j))
else:
replace.append(float(re.sub('[a-zA-Z]','',j))/1000000)
else:
for j in data[col]:
if j == '0' or j == 0:
replace.append(float(j))
else:
replace.append(float(re.sub('[a-zA-Z]','',j)))
data[col] = replace
data.rename({col:col+'(g)'}, axis =1, inplace = True)
break
Berikut adalah hasil akhir dari data yang akan saya eksplorasi lebih lanjut. Perlu dicatat bahwa saya tahu semua data sekarang dalam ukuran gram kecuali kalori, tetapi hanya untuk tujuan pembelajaran, saya menambahkannya dalam nama kolom saya agar kita tidak lupa tentang itu.
Saya juga membuat satu fitur lagi yang disebut food_categories karena ketika memeriksa dengan cermat fitur nama, kata pertama sebelum koma adalah nama makanan.
data['food_categories'] = data['name'].apply(lambda x: x.split(',')[0])
Numerical Statistic
Jika kita mencoba memvisualisasikan kolom satu per satu, itu akan sangat banyak dan agak berulang karena tidak akan memberi kita banyak informasi. Kamu bisa mencobanya jika kamu mau. Saya bisa memberikan kodenya di bawah ini.
for i in data.select_dtypes('number').columns:
sns.distplot(data[i])
plt.title(i)
plt.show()
Terkadang dalam kasus seperti ini, jika kita benar-benar hanya ingin mengeksplorasi data, akan lebih intuitif melihat dengan angka daripada memvisualisasikannya (saya lebih suka angka karena saya percaya terkadang visualisasi bisa bias).
pd.set_option('display.max_columns', None)
data.agg(['mean', 'median', 'std', 'skew', 'kurtosis'])
Di sini saya menggunakan metode .agg dari Dataframe untuk mendapatkan informasi tentang mean, median, std, skewness, dan kurtosis dari setiap kolom. Di sinilah angka berbicara lebih dari sekedar visualisasi.
Seperti yang kita tahu, mean adalah rata-rata dari data. Beberapa fitur bisa memiliki mean yang sama, tetapi berbeda dalam cara penyebarannya di sekitar mean yang ditunjukkan oleh standar deviasi (std). Ada aturan yang disebut aturan empiris di mana kita bisa mendapatkan probabilitas penyebaran data melalui standar deviasi. Aturan empiris menyatakan bahwa:
68% dari data kita berada di bawah mean ± 1*std
95% dari data kita berada di bawah mean ± 2*std
99,7% dari data kita berada di bawah mean ± 3*std
Aturan empiris atau beberapa juga menyebutnya aturan 68–95–99,7 sering digunakan untuk menganalisis data outlier. Masalah utama dengan statistik ini adalah mereka dipengaruhi oleh outlier atau nilai ekstrem dan sering menyebabkan data menjadi skewed. Saya tunjukkan dengan gambar apa itu data yang skewed.
Di atas adalah plot dari fitur total_fat(g). Itu skewed ke kanan karena ekornya ada di kanan. Tapi, seberapa skewed skewnessnya? Itu adalah tujuan dari statistik skewness. Beberapa aturan yang bisa kita ingat tentang skewness adalah:
Jika skewness antara -0,5 dan 0,5, data cukup simetris
Jika skewness antara -1 dan -0,5 atau antara 0,5 dan 1, data sedikit skewed
Jika skewness kurang dari -1 atau lebih dari 1, data sangat skewed
Jadi kita tahu bahwa data kita sangat skewed, yang sebenarnya sebagian besar data yang akan kamu temui akan seperti ini. Sekarang, bagaimana dengan kurtosis? Apa yang statistik ini beritahukan kepada kita?
Kurtosis adalah ukuran apakah data memiliki heavy-tailed atau light-tailed relatif terhadap distribusi normal. Analisisnya bisa dirangkum seperti di bawah ini:
Jika kurtosis mendekati 0, maka diasumsikan sebagai distribusi normal. Ini disebut mesokurtic distributions.
Jika kurtosis kurang dari 0, maka distribusi memiliki light tails dan disebut platykurtic distribution.
Jika kurtosis lebih dari 0, maka distribusi memiliki heavier tails dan disebut leptokurtic distribution.
Jika kita memvisualisasikannya, itu akan terlihat seperti gambar di bawah ini.
Lebih tepatnya disebut Excess Kurtosis, di mana distribusi normal diukur dalam kurtosis sebagai 0. Jika kita hanya berbicara tentang Kurtosis, distribusi normal akan sama dengan 3 sehingga dalam Excess Kurtosis kita mengurangkan kurtosis dengan 3.
Ternyata sebagian besar data kita skewed. Data yang skewed sebenarnya sangat menarik karena kamu bisa mencoba mengeksplorasinya. Misalnya, makanan apa yang dianggap sebagai outlier berdasarkan kalori.
Karena data kita cukup skewed, saya tidak akan mengandalkan mean untuk menemukan outlier; sebagai gantinya, saya akan menerapkan metode IQR yang didasarkan pada median.
IQR atau Interquartile Range didasarkan pada posisi data. Misalnya, jika kita menggambarkan fitur ‘kalori’ kita akan mendapatkan deskripsi di bawah ini.
data['calories'].describe()
IQR didasarkan pada posisi 25% atau Q1 dan posisi 75% atau Q3. Kita mendapatkan nilai IQR dengan mengurangi Q3 dengan Q1 (Q3-Q1). Dengan metode IQR, kita bisa menentukan data mana yang dianggap outlier berdasarkan batas atas (Upper Limit) atau batas bawah (Lower Limit).
Lower Limit= Q1–1.5 * IQR
Upper Limit= Q3 + 1.5 * IQR
Setiap data di atas atau di bawah batas ini dianggap sebagai outlier. Mari kita coba menerapkan metode ini dan lihat jenis makanan apa yang dianggap outlier berdasarkan kalori.
cal_Q1 = data.describe()['calories']['25%']
cal_Q3 = data.describe()['calories']['75%']
cal_IQR = cal_Q3 - cal_Q1
data[(data['calories'] < 1.5 * (cal_Q1 - cal_IQR)) | (data['calories'] > 1.5 * (cal_Q3 + cal_IQR)) ]['food_categories'].value_counts()
Ternyata, sebagian besar kategori makanan dengan kalori tinggi adalah minyak, tentu tidak mengejutkan.
Unsupervised Learning
Saya telah menunjukkan cara mengeksplorasi data secara numerik, sekarang saya ingin menunjukkan contoh bagaimana machine learning bisa membantu kita mengeksplorasi data.
Unsupervised learning adalah kasus machine learning di mana kita tidak memiliki target spesifik untuk dipelajari. Salah satu contohnya adalah Clustering Analysis, di mana kita memberi model data dan output-nya adalah cluster data di mana data terdekat dianggap sebagai satu cluster.
Yang saya suka lakukan jika kita tidak memiliki target spesifik untuk mengeksplorasi data, kita bisa membiarkan machine learning belajar untuk kita. Menggunakan unsupervised learning, kita bisa mendapatkan perspektif baru yang sebelumnya tidak kita sadari. Mari kita lakukan dengan contoh menggunakan algoritma clustering favorit saya.
Algoritma clustering favorit saya adalah Agglomerative Clustering Analysis yang bisa kamu baca lebih detail di sini. Pada dasarnya, analisis ini menetapkan setiap titik data sebagai satu cluster dan melanjutkan dengan menggabungkan setiap cluster sehingga kita hanya memiliki satu cluster.
Sekarang, sebelum kita melanjutkan analisis, kita perlu menyiapkan data. Clustering analysis bergantung pada jarak antara data. Jarak data dipengaruhi oleh skala mereka, itulah mengapa kita juga perlu mengubah semua fitur agar memiliki skala yang sama. Jika kamu ingat, kita sudah memiliki setiap kolom dalam ukuran gram, tetapi ada kolom ‘Kalori’ yang tidak pada skala yang sama. Inilah mengapa kita masih perlu mengubah data kita. Sering kali kita mengubah data mengikuti distribusi standar dan itulah yang akan kita lakukan.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
training = pd.DataFrame(scaler.fit_transform(data.drop('name', axis =1)), columns = data.drop('name', axis =1).columns)
Ini adalah hasil akhir kita, dataset dengan skala yang sama untuk setiap fitur. Sekarang, mari kita coba clustering data menggunakan Agglomerative Clustering. Pertama, kita visualisasikan hasil clustering.
from scipy.cluster.hierarchy import linkage, dendrogram
Z = linkage(training,method = 'ward')
dendrogram(Z, truncate_mode = 'lastp')
plt.xticks(rotation = 90, fontsize = 10)
plt.ylabel('Distance')
plt.xlabel('Cluster')
plt.title('Agglomerative Clustering')
Gambar di atas adalah bagan yang dihasilkan oleh Agglomerative Clustering. Data ini hanya menampilkan sekitar 30 penggabungan karena akan terlalu padat jika kita menunjukkan semuanya di sini. Seperti yang kita lihat, tampaknya data bisa dibagi menjadi 2 cluster; tentu saja, jika kamu ingin lebih konservatif, itu bisa dibagi menjadi 3 cluster karena ada bukti dalam visualisasi di atas bahwa itu bisa terjadi. Meskipun demikian, saya akan tetap dengan 2 cluster untuk saat ini.
Mari kita kembali ke data sebelumnya dan masukkan hasil Agglomerative Clustering ke dalam data kita.
from sklearn.cluster import AgglomerativeClustering
ach = AgglomerativeClustering(n_clusters = 2)
ach.fit(training)
data['label'] = ach.labels_
Sekarang, melalui unsupervised learning, kita sebenarnya bisa mencoba memvisualisasikan data multidimensi menjadi dua sumbu. Ada beberapa cara untuk melakukannya, tetapi saya akan menunjukkan teknik yang disebut t-SNE.
from sklearn.manifold import TSNE
tsne = TSNE(random_state=0)
tsne_results = tsne.fit_transform(training)
tsne_results=pd.DataFrame(tsne_results, columns=['tsne1', 'tsne2'])
tsne_results['label'] = data['label']
sns.scatterplot(data = tsne_results, x = 'tsne1', y = 'tsne2', hue='label')
plt.show()
Sekarang data multidimensi kita telah divisualisasikan dan jelas bahwa metode agglomerative clustering memisahkan data kita dengan garis yang jelas. Nah, apa yang membuat mereka terpisah? Itu yang perlu kita analisis. Tidak ada cara mudah kecuali kita mendalami angka lagi. Tentu saja, visualisasi akan membantu di sini. Saya akan memberikan kode untuk distribusi setiap kolom di bawah ini.
for i in data.select_dtypes('number').columns:
sns.distplot(data[data['label'] == 0][i], label = 'label 0')
sns.distplot(data[data['label'] == 1][i], label = 'label 1')
plt.title(i)
plt.legend()
plt.show()
Di atas adalah plot distribusi untuk setiap kolom, tetapi jika kamu lebih suka menggunakan angka seperti saya; kita bisa menggunakan metode groupby dari objek DataFrame.
data.groupby('label').agg(['mean', 'median', 'std'])
Hasilnya akan terlihat seperti di atas. Saya telah melakukan beberapa analisis yang membuat mereka terpisah. Berikut adalah ringkasan saya:
Label 0: Makanan dengan protein lebih rendah, gula dan karbohidrat lebih tinggi, serat lebih banyak, lemak dan kolesterol lebih rendah, dan kalori tersebar kecuali sekitar 200 kalori.
Label 1: Makanan dengan protein lebih tinggi, gula dan karbohidrat lebih rendah, serat lebih sedikit, lemak dan kolesterol lebih tinggi, dan kalori hanya terpaut sekitar 200 kalori.
Kita juga bisa melihat jenis makanan apa yang kita miliki dari label di atas.
data[data['label'] == 0]['food_categories'].value_counts()
Lima makanan teratas dari makanan dengan label 0 adalah minuman, sereal, makanan bayi, sup, dan makanan ringan yang diharapkan untuk makanan yang tidak mengandung banyak protein dan lemak.
data[data['label'] == 1]['food_categories'].value_counts()
Pada label 1, lima makanan teratas semuanya adalah daging. Ini tidak mengejutkan, mengingat label ini untuk makanan yang mengandung lebih banyak lemak dan protein dibandingkan dengan label 0.
Kesimpulan
Di sini saya hanya mencoba bermain-main dengan data dan mencoba mendapatkan pola apa yang bisa saya dapatkan dari data tersebut. Saya tidak memiliki tujuan spesifik kecuali untuk mendapatkan wawasan tentang apa yang data saya dapat berikan.
Kita bisa melihat bahwa terkadang angka bisa memberikan lebih banyak informasi dibandingkan dengan visualisasi dan machine learning tidak selalu digunakan untuk prediksi tetapi juga bisa digunakan untuk analisis.
コメント