Ketiga sumber dari Bagian 1 kini berdiri di titik yang sama: CSV dengan tanggal DD/MM/YYYY, JSON dengan harga_total dan label kota campur, serta tabel SQLite dengan kolom kasir yang tidak diperlukan. Tugas Bagian 2 adalah mengubah ketiganya menjadi satu DataFrame bersih yang mengikuti satu skema, lalu menghitung omzet per transaksi. Kita melakukan ini dalam enam langkah yang berurutan: menstandardisasi tiap sumber, memilih kolom bersama, menggabungkan dengan pd.concat(), membuang duplikat lintas sumber, merapikan index, dan menambahkan kolom total. Hasil akhirnya 19 baris bersih dari 25 baris mentah, lengkap dengan kolom sumber yang mencatat asal tiap baris.
Skema Bersama sebagai Target Transformasi #
Transformasi bermakna bila targetnya jelas. Kita menetapkan sembilan kolom berikut sebagai skema bersama; setiap sumber lalu diperlakukan sesuai jaraknya ke skema itu.
| Kolom target | Makna | Asal di tiap sumber |
|---|---|---|
id_transaksi | unique key gabungan | kode_trx, order_id, trx_id |
tanggal | datetime64[us] | DD/MM/YYYY, created_at + jam, tgl_trx teks |
produk, kategori | katalog tetap enam barang | produk, item, nama_barang |
kota | penulisan Title Case | kota, kota_tujuan, wilayah |
qty | int64 positif | qty, jml_barang, jml |
harga_satuan | int64 rupiah per barang | harga_satuan, harga, hasil bagi harga_total |
sumber | lineage: sistem asal baris | ditambahkan saat standarisasi |
Tiga langkah pertama skrip menyeragamkan tiap sumber secara terpisah. Laporan CSV hanya butuh satu rename() (kode_trx menjadi id_transaksi), pd.to_datetime() dengan format="%d/%m/%Y", normalisasi kota dengan str.strip().str.title(), dan filter harga_satuan > 0 yang membuang satu baris kesalahan perekaman. Sumber JSON adalah yang paling jauh dari skema: setelah rename(), kita saring qty > 0, hitung harga_satuan = harga_total // qty, ubah created_at menjadi tanggal dengan pd.to_datetime().dt.normalize() untuk membuang komponen jam, lalu buang kolom perantara created_at dan harga_total dengan drop(). Sumber SQLite dinormalisasi dengan cara yang sama, ditambah pembuangan kolom kasir.
Menggabungkan dan Membuang Duplikat Lintas Sumber #
Langkah keempat memilih kolom skema bersama dari ketiga DataFrame, lalu pd.concat() menyatukannya dengan ignore_index=True agar index baru berjalan 0 sampai 22 tanpa duplikasi nomor. Langkah kelima adalah inti pembersihan proyek ini: drop_duplicates(subset="id_transaksi", keep="last") mengenali empat salinan berlebih — satu baris yang tercatat ganda di dalam laporan CSV dan tiga transaksi Cabang B yang muncul di laporan maupun di database kasir. Argumen keep="last" membuat versi dari kasir_sqlite yang tersisa, karena database kasir dianggap sumber resmi transaksi Cabang B. Karena penghapusan meninggalkan index bolong, reset_index(drop=True) menata ulang nomor baris sebelum kolom total dihitung sebagai qty * harga_satuan.
"""Standardisasi tiga sumber, gabungkan, lalu bersihkan menjadi satu DataFrame."""
from __future__ import annotations
import tempfile
from pathlib import Path
import pandas as pd
from data_sumber import muat_csv, muat_json, muat_sqlite
def main() -> None:
with tempfile.TemporaryDirectory() as tmp:
folder = Path(tmp)
csv_raw = muat_csv(folder)
json_raw = muat_json()
sqlite_raw = muat_sqlite(folder)
# 1. Laporan CSV: seragamkan nama kolom, ubah format tanggal, saring harga 0.
csv_df = csv_raw.rename(columns={"kode_trx": "id_transaksi"})
csv_df["tanggal"] = pd.to_datetime(csv_df["tanggal"], format="%d/%m/%Y")
csv_df["kota"] = csv_df["kota"].str.strip().str.title()
csv_df = csv_df[csv_df["harga_satuan"] > 0]
csv_df["sumber"] = "laporan_csv"
# 2. JSON marketplace: seragamkan nama kolom, saring qty 0,
# turunkan harga satuan dari harga total.
json_df = json_raw.rename(
columns={
"order_id": "id_transaksi",
"item": "produk",
"kota_tujuan": "kota",
"jml_barang": "qty",
}
)
json_df = json_df[json_df["qty"] > 0]
json_df["harga_satuan"] = json_df["harga_total"] // json_df["qty"]
json_df["tanggal"] = pd.to_datetime(json_df["created_at"]).dt.normalize()
json_df["kota"] = json_df["kota"].str.strip().str.title()
json_df = json_df.drop(columns=["created_at", "harga_total"])
json_df["sumber"] = "marketplace_json"
# 3. SQLite kasir: seragamkan nama kolom, ubah tanggal teks, buang kolom kasir.
sqlite_df = sqlite_raw.rename(
columns={
"trx_id": "id_transaksi",
"tgl_trx": "tanggal",
"nama_barang": "produk",
"wilayah": "kota",
"jml": "qty",
"harga": "harga_satuan",
}
)
sqlite_df["tanggal"] = pd.to_datetime(sqlite_df["tanggal"])
sqlite_df = sqlite_df.drop(columns="kasir")
sqlite_df["sumber"] = "kasir_sqlite"
print("== Standarisasi per sumber ==")
print(f"laporan_csv : {len(csv_raw)} -> {len(csv_df)} baris (harga_satuan 0 dibuang)")
print(f"marketplace_json : {len(json_raw)} -> {len(json_df)} baris (qty 0 dibuang)")
print(f"kasir_sqlite : {len(sqlite_raw)} -> {len(sqlite_df)} baris (sudah lengkap)")
# 4. Pilih kolom skema bersama, lalu gabungkan ketiganya.
kolom_inti = [
"id_transaksi", "tanggal", "produk", "kategori", "kota", "qty", "harga_satuan",
]
csv_df = csv_df[kolom_inti + ["sumber"]]
json_df = json_df[kolom_inti + ["sumber"]]
sqlite_df = sqlite_df[kolom_inti + ["sumber"]]
gabung = pd.concat([csv_df, json_df, sqlite_df], ignore_index=True)
# 5. Deduplikasi lintas sumber memakai id transaksi.
bersih = gabung.drop_duplicates(subset="id_transaksi", keep="last")
dibuang = gabung[gabung.duplicated(subset="id_transaksi", keep="last")]
print("\n== Penggabungan dan deduplikasi ==")
print(f"gabungan 3 sumber : {len(gabung)} baris")
print(
f"duplikat id_transaksi : {len(dibuang)} dibuang"
f" ({dibuang['sumber'].value_counts().to_dict()})"
)
print(f"data bersih : {len(bersih)} baris")
bersih = bersih.reset_index(drop=True)
# 6. Kolom total sebagai omzet tiap baris, lalu rapikan urutan kolom.
bersih["total"] = bersih["qty"] * bersih["harga_satuan"]
bersih = bersih[
["id_transaksi", "tanggal", "produk", "kategori", "kota",
"qty", "harga_satuan", "total", "sumber"]
]
print("\n== Lima baris pertama data bersih ==")
print(bersih.head(5).to_string(index=False))
print(f"\nomzet keseluruhan: {int(bersih['total'].sum())}")
if __name__ == "__main__":
main()Output yang Dihasilkan:
== Standarisasi per sumber ==
laporan_csv : 10 -> 9 baris (harga_satuan 0 dibuang)
marketplace_json : 8 -> 7 baris (qty 0 dibuang)
kasir_sqlite : 7 -> 7 baris (sudah lengkap)
== Penggabungan dan deduplikasi ==
gabungan 3 sumber : 23 baris
duplikat id_transaksi : 4 dibuang ({'laporan_csv': 4})
data bersih : 19 baris
== Lima baris pertama data bersih ==
id_transaksi tanggal produk kategori kota qty harga_satuan total sumber
TRX-A-001 2026-01-14 Kopi Robusta Bubuk 200g Minuman Bandung 4 38500 154000 laporan_csv
TRX-A-002 2026-01-16 Gula Pasir 1kg Bahan Pokok Bandung 2 16000 32000 laporan_csv
TRX-A-003 2026-01-21 Beras Pandan Wangi 5kg Bahan Pokok Bandung 6 72000 432000 laporan_csv
TRX-A-005 2026-02-18 Teh Celup Melati 25 Minuman Bandung 5 12500 62500 laporan_csv
TRX-A-006 2026-02-21 Mie Goreng Instan 85g Bahan Pokok Bandung 5 3500 17500 laporan_csv
omzet keseluruhan: 2261000Mari kita telaah hasilnya:
- Rekap standarisasi: laporan CSV berubah 10 menjadi 9 baris, JSON 8 menjadi 7, dan SQLite tetap 7 — dua baris tidak valid terbuang di sumbernya masing-masing.
- Penggabungan: 23 baris masuk ke
pd.concat(), lalu 4 duplikatid_transaksidibuang;value_counts()pada baris yang dibuang menunjukkan keempatnya berasal darilaporan_csv. - Data bersih 19 baris: lima baris pertama memperlihatkan kolom
tanggalyang kini seragam,totalyang terisi, dan kolomsumbersebagai penanda lineage. - Omzet keseluruhan 2261000: penjumlahan kolom
totalyang menjadi angka rujukan validasi di Bagian 3.
Jembatan ke Validasi #
Skema sudah tertata, tetapi “sudah tertata” adalah klaim, bukan bukti. Bagian 3 membangun gerbang validasi yang menguji keberadaan kolom, tipe data, rentang nilai, keunikan id_transaksi, dan persamaan hasil dengan ekspektasi beku — sehingga klaim tersebut berubah menjadi pemeriksaan yang bisa diulang kapan pun.
Sumber bacaan resmi: pandas.DataFrame.rename , pandas.concat , dan pandas.DataFrame.drop_duplicates .