Tata Bahasa Grafik

Mental model ggplot2: data, aestetik, dan geom sebagai lapisan yang tersusun.

Daftar isi

Topik ini memperkenalkan tata bahasa grafik (grammar of graphics), mental model yang menopang seluruh cara kerja paket ggplot2. Pembahasan mencakup tiga komponen pertamanya, yaitu data, aestetik (aesthetic), dan geom, perbedaan pemetaan (mapping) kolom data melalui fungsi aes() dengan pengaturan nilai tetap di luarnya, serta peran tanda + dalam menyusun lapisan (layer). Setelah topik ini dipelajari, grafik sebar sederhana dapat dibangun dari nol dan disimpan sebagai berkas SVG (Scalable Vector Graphics).

Tiga komponen pembentuk grafik#

Setiap grafik ggplot2 dibangun dari tiga komponen dasar, yaitu data, aestetik, dan geom. Data adalah tabel yang memuat angka yang akan digambarkan. Aestetik menghubungkan kolom data dengan properti visual, misalnya kolom waktu menuju posisi sumbu x dan kolom suhu menuju posisi sumbu y. Geom menentukan bentuk tampilannya, apakah titik, garis, batang, atau bentuk lainnya. Ketiganya dinyatakan dalam satu rangkaian pemanggilan fungsi yang tersusun berlapis.

Sebagai contoh, paket tidyverse dimuat terlebih dahulu agar ggplot2 beserta pendukungnya tersedia, kemudian data iris bawaan R diperiksa. Data ini memuat 150 pengukuran bunga iris dalam lima kolom, dan kolom Petal.Length serta Petal.Width yang mengukur kelopak menjadi bahan grafik pada topik ini. Fungsi glimpse() dari tibble menampilkan ringkasan kolomnya secara ringkas.

r Memuat tidyverse
library(tidyverse)
text Keluaran
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.2.1     ✔ readr     2.2.0
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ ggplot2   4.0.3     ✔ tibble    3.3.1
✔ lubridate 1.9.5     ✔ tidyr     1.3.2
✔ purrr     1.2.2
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
r Memeriksa struktur data iris
glimpse(iris)
text Keluaran
Rows: 150
Columns: 5
$ Sepal.Length <dbl> 5.1, 4.9, 4.7, 4.6, 5.0, 5.4, 4.6, 5.0, 4.4, 4.9, 5.4, 4.…
$ Sepal.Width  <dbl> 3.5, 3.0, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3.7, 3.…
$ Petal.Length <dbl> 1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4, 1.5, 1.5, 1.…
$ Petal.Width  <dbl> 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0.2, 0.…
$ Species      <fct> setosa, setosa, setosa, setosa, setosa, setosa, setosa, s…

Nama kolom pada keluaran glimpse() itulah yang kelak ditulis di dalam aes(). ggplot2 tidak menerima nomor kolom atau nama yang kira-kira mirip, sehingga ejaan persisnya perlu diperhatikan sejak awal.

Grafik pertama dari tiga komponen#

Grafik pertama disusun dengan pola yang sama untuk hampir semua kasus, yaitu ggplot(data, aes(...)) diikuti satu atau beberapa geom. Pemanggilan ggplot() mengikat data dan pemetaannya, sedangkan geom menambahkan lapisan tampilan di atas dasar grafik yang terbentuk. Kode berikut memetakan Petal.Length ke sumbu x, Petal.Width ke sumbu y, lalu menggambarkan setiap baris data sebagai satu titik melalui geom_point().

r Grafik sebar panjang dan lebar kelopak
p <- ggplot(iris, aes(x = Petal.Length, y = Petal.Width)) +
  geom_point()
ggsave("kelopak-sebar.svg", p, width = 5, height = 3.2)

Hasilnya disimpan sebagai berkas berikut.

0.00.51.01.52.02.5246Petal.LengthPetal.Width
Grafik sebar 150 pengukuran kelopak iris

Pada kode di atas, ggplot(iris, aes(...)) saja belum menggambar apa pun selain panel dan sumbu kosong, karena belum ada lapisan tampilan. Fungsi geom_point() baru menambahkan lapisan titik, dan tanda + menempelkan lapisan itu ke grafik. Tanda + berperan seperti operator penyusun, bukan penjumlah aritmetika, sehingga setiap baris kode menambahkan satu komponen baru. Apabila nama fungsi geom salah ketik, R melaporkan error karena komponen yang hendak ditempelkan tidak dikenal.

