Basis data relasional dan format kolom Parquet menutup kebutuhan penyimpanan yang tidak terlayani CSV, yaitu kueri terstruktur di satu sisi dan pelestarian tipe data pada volume besar di sisi lain. Topik ini membahas penulisan dan pembacaan tabel SQLite melalui to_sql dan read_sql tanpa library tambahan, pemetaan konsep SQL ke operasi pandas, serta bolak-balik Parquet dengan to_parquet dan read_parquet. Dataset yang dipakai tetap log sensor tiga ruangan, dan kini dibangkitkan dari generator deterministik sehingga seluruh keluaran dapat direproduksi.
Menulis tabel ke SQLite#
Metode to_sql menulis isi DataFrame menjadi tabel basis data, dan SQLite dipakai di sini karena modulnya ikut terpasang bersama Python tanpa instalasi tambahan. Blok berikut membangkitkan dataset log sensor, menulisnya menjadi tabel, lalu membaca lima baris pertamanya kembali.
import sqlite3
import numpy as np
import pandas as pd
rng = np.random.default_rng(42)
n = 96
df = pd.DataFrame({
"waktu": pd.date_range("2026-01-05 07:00", periods=n, freq="15min"),
"ruang": rng.choice(["Lab A", "Lab B", "Ruang Arsip"], n, p=[0.4, 0.35, 0.25]),
"suhu_c": np.round(rng.normal(24, 1.5, n), 1),
"lembap_pct": np.round(rng.normal(60, 5, n), 1),
"status_sensor": rng.choice(["normal", "kalibrasi", "rusak"], n, p=[0.85, 0.10, 0.05]),
})
kolom = ["ruang", "suhu_c", "lembap_pct", "status_sensor"]
con = sqlite3.connect("sensor.db")
df[kolom].to_sql("log_sensor", con, if_exists="replace", index=False)
tabel = pd.read_sql("SELECT * FROM log_sensor LIMIT 5", con)
print(tabel)
print(tabel.dtypes)
con.close() ruang suhu_c lembap_pct status_sensor
0 Ruang Arsip 21.5 68.6 normal
1 Lab B 21.8 59.1 normal
2 Ruang Arsip 22.0 58.1 normal
3 Lab B 22.5 67.3 normal
4 Lab A 24.6 54.5 normal
ruang str
suhu_c float64
lembap_pct float64
status_sensor str
dtype: objectKoneksi dibuka dengan sqlite3.connect("sensor.db"), dan berkas basis data yang belum ada dibuat otomatis olehnya. Parameter if_exists="replace" mengganti tabel lama dengan yang baru sehingga blok ini aman dijalankan berulang, sedangkan index=False mencegah index DataFrame ikut menjadi kolom tabel. Kolom waktu sengaja tidak ikut karena SQLite tidak memiliki tipe tanggal bawaan, sehingga tabel berfokus pada kolom pengukuran. Hasil bacaan menunjukkan tipe terjaga dengan baik, yaitu teks bertipe str dan angka bertipe float64.
Query dengan parameter#
Metode read_sql menjalankan pernyataan SELECT dan mengembalikan hasilnya sebagai DataFrame, dan parameter params mengisi tanda tanya pada query dengan nilai yang diberikan. Blok berikut menyaring baris terpanas yang berstatus normal, lalu merangkum tabel per ruangan dengan agregasi SQL.
import sqlite3
import pandas as pd
con = sqlite3.connect("sensor.db")
query = """
SELECT ruang, suhu_c, lembap_pct
FROM log_sensor
WHERE suhu_c > ? AND status_sensor = ?
ORDER BY suhu_c DESC
"""
hasil = pd.read_sql(query, con, params=(24.0, "normal"))
print(hasil.head(5))
ringkas = pd.read_sql(
"SELECT ruang, COUNT(*) AS jumlah, ROUND(AVG(suhu_c), 2) AS rata_suhu_c "
"FROM log_sensor GROUP BY ruang", con,
)
print(ringkas)
con.close() ruang suhu_c lembap_pct
0 Ruang Arsip 28.4 56.7
1 Lab A 27.2 59.3
2 Ruang Arsip 27.0 56.4
3 Lab B 26.7 61.7
4 Lab B 26.5 57.6
ruang jumlah rata_suhu_c
0 Lab A 36 23.73
1 Lab B 38 23.81
2 Ruang Arsip 22 24.26Tanda tanya pada query adalah penanda tempat nilai, dan pasangan nilainya diberikan lewat params dalam bentuk tuple. Nilai tidak pernah ditempelkan langsung ke string query, sehingga pembacaan aman dari penyuntikan SQL (SQL injection) dan terbebas dari masalah tanda kutip ketika nilainya mengandung karakter khusus. Query kedua memperlihatkan agregasi di sisi basis data, yaitu menghitung jumlah baris dan rata-rata suhu per ruangan sebelum data menyentuh pandas. Koneksi ditutup dengan con.close() setelah seluruh pembacaan selesai.
Peta konsep SQL dan pandas#
Operasi SELECT pada SQL memiliki padanan langsung pada operasi pandas, dan pemetaan ini mempermudah pembaca yang sudah terbiasa dengan basis data maupun yang sebaliknya. Tabel berikut menyusun padanan operasi yang paling sering dipakai pada dataset log sensor ini.
| SQL | pandas |
|---|---|
SELECT ruang, suhu_c | df[["ruang", "suhu_c"]] |
WHERE suhu_c > 24 | df[df["suhu_c"] > 24] |
ORDER BY suhu_c DESC | df.sort_values("suhu_c", ascending=False) |
GROUP BY ruang dengan AVG | df.groupby("ruang")["suhu_c"].mean() |
LIMIT 5 | df.head(5) |
JOIN antartabel | pd.merge pada modul Penggabungan Data |
Padanan WHERE adalah indeks boolean yang dibahas pada topik Indeks Boolean, sedangkan padanan GROUP BY beserta agregasinya dibahas pada topik Agregasi Dasar. Kedua pendekatan menghasilkan data yang sama, tetapi batas kerjanya berbeda. Agregasi berat sebaiknya ditumpangkan ke basis data lewat query agar data yang berpindah ke memori sudah ringkas, sedangkan penyaringan dan pengolahan lanjutan lebih lentur dilakukan pada pandas. Pengetahuan kunci join dari SQL juga tetap terpakai karena modul Penggabungan Data membahas merge dengan istilah yang sama, yaitu inner, left, right, dan outer.
Parquet mempertahankan dtype#
Format Parquet menyimpan tipe data setiap kolom bersama nilainya, sehingga bolak-balik to_parquet dan read_parquet tidak mengubah dtype sama sekali. Blok berikut membuktikannya pada dataset log sensor yang memuat kolom datetime, teks, dan angka sekaligus.
import numpy as np
import pandas as pd
rng = np.random.default_rng(42)
n = 96
df = pd.DataFrame({
"waktu": pd.date_range("2026-01-05 07:00", periods=n, freq="15min"),
"ruang": rng.choice(["Lab A", "Lab B", "Ruang Arsip"], n, p=[0.4, 0.35, 0.25]),
"suhu_c": np.round(rng.normal(24, 1.5, n), 1),
"lembap_pct": np.round(rng.normal(60, 5, n), 1),
"status_sensor": rng.choice(["normal", "kalibrasi", "rusak"], n, p=[0.85, 0.10, 0.05]),
})
df.to_parquet("log_sensor.parquet")
df_baca = pd.read_parquet("log_sensor.parquet")
print(df.dtypes)
print(df_baca.dtypes)
print(df.equals(df_baca))waktu datetime64[us]
ruang str
suhu_c float64
lembap_pct float64
status_sensor str
dtype: object
waktu datetime64[us]
ruang str
suhu_c float64
lembap_pct float64
status_sensor str
dtype: object
TrueDua dtypes pada keluaran identik dan equals mengembalikan True, yang berarti isi tabel sama persis sebelum dan sesudah disimpan. Kolom waktu tetap datetime64[us] tanpa konversi apa pun, sedangkan teks tetap str. Bandingkan dengan CSV yang menulis tanggal sebagai teks dan menuntut parse_dates saat dibaca ulang seperti pada topik read_csv Secara Mendalam. Sifat inilah yang membuat Parquet cocok menjadi format kerja, yaitu wadah data antartahap pipeline yang tidak menuntut perbaikan tipe setiap kali dibuka.
Ukuran berkas CSV versus Parquet#
Untuk data numerik besar, ukuran berkas Parquet jauh lebih kecil daripada CSV yang isinya setara. Blok berikut membangkitkan seratus ribu baris pengukuran sensor yang dibulatkan satu desimal, lalu membandingkan ukuran kedua berkasnya.
import os
import numpy as np
import pandas as pd
rng = np.random.default_rng(7)
besar = pd.DataFrame({
"suhu_c": np.round(rng.normal(24, 1.5, 100_000), 1),
"lembap_pct": np.round(rng.normal(60, 5, 100_000), 1),
"tekanan_hpa": np.round(rng.normal(1013, 5, 100_000), 1),
})
besar.to_csv("besar.csv", index=False)
besar.to_parquet("besar.parquet")
ukuran_csv = os.path.getsize("besar.csv")
ukuran_parquet = os.path.getsize("besar.parquet")
print(f"CSV : {ukuran_csv:,} byte")
print(f"Parquet : {ukuran_parquet:,} byte")
print(f"rasio : {ukuran_csv / ukuran_parquet:.1f} kali")CSV : 1,699,590 byte
Parquet : 320,439 byte
rasio : 5.3 kaliParquet berukuran sekitar seperlima CSV untuk data yang sama, dan keuntungannya bergerak mengikuti pola pada data. Dua hal menjelaskannya. CSV menyimpan setiap baris sebagai teks yang dipisahkan koma, sedangkan Parquet menyimpan nilai per kolom dalam bentuk biner yang dipadatkan secara otomatis dengan kompresi snappy sebagai nilai bawaannya. Pembulatan satu desimal juga membantu kompresi karena banyak nilai yang berulang dan mudah diringkas, dan pembacaan yang sama tanpa pembulatan hanya memberi rasio sekitar dua kali. Dengan selisih seperti ini, menyimpan data kerja dalam CSV berarti memboroskan ruang penyimpanan sekaligus kecepatan baca, kecuali berkasnya memang ditujukan untuk pertukaran antaralat.
Ringkasan#
to_sqlmenulis DataFrame menjadi tabel SQLite dengan koneksisqlite3tanpa SQLAlchemy, danread_sqlmembacanya kembali sebagai DataFrame bertipe terjaga.- Parameter
paramsmengisi tanda tanya pada query sehingga nilainya tidak ditempelkan ke string dan aman dari penyuntikan SQL. to_parquetdanread_parquetmempertahankan seluruh dtype, termasuk datetime dan teks, tanpa konversi apa pun.- Untuk data numerik besar, Parquet berukuran beberapa kali lebih kecil daripada CSV karena menyimpan kolom biner yang dipadatkan.
Referensi#
- pandas.read_sql — pandas documentation
— Parameter
read_sql, termasukparamsdan dukungan koneksi DBAPI sepertisqlite3. - pandas.read_parquet — pandas documentation
— Parameter
read_parquetdan perannya sebagai pembaca format Parquet. - Documentation — Apache Parquet — Dokumentasi resmi format kolom Parquet beserta model data dan kompresinya.