Line Plot & Smoothing  ·  Time Series  ·  RPubs

Dokumen ini menyajikan dua visualisasi intiline plot deret waktu harian dan smoothing (LOESS + Moving Average) — beserta interpretasi mendalam tentang pola, tren, dan siklus musiman yang tertangkap dari data penyewaan sepeda 2011 hingga 2012.

Observasi
731
Hari · Jan 2011 – Des 2012
Total Penyewaan
3.29M
Kumulatif 2 tahun
Rata-rata Harian
4,504
Penyewaan per hari
Pertumbuhan YoY
+66%
2011 ke 2012
Peak Harian
8,714
Maksimum dalam sehari

1. Persiapan Data

1.1 Library & Load Data

library(tidyverse)
library(lubridate)
library(zoo)
library(scales)
library(knitr)
library(kableExtra)
# Load dataset
bike_data <- read.csv("C:/Users/yolan/Downloads/bike_sharing_dataset_by-day.csv")

# Parsing & feature engineering
bike_data <- bike_data %>%
  mutate(
    dteday       = as.Date(dteday),
    yr_label     = factor(yr, labels = c("2011", "2012")),
    season_label = factor(season, levels = 1:4,
                          labels = c("Spring","Summer","Fall","Winter")),
    mnth_label   = factor(mnth, levels = 1:12, labels = month.abb),
    ma7  = rollmean(cnt, 7,  fill = NA, align = "right"),
    ma30 = rollmean(cnt, 30, fill = NA, align = "right"),
    ma60 = rollmean(cnt, 60, fill = NA, align = "right")
  )

cat("Periode  :", format(min(bike_data$dteday)), "s.d.",
                  format(max(bike_data$dteday)), "\n")
## Periode  : 2011-01-01 s.d. 2012-12-31
cat("Baris    :", nrow(bike_data), "| Kolom:", ncol(bike_data), "\n")
## Baris    : 731 | Kolom: 22
cat("Total cnt:", format(sum(bike_data$cnt), big.mark = ","), "\n")
## Total cnt: 3,292,679

1.2 Ringkasan Awal

bike_data %>%
  select(dteday, season_label, yr_label, temp, hum,
         casual, registered, cnt, ma7, ma30) %>%
  head(10) %>%
  kable(
    caption     = "Tabel 1 — 10 Observasi Pertama (dengan kolom Moving Average)",
    digits      = 3,
    col.names   = c("Tanggal","Musim","Tahun","Suhu","Kelembaban",
                    "Kasual","Terdaftar","Total","MA-7","MA-30"),
    format.args = list(big.mark = ",")
  ) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed"),
                full_width = TRUE, font_size = 13) %>%
  column_spec(8, bold = TRUE, color = "#00dcff") %>%
  row_spec(0, bold = TRUE, color = "#2d5070")
Tabel 1 — 10 Observasi Pertama (dengan kolom Moving Average)
Tanggal Musim Tahun Suhu Kelembaban Kasual Terdaftar Total MA-7 MA-30
2011-01-01 Spring 2011 0.344 0.806 331 654 985 NA NA
2011-01-02 Spring 2011 0.363 0.696 131 670 801 NA NA
2011-01-03 Spring 2011 0.196 0.437 120 1,229 1,349 NA NA
2011-01-04 Spring 2011 0.200 0.590 108 1,454 1,562 NA NA
2011-01-05 Spring 2011 0.227 0.437 82 1,518 1,600 NA NA
2011-01-06 Spring 2011 0.204 0.518 88 1,518 1,606 NA NA
2011-01-07 Spring 2011 0.197 0.499 148 1,362 1,510 1,344.714 NA
2011-01-08 Spring 2011 0.165 0.536 68 891 959 1,341.000 NA
2011-01-09 Spring 2011 0.138 0.434 54 768 822 1,344.000 NA
2011-01-10 Spring 2011 0.151 0.483 41 1,280 1,321 1,340.000 NA

2. Visualisasi 1 — Line Plot Deret Waktu Harian

2.1 Plot

Line plot ini menampilkan seluruh 731 titik observasi harian dari Januari 2011 hingga Desember 2012. Setiap titik pada garis mewakili total penyewaan sepeda dalam satu hari, sehingga fluktuasi jangka pendek maupun pola jangka panjang terlihat bersamaan dalam satu tampilan.

cb   <- "#04090f"
cp   <- "#08111f"
cg   <- "#0b1e36"
ct   <- "#5a88aa"
craw <- "#3ab8d8"

peak <- bike_data %>% slice_max(cnt, n = 1)
low  <- bike_data %>% slice_min(cnt, n = 1)