Nilai pada pemetaan berlaku untuk seluruh baris data sekaligus. Ekspresi aes(x = Petal.Length, y = Petal.Width) tidak mengulang per titik, melainkan menyatakan aturan baca, yaitu setiap baris dibaca dari kolom yang sama menuju properti yang sama. ggplot2 kemudian menerapkan aturan itu pada 150 baris iris tanpa perulangan eksplisit.

Pemetaan melalui aes() dan pengaturan nilai tetap#

Argumen yang nilainya berubah mengikuti data ditaruh di dalam aes(), sedangkan argumen yang nilainya sama untuk semua titik ditaruh langsung pada pemanggilan geom. Perbedaan ini adalah konsep paling sering keliru pada pemula. Warna biru untuk semua titik adalah satu nilai yang ditetapkan sendiri, sehingga tempatnya di luar aes().

r Grafik sebar dengan warna dan ukuran tetap
p_tetap <- ggplot(iris, aes(x = Petal.Length, y = Petal.Width)) +
  geom_point(color = "#2c7fb8", size = 2)
ggsave("kelopak-sebar-tetap.svg", p_tetap, width = 5, height = 3.2)

Hasilnya seluruh titik berwarna sama dan sama besar.

0.00.51.01.52.02.5246Petal.LengthPetal.Width
Grafik sebar dengan warna dan ukuran yang diatur tetap

Tidak ada legenda pada grafik warna tetap di atas karena tidak ada kolom data yang dipetakan. Perbandingan kedua letak argumen dirangkum pada tabel berikut.

Letak argumenMaknaContoh
Di dalam aes()pemetaan, nilainya mengikuti dataaes(color = Species)
Di luar aes()pengaturan tetap, nilainya sama untuk semua titikgeom_point(color = "steelblue")

Aturan praktisnya singkat. Apabila nilai berasal dari kolom data, tulis di dalam aes(). Apabila nilai ditentukan sendiri dan berlaku seragam, tulis sebagai argumen geom di luar aes(). Kanal lain seperti ukuran dan bentuk mengikuti aturan letak yang sama.

Kesalahan umum#

Kesalahan pertama adalah salah ketik nama kolom. Ekspresi di dalam aes() baru dinilai ketika grafik digambar, bukan ketika kodenya diketik, sehingga kesalahan ejaan baru terlihat pada saat print() atau ggsave() dijalankan.

r Kesalahan nama kolom yang baru terlihat saat digambar
p_salah <- ggplot(iris, aes(x = Petal.Lenght, y = Petal.Width)) +
  geom_point()
print(p_salah)
text Keluaran
Error in geom_point() : Problem while computing aesthetics.
ℹ Error occurred in the 1st layer.
Caused by error:
! object 'Petal.Lenght' not found

Pesan error menunjuk geom_point(), padahal akar masalahnya adalah ejaan Petal.Lenght yang tidak cocok dengan kolom mana pun pada iris. Perbaikannya adalah menulis ulang ejaan kolom sesuai data, dan ejaannya diperiksa dengan glimpse() atau names() terlebih dahulu apabila ragu.

Kesalahan kedua adalah menukar tempat argumen. Nilai tetap seperti "steelblue" yang ditulis di dalam aes() dibaca sebagai nama kolom, sehingga ggplot2 mencari kolom bernama steelblue yang memang tidak ada. Sebaliknya, nama kolom yang ditulis di luar aes() dibaca sebagai teks biasa, bukan sebagai rujukan data. Menempatkan argumen sesuai maknanya, yaitu pemetaan di dalam aes() dan pengaturan di luarnya, mencegah kedua kekeliruan tersebut.

Ringkasan#

  • Setiap grafik ggplot2 tersusun dari data, aestetik yang memetakan kolom ke properti visual, dan geom yang menentukan bentuk tampilannya.
  • Tanda + menyusun komponen menjadi lapisan berurutan, dan pola ggplot(data, aes(...)) + geom_*() berlaku untuk hampir semua grafik.
  • Nilai yang mengikuti data ditaruh di dalam aes(), sedangkan nilai tetap yang berlaku seragam ditaruh sebagai argumen geom di luarnya.
  • Ekspresi aes() dievaluasi saat grafik digambar, sehingga salah ketik nama kolom baru muncul sebagai error pada saat print() atau ggsave() dijalankan.

Referensi#