Basic Data Science Library

Aktivitas yang paling sering dilakukan oleh data scientist adalah eksplorasi data. Bahkan, IBM menyebutkan bahwa 80% waktu data scientist habis untuk mengeksplor dan memahami data. Untuk melakukannya, data scientist umumnya tidak cukup hanya menggunakan built-in library dari Python, diperlukan third-party library yang lebih efisien dalam mengolah dan memproses data yang biasanya sangat besar. Beberapa library yang umum digunakan seperti:

  1. NumPy

  2. Pandas

  3. Matplotlib

  4. Seaborn

Oleh karena itu, kita akan berkenalan dengan 4 library di atas dan mencoba melakukan data eksplorasi pada dataset publik.

Persiapan

Sebelum kita mulai menggunakan ke-4 library ini, pastikan semuanya sudah terpasang. Kita bisa cek menjalankan perintah berikut pada cell baru:

# uncomment below code to check installed packages
#!pip list | grep -E "numpy|matplotlib|pandas|seaborn"

Perintah di atas akan menampilkan nama library beserta angka version yang terpasang. Jika ternyata salah satu atau bahkan semua library belum terpasang, kita harus install terlebih dahulu menggunakan perintah:

# uncomment below code to install necessary packages, if you haven't
#!pip install numpy matplotlib pandas seaborn

Setelah semuanya siap, kita bisa lansung import 4 library tersebut.

# import things
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns

The Power of NumPy

Apa Itu NumPy?

NumPy is an open source project aiming to enable numerical computing with Python. It was created in 2005, building on the early work of the Numerical and Numarray libraries - numpy official site

Dari definisi resmi di atas, kita sudah paham bahwa NumPy adalah sebuah package yang digunakan untuk komputasi numerik.

Why NumPy (instead of built-in List)?

Dengan menggunakan NumPy array, kita bisa melakukan operasi matematika yang tidak bisa dilakukan oleh List. Selain itu, NumPy array lebih cepat daripada List (referensi).

Array

NumPy Array

Fig. 1 NumPy Array

Array adalah struktur data utama dalam NumPy. NumPy array juga mengonsumsi lebih sedikit memori dan mempunyai mekanisme menentukan tipe data (referensi).

arr_1d = np.array([1, 2, 3])
arr_1d
type(arr_1d)

Note

numpy.ndarray adalah sebuah class yang merepresentasikan vektor dan juga matriks

# dimensi array
arr_1d.shape

Apa perbedaan array, vektor, dan matriks?

Vektor adalah array berdimensi satu (vektor baris = vektor kolom), sedangkan matriks adalah array berdimensi dua. Sehingga, array merupakan sebuah representasi data dalam n-dimensi.

arr_2d = np.array([[1, 2], [3, 4], [5, 6]])
arr_2d
arr_2d.shape
arr_3d = np.array([[[0, 1, 2], [1, 2, 3]], [[10, 11, 12], [11, 12, 13]]])
arr_3d
arr_3d.shape

Pembuatan Array Otomatis

Selain dengan np.array, kita juga bisa membuat array secara otomatis dengan nilai dan pola tertentu.

arr_zeros = np.zeros((3, 3))
arr_zeros
arr_ones = np.ones((2, 2))
arr_ones
arr_empty = np.empty(2)
arr_empty
arr_arange = np.arange(start=-3, stop=4, step=1)
arr_arange
arr_space = np.linspace(start=1, stop=2)
arr_space
arr_identity = np.eye(4)
arr_identity
arr_full = np.full((4, 4), 5)
arr_full

Indexing dan Slicing

NumPy array juga memberikan kita keleluasaan untuk mengambil sebagian nilai dalam suatu array. Hampir mirip dengan list yang juga membolehkan kita untuk indexing dan slicing, akan tetapi dengan cara yang lebih elegan.

Array 2D Indexing

Fig. 2 Indexing in 2-Dimensional NumPy Array

List vs NumPy

Misal kita gunakan arr_2d. Kita ingin menampilkan nilai pada:

  1. Baris pertama dan kolom kedua

  2. Baris kedua dan ketiga dan kolom kedua

List

  1. list_arr_2d[0][1]

  2. list_arr_2d[-2:][1]

NumPy

  1. arr_2d[0, 1]

  2. arr_2d[-2:, 1]

Penomoran indeks pada NumPy array sama dengan indeks pada list.

Array Index

Fig. 3 Penomoran indeks pada array

arr_2d
print("row 0:", arr_2d[2, :])
print("row 0:", arr_2d[0, ...])
print("row 1:", arr_2d[1, :])

Eksplorasi