ggplot(bike_data, aes(x = dteday, y = cnt)) +
  geom_area(fill = craw, alpha = 0.07) +
  geom_line(color = craw, alpha = 0.55, linewidth = 0.5) +
  geom_point(data = peak, color = "#ff6b35", size = 3.5, shape = 18) +
  geom_point(data = low,  color = "#ff3e9d", size = 3.5, shape = 18) +
  annotate("text",
           x = peak$dteday - 18, y = peak$cnt + 320,
           label = paste0("Puncak: ", format(peak$cnt, big.mark = ",")),
           color = "#ff6b35", family = "mono", size = 3.2, hjust = 1) +
  annotate("segment",
           x = peak$dteday - 16, xend = peak$dteday - 2,
           y = peak$cnt + 230,   yend = peak$cnt + 60,
           color = "#ff6b35", linewidth = 0.4,
           arrow = arrow(length = unit(4,"pt"), type = "closed")) +
  annotate("text",
           x = low$dteday + 28, y = low$cnt - 300,
           label = paste0("Terendah: ", format(low$cnt, big.mark = ",")),
           color = "#ff3e9d", family = "mono", size = 3.2, hjust = 0) +
  geom_vline(xintercept = as.numeric(as.Date("2012-01-01")),
             color = "#1e3a55", linetype = "dashed", linewidth = 0.7) +
  annotate("text", x = as.Date("2012-01-12"), y = 8600,
           label = "Masuk 2012", color = "#2d5070",
           family = "mono", size = 3, hjust = 0) +
  scale_x_date(date_breaks = "2 months", date_labels = "%b\n%Y",
               expand = expansion(mult = 0.01)) +
  scale_y_continuous(labels = comma, breaks = seq(0, 9000, 1500),
                     expand = expansion(mult = c(0.02, 0.09))) +
  labs(
    title    = "Total Penyewaan Sepeda Harian — Januari 2011 s.d. Desember 2012",
    subtitle = "Setiap titik pada garis = 1 hari observasi  |  731 data point",
    x = NULL, y = "Jumlah Penyewaan (unit/hari)"
  ) +
  theme_minimal(base_family = "mono") +
  theme(
    plot.background  = element_rect(fill = cb, color = NA),
    panel.background = element_rect(fill = cp, color = NA),
    panel.grid.major = element_line(color = cg, linewidth = 0.45),
    panel.grid.minor = element_blank(),
    plot.title       = element_text(color = "#ddeeff", size = 14,
                                    face = "bold", margin = margin(b = 5)),
    plot.subtitle    = element_text(color = ct, size = 11, margin = margin(b = 18)),
    axis.text        = element_text(color = ct, size = 10),
    axis.title.y     = element_text(color = ct, size = 11, margin = margin(r = 10)),
    plot.margin      = margin(22, 22, 16, 22)
  )

2.2 Interpretasi Pola dari Line Plot

Pola 1 — Tren Naik Jangka Panjang

Dari kiri ke kanan plot, garis secara keseluruhan bergerak ke atas. Rata-rata harian di tahun 2011 berada di kisaran 2.900 penyewaan/hari, sementara di 2012 meningkat menjadi sekitar 5.600 penyewaan/hari — hampir dua kali lipat. Ini bukan lonjakan sesaat, melainkan pertumbuhan adopsi layanan yang konsisten sepanjang dua tahun. Garis pemisah tahun (putus-putus) memperjelas betapa berbedanya level dasar di 2011 dibanding 2012.
Pola 2 — Siklus Naik-Turun yang Berulang

Terlihat dua gelombang besar berbentuk busur — satu di 2011 dan satu di 2012. Garis dimulai rendah di awal tahun (Januari–Februari), naik perlahan menuju pertengahan tahun (Juni–September), lalu turun kembali menjelang akhir tahun (November–Desember). Pola ini berulang sangat konsisten di kedua tahun, membuktikan adanya komponen musiman tahunan (annual seasonality) yang kuat dan dapat diprediksi — dikendalikan oleh ritme cuaca dan suhu sepanjang tahun.
Pola 3 — Fluktuasi Harian yang Tinggi

Meski tren dan siklus musiman sudah terlihat, garis harian tampak bergerigi dan penuh naik-turun jangka pendek. Inilah yang disebut noise atau residual — variasi acak akibat faktor seperti hari kerja vs akhir pekan, cuaca mendadak berubah buruk, atau hari libur nasional. Amplitudo fluktuasi ini dapat mencapai ±2.000 unit dalam satu minggu, sehingga tanpa teknik smoothing, tren dan pola musiman sangat sulit dibaca secara visual.
Pola 4 — Titik Ekstrem (Outlier)

