Pertanyaan klasik pada data deret waktu berbunyi: “berapa perubahan penjualan dibanding hari sebelumnya?”. Untuk menjawabnya, nilai “kemarin” harus ditempatkan berdampingan dengan nilai “hari ini” pada baris yang sama. shift() melakukan tepi itu: method ini menggeser seluruh nilai maju atau mundur sebanyak n posisi tanpa mengubah index tanggalnya. Pada chapter ini kita membahas arah pergeseran, pengisian posisi kosong dengan fill_value, perbedaan menggeser nilai versus menggeser index lewat freq, dan penerapannya untuk membandingkan total penjualan antarbulan.
Menggeser Nilai Antarbaris #
df["penjualan"].shift(1) menurunkan setiap nilai satu baris: nilai 2026-01-01 pindah ke baris 2026-01-02, dan seterusnya. Akibatnya baris pertama tidak memiliki pembanding dan diisi NaN, sedangkan dtype kolom hasil menjadi float64 karena harus menampung NaN. Arah sebaliknya, shift(-1), mengangkat nilai satu baris sehingga baris terakhir yang kosong. Dari situ, selisih harian cukup dihitung dengan pengurangan biasa antara kolom asli dan kolom hasil geser.
import numpy as np
import pandas as pd
tanggal = pd.date_range("2026-01-01", "2026-01-07", freq="D")
df = pd.DataFrame(
{"penjualan": [200, 202, 284, 286, 208, 210, 212]},
index=tanggal,
)
df.index.name = "tanggal"
df["kemarin"] = df["penjualan"].shift(1)
df["perubahan"] = df["penjualan"] - df["kemarin"]
print("Penjualan vs hari sebelumnya:")
print(df)
print("\nshift(1, fill_value=0) mengisi nilai awal:")
print(df["penjualan"].shift(1, fill_value=0))
print("\nshift(1, freq='D') menggeser index, bukan nilai:")
print(df["penjualan"].shift(1, freq="D"))
kalender = pd.date_range("2026-01-01", "2026-03-31", freq="D")
harian = pd.Series(
200 + 2 * np.arange(len(kalender)) + np.where(kalender.dayofweek >= 5, 80, 0),
index=kalender,
name="penjualan",
)
bulanan = harian.resample("ME").sum()
ringkas = pd.DataFrame({"penjualan": bulanan, "bulan_lalu": bulanan.shift(1)})
ringkas["perubahan"] = ringkas["penjualan"] - ringkas["bulan_lalu"]
print("\nPerbandingan total penjualan antarbulan:")
print(ringkas)Output yang Dihasilkan:
Penjualan vs hari sebelumnya:
penjualan kemarin perubahan
tanggal
2026-01-01 200 NaN NaN
2026-01-02 202 200.0 2.0
2026-01-03 284 202.0 82.0
2026-01-04 286 284.0 2.0
2026-01-05 208 286.0 -78.0
2026-01-06 210 208.0 2.0
2026-01-07 212 210.0 2.0
shift(1, fill_value=0) mengisi nilai awal:
tanggal
2026-01-01 0
2026-01-02 200
2026-01-03 202
2026-01-04 284
2026-01-05 286
2026-01-06 208
2026-01-07 210
Freq: D, Name: penjualan, dtype: int64
shift(1, freq='D') menggeser index, bukan nilai:
tanggal
2026-01-02 200
2026-01-03 202
2026-01-04 284
2026-01-05 286
2026-01-06 208
2026-01-07 210
2026-01-08 212
Freq: D, Name: penjualan, dtype: int64
Perbandingan total penjualan antarbulan:
penjualan bulan_lalu perubahan
2026-01-31 7850 NaN NaN
2026-02-28 8732 7850.0 882.0
2026-03-31 11508 8732.0 2776.0Mari kita telaah baris-baris kuncinya:
- Baris 4-9: DataFrame penjualan tujuh hari dibuat dari
date_rangeharian, laludf.index.name = "tanggal"memberi label pada kolom index agar repr lebih terbaca. - Baris 11 (
shift(1)): kolomkemarinberisi nilai hari sebelumnya; baris2026-01-01bernilaiNaNkarena tidak ada data sebelum tanggal itu. - Baris 12 (
perubahan):df["penjualan"] - df["kemarin"]menghitung selisih harian; angka82.0pada2026-01-03adalah lonjakan akhir pekan, dan-78.0pada2026-01-05adalah koreksi kembali ke hari kerja. - Baris 16-17 (
fill_value):shift(1, fill_value=0)mengganti posisi kosong dengan nol sehingga dtype tetapint64. - Baris 19-20 (
freq):shift(1, freq="D")menggeser index satu hari sedangkan nilainya tetap. - Baris 22-30 (analisis bulanan): dataset penuh Januari-Maret 2026 dibentuk dari rumus tren, diringkas
resample("ME").sum(), lalushift(1)menempelkan total bulan sebelumnya.
Mengisi Baris Tepi dengan fill_value #
Parameter fill_value hanya mengisi posisi yang kehilangan pasangannya akibat pergeseran, yaitu baris paling awal untuk shift(1). Perhatikan dua efeknya pada output: nilai awal menjadi 0, dan dtype kolom tetap int64 karena tidak lagi ada NaN. Gunakan opsi ini bila angka nol memang bermakna pada konteks data, misalnya stok habis atau hari toko tutup. Untuk penjualan, mengisi nol akan menekan rata-rata dan selisih pertama, jadi pastikan pilihan tersebut disengaja, bukan sekadar untuk menghilangkan NaN.
Menggeser Index, Bukan Nilai #
Bentuk shift(1, freq="D") sering tertukar dengan shift(1) biasa, padahal hasilnya berbeda total. Tanpa freq, yang berpindah adalah nilai: setiap nilai turun satu baris sehingga angka milik 2026-01-01 kini berada pada baris 2026-01-02, sementara index tidak berubah sama sekali. Dengan freq="D", yang berpindah adalah label index: seluruh tanggal maju satu hari (2026-01-02 hingga 2026-01-08) sementara urutan nilai tidak berubah. Bentuk dengan freq berguna ketika stempel waktu perlu dikoreksi, misalnya seluruh catatan tertulis mundur satu hari karena kesalahan sistem perekam.
Membandingkan Total Antarbulan #
Bagian akhir skrip menerapkan shift() pada granularitas yang lebih kasar. Dataset penuh dibentuk dari rumus 200 + 2 × nomor_hari + 80 untuk akhir pekan, lalu resample("ME").sum() menjumlahkannya per bulan. Kolom bulan_lalu berasal dari bulanan.shift(1), sehingga baris Februari memuat total Januari (7850.0) dan baris Maret memuat total Februari (8732.0). Kolom perubahan lalu menunjukkan pertambahan 882.0 unit pada Februari dan 2776.0 unit pada Maret — pola yang sama dengan analisis harian, hanya pada skala bulanan.
Dokumentasi pandas.DataFrame.shift
dan pandas.Series.shift
merinci parameter periods, freq, dan fill_value, sementara user guide timeseries
mengulas pergeseran dalam konteks deret waktu yang lebih luas. Selisih manual df["penjualan"] - df["kemarin"] pada chapter ini masih memerlukan dua langkah. Chapter berikutnya menyederhanakannya menjadi satu method: diff(), lengkap dengan turunannya untuk perubahan persentase.