Dengan menggunakan arr_3d:

  1. Tampilkan array 2-dimensi dengan nilai [[0, 1, 2], [11, 12, 13]]

  2. Tampilkan array 2-dimensi dengan nilai [[3, 2], [13, 12]]

# TODO: [[0, 1, 2], [11, 12, 13]]

# TODO: [[3, 2], [13, 12]]

Fungsi yang Sering Digunakan dalam NumPy

Beberapa fungsi yang sering digunakan saat eksplorasi data.

Pengambilan sampel acak: np.random

arr_random = np.random.rand(5, 5)    # bilangan acak berdistribusi uniform dengan ukuran 5x5
arr_random
arr_randn = np.random.randn(4, 10)
arr_randn
arr_randint = np.random.randint(low=-10, high=10, size=(2, 3))
arr_randint

Warning

Sebagai catatan, versi terbaru NumPy menyarankan untuk menggunakan np.random.default_rng() setiap kali kita ingin membangkitkan bilangan acak.

generator = np.random.default_rng(41)

y = generator.random((1, 10))
print(y)

yhat = generator.random((1, 10))
print(yhat)

Pengubahan ukuran array: np.reshape, arr.flatten

print("ukuran array:", arr_randn.shape)

arr_randn_2 = arr_randn.reshape(5, 8)    # atau dengan np.reshape
print("ukuran array baru:", arr_randn_2.shape)
print(arr_randn_2)
arr_flatten = arr_randn.flatten()
print("ukuran array flatten:", arr_flatten.shape)
print(arr_flatten)

Penggabungan array: np.concatenate

arr_randint_2 = np.concatenate([arr_randint, arr_randint[-1].reshape(1, arr_randint.shape[1])*-1])
arr_randint_2
arr_ero = np.concatenate([np.eye(arr_randint_2.shape[0]), arr_randint_2], axis=1)
arr_ero

Pengurutan: np.sort

np.sort(arr_2d)    # mengurutkan nilai column-wise (default)
np.sort(arr_2d, axis=0)

Mathematical Formula

Salah satu keunggulan NumPy array adalah kemampuannya untuk melakukan komputasi numerik dengan cepat. Mari kita coba menghitung nilai MSE dengan NumPy array.

Tip

Bisa menggunakan numpy.sum atau built-in function sum dari Python.

\[ MSE = \frac{1}{n} \sum_{i=1}^{n}{(\hat{y} - y)^2} \]
# TODO: hitung nilai MSE dengan fungsi numpy

Pandas Is All You Need

Apa Itu Pandas

Pandas is a software library written for the Python programming language for data manipulation and analysis. In particular, it offers data structures and operations for manipulating numerical tables and time series - wikipedia

Pandas (dari panel data) berfungsi sebagai pondasi dasar tingkat tinggi untuk melakukan analisis data secara praktis (source). Perbedaan dengan NumPy adalah pandas didesain untuk bekerja dengan data tabular atau heterogen, sedangkan NumPy didesain untuk bekerja dengan data numerik homogen.

Series

Series adalah struktur data berdimensi satu (seperti array 1 dimensi pada NumPy) yang memiliki sebuah label yang biasa disebut index.

Pandas Series

Fig. 4 Pandas Series (ref)

series_1 = pd.Series([-3, -2, -1, 0, 1, 2, 3])
series_1
series_1.name = "integers"
series_1
print("index:", series_1.index)

# ubah index dengan alfabet
series_1.index = ["a", "b", "c", "d", "e", "f", "g"]

print("index baru:", series_1.index)
series_1
dict_population = {"Texas": 71000, "Oregon": 16000, "Ohio": 35000, "Utah": 500}
states = ["California", "Texas", "Ohio", "Utah"]
s_population = pd.Series(dict_population, index=states, name="population")

DataFrame

DataFrame merepresentasikan sebuah data berbentuk tabel yang terdiri dari beberapa kolom dengan tipe data tertentu dan baris dengan index-nya.

Pandas DataFrame

Fig. 5 Pandas DataFrame (ref)

Seperti halnya NumPy array, kita bisa mendapatkan ukuran dari sebuah DataFrame/Series dengan variabel shape.

df_marvel = pd.DataFrame({
    "Name": ["Tony Stark", "Strange", "Peter Parker", "Natasha"],
    "Age": [35, 34, 22, 30],
    "Sex": ["Male", "Male", "Male", "Female"]
})
df_marvel
df_array = pd.DataFrame(arr_ero)
df_array
print("Nama kolom:", df_array.columns)
print("Nama index:", df_array.index)
n_row, n_col = df_array.shape
print("Jumlah baris:", n_row)
print("Jumlah kolom:", n_col)