Terdapat beberapa titik yang turun drastis jauh di bawah kurva normal — terlihat sebagai celupan vertikal yang tajam. Ini hampir pasti disebabkan cuaca ekstrem seperti badai salju atau hujan lebat yang membuat orang enggan bersepeda. Sebaliknya, puncak tertinggi sebesar 8.714 unit terjadi di musim gugur 2012, ketika suhu paling nyaman bertepatan dengan layanan yang sudah semakin dikenal luas oleh masyarakat.

3. Visualisasi 2 — Smoothing: LOESS & Moving Average

3.1 Plot

Smoothing diterapkan untuk menekan noise harian dan memunculkan struktur tersembunyi dalam data. Tiga lapis smoothing digunakan dengan fungsi berbeda: MA-7 memperlihatkan dinamika mingguan, MA-30 mengungkap siklus bulanan, dan LOESS menangkap tren keseluruhan secara fleksibel tanpa asumsi bentuk kurva tertentu.

cma7 <- "#ff3e9d"
cma3 <- "#a8ff3e"
clos <- "#ff6b35"

ggplot(bike_data, aes(x = dteday, y = cnt)) +
  geom_line(color = craw, alpha = 0.16, linewidth = 0.45) +
  geom_smooth(method = "loess", span = 0.28,
              color = clos, fill = clos, alpha = 0.13,
              linewidth = 2.2, se = TRUE) +
  geom_line(aes(y = ma30), color = cma3, linewidth = 1.4, na.rm = TRUE) +
  geom_line(aes(y = ma7),  color = cma7, linewidth = 0.85, alpha = 0.85, na.rm = TRUE) +
  geom_vline(xintercept = as.numeric(as.Date("2012-01-01")),
             color = "#1e3a55", linetype = "dashed", linewidth = 0.7) +
  annotate("segment",
           x    = as.Date("2011-01-05"), xend = as.Date("2011-02-08"),
           y    = c(500, 850, 1200),     yend = c(500, 850, 1200),
           color = c(cma7, cma3, clos),  linewidth = c(0.85, 1.4, 2.2)) +
  annotate("text",
           x     = as.Date("2011-02-18"),
           y     = c(500, 850, 1200),
           label = c("MA-7  — Pola Mingguan",
                     "MA-30 — Siklus Bulanan",
                     "LOESS — Tren Global"),
           color = c(cma7, cma3, clos),
           hjust = 0, family = "mono", size = 3) +
  scale_x_date(date_breaks = "2 months", date_labels = "%b\n%Y",
               expand = expansion(mult = 0.01)) +
  scale_y_continuous(labels = comma, breaks = seq(0, 9000, 1500),
                     expand = expansion(mult = c(0.02, 0.09))) +
  labs(
    title    = "Smoothing Time Series — LOESS · MA-30 · MA-7",
    subtitle = "Noise harian diredam; tren dan siklus musiman terlihat jelas",
    x = NULL, y = "Jumlah Penyewaan (unit/hari)"
  ) +
  theme_minimal(base_family = "mono") +
  theme(
    plot.background  = element_rect(fill = cb, color = NA),
    panel.background = element_rect(fill = cp, color = NA),
    panel.grid.major = element_line(color = cg, linewidth = 0.45),
    panel.grid.minor = element_blank(),
    plot.title       = element_text(color = "#ddeeff", size = 14,
                                    face = "bold", margin = margin(b = 5)),
    plot.subtitle    = element_text(color = ct, size = 11, margin = margin(b = 18)),
    axis.text        = element_text(color = ct, size = 10),
    axis.title.y     = element_text(color = ct, size = 11, margin = margin(r = 10)),
    plot.margin      = margin(22, 22, 16, 22)
  )

3.2 Interpretasi Pola dari Smoothing

LOESS — Membaca Tren Makro Dua Tahun

Kurva LOESS (oranye, span = 0.28) adalah gambaran paling bersih dari tren jangka panjang tanpa terpengaruh noise mingguan. Kurva ini bergerak naik secara mulus dari sekitar 2.000 unit/hari di awal 2011 menuju hampir 6.000 unit/hari di puncak 2012. Yang penting diperhatikan: laju kenaikannya tidak linear — kurva lebih curam di 2012 dibanding 2011, mengindikasikan akselerasi pertumbuhan yang semakin cepat seiring bertambahnya pengguna. Confidence interval (area berbayang) yang sempit membuktikan tren ini stabil dan tidak mudah terganggu outlier.
MA-30 — Mengungkap Siklus Musiman Bulanan

Moving Average 30 hari (lime) meredam fluktuasi mingguan sehingga gelombang musiman bulanan menjadi sangat jelas. Di setiap tahun, kurva MA-30 membentuk pola bukit simetris: naik dari Januari, mencapai puncak di kisaran Agustus–September (musim gugur), lalu turun kembali ke Desember. Jarak antara lembah awal tahun dan puncak pertengahan tahun mencapai 2.500–3.000 unit/hari, membuktikan bahwa musim adalah faktor penentu permintaan yang sangat dominan. Di 2012, seluruh posisi kurva MA-30 berada di atas level 2011 — artinya pertumbuhan terjadi merata di semua bulan.
MA-7 — Mendeteksi Pola Mingguan dan Anomali

Moving Average 7 hari (merah muda) mempertahankan sebagian variasi jangka pendek, cocok untuk menganalisis dinamika mingguan. Terlihat osilasi kecil berperiodik setiap 7 hari — mencerminkan ritme hari kerja vs akhir pekan. Puncak MA-7 yang melebihi MA-30 secara signifikan mengindikasikan minggu dengan cuaca sangat baik atau periode liburan aktif. Sebaliknya, celupan MA-7 yang tajam jauh di bawah MA-30 adalah sinyal kuat cuaca ekstrem atau hari libur panjang yang menekan permintaan jangka pendek.
Membaca Ketiga Kurva Secara Bersamaan

Ketiga kurva dibaca sekaligus mengungkap tiga lapisan struktur aditif dalam data ini.

Lapisan 1 — Tren (LOESS): pertumbuhan bisnis jangka panjang yang berjalan perlahan namun konsisten.

Lapisan 2 — Musiman (MA-30): siklus naik-turun tahunan yang dikendalikan cuaca, suhu, dan perilaku pengguna.

Lapisan 3 — Variasi Mingguan (MA-7): respons cepat terhadap kondisi cuaca harian dan pola aktivitas manusia sehari-hari.

Pemahaman atas ketiga lapisan ini adalah fondasi utama untuk membangun model prediksi permintaan yang akurat di masa depan.

4. Ringkasan Statistik & Temuan

bike_data %>%
  group_by(Tahun = yr_label, Musim = season_label) %>%
  summarise(
    `Rata-rata/Hari` = round(mean(cnt)),
    `Minimum`        = min(cnt),
    `Maksimum`       = max(cnt),
    `Std. Deviasi`   = round(sd(cnt)),
    .groups = "drop"
  ) %>%
  kable(
    caption     = "Tabel 2 — Statistik Penyewaan per Musim dan Tahun",
    format.args = list(big.mark = ","),
    align       = c("c","l","r","r","r","r")
  ) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed"),
                full_width = TRUE, font_size = 13) %>%
  column_spec(3, bold = TRUE, color = "#00dcff") %>%
  column_spec(6, color = "#ff6b35") %>%
  row_spec(0, bold = TRUE, color = "#2d5070")
Tabel 2 — Statistik Penyewaan per Musim dan Tahun
Tahun Musim Rata-rata/Hari Minimum Maksimum Std. Deviasi
2011 Spring 1,667 431 3,239 614
2011 Summer 3,775 795 5,805 1,139
2011 Fall 4,464 1,115 6,043 798
2011 Winter 3,664 627 5,511 983
2012 Spring 3,531 441 7,836 1,341
2012 Summer 6,209 1,027 8,362 1,221
2012 Fall 6,824 4,073 8,714 913
2012 Winter 5,792 22 8,555 1,599
Kesimpulan Analisis Time Series

Dari kedua visualisasi, data bike sharing 2011–2012 memperlihatkan tiga karakteristik utama deret waktu yang bekerja secara simultan:

(1) Tren Naik — Ada pertumbuhan nyata dan konsisten. Line plot menunjukkan level dasar lebih tinggi di 2012, dikonfirmasi kemiringan positif kurva LOESS sepanjang periode.

(2) Musiman Tahunan — Pola musiman sangat dominan. MA-30 memperlihatkan gelombang hampir identik di 2011 dan 2012 — tinggi di musim panas–gugur (Juni–Oktober), rendah di musim dingin–semi (November–Maret).

(3) Variasi Mingguan dan Noise — Fluktuasi jangka pendek cukup besar namun terstruktur. MA-7 mengungkap pola hari kerja vs akhir pekan yang berulang; outlier bawah mencerminkan dampak cuaca ekstrem yang bersifat episodik.

Smoothing terbukti esensial untuk mengungkap lapisan-lapisan struktur yang tersembunyi di balik noise data harian.