rolling dan expanding

Rata-rata bergerak dengan rolling dan statistik kumulatif dengan expanding.

Daftar isi

Bacaan mentah log sensor bergerak naik turun pada setiap lima belas menit, sehingga kecenderungan jangka pendeknya sulit dibaca tanpa perataan. Topik ini membahas rolling untuk rata-rata bergerak dalam jendela tetap, expanding untuk statistik kumulatif sejak awal deret, cara menggabungkan keduanya ke dalam tabel, dan pembuatan grafik garis dengan matplotlib. Setelah membaca, tren suhu pada dataset kontrak dapat ditampilkan berdampingan dengan nilai mentahnya.

Jendela empat bacaan dengan rolling#

Method rolling(window=4) membentuk window, yaitu jendela bergerak yang setiap posisinya memuat 4 baris terakhir termasuk baris saat ini, lalu mean() menghitung rata-ratanya. Pada log berjarak tetap lima belas menit, empat bacaan setara dengan satu jam, sehingga hasilnya lazim disebut rata-rata satu jam. Nilai bawaan min_periods sama dengan ukuran jendela, sehingga posisi yang jendelanya belum penuh menghasilkan NaN.

python Rata-rata bergerak empat bacaan dan min_periods
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"])
s = df["suhu_c"]
print(s.rolling(window=4).mean().head(6))
print(s.rolling(window=4, min_periods=1).mean().head(6))
text Keluaran
0       NaN
1       NaN
2       NaN
3    21.950
4    22.725
5    22.925
Name: suhu_c, dtype: float64
0    21.500000
1    21.650000
2    21.766667
3    21.950000
4    22.725000
5    22.925000
Name: suhu_c, dtype: float64

Blok pertama keluaran memperlihatkan tiga NaN awal karena jendela 4 bacaan baru penuh pada baris keempat, dan nilai 21.950 pada baris itu adalah rata-rata bacaan 21.5, 21.8, 22.0, dan 22.5. Blok kedua memakai min_periods=1, sehingga setiap posisi dihitung dari bacaan yang tersedia dan tidak ada baris yang kosong. Keduanya menyatu pada baris keempat dan seterusnya, sehingga pilihan min_periods hanya mengubah awal deret, bukan isi jendela yang sudah penuh.

Rata-rata kumulatif dengan expanding#

Method expanding().mean() menghitung rata-rata semua baris dari awal deret sampai baris saat ini, sehingga jendelanya tumbuh terus dan bukan tetap sepanjang 4 baris. Pada baris pertama, hasilnya sama dengan nilai mentahnya karena hanya ada satu bacaan, dan nilainya berubah makin lambat seiring bertambahnya banyak data. Method ini tidak menghasilkan NaN sejak baris pertama, berbeda dengan rolling pada nilai bawaannya.

python Rata-rata kumulatif dengan expanding
import pandas as pd

df = pd.read_csv("sensor_log.csv", parse_dates=["waktu"])
print(df["suhu_c"].expanding().mean().head(4))
text Keluaran
0    21.500000
1    21.650000
2    21.766667
3    21.950000
Name: suhu_c, dtype: float64

Keluaran menunjukkan rata-rata yang bergerak dari 21.500000 menuju 21.950000 selama empat bacaan pertama, dan angka 21.950000 yang terakhir sama persis dengan rata-rata jendela 4 bacaan pada bagian sebelumnya karena pada baris tersebut seluruh data yang ada adalah jendelanya. Fungsi lain seperti max dan min dapat dipanggil pada objek expanding yang sama dengan mekanisme tumbuh yang serupa. Statistik kumulatif ini menjawab pertanyaan rata-rata sejauh ini, sedangkan rolling menjawab rata-rata dalam satu jam terakhir.

Menggabungkan hasil ke tabel dan grafik#

Hasil rolling dan expanding ditambahkan sebagai kolom baru dengan penugasan df["nama_kolom"] = deret_hasil, sehingga tabel memuat nilai mentah beserta perataannya dalam satu baris per waktu. Kedua deret hasil memiliki urutan baris yang sama dengan tabel asalnya, sehingga penugasan kolom langsung selaras tanpa langkah penggabungan tambahan. Sepuluh baris pertama tabel berikut membandingkan ketiganya secara langsung.

python Menambahkan kolom rata-rata bergerak dan kumulatif
import pandas as pd

df = pd.read_csv("sensor_log.csv", parse_dates=["waktu"])
df["suhu_menit_60"] = df["suhu_c"].rolling(window=4).mean()
df["suhu_sejauh_ini"] = df["suhu_c"].expanding().mean()
kolom = ["waktu", "suhu_c", "suhu_menit_60", "suhu_sejauh_ini"]
print(df[kolom].head(10))
text Keluaran
                waktu  suhu_c  suhu_menit_60  suhu_sejauh_ini
0 2026-01-05 07:00:00    21.5            NaN        21.500000
1 2026-01-05 07:15:00    21.8            NaN        21.650000
2 2026-01-05 07:30:00    22.0            NaN        21.766667
3 2026-01-05 07:45:00    22.5         21.950        21.950000
4 2026-01-05 08:00:00    24.6         22.725        22.480000
5 2026-01-05 08:15:00    22.6         22.925        22.500000
6 2026-01-05 08:30:00    23.4         23.275        22.628571
7 2026-01-05 08:45:00    25.9         24.125        23.037500
8 2026-01-05 09:00:00    23.5         23.850        23.088889
9 2026-01-05 09:15:00    25.1         24.475        23.290000

Kolom suhu_menit_60 bernilai NaN pada tiga baris pertama sebagaimana keluaran bagian Jendela empat bacaan dengan rolling, sedangkan suhu_sejauh_ini terisi sejak baris pertama. Perbandingan baris demi baris terlihat langsung, misalnya bacaan mentah 25.9 pada pukul 08.45 disertai rata-rata satu jam 24.125 yang lebih landai. Kolom tambahan seperti ini tetap ikut tersalin ketika tabel difilter atau ditulis ulang, sehingga analisis berikutnya tidak mengulang perhitungannya.

Grafik garis memperlihatkan hubungan ketiga deret tersebut sekaligus, dan blok berikut membuatnya dengan matplotlib lalu menyimpannya sebagai berkas suhu-rolling.svg di direktori kerja. Deret mentah digambar tipis, sedangkan kedua rata-rata digambar lebih tebal agar kecenderungannya menonjol.

python Grafik nilai mentah, rata-rata bergerak, dan kumulatif
import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import pandas as pd

df = pd.read_csv("sensor_log.csv", parse_dates=["waktu"]).head(24)
plt.figure(figsize=(6.4, 2.8), constrained_layout=True)
plt.plot(df["waktu"], df["suhu_c"], linewidth=1.0, label="nilai mentah")
plt.plot(df["waktu"], df["suhu_c"].rolling(window=4, min_periods=1).mean(), linewidth=1.8, label="rata-rata bergerak 4 bacaan")
plt.plot(df["waktu"], df["suhu_c"].expanding().mean(), linewidth=1.8, label="rata-rata kumulatif")
plt.ylabel("suhu (derajat Celsius)")
plt.legend(bbox_to_anchor=(0.5, -0.42), loc="upper center", ncol=3, fontsize=8)
plt.savefig("suhu-rolling.svg")
print("tersimpan suhu-rolling.svg")
text Keluaran
tersimpan suhu-rolling.svg

Berkas suhu-rolling.svg memuat grafik 24 bacaan pertama, yaitu enam jam pertama log. Garis nilai mentah berayun paling tajam, garis rata-rata bergerak mengikutinya dengan ayunan yang dipangkas, dan garis kumulatif hampir mendatar karena seluruh riwayat ikut dirata-rata. Perbandingan visual ini menegaskan bahwa makin panjang jendela perataannya, makin landai pula deret hasilnya, dengan konsekuensi informasi detail yang makin sedikit.

Kesalahan umum#

Nilai NaN pada awal hasil rolling sering dikira data sensor yang hilang, padahal ia adalah jendela yang belum penuh. Kekeliruan ini berbahaya ketika jumlah NaN dipakai sebagai ukuran kualitas data, karena hitungannya ikut menaik sebesar ukuran jendela dikurangi satu. Blok berikut membandingkan hitungan NaN dari kedua sumber, lalu menunjukkan bahwa jendela menghitung baris dan bukan durasi waktu ketika deret berlubang.

python NaN akibat jendela dan jendela pada deret berlubang
import pandas as pd

df = pd.read_csv("sensor_log.csv", parse_dates=["waktu"])
rata = df["suhu_c"].rolling(window=4).mean()
print("NaN akibat jendela:", rata.isna().sum())
print("data hilang pada kolom asal:", df["suhu_c"].isna().sum())
berlubang = df["suhu_c"].head(6).drop(index=[2, 3])
print(berlubang.rolling(window=4, min_periods=4).mean())
text Keluaran
NaN akibat jendela: 3
data hilang pada kolom asal: 0
0       NaN
1       NaN
4       NaN
5    22.625
Name: suhu_c, dtype: float64

Hitungan 3 berbanding 0 menunjukkan bahwa seluruh NaN berasal dari jendela dan bukan dari data, sehingga min_periods=1 menghilangkannya bila jendela parsial dapat diterima. Deret berlubang menyisihkan bacaan pukul 07.30 dan 07.45, sehingga jendela 4 baris pada posisi terakhirnya merentang dari pukul 07.00 sampai 08.15 atau tujuh puluh lima menit, bukan empat puluh lima menit seperti pada deret utuh. Nilai 22.625 pada baris itu pun merata-ratakan baris yang berjauhan waktunya, sehingga jendela tetap perlu diperiksa terhadap kelengkapan waktu bila maknanya berbasis durasi.

Ringkasan#

  • rolling(window=4).mean() merata-ratakan 4 baris terakhir pada setiap posisi, dan min_periods=1 mengisi awal deret dengan jendela parsial.
  • expanding().mean() menghitung rata-rata seluruh riwayat sampai baris saat ini tanpa NaN, dengan jendela yang terus tumbuh.
  • Hasil keduanya ditambahkan sebagai kolom baru lewat penugasan df["nama_kolom"] = deret_hasil sehingga tabel memuat nilai mentah dan perataannya sekaligus.
  • NaN awal pada rolling adalah jendela yang belum penuh, dan jendela menghitung baris serta bukan durasi waktu pada deret yang berlubang.

Referensi#