filter dan apply pada Grup

Menyaring grup utuh dengan filter dan memakai apply untuk operasi lentur.

Daftar isi

Menyaring data pada level grup menuntut keputusan atas pecahan utuh, bukan atas baris tunggal. Topik ini membahas method filter yang mempertahankan atau membuang seluruh pecahan menurut satu syarat boolean per grup, serta apply yang menerima operasi lentur apa pun per grup. Setelah membaca, penyaringan grup dan operasi per grup yang tidak tercakup agg atau transform dapat dilakukan dengan tahu-menahu kapan keduanya dipilih.

filter memutuskan atas grup utuh#

Method filter pada objek groupby menerima satu fungsi yang dievaluasi satu kali per pecahan dan wajib mengembalikan satu nilai boolean. Grup yang lolos dipertahankan utuh dengan seluruh barisnya, sedangkan grup yang gagal dibuang seluruhnya, sehingga jumlah baris hasil filter adalah gabungan baris grup-grup yang lolos. Pola kerjanya berbeda dari mask boolean yang memutuskan per baris, karena keputusan filter berada satu tingkat di atasnya.

Sebagai contoh, ambang kelayakan berbasis sebaran suhu membutuhkan statistik per ruang terlebih dahulu. Tabel berikut menghitung rata-rata dan simpangan baku suhu untuk setiap ruang dengan agregasi bernama pada Series per grup.

python Statistik rata-rata dan simpangan per ruang
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_csv("sensor_log.csv", index=False)  # dataset bersama untuk seluruh modul
df = pd.read_csv("sensor_log.csv", parse_dates=["waktu"])
print(df.groupby("ruang")["suhu_c"].agg(suhu_rata="mean", simpangan="std").round(2))
text Keluaran
             suhu_rata  simpangan
ruang
Lab A            23.73       1.26
Lab B            23.81       1.56
Ruang Arsip      24.26       1.78

Simpangan baku ketiga ruang terbaca jelas, yaitu 1.26 untuk Lab A, 1.56 untuk Lab B, dan 1.78 untuk Ruang Arsip. Angka-angka inilah yang menjadi dasar ambang penyaringan pada bagian berikutnya, dan pilihan ambangnya menentukan ruang mana yang dipertahankan utuh.

Menyaring ruang dengan simpangan di bawah ambang#

Syarat std() < 1.5 pada fungsi filter mempertahankan hanya grup dengan sebaran suhu di bawah ambang. Fungsi lambda menerima satu pecahan DataFrame, menghitung simpangan baku kolom suhu_c pecahan itu, lalu membandingkannya dengan 1.5, dan seluruh keputusan itu berlangsung satu kali per grup.

python Menyaring grup dengan simpangan di bawah ambang
import pandas as pd

df = pd.read_csv("sensor_log.csv", parse_dates=["waktu"])
stabil = df.groupby("ruang").filter(lambda x: x["suhu_c"].std() < 1.5)
print(stabil["ruang"].value_counts())
print("baris awal:", len(df), "baris hasil filter:", len(stabil))
text Keluaran
ruang
Lab A    36
Name: count, dtype: int64
baris awal: 96 baris hasil filter: 36

Hasilnya hanya berisi Lab A dengan 36 baris, sedangkan Lab B dan Ruang Arsip dibuang utuh karena simpangannya melampaui ambang. Jumlah baris menyusut dari 96 menjadi 36, dan tidak ada satu baris pun dari grup yang lolos yang hilang. Mekanisme inilah yang perlu digarisbawahi, yaitu filter mempertahankan atau membuang seluruh pecahan dan bukan baris individual, sehingga hasilnya selalu gabungan utuh beberapa grup.

apply untuk operasi lentur per grup#

Method apply menerima fungsi apa pun yang menerima satu pecahan dan mengembalikan Series atau DataFrame, sehingga operasi yang tidak tercakup agg maupun transform tetap dapat dilakukan per grup. Contoh klasiknya adalah normalisasi min-max suhu per ruang, yang memetakan setiap bacaan ke rentang 0 sampai 1 berdasarkan nilai minimum dan maksimum ruangnya sendiri. Parameter group_keys=False menjaga index asal agar hasilnya dapat ditempelkan langsung sebagai kolom baru.

python Normalisasi min-max suhu per ruang dengan apply
import pandas as pd

df = pd.read_csv("sensor_log.csv", parse_dates=["waktu"])

def normalisasi(x):
    return (x - x.min()) / (x.max() - x.min())

df["suhu_norm"] = df.groupby("ruang", group_keys=False)["suhu_c"].apply(normalisasi).round(3)
print(df[["ruang", "suhu_c", "suhu_norm"]].head(4))
print("bentuk tabel:", df.shape)
text Keluaran
         ruang  suhu_c  suhu_norm
0  Ruang Arsip    21.5      0.000
1        Lab B    21.8      0.169
2  Ruang Arsip    22.0      0.072
3        Lab B    22.5      0.288
bentuk tabel: (96, 6)

Bacaan pertama Ruang Arsip bernilai 21.5 dan menjadi 0.000 karena itulah minimum suhu ruangnya, sedangkan bacaan Lab B 21.8 menerima 0.169 relatif terhadap rentang Lab B. Bentuk tabel bertambah satu kolom menjadi enam dan tetap 96 baris, sehingga hasil apply siap dipakai untuk perbandingan lintas ruang yang sebarannya berbeda.

apply sebagai pilihan terakhir#

Kelenturan apply punya harga, dan urutan keputusan yang sehat adalah agg terlebih dahulu, kemudian transform, lalu apply hanya bila keduanya tidak memenuhi kebutuhan. agg dan transform menjamin bentuk keluaran yang terprediksi, yaitu satu baris per grup atau sepanjang baris asal, sedangkan bentuk hasil apply sepenuhnya bergantung pada fungsi yang ditulis. Kecepatannya juga lebih lambat karena fungsi Python dijalankan satu per satu pada setiap pecahan tanpa vektorisasi penuh.

Pemakaian apply tetap wajar untuk kebutuhan yang benar-benar lentur, misalnya mengambil dua bacaan terpanas dari setiap ruang dengan nlargest di dalam fungsinya, atau operasi yang melibatkan beberapa kolom dengan logika bersyarat. Untuk kebutuhan rutin seperti rata-rata, jumlah, selisih dari rata-rata, dan normalisasi satu kolom, bentuk agg atau transform selalu lebih ringkas dan lebih cepat. Periksa dahulu apakah kebutuhannya terpenuhi oleh keduanya sebelum menulis fungsi apply, dan bila apply tetap dipilih, uji hasilnya dengan memeriksa jumlah baris dan indexnya seperti yang dicontohkan di atas.

Kesalahan umum#

Kesalahan khas filter adalah menulis fungsi yang mengembalikan nilai per baris, misalnya lambda x: x["suhu_c"] > 25, yang menghasilkan Series boolean sepanjang pecahan dan bukan satu keputusan per grup. Method filter menolak bentuk itu karena ia membutuhkan satu boolean untuk menentukan nasib grup. Perbaikannya adalah mereduksi pecahan menjadi satu angka terlebih dahulu, misalnya x["suhu_c"].mean() > 25 atau x["suhu_c"].std() < 1.5, sehingga keluarannya satu boolean per grup.

python Fungsi filter yang mengembalikan nilai per baris
import pandas as pd

df = pd.read_csv("sensor_log.csv", parse_dates=["waktu"])
try:
    df.groupby("ruang").filter(lambda x: x["suhu_c"] > 25)
except TypeError as e:
    print(type(e).__name__, ":", e)
text Keluaran
TypeError : filter function returned a Series, but expected a scalar bool

Pesan TypeError: filter function returned a Series, but expected a scalar bool menyebutkan masalahnya secara harfiah, yaitu fungsi mengembalikan Series padahal yang diharapkan satu bool. Bila tujuannya memang menyaring baris dengan suhu di atas 25 derajat, mask boolean biasa tanpa groupby adalah alat yang tepat, sedangkan filter dipakai hanya bila keputusannya memang menyangkut statistik grup.

Ringkasan#

  • groupby().filter(fungsi) mengevaluasi satu boolean per grup dan mempertahankan atau membuang seluruh pecahan, bukan baris individual.
  • Fungsi filter wajib mereduksi pecahan menjadi satu boolean seperti x["suhu_c"].std() < 1.5, dan bukan Series per baris.
  • apply menerima fungsi lentur apa pun per grup, misalnya normalisasi min-max, dengan group_keys=False untuk menjaga index asal.
  • Urutan keputusan yang sehat adalah agg, lalu transform, dan apply hanya bila keduanya tidak memenuhi kebutuhan.

Referensi#