Membaca File

Salah satu keunggulan Pandas adalah kemampuan untuk membaca sebuah file data dan menampilkannya dalam format DataFrame. Pandas menyediakan beberapa function untuk membaca file sesuai dengan formatnya, read_csv untuk file .csv, read_excel untuk file Excel, read_gbq untuk membaca tabel dari Google BigQuery, dan lainnya.

Sebagai contoh, kita coba baca sebuah data konten akun perusahaan-perusahaan dari LinkedIn. Terlebih dahulu kita harus unduh datanya melalui link ini. Kemudian, simpan data tersebut di dalam sebuah folder data.

df_company = pd.read_csv("data/company_data.csv")
df_company.head()    # menampilkan pratinjau 5 baris pertama data
df_company.tail()    # menampilkan pratinjau 5 baris terakhir data

Jika data berhasil dibaca dengan benar, kita akan temui beberapa kolom dengan nilai NaN atau singkatan dari Not a number. Jenis nilai ini diturunkan dari NumPy array (np.nan) yang juga digunakan dalam Pandas. Salah satu method yang bisa kita gunakann untuk mendapatkan pratinjau keseluruhan tipe data tiap kolom pada DataFrame adalah info.

Ringkasan DataFrame

df_company.info()

Informasi di atas menjelaskan beberapa hal:

  1. RangeIndex: 11654 entries, 0 to 11653 menjelaskan bahwa terdapat 11654 baris dengann indeks 0 sampai 11653.

  2. Data columns (total 19 columns): menjelaskan terdapat total 19 kolom yang diikuti informasi tiap kolom di bawahnya

  3. Pada tabel di atas, kolom paling kanan, Dtype menjelaskan tipe data yang ada pada masing-masing kolom. Beberapa tipe data yang didukung oleh Pandas bisa dilihat di sini.

  4. Kolom kedua dari kanan merepresentasikan jumlah nilai non-null atau nilai yang bukan NaN atau NaT (nilai N/A untuk tipe data datetime).

  5. Dua kolom paling kiri menjelaskan nama kolom serta indeks kolom.

Dari informasi yang disediakan oleh method info di atas, kita sudah bisa membuat pertanyaan-pertanyaan awal seperti:

  • Kolom mana saja yang memiliki missing value?

  • Berapa persentase missing value pada masing-masing kolom?

  • Apakah semua kolom sudah berasosiasi dengan tipe data yang benar?

cols_with_missing = df_company.columns[df_company.isna().any()].tolist()
cols_without_missing = df_company.columns[df_company.notna().all()].tolist()

print("Kolom dengan missing value:", cols_with_missing)
print("Kolom tanpa missing value:", cols_without_missing)

Eksplorasi

Berapa persentasi missing value tiap kolom?

Untuk menjawab pertanyaan di atas, kita bisa secara sederhana menghitung jumlah missing value pada setiap kolom yang memiliki missing value, kemudian bagi dengan jumlah barisnya.

# TODO: hitung persentase missing value tiap kolom

Selain method info, Pandas juga menyediakan function untuk menunjukkan statistics summary sesuai dengan tipe data tiap kolom, yaitu describe.

df_company.describe()    # stats summary untuk kolom bertipe data numerik
df_company.describe(exclude="number")    # stats summary untuk kolom-kolom selain kolom numerik

Dari ringkasan statistik di atas, ada 2 kolom dengan persentase missing value yang sangat besar, yaitu views dan votes. Ada 2 cara untuk menangani missing value pada suatu data, yaitu kita bisa drop kolom atau baris dengan missing value tersebut atau kita bisa beri nilai berdasarkan rata-rata, median, modus, atau cara lain, sesuai dengan kebutuhan. Untuk kali ini, karena tujuan kita adalah eksplorasi data, kita bisa “buang” 2 kolom tersebut dengan method drop atau dropna.

Tip

drop mengizinkan kita untuk membuang data dengan menentukan baris atau kolom yang hendak dibuang. Sedangkan, dropna akan membuang baris atau kolom berdasarkan kondisi ada atau tidaknnya missing value pada baris atau kolom tersebut.

df_company.drop(columns=["views", "votes", "Unnamed: 0"])

Eksplorasi

  1. Ada berapa jenis media_type yang di-post oleh perusahaan-perusahaan tersebut?

  2. Tampilkan ragam kategori media tersebut!

  3. Tampilkan frekuensi penggunaan kategori media secara keseluruhan!

Terdapat kolom Kategori yang berisi nilai dengan tipe data object. Kita bisa mendapatkan ragam nilai dalam kolom tersebut dengan menggunakan method nunique, unique, dan value_counts.

