04 — Data Structures
Estimasi: 8 jam Tujuan: Kuasai list, tuple, dict, set, dan comprehension. Ini fondasi semua data manipulation di Python — termasuk preprocessing data ML nanti.
Kenapa Materi Ini Penting?
Setiap dataset ML/AI yang akan kamu olah pada akhirnya direpresentasikan sebagai struktur data: list of records, dict of features, set of unique tokens. NumPy array, Pandas DataFrame, PyTorch tensor — semuanya dibangun di atas konsep yang dipelajari di file ini. Kalau kamu lancar pakai list comprehension dan dict, transisi ke vectorized operation di Fase 4 akan terasa natural.
Lebih jauh: pemilihan struktur data yang tepat = perbedaan antara kode yang jalan 1 detik vs 10 menit. Cek "kata haram" di list 100rb element vs set: O(n) vs O(1). Saat dataset jadi besar (jutaan baris), pilihan ini menentukan apakah training kamu jalan dalam menit atau jam.
Analogi besar: struktur data = wadah penyimpanan dengan karakteristik beda.
- List = rak buku berurutan (boleh duplikat, ada urutan).
- Tuple = album foto cetak (sekali jadi tidak bisa diubah).
- Dict = laci dengan label (akses cepat by nama).
- Set = kantong kelereng (unik, tidak peduli urutan).
Peta Konsep
flowchart TD
A[📦 Data Structures] --> B[📋 List]
A --> C[🔒 Tuple]
A --> D[🗂️ Dict]
A --> E[🎯 Set]
A --> F[✨ Comprehensions]
B --> B1[Mutable, ordered]
C --> C1[Immutable, ordered]
D --> D1[Key-value, fast lookup]
E --> E1[Unique, unordered]
F --> F1[List comp]
F --> F2[Dict comp]
F --> F3[Set comp]
F --> F4[Generator expr]
Tabel Karakteristik Cepat
Cara Membaca Diagram
Decision tree dengan 2 pertanyaan kunci. Mulai dari root "Pilih Struktur Data", jawab pertanyaan, ikuti edge sesuai jawaban, sampai ke leaf node (tipe konkret). Setiap path ke leaf adalah kombinasi karakteristik unik.
Walkthrough Step-by-Step
- Tanya 1: Urutan penting?
- Ya → lanjut ke tanya 2 (mutable?)
- Tidak → lanjut ke tanya tentang key
- Tanya 2: Boleh diubah (mutable)?
- Ya → list
[1, 2, 3] - Tidak → tuple
(1, 2, 3)
- Ya → list
- Tanya: Akses by key?
- Ya → dict
{"key": "value"} - Tidak → set
{1, 2, 3}(cuma unik values)
- Ya → dict
- Pilih sesuai use case: data dinamis = list, record fixed = tuple, lookup cepat = dict, dedup = set.
Analogi Sehari-hari
Pilih wadah penyimpanan di rumah. List = rak buku berurutan (boleh tambah/buang, ada urutan). Tuple = album foto cetak (sudah jadi, tidak bisa diubah). Dict = laci dengan label nama (akses cepat by nama). Set = kantong kelereng (tidak peduli urutan, tidak ada duplikat). Pilih berdasarkan kebutuhan akses, bukan kebiasaan.
Diagram statis Mermaid sebagai fallback:
flowchart LR
subgraph Pilih
Q1{Urutan penting?}
Q2{Boleh diubah?}
Q3{Akses by key?}
end
Q1 -->|Ya| Q2
Q1 -->|Tidak| Q3
Q2 -->|Ya| L[📋 list]
Q2 -->|Tidak| T[🔒 tuple]
Q3 -->|Ya| D[🗂️ dict]
Q3 -->|Tidak| S[🎯 set]
Bagian 1 — List
List = collection terurut, mutable, boleh duplikat.
Bikin List
kosong = []
angka = [1, 2, 3, 4, 5]
campuran = [1, "dua", 3.0, True, None]
nested = [[1, 2], [3, 4], [5, 6]]
Visualisasi: List Indexing
Cara Membaca Diagram
Layer atas = root list dengan referensi ke 4 element (indexes 0-3). Layer tengah = index positif dengan emoji buah. Layer bawah = index negatif (-1 ke last, -4 ke first). Edge dashed dari index negatif → index positif menunjukkan ekuivalensi. Sisi kanan: methods dan slicing.
Walkthrough Step-by-Step
- List =
["apel", "pisang", "mangga", "jeruk"]— 4 element berurutan. - Index positif mulai dari 0 (apel) sampai 3 (jeruk). Akses:
buah[0]= "apel". - Index negatif mulai dari -1 (jeruk = last) sampai -4 (apel = first). Akses:
buah[-1]= "jeruk". - Methods —
.append(),.pop(),.sort()untuk modifikasi in-place. - Slicing —
buah[1:3]=["pisang", "mangga"]. Range[start:end]dengan end exclusive.
Analogi Sehari-hari
List = deretan rumah di gang. Rumah pertama = rumah ke-0 (Python style). Kalau bilang "rumah terakhir" = rumah ke-(-1), tidak perlu hitung total dulu. Slicing = "ambil rumah ke-1 sampai sebelum ke-3" (rumah 1 dan 2). Append = bangun rumah baru di ujung. Pop = pindahkan penghuni rumah terakhir.
Diagram statis Mermaid sebagai fallback:
flowchart LR
subgraph List
i0["[0]<br/>apel"]
i1["[1]<br/>pisang"]
i2["[2]<br/>mangga"]
i3["[3]<br/>jeruk"]
end
subgraph "Index Negatif"
n4["[-4]"] -.-> i0
n3["[-3]"] -.-> i1
n2["[-2]"] -.-> i2
n1["[-1]"] -.-> i3
end
Akses Element (Indexing)
buah = ["apel", "pisang", "mangga", "jeruk"]
buah[0] # "apel" (indexing dari 0)
buah[1] # "pisang"
buah[-1] # "jeruk" (negatif = dari belakang)
buah[-2] # "mangga"
Slicing
Sintaks: list[start:end:step]
buah = ["apel", "pisang", "mangga", "jeruk", "anggur"]
buah[1:4] # ["pisang", "mangga", "jeruk"] (index 1-3)
buah[:3] # ["apel", "pisang", "mangga"] (dari awal)
buah[2:] # ["mangga", "jeruk", "anggur"] (sampai akhir)
buah[:] # full copy
buah[::2] # ["apel", "mangga", "anggur"] (step 2)
buah[::-1] # ["anggur", ..., "apel"] (reverse)
Penting: slicing end tidak inclusive.
buah[1:4]= index 1, 2, 3 (bukan 4).
Method List Penting
buah = ["apel", "pisang"]
# Tambah
buah.append("mangga") # ["apel", "pisang", "mangga"]
buah.insert(0, "anggur") # ["anggur", "apel", "pisang", "mangga"]
buah.extend(["jeruk", "lemon"]) # tambah multiple
# Hapus
buah.remove("apel") # hapus berdasarkan value
hilang = buah.pop() # hapus & return last
hilang = buah.pop(0) # hapus & return index 0
del buah[0] # hapus by index, no return
# Pencarian
buah.index("pisang") # cari index
"pisang" in buah # True/False
buah.count("pisang") # berapa kali muncul
# Manipulasi
buah.sort() # in-place sort (modify list)
buah.sort(reverse=True) # descending
sorted_buah = sorted(buah) # return new list, original tidak berubah
buah.reverse() # in-place reverse
# Length
len(buah) # jumlah element
List Methods: In-place vs Return
| Method | Modifikasi list? | Return? |
|---|---|---|
.append() |
Ya | None |
.sort() |
Ya | None |
.reverse() |
Ya | None |
sorted() |
Tidak | List baru |
reversed() |
Tidak | Iterator |
Bug umum:
lst = lst.sort()akan bikinlst = None. Pakailst.sort()saja, ataulst = sorted(lst).
Iterasi List
buah = ["apel", "pisang", "mangga"]
# Cara biasa
for item in buah:
print(item)
# Dengan index (enumerate)
for i, item in enumerate(buah):
print(f"{i}: {item}")
# Reverse
for item in reversed(buah):
print(item)
Copy List (Penting!)
a = [1, 2, 3]
b = a # ❌ INI BUKAN COPY! b dan a referensi yang SAMA
b.append(4)
print(a) # [1, 2, 3, 4] — modified!
# Cara copy yang benar
b = a.copy()
# atau
b = a[:]
# atau
b = list(a)
import copy
b = copy.deepcopy(a) # untuk nested list
Bagian 2 — Tuple
Tuple = list immutable (tidak bisa diubah).
Bikin Tuple
kosong = ()
satu = (1,) # WAJIB pakai koma untuk single-element tuple
beberapa = (1, 2, 3)
tanpa_kurung = 1, 2, 3 # juga tuple
# Mixed
data = ("Budi", 25, "Bandung")
Akses dan Slicing — Sama dengan List
data = ("Budi", 25, "Bandung")
data[0] # "Budi"
data[1:] # (25, "Bandung")
Tuple Tidak Bisa Diubah
data = (1, 2, 3)
data[0] = 99 # ❌ TypeError!
data.append(4) # ❌ TypeError!
Tuple Unpacking
data = ("Budi", 25, "Bandung")
nama, umur, kota = data # unpacking
# Swap variabel
a, b = 10, 20
a, b = b, a # Pythonic swap
# Skip dengan _
_, umur, _ = data
Kapan Pakai Tuple vs List?
| Pakai Tuple kalau... | Pakai List kalau... |
|---|---|
| Data tidak akan diubah | Akan tambah/hapus/edit |
| Heterogen (tipe campur) | Homogen (semua tipe sama) |
| Sebagai key dict | Iterate banyak |
| Return multiple values | Collect data dinamis |
# Tuple untuk "record"
user = ("Budi", 25, "Bandung")
# List untuk "collection"
buah_list = ["apel", "pisang", "mangga"]
Bagian 3 — Dictionary (Dict)
Visualisasi Dict
flowchart LR
subgraph "🗂️ user dict"
K1["'nama'"] --> V1["'Budi'"]
K2["'umur'"] --> V2["25"]
K3["'kota'"] --> V3["'Bandung'"]
end
Analogi: dict = buku alamat. Kamu cari "Budi" → langsung dapat nomor HP. Tidak perlu scan satu per satu kayak list.
Dict = key-value pairs. Mirip JSON object atau hash map.
Bikin Dict
kosong = {}
user = {"nama": "Budi", "umur": 25, "kota": "Bandung"}
# Bikin dari list of tuple
data = dict([("a", 1), ("b", 2)])
# Bikin dengan keyword
data = dict(a=1, b=2)
Akses
user = {"nama": "Budi", "umur": 25}
user["nama"] # "Budi"
user["email"] # ❌ KeyError!
user.get("nama") # "Budi"
user.get("email") # None (tidak error)
user.get("email", "N/A") # "N/A" (default value)
Modifikasi
user = {"nama": "Budi"}
# Tambah / update
user["umur"] = 25
user["nama"] = "Ani" # update existing
# Hapus
del user["umur"]
hilang = user.pop("nama") # hapus & return
hilang = user.pop("xyz", None) # default kalau tidak ada
# Update multiple
user.update({"a": 1, "b": 2})
Iterasi
data = {"a": 1, "b": 2, "c": 3}
# Loop key
for key in data:
print(key)
# Loop value
for value in data.values():
print(value)
# Loop key + value (paling sering)
for key, value in data.items():
print(f"{key}: {value}")
Cek Existence
"a" in data # True
"z" in data # False
"z" not in data # True
Method Penting
data.keys() # dict_keys(['a', 'b', 'c'])
data.values() # dict_values([1, 2, 3])
data.items() # dict_items([('a', 1), ('b', 2), ('c', 3)])
list(data.keys()) # ['a', 'b', 'c'] ← convert ke list
Nested Dict
users = {
"user1": {"nama": "Budi", "umur": 25},
"user2": {"nama": "Ani", "umur": 30},
}
users["user1"]["nama"] # "Budi"
Dict untuk Counting (Pattern Umum)
text = "apel pisang apel mangga apel pisang"
words = text.split()
count = {}
for word in words:
count[word] = count.get(word, 0) + 1
print(count)
# {'apel': 3, 'pisang': 2, 'mangga': 1}
# Cara lebih Pythonic (collections.Counter)
from collections import Counter
count = Counter(words)
print(count)
Bagian 4 — Set
Visualisasi: Operasi Set
flowchart LR
subgraph A["Set A"]
a1[1]
a2[2]
a3[3]
a4[4]
end
subgraph B["Set B"]
b3[3]
b4[4]
b5[5]
b6[6]
end
A --> U["A ∪ B<br/>{1,2,3,4,5,6}"]
B --> U
A --> I["A ∩ B<br/>{3,4}"]
B --> I
A --> D["A - B<br/>{1,2}"]
Analogi: set = kantong kelereng. Kalau kamu masukkan kelereng yang sama dua kali, di kantong cuma ada satu. Tidak ada urutan, tidak ada duplikat.
Set = collection unik, tidak terurut, mutable.
Bikin Set
kosong = set() # WAJIB pakai set(), {} = dict
angka = {1, 2, 3, 4, 5}
duplikat = {1, 1, 2, 2, 3} # {1, 2, 3} — duplikat dihapus
Operasi Set
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
a | b # union: {1, 2, 3, 4, 5, 6}
a & b # intersection: {3, 4}
a - b # difference: {1, 2}
a ^ b # symmetric diff: {1, 2, 5, 6}
# Method equivalent
a.union(b)
a.intersection(b)
a.difference(b)
a.symmetric_difference(b)
Method
s = {1, 2, 3}
s.add(4) # {1, 2, 3, 4}
s.remove(2) # {1, 3, 4} — error kalau tidak ada
s.discard(99) # {1, 3, 4} — no error
s.pop() # remove random element
5 in s # False
len(s) # 3
Use Case: Hapus Duplikat
data = [1, 2, 2, 3, 4, 4, 5]
unique = list(set(data))
print(unique) # [1, 2, 3, 4, 5] (urutan tidak dijamin)
Bagian 5 — Comprehensions
Diagram: Anatomi List Comprehension
flowchart LR
I[📋 iterable<br/>range/list] --> F{filter:<br/>if condition?}
F -->|Lulus| T[🔧 transform:<br/>expression]
F -->|Skip| X[❌]
T --> O[📦 list output]
Analogi: comprehension = conveyor belt pabrik.
- Item lewat conveyor (iterable)
- Inspector cek (if condition)
- Lulus → masuk mesin transform
- Hasilnya jatuh ke kotak output
Cara Pythonic bikin list/dict/set dari sequence.
List Comprehension
Bentuk: [expression for item in iterable if condition]
# Tanpa comprehension
kuadrat = []
for i in range(10):
kuadrat.append(i ** 2)
# Dengan comprehension (Pythonic)
kuadrat = [i ** 2 for i in range(10)]
Filter dengan Condition
nilai = [85, 60, 75, 90, 55]
# Cara biasa
lulus = []
for n in nilai:
if n >= 70:
lulus.append(n)
# Comprehension
lulus = [n for n in nilai if n >= 70]
If-Else dalam Comprehension
nilai = [85, 60, 75, 90, 55]
status = ["lulus" if n >= 70 else "gagal" for n in nilai]
print(status) # ['lulus', 'gagal', 'lulus', 'lulus', 'gagal']
Catatan: if-else di awal untuk transformasi, if di akhir untuk filter. Bisa kombinasi keduanya.
Nested Comprehension
# Flatten 2D list
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
Dict Comprehension
# Dict dari list
nama = ["Budi", "Ani", "Cici"]
panjang = {n: len(n) for n in nama}
print(panjang) # {'Budi': 4, 'Ani': 3, 'Cici': 4}
# Filter
nilai = {"Budi": 85, "Ani": 60, "Cici": 75}
lulus = {k: v for k, v in nilai.items() if v >= 70}
print(lulus) # {'Budi': 85, 'Cici': 75}
# Inversi
inversi = {v: k for k, v in nilai.items()}
Set Comprehension
# Set kuadrat unik
data = [1, 2, 2, 3, 3, 4]
kuadrat_unik = {x ** 2 for x in data}
print(kuadrat_unik) # {1, 4, 9, 16}
Generator Expression (Bonus)
Mirip list comprehension tapi pakai () — return generator (lazy, hemat memori):
# Sum 1 sampai 1 juta
sum([i for i in range(1_000_000)]) # bikin list dulu (boros memori)
sum(i for i in range(1_000_000)) # generator (hemat)
Bagian 6 — Performance & Big-O
Penting di AI: dataset besar, performance matters.
| Operasi | List | Dict | Set |
|---|---|---|---|
| Akses by index | O(1) | - | - |
| Akses by key | - | O(1) | - |
x in collection |
O(n) | O(1) | O(1) |
| Append/add | O(1) | O(1) | O(1) |
| Remove | O(n) | O(1) | O(1) |
| Search | O(n) | O(1) | O(1) |
Aturan praktis:
- Membership check (
x in lst) sering? → pakai set atau dict- Cari by key? → dict
- Urutan penting? → list
Contoh Optimasi
# Cek kata haram (10000 kata)
kata_haram = ["spam", "scam", ...] # list
text = "ini adalah spam jelas"
# JELEK — O(n×m), lambat untuk dataset besar
for kata in text.split():
if kata in kata_haram: # O(n) tiap iterasi
print("spam!")
# BAIK — convert ke set sekali
kata_haram_set = set(kata_haram)
for kata in text.split():
if kata in kata_haram_set: # O(1) tiap iterasi
print("spam!")
Common Mistakes & FAQ
❌ Mistake 1: b = a bukan copy
a = [1, 2, 3]
b = a # SAMA referensi
b.append(4)
print(a) # [1, 2, 3, 4] — ikut berubah!
# Fix:
b = a.copy() # atau a[:] atau list(a)
❌ Mistake 2: {} itu dict, bukan set kosong
empty = {} # ❌ ini dict
empty = set() # ✅ set kosong
s = {1, 2, 3} # ✅ set dengan element
❌ Mistake 3: Modify dict saat iterate
data = {"a": 1, "b": 2, "c": 3}
for k in data:
if data[k] < 2:
del data[k] # RuntimeError: dict changed size during iteration
# Fix:
for k in list(data.keys()): # iterate atas copy keys
if data[k] < 2:
del data[k]
❌ Mistake 4: Mutable as dict key
d = {[1, 2]: "value"} # ❌ TypeError: unhashable type: 'list'
# Fix: pakai tuple
d = {(1, 2): "value"} # ✅
Hanya immutable type bisa jadi dict key (str, int, float, tuple, frozenset).
❌ Mistake 5: Tuple satu element tanpa koma
t = (5) # ini int 5, BUKAN tuple!
t = (5,) # ini tuple
print(type(t)) # <class 'tuple'>
❌ Mistake 6: List comprehension overkill
# Susah dibaca, nested 3x
result = [x for sub in data for item in sub for x in item if x > 0]
# Pakai for loop biasa kalau lebih clear
result = []
for sub in data:
for item in sub:
for x in item:
if x > 0:
result.append(x)
FAQ
Q: Kapan pakai list vs tuple? A:
- List = collection homogen yang akan diubah (data dinamis)
- Tuple = record heterogen yang fixed (return multiple values, dict key)
Q: Kenapa dict.get() lebih baik dari dict[key]?
A: .get() tidak error kalau key tidak ada — return None (atau default yang kamu kasih). Aman untuk handle key opsional.
Q: Set vs list untuk dedup?
A: Set jauh lebih cepat untuk dedup (list(set(data))) dan untuk membership check (x in s). Tapi set tidak preserve urutan. Kalau urutan penting, pakai dict.fromkeys(data) (Python 3.7+).
Q: Generator expression vs list comprehension? A:
- List comp
[...]— buat list lengkap di memori. Cepat akses ulang. - Generator expr
(...)— lazy, hemat memori. Cuma bisa iterate sekali.
Untuk data besar yang cuma di-iterate sekali (sum, max, loop), pakai generator.
Q: Dict di Python 3.7+ ordered? A: Ya, sejak Python 3.7 dict mempertahankan insertion order. Tapi semantically dict tetap "unordered collection of key-value pairs" — tidak bergantung urutan untuk operasi.
Cek Pemahaman
- Tahu beda list, tuple, dict, set?
- Bisa slice list dengan step?
- Tahu kapan pakai
.get()vs["key"]di dict? - Bisa nested dict access?
- Bisa list comprehension dengan filter?
- Tahu kenapa cek membership di set lebih cepat dari list?
- Tahu pitfall
b = a(shallow reference)?
Challenge 2.4
Challenge 1 — Inventory System
inventory = [
{"nama": "Buku", "harga": 50000, "stok": 10},
{"nama": "Pulpen", "harga": 5000, "stok": 100},
{"nama": "Pensil", "harga": 3000, "stok": 50},
]
Bikin function:
total_value(inv)— total harga × stok semua barangcari_barang(inv, nama)— return dict barang atau Nonebarang_habis(inv)— list barang dengan stok < 5update_stok(inv, nama, delta)— tambah/kurangi stok
Challenge 2 — Word Frequency
Input: paragraph text Output: dict {kata: frekuensi}
Bonus: top 5 kata terbanyak (pakai sorted + lambda).
Challenge 3 — Anagram Detector
Cek apakah dua string adalah anagram (huruf sama, urutan beda):
is_anagram("listen", "silent") # True
is_anagram("hello", "world") # False
Hint: convert ke set atau sorted list.
Challenge 4 — Matrix Operations
matrix_a = [[1, 2], [3, 4]]
matrix_b = [[5, 6], [7, 8]]
Bikin function:
transpose(m)— transpose matrix (baris jadi kolom)add_matrix(a, b)— penjumlahan element-wiseflatten(m)— convert 2D ke 1D list
Pakai list comprehension.
Challenge 5 — Student Grades
students = {
"Budi": [85, 90, 78],
"Ani": [60, 70, 65],
"Cici": [95, 92, 98],
}
Bikin:
- Dict
{nama: avg}— rata-rata tiap siswa - Nama siswa dengan avg tertinggi
- Dict siswa lulus (avg >= 70)
- Subject report — list of
(siswa, subject_idx, nilai)untuk semua nilai >= 80
Challenge 6 — Set Operations
kelas_a = {"Budi", "Ani", "Cici", "Doni", "Eka"}
kelas_b = {"Cici", "Doni", "Fani", "Gugun"}
Cari:
- Siswa di kelas A saja
- Siswa di kelas B saja
- Siswa di kedua kelas
- Siswa di salah satu (tapi tidak keduanya)
- Total siswa unik
Challenge 7 — JSON-like Data Munging
orders = [
{"user": "Budi", "items": [{"name": "Buku", "qty": 2, "price": 50000}]},
{"user": "Ani", "items": [
{"name": "Pulpen", "qty": 5, "price": 5000},
{"name": "Pensil", "qty": 3, "price": 3000},
]},
{"user": "Budi", "items": [{"name": "Pulpen", "qty": 10, "price": 5000}]},
]
Hitung:
- Total transaksi tiap user
- Item terlaris (jumlah terbanyak)
- User dengan spending tertinggi
Challenge 8 — Comprehension Master
Pakai comprehension untuk:
- Bikin dict
{x: x**2 for x in range(10) if x % 2 == 0} - Bikin list pasangan
(i, j)untuk semua kombinasi i in 1-3, j in 1-3, dengan i != j - Hitung dict
{huruf: jumlah}dari string, hanya untuk huruf yang muncul ≥ 2 kali - Convert list of dicts jadi dict of lists:
[{"a": 1, "b": 2}, {"a": 3, "b": 4}] → {"a": [1, 3], "b": [2, 4]}
Selanjutnya: 05-string-handling.md — string manipulation, formatting, dan regex dasar. Krusial untuk text processing di NLP/LLM.