# TODO: jumlah kategori `media_type`

# TODO: ragam kategori media

# TODO: frekuensi penggunaan media berdasarkan kategorinya

Sekarang, misalkan kita ingin mengetahui statistik keseluruhan dari masing-masing perusahaan dalam hal jumlah koneksi (connections), jumlah pengikut (followers), jumlah reaksi terhadap post (reactions), dan jumlah komentar (comments). Langkah pertama mungkin kita ingin melihat beberapa contoh perusahaan satu per satu. Kita bisa gunakan atribut .loc ataupun .iloc untuk melakukan indexing dan juga slicing.

df_company.loc[df_company.name == "Pfizer"]
df_company.loc[df_company.name == "AT&T"].head()

Kita tidak mungkin untuk menghitung satu per satu statistik perusahaan karena frekuensi post tiap perusahaan yang sangat banyak. Untuk itu, kita bisa menggunakan fitur pandas, yaitu gruopby, yang akan mengelompokkan data berdasarkan suatu kolom yang diberikan, kemudian melakukan operasi atau agregasi apapun yang kita hendaki.

df_company.groupby("name").agg({"reactions": sum, "comments": sum})

Jika kita ingin menghasilkan data agregasi yang lebih rapi dan representatif, kita bisa tambahkan beberapa fungsi bawaan pandas seperti berikut.

df_company.groupby("name").agg({"reactions": sum, "comments": sum}).rename(columns={"reactions": "sum_reactions", "comments": "sum_comments"}).rename_axis(None)

Visualizing Data with Matplotlib & Seaborn

Visualisasi data sebenanrya sebagian seni dan sebagian lagi sains. Tantangannya adalah bagaimana menghasilkan visual (seni) yang benar tanpa merusak sains dalam data dan sebaliknya. Visualisasi bisa kita bagi menjadi just right, ugly, bad, dan wrong (referensi).

Ugly Bad Wrong Viz

Fig. 6 Contoh visualisasi yang cenderung ugly, bad, dan wrong.

Apa Itu Matplotlib?

Matplotlib is a Python 2D plotting library which produces publication quality figures in a variety of hardcopy formats and interactive environments across platforms. Matplotlib can be used in Python scripts, the Python and IPython shells, the Jupyter notebook, web application servers, and four graphical user interface toolkits. - TDS

Sebagai latihan untuk visualisasi data, kita akan menggunakan data dari Food Demand Forecasting. Pertama, kita coba baca data dari file data/food-demand-forecasting/. Ada 3 data yang akan kita muat:

  1. fulfilment_center_info.csv: informasi tentang dapur kota (fulfilment center)

  2. meal_info.csv: informasi tentang makanan yang disediakan

  3. food_demand.csv: histori data permintaan makanan

Perlu diketahui, tujuan kita bukan untuk membuat model machine learning, tapi eksplorasi data yang dibantu dengan visualisasi.

df_center = pd.read_csv("data/food-demand-forecasting/fulfilment_center_info.csv")
df_center.head()
df_meal = pd.read_csv("data/food-demand-forecasting/meal_info.csv")
df_meal.head()
df_demand = pd.read_csv("data/food-demand-forecasting/food_demand.csv")
df_demand.head()

Pada df_demand, kolom center_id dan meal_id berturut-turut merepresentasikan identifier pada df_center dan df_meal. Daripada kita harus melihat 3 tabel sepanjang melakukan eksplorasi, kita bisa menggabungkan ketiga data ini menjadi satu tabel berdasarkan center_id dan meal_id menggunakan method merge dari Pandas.

df = pd.merge(df_demand, df_center, on="center_id")
df.head()
df = pd.merge(df, df_meal, on="meal_id")
df.head()

What to Visualize

Ada beberapa jenis visualisasi yang bisa dibuat sesuai dengan jenis data dan tujuan yang ingin disampaikan.

Kuantitas: Bentuk visualisasi yang sering digunakan untuk menunjukkan sebuah jumlah/kuantitas dalam suatu kategori adalah bar plot (vertikal atau horizontal)

https://serialmentor.com/dataviz/directory_of_visualizations_files/figure-html/amounts-1.png

Fig. 7 Contoh visualisasi yang merepresentasikan kuantitas (ref).

Jika terdapat beberapa grup kategori yang ditampilkan nilai jumlahnya, kita bisa membuat grouped bar atau stacked bar.

https://serialmentor.com/dataviz/directory_of_visualizations_files/figure-html/amounts_multi-1.png

Fig. 8 Contoh visualisasi yang merepresentasikan kuantitas berdasarkan grup(ref).

Distribusi: Bentuk visualisasi yang sering digunakan untuk menunjukkan distribusi dari suatu kolom adalah histogram atau density plot.

https://serialmentor.com/dataviz/directory_of_visualizations_files/figure-html/single-distributions-1.png

Fig. 9 Contoh visualisasi yang merepresentasikan distribusi (ref).

Jika kita ingin menampilkan beberapa distribusi untuk dibandingkan satu sama lain, kita bisa menggunakan visualisasi seperti boxplot, violins, dan lainnya seperti contoh di bawah ini.

https://serialmentor.com/dataviz/directory_of_visualizations_files/figure-html/multiple-distributions-1.png

Fig. 10 Contoh visualisasi yang merepresentasikan perbandingan distribusi (ref).

Proporsi: Untuk tujuan proporsi, kita bisa menggunakan beberapa bentuk visualisasi seperti di bawah ini.

https://serialmentor.com/dataviz/directory_of_visualizations_files/figure-html/proportions-1.png

Fig. 11 Contoh visualisasi yang merepresentasikan proporsi (ref).

Relasi x-y: Untuk tujuan ini, kita bisa menggunakan visualisasi seperti scatter plot, line plot, ataupun juga bubble chart (3 variabel). Berikut beberapa contoh visualisasinya.

https://serialmentor.com/dataviz/directory_of_visualizations_files/figure-html/basic-scatter-1.png

Fig. 12 Contoh visualisasi yang merepresentasikan relasi antara variabel x dan y (ref).

Bar Plot

Misal kita ingin menunjukkan kategori makanan terpopuler dari semua permintaan makanan pada tiap kota. Kita harus menghitung jumlah permintaan untuk setiap kategori terlebih dahulu menggunakan method groupby.

Selanjutnya, untuk membuat bar plot, kita gunakan function plt.bar.

grouped_category = df.groupby(by="category")

sum_category_orders = grouped_category.aggregate({
    "num_orders": np.sum
})
plt.bar(sum_category_orders.index, sum_category_orders["num_orders"])
plt.show()
# ukuran canvas
plt.figure(figsize=(12, 6))

plt.bar(sum_category_orders.index, sum_category_orders["num_orders"])

# konfigurasi di x-axis
plt.xticks(rotation=75)
plt.xlabel("Food Category")

# konfigurasi di y-axis
plt.ylabel("Quantity Sold")

# judul plot
plt.title("Most Popular Food")

plt.show()
plt.figure(figsize=(12, 6))
sns.barplot(sum_category_orders.index, sum_category_orders["num_orders"])
plt.xticks(rotation=75)
plt.xlabel("Food Category")
plt.ylabel("Quantity Sold")
plt.title("Most Popular Food")
plt.show()

Pie Chart

Mari kita lihat presentase pesanan untuk setiiap cuisine.

total_orders = df["num_orders"].sum()
dict_cuisine = {
    cuisine: df.loc[df['cuisine']==cuisine, "num_orders"].sum() / total_orders
    for cuisine in df["cuisine"].unique()
}
plt.figure(figsize=(12, 6))
plt.pie(
    dict_cuisine.values(),
    labels=dict_cuisine.keys(),
    autopct="%.1f",
    explode=[0., 0., .1, 0.]
)
plt.title("Cuisine Share %")
plt.show()

Histogram

Histogram digunakan untuk menampilkan distribusi dari suatu data numerik.

plt.hist(df["base_price"])
plt.show()
plt.figure(figsize=(15, 6))
plt.hist(
    df["base_price"],
    bins=15,
    rwidth=.9,
    color="#F57C01",
    edgecolor="blue"
)
plt.show()
plt.figure(figsize=(15, 6))
sns.distplot(df["base_price"])
plt.show()

Line Plot

Line plot sering digunakan untuk menampilkan data tren. Dalam kasus ini, kita akan menggunakan data food demand.

avg_weekly_food_demand = df.groupby("week")[["checkout_price", "num_orders", "base_price"]].mean()
plt.figure(figsize=(15, 6))
plt.plot(avg_weekly_food_demand.index, avg_weekly_food_demand["base_price"], label="base_price")
plt.plot(avg_weekly_food_demand.index, avg_weekly_food_demand["checkout_price"], label="checkout_price")
plt.legend()
plt.show()
plt.figure(figsize=(18, 9))
sns.lineplot(x=avg_weekly_food_demand.index, y="base_price", data=avg_weekly_food_demand)
sns.lineplot(x=avg_weekly_food_demand.index, y="checkout_price", data=avg_weekly_food_demand)
plt.show()