Objektif

Mengenalkan R sebagai program analisis data untuk mengelola dan visualisasi data. Pengelolaan dan visualisasi data yang dilakukan diharapkan dapat memperoleh informasi yang dibutuhkan. Pada kesempatan ini akan dibahas membuat visualisasi menggunakan data kasus COVID-19 di berbagai negara dan juga Indonesia. Package yang akan digunakan adalah tidyverse.

Data & Script

Beberapa hal yang dibutuhkan pada kesempatan ini :

Program R dan RStudio, diharapkan minimal R dengan versi 3.6.2 dan beberapa package yang dibutuhkan.

Anda dapat memperoleh materi webinar ini (data, script dan file Rmd) di repository github.

  • Pilih Code (tombol berwarna hijau).
  • Kemudian pilih Download ZIP.
  • Setelah selesai download, ekstrak file zip tersebut dan double-click file kemenkes-coviz.Rproj.

Atau Anda juga dapat mengakses project tersebut yang sudah tersedia di RStudio Cloud https://rstudio.cloud/project/1492699. Anda perlu login atau membuat akun terlebih dahulu jika belum mempunyai akun RStudio Cloud. Semua materi dan package yang akan digunakan sudah tersedia.

Jika Anda ingin memperbarui data yang sudah disediakan, Anda dapat membuka dan menjalankan script pada file get_raw_data.R di dalam folder R. Tentu saja Anda perlu install terlebih dahulu package yang digunakan jika Anda belum mempunyai package tersebut pada library R Anda.

Package tambahan yang dibutuhkan untuk membuat visualisasi pada kesempatan ini adalah {tidyverse}; khususnya {dplyr}, {ggplot2}, dan {scales}. Jika Anda belum menginstall package tersebut, silahkan jalankan perintah berikut. Pastikan Anda mempunyai akses internet yang baik.

install.packages(c("tidyverse", "scales"))

Selanjutnya kita dapat menggunakan package tersebut dengan menjalankan

library(tidyverse)
library(scales)

Pertama, kita siapkan data yang akan digunakan dengan cara import data dari file CSV yang sudah disediakan di folder data. Ada 2 file yang akan digunakan, yaitu world_covid.csv dan dailynational.csv. Import data tersebut menjadi dataframe bernama world_covid menggunakan fungsi read.csv() dengan argument file = adalah lokasi dan nama file CSV yang akan diimport, header = TRUE karena baris pertama adalah nama kolom, dan stringsAsFactors = FALSE agar semua kolom bertipe character di file CSV diimport sebagai character, bukan sebagai factor. Untuk lebih jelas tentang factor Anda bisa membaca dokumentasi factor.

Kemudian kita lihat struktur dari dataframe (banyaknya observasi dan variable, nama variable dan tipenya) yang sudah diimport dengan fungsi str(). Data world_covid adalah data mengenai banyaknya kasus covid di setiap negara/negara bagian dan populasi penduduknya yang diambil dari https://www.worldometers.info/coronavirus dan https://www.worldometers.info/world-population.

world_covid <- read.csv(file = "../data-raw/world_covid.csv", header = TRUE, stringsAsFactors = FALSE, fileEncoding = "latin1")
str(world_covid)
## 'data.frame':    209 obs. of  29 variables:
##  $ country            : chr  "USA" "Brazil" "India" "Russia" ...
##  $ total_cases        : int  4371500 2419901 1436019 812485 445433 385036 379884 345790 319501 299426 ...
##  $ new_cases          : int  55791 23467 50525 5765 11233 6751 0 2198 0 745 ...
##  $ total_deaths       : int  149845 87052 32812 13269 6769 43374 18030 9112 28432 45752 ...
##  $ new_deaths         : int  447 556 716 77 114 729 0 92 0 14 ...
##  $ total_recovered    : int  2090129 1634274 918735 600250 265077 247178 263130 318095 0 0 ...
##  $ new_recovered      : int  28437 16794 32500 3110 2023 4486 0 1926 0 0 ...
##  $ active_cases       : int  2131526 698575 484472 198966 173587 94484 98724 18583 0 0 ...
##  $ serious_critical   : int  18986 8318 8944 2300 539 3922 1402 1592 617 104 ...
##  $ tot_cases_1m_pop   : int  13201 11379 1040 5567 7504 2984 11511 18078 6833 4409 ...
##  $ deaths_1m_pop      : int  453 409 24 91 114 336 546 476 608 674 ...
##  $ total_tests        : int  54203902 4911063 16291331 26902291 2773778 911853 2206105 1524533 6320836 14794369 ...
##  $ tests_1m_pop       : int  163689 23093 11797 184339 46729 7067 66847 79703 135187 217848 ...
##  $ population         : chr  "331,139,904" "212,665,350" "1,380,937,553" "145,939,049" ...
##  $ continent          : chr  "North America" "South America" "Asia" "Europe" ...
##  $ x1_caseevery_x_ppl : chr  "76" "88" "962" "180" ...
##  $ x1_deathevery_x_ppl: chr  "2,210" "2,443" "42,086" "10,998" ...
##  $ x1_testevery_x_ppl : chr  "6" "43" "85" "5" ...
##  $ fatality_rate      : num  0.0343 0.036 0.0228 0.0163 0.0152 ...
##  $ population_2020    : int  331002651 212559417 1380004385 145934462 59308690 128932753 32971854 19116201 46754778 67886011 ...
##  $ yearly_change      : num  0.0059 0.0072 0.0099 0.0004 0.0128 0.0106 0.0142 0.0087 0.0004 0.0053 ...
##  $ net_change         : int  1937734 1509890 13586631 62206 750420 1357224 461401 164163 18002 355839 ...
##  $ density_km2        : int  36 25 464 9 49 66 26 26 94 281 ...
##  $ land_area_km2      : num  9147420 8358140 2973190 16376870 1213090 ...
##  $ migrants_net       : int  954806 21200 -532687 182456 145405 -60000 99069 111708 40000 260650 ...
##  $ fert_rate          : num  1.78 1.74 2.24 1.82 2.41 ...
##  $ med_age            : int  38 33 28 40 28 29 31 35 45 40 ...
##  $ urban_pop_percent  : num  0.828 0.876 0.35 0.737 0.667 0.838 0.791 0.848 0.803 0.832 ...
##  $ world_share        : num  0.042 0.027 0.177 0.019 0.008 0.017 0.004 0.002 0.006 0.009 ...

Berikutnya kita lihat beberapa baris pertama dari data world_covid. Menggunakan fungsi head() untuk menampilkan beberapa baris pertama. Dengan n = 10 artinya menapilkan 10 baris pertama dari data world_covid.

head(world_covid, n = 10)

Top Fatality Rate Countries

Dengan visualisasi yang pertama ini, kita akan menjawab pertanyaan “Negara apa saja yang memiliki Fatality Rate tertinggi?” dengan chart seperti berikut.

Untuk menghasilkan visualisasi seperti di atas kita akan menggunakan data world_covid dengan country dan fatality_rate sebagai variable untuk membuat visualisasi berupa barchart. Data yang digunakan adalah seperti berikut. Karena data yang digunakan hanya nama negara (country) dan fatality_rate maka kita pilih variable tersebut dengan fungsi select().

data1 <- world_covid %>% 
  select(country, fatality_rate)
data1
ggplot(data = data1, mapping = aes(x = country, y = fatality_rate)) + 
  geom_bar(stat = "identity")

Sulit untuk memperoleh informasi yang baik dari hasil visualisasi di atas. Kita akan coba untuk membuat label nama negara-negara lebih terlihat dengan merubah posisi label nama negara miring 90 derajat.

# rotate x-axis label 90 deg
ggplot(data = data1, mapping = aes(x = country, y = fatality_rate)) + 
  geom_bar(stat = "identity") + 
  theme(axis.text.x = element_text(angle = 90))

Ternyata masih belum cukup terlihat dan kita belum bisa mendapatkan informasi apapun dari chart tersebut.

Kita akan coba untuk mengurutkan nama-nama negara berdasarkan Fatality Rate, yaitu variable fatality_rate. Kita gunakan fungsi reorder(country, fatality_rate) untuk mengurutkan barchart berdasarkan Fatality Rate.

# sorted barchart
ggplot(data = data1, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) + 
  geom_bar(stat = "identity") + 
  theme(axis.text.x = element_text(angle = 90))

Agar informasi dapat diperoleh dengan baik maka tidak semua negara akan kita tampilkan. Misalnya kita hanya perlu menampilkan daftar 20 negara berdasarkan Fatality Rate tertinggi. Dengan fungsi arrange() dan desc() dari package {dplyr} dan fungsi head() dengan n = 20 kita akan mendapatkan 20 negara dengan Fatality Rate tertinggi.

topn <- data1 %>% 
  arrange(desc(fatality_rate)) %>%
  head(n = 20)
topn

Selanjutnya dari data tersebut kita buat visualisasinya.

# sorted barchart
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) + 
  geom_bar(stat = "identity") + 
  theme(axis.text.x = element_text(angle = 90))

Kita sudah dapat menampilkan 20 negara dengan Fatality Rate tertinggi. Namun tentu saja untuk publikasi ataupun dari segi estetika masih perlu ditambahkan beberapa “aksesoris”.

Selanjutnya kita akan menambahkan judul chart dan merubah judul pada masing-masing sumbu x dan y. Kita dapat menambahkan judul chart dan masing-masing sumbu, kita dapat menggunakan fungsi labs(). Argumen title = untuk judul utama, x = untuk judul pada sumbu x, dan y = untuk judul sumbu y.

ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) + 
  geom_bar(stat = "identity") + 
  theme(axis.text.x = element_text(angle = 90)) +
  labs(title = "Top Fatality Rate Countries",
       x = "Country",
       y = "Fatality Rate")

Nilai Fatality Rate akan lebih mudah dimengerti jika ditampilkan dalam format persentase. Karena variable Fatality Rate ada pada sumbu y dan merupakan variable kontinu, maka kita dapat gunakan fungsi scale_y_continuous() dari {ggplot2}. Pada argumen label = kita gunakan fungsi percent_format() dari package {scales} agar tampilan label di sumbu Fatality Rate berupa persentase. Argumen accuracy = 1 pada fungsi percent_format() menandakan tidak ada angka dibelakang desimal.

# Custom axis scale 
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) + 
  geom_bar(stat = "identity") +
  theme(axis.text.x = element_text(angle = 90)) +
  labs(title = "Top Fatality Rate Countries",
       x = "Country",
       y = "Fatality Rate") + 
  scale_y_continuous(labels = scales::percent_format(accuracy = 1))

Jika Anda ingin format persentase dengan satu angka di belakang desimal, gunakan accuracy = 0.1. Jika ingin dua angka di belakang desimal maka accuracy = 0.01, dan seterusnya.

Saya pribadi lebih suka ketika membuat visualisasi seperti pada kasus ini, barchart dengan label yang cukup panjang, membuatnya menjadi barchart horizontal. Karena orang awam pun akan dengan mudah mendapatkan informasi yang ingin kita sampaikan melalui visualisasi tersebut. Untuk membuat barchart horizontal, kita dapat digunakan fungsi coord_flip() dari package {ggplot2}. Fungsi ini akan mentransformasi sumbu-x (horizontal) menjadi sumbu-y (vertikal), dan sebaliknya.

Coba perhatikan bar negara Yaman. Pada chart sebelumnya bar negara Yaman berada pada ujung kanan sumbu-x. Dengan fungsi coord_flip() menjadi di ujung atas sumbu-y. Artinya coord_flip() mentransformasi dengan titik pojok kiri bawah sebagai titik tumpunya.

# Flip barchart
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) + 
  geom_bar(stat = "identity") + 
  theme(axis.text.x = element_text(angle = 90)) +
  labs(title = "Top Fatality Rate Countries",
       x = "Country",
       y = "Fatality Rate") + 
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
  coord_flip()

Tampilan label pada sumbu Fatality Rate harusnya tetap horizontal. Maka kita tidak perlu menggunakan theme(axis.text.x = element_text(angle = 90)) lagi.

# Remove text angle 
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) + 
  geom_bar(stat = "identity") + 
  labs(title = "Top Fatality Rate Countries",
       x = "Country",
       y = "Fatality Rate") +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
  coord_flip()

Kita dapat mengatur sebuah theme dari ggplot2 agar dapat digunakan pada chart ggplot yang kita hasilkan berikutnya tanpa harus menuliskan + theme_minimal() di setiap script dengan theme_set(theme_minimal()). Namun pada kesempatan ini di setiap script yang menghasilkan chart dari ggplot2 akan dituliskan + theme_minimal().

# Custom theme 
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) + 
  geom_bar(stat = "identity") + 
  labs(title = "Top Fatality Rate Countries",
       x = "Country",
       y = "Fatality Rate") + 
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
  coord_flip() +
  theme_minimal()

Langkah berikutnya kita akan merubah warna bar dan grid dari chart tersebut.

Pertama kita rubah warna bar terlebih dahulu. Warna bar bisa kita rubah dengan menambahkan argumen fill =. Disini saya pribadi menggunakan warna "coral" dengan alpha = 0.7. Nilai alpha ini adalah tingkat transparansi warna atau opacity, antara 0 dan 1. Anda bisa memilih menggunakan warna lain seperti "red", "yellow", "green", "skyblue" dan lain-lain. Bisa juga menggunakan kode warna Hex (misalnya #E1404B) atau RGB (misalnya rgb(225, 64, 75, maxColorValue = 255)). Saya pribadi sering menggunakan color picker dari google search untuk memilih warna yang ingin digunakan.

# Change color & grid
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) + 
  geom_bar(stat = "identity", fill = "coral", alpha = 0.7) +
  labs(title = "Top Fatality Rate Countries",
       x = "Country",
       y = "Fatality Rate") +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) + 
  coord_flip() + 
  theme_minimal() 

Selanjutnya kita hilangkan garis major dan minor horizontal dari sumbu y dan garis minor vertikal dari sumbu x. Kita dapat melakukan ini dengan menggunakan fungsi theme() dari {ggplot2}. Argumen yang digunakan adalah panel.grid.major.y =, panel.grid.minor.y = dan panel.grid.major.x =. Fungsi element_blank() untuk setiap argumen tersebut menandakan atribut yang bersesuaian dengan argumen yang digunakan ditiadakan.

# Change color & grid
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) + 
  geom_bar(stat = "identity", fill = "coral", alpha = 0.7) +
  labs(title = "Top Fatality Rate Countries",
       x = "Country",
       y = "Fatality Rate") +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) + 
  coord_flip() + 
  theme_minimal() +
  theme(panel.grid.major.y = element_blank(),
        panel.grid.minor.y = element_blank(),
        panel.grid.minor.x = element_blank())

Kita sudah membuat chart yang cukup baik. Namun saya ingin menambahkan nilai Fatality Rate di setiap ujung bar agar informasi yang disampaikan lebih lengkap. Kita dapat gunakan fungsi geom_text() atau geom_label(), tinggal disesuaikan dengan selera masing-masing. Karena nilai dari variable fatality_rate antara 0 dan 1, maka perlu dikalikan 100 untuk mendapatkan nilai persentase sebagai text kemudian ditambahkan tanda “%” dengan fungsi paste0(). Untuk menyesuaikan ukuran text-nya kita gunakan argumen size =. Semakin besar nilai size = maka semakin besar juga ukuran text. Argumen hjust = -0.1 untuk mengatur posisi secara horizontal.

# Add label & y-limit 
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) + 
  geom_bar(stat = "identity", fill = "coral", alpha = 0.7) + 
  labs(title = "Top Fatality Rate Countries",
       x = "Country",
       y = "Fatality Rate") +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
  coord_flip() +
  theme_minimal() + 
  theme(panel.grid.major.y = element_blank(),
        panel.grid.minor.y = element_blank(),
        panel.grid.minor.x = element_blank()) + 
  geom_text(aes(label = paste0(fatality_rate*100, "%")), size = 3, hjust = -0.1)

Seperti yang dilihat, ada text yang terpotong karena sumbu Fatality Rate yang kurang panjang. Kita dapat mengatur batas (limits) dari sumbu Fatality Rate ini dengan menambahkan argumen limits = c(<min>, <max>) pada fungsi scale_y_continuous(). Nilai <min> dan <max> adalah pangkal dan batas ujung sumbu. Karena nilai fatality_rate antara 0 dan 1 dan saya ingin batasnya sampai nilai 30% maka limits = c(0, 0.30). Saya pilih 30% hanya agar semua text dari masing-masing bar tidak ada yang terpotong.

# Add label & y-limit 
ggplot(data = topn, mapping = aes(x = reorder(country, fatality_rate), y = fatality_rate)) + 
  geom_bar(stat = "identity", fill = "coral", alpha = 0.7) + 
  labs(title = "Top Fatality Rate Countries",
       x = "Country",
       y = "Fatality Rate") +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1), limits = c(0, 0.30)) +
  coord_flip() +
  theme_minimal() + 
  theme(panel.grid.major.y = element_blank(),
        panel.grid.minor.y = element_blank(),
        panel.grid.minor.x = element_blank()) + 
  geom_text(aes(label = paste0(fatality_rate*100, "%")), size = 3, hjust = -0.1)

Kita juga bisa membuat visualisasi seperti di atas berdasarkan banyaknya kasus yang terjadi di suatu negara.

# By Total Cases 
## top 20 barchart
top20 <- world_covid %>% 
  arrange(desc(total_cases)) %>%
  head(n = 20)

ggplot(data = top20, mapping = aes(x = reorder(country, total_cases), y = total_cases)) + 
  geom_bar(stat = "identity", fill = "coral", alpha = 0.7) +
  geom_text(aes(label = formatC(total_cases, big.mark = ",", decimal.mark = ".", format = "d")), size = 3, hjust = -0.1) + 
  coord_flip() + 
  labs(title = "Top 20 Total Cases Countries",
       x = "Country",
       y = "Total Cases") +
  theme_minimal() + 
  scale_y_continuous(labels = scales::number_format(big.mark = ","), limits = c(0, 5.0*10^6)) +
  theme(panel.grid.major.y = element_blank(),
        panel.grid.minor.y = element_blank(),
        panel.grid.minor.x = element_blank())

Indonesia Amongst ASEAN

Pada visualisasi kedua ini kita akan melakukan eksplorasi data world_covid dengan continent Asia saja. Kemudian melihat posisi Indonesia jika dibandingkan negara Asia dan ASEAN yang lain melalui visualisasi berupa bubble chart seperti berikut ini.

Untuk membuat bubble chart di ggplot2 kita dapat menggunakan geom_point(). Dasarnya adalah membuat scatter plot dengan geom_point() kemudian ukuran titiknya disesuaikan dengan nilai masing-masing. Untuk membuat scatter plot dibutuhkan dua variable numerik untuk menentukan koordinat x (horizontal) dan y (vertikal). Pada kasus ini kita akan menggunakan variable med_age yang merupakan median usia dari seluruh populasi penduduk di suatu negara sebagai sumbu x, dan variable fatality_rate sebagai sumbu y.

Kita buat dahulu sebuah vector character berisi nama-nama negara ASEAN dengan nama asean_list.

asean_list <- c("Philippines",
"Indonesia",
"Malaysia",
"Singapore",
"Thailand",
"Brunei",
"Vietnam",
"Laos",
"Myanmar",
"Cambodia")

Selanjutnya filter data dengan continent "Asia" saja dan urutkan berdasarkan nilai pada variable total_cases. Kemudian buat sebuah variable bernama label_negara. Variable ini berisi nama negara jika negara pada variable country ada diantara nama negara ASEAN (asean_list). Jika tidak, maka nilainya blank.

asean <- world_covid %>% 
  filter(continent == "Asia") %>% 
  arrange(total_cases) %>% 
  mutate(label_negara = case_when(country %in% asean_list ~ country,
                          TRUE ~ ""))

Setelah itu buat visualisasinya dengan x adalah med_age dan y adalah fatality_rate.

# Scatter plot
ggplot(data = asean, mapping = aes(x = med_age, y = fatality_rate)) + 
  geom_point() + 
  theme_minimal() 

Belum banyak informasi yang bisa kita peroleh dari visualisasi tersebut kecuali satu titik yang berada paling atas yang jauh dari titik yang lain.

Kita atur tingkat transparansi dari masing-masing titik tersebut agar dapat terlihat apakah ada titik yang bertumpuk dengan alpha = 0.5.

# Point opacity
ggplot(data = asean, mapping = aes(x = med_age, y = fatality_rate)) + 
  geom_point(alpha = 0.5) +
  theme_minimal() 

Ternyata ada beberapa negara yang nilai med_age dan fatality_rate-nya hampir sama. Terlihat dari adanya titik yang lebih tebal warnanya dibanding titik lain karena titiknya bertumpuk.

Berikutnya kita atur ukuran dari masing-masing titik sesuai dengan nilai kepadatan penduduk per Km2 pada negara tersebut, yaitu variable denisty_km2. Karena nilai yang akan kita gunakan adalah sebuah variable maka kita harus menuliskannya dalam sebuah mapping aes().

# Bubble chart size
ggplot(data = asean, mapping = aes(x = med_age, y = fatality_rate)) + 
  geom_point(aes(size = density_km2), alpha = 0.5) +
  theme_minimal()

Kita juga bisa merubah warna masing-masing titik dengan warna tertentu atau berdasarkan suatu variable dengan menambahkan color = "coral" misalnya, di luar aes(). Jika warna yang akan digunakan berdasarkan suatu variable dari data, maka argumen color = <variable> diletakkan di dalam aes(). Selain itu Anda juga bisa membuang legend dari grafik tersebut menggunakan theme(legend.position = "none").

# Buble chart color
ggplot(data = asean, mapping = aes(x = med_age, y = fatality_rate)) + 
  geom_point(aes(size = density_km2), color = "coral", alpha = 0.5) +
  theme_minimal() + 
  theme(legend.position = "none")

Tambahkan judul utama, judul sumbu. Ganti juga judul legend. Kita bisa lakukan hal ini dengan menggunakan fungsi labs().

# Custom title & axis title
ggplot(data = asean, mapping = aes(x = med_age, y = fatality_rate)) + 
  geom_point(aes(size = density_km2), color = "coral", alpha = 0.5) +
  theme_minimal() +
  theme(legend.position = "none") +
  labs(title = "Indonesia Amongst ASEAN",
       x = "Median Population Age",
       y = "Fatality Rate")

Ganti format teks pada sumbu Fatality Rate (vertikal) menjadi persentase. Gunakan fungsi scale_y_continuous() seperti pada chart sebelumnya.

# Buble chart custom axis title
ggplot(data = asean, mapping = aes(x = med_age, y = fatality_rate)) + 
  geom_point(aes(size = density_km2), color = "coral", alpha = 0.5) +
  theme_minimal() +
  theme(legend.position = "none") + 
  labs(title = "Indonesia Amongst ASEAN",
       x = "Median Population Age",
       y = "Fatality Rate") +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1))

Tambahkan label nama negara pada variable label_negara dengan menggunakan geom_text(aes(label = label_negara)).

# Buble chart add label
ggplot(data = asean, mapping = aes(x = med_age, y = fatality_rate)) + 
  geom_point(aes(size = density_km2), color = "coral", alpha = 0.5) +
  theme_minimal() +
  theme(legend.position = "none") + 
  labs(title = "Indonesia Amongst ASEAN",
       x = "Median Population Age",
       y = "Fatality Rate") +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
  geom_text(aes(label = label_negara))

Karena fokus kita adalah Indonesia diantara negara-negara ASEAN, maka Yaman/Yemen dapat kita sisihkan dengan memotong nilai Fatality Rate pada grafik dengan menambahkan limits = c(0, 0.06). Kemudian nama negara sedikit digeser ke atas dari titiknya dengan menggunakan argumen vjust = -0.5.

# Buble chart custom label position
ggplot(data = asean, mapping = aes(x = med_age, y = fatality_rate)) + 
  geom_point(aes(size = density_km2), color = "coral", alpha = 0.5) +
  theme_minimal() + 
  theme(legend.position = "none") + 
  labs(title = "Indonesia Amongst ASEAN",
       x = "Median Population Age",
       y = "Fatality Rate") +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1), limits = c(0, 0.06)) +
  geom_text(aes(label = label_negara), vjust = -0.5)

Warning yang muncul disebabkan ada data yang tidak masuk di dalam grafik karena penentuan limits pada scale_y_continuous().

Indonesia Daily Cases Trend

Terakhir, kita akan membuat chart yang dapat menunjukkan pola penambahan kasus harian di Indonesia. Data yang digunakan adalah data dailynational yg diolah dari sumber https://bnpb-inacovid19.hub.arcgis.com/ melalui API.

Kita import kemudian lihat struktur dan beberapa baris pertama dari data tersebut.

dailynational <- read.csv("../data-raw/dailynational.csv", header = TRUE, stringsAsFactors = FALSE)
dailynational$dates <- as.Date(dailynational$dates)
str(dailynational)
## 'data.frame':    147 obs. of  19 variables:
##  $ dates                      : Date, format: "2020-03-02" "2020-03-03" ...
##  $ total_cases                : int  2 2 2 2 4 4 6 19 27 34 ...
##  $ recovered                  : int  0 0 0 0 0 0 0 0 2 2 ...
##  $ deaths                     : int  0 0 0 0 0 0 0 0 0 1 ...
##  $ treated                    : int  2 2 2 2 4 4 6 19 25 31 ...
##  $ daily_cases                : int  2 0 0 0 2 0 2 13 8 7 ...
##  $ daily_recovered            : int  0 0 0 0 0 0 0 0 2 0 ...
##  $ daily_deaths               : int  0 0 0 0 0 0 0 0 0 1 ...
##  $ daily_treated              : int  2 0 0 0 2 0 2 13 6 6 ...
##  $ pct_recovered              : num  0 0 0 0 0 ...
##  $ pct_deaths                 : num  0 0 0 0 0 ...
##  $ pct_treated                : num  100 100 100 100 100 ...
##  $ examined_specimen          : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ total_examined_specimen    : int  339 341 372 388 450 454 483 543 694 793 ...
##  $ negative                   : int  335 337 356 371 422 422 445 487 648 744 ...
##  $ daily_new_specimen         : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ kasus_diperiksa_baru_harian: int  NA 2 31 16 62 4 29 60 151 99 ...
##  $ pdp                        : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ odp                        : int  NA NA NA NA NA NA NA NA NA NA ...
head(dailynational, n = 10)

Karena data yang dimiliki adalah laporan harian, terdapat tanggal, dan kita ingin mengetahui pola kasus harian, maka akan lebih tepat jika kita gunakan chart garis (line chart) yang biasa digunakan untuk visualisasi data deret waktu atau time series.

Kita akan menampilkan kasus penambahan baru harian (daily_cases), sembuh harian (daily_recovered), menginggal harian (daily_deaths) dan sebuah trend untuk kasus penambahan baru harian pada line chart tersebut seperti berikut ini.

Yang perlu kita ingat adalah struktur data yang kita gunakan seperti berikut.

dailynational %>% 
  select(dates, daily_cases, daily_recovered, daily_deaths) %>% 
  head(n = 10)

Dengan ggplot2 kita akan membuat sebuah line chart untuk masing-masing variable tersebut terhadap variable tanggal. Karena yang menjadi sumbu-x ( horizontal ) adalah variable tanggal untuk semua variable kasus, maka aes(x = dates) dapat kita tuliskan satu kali saja di argumen mapping pada fungsi ggplot(), sedangkan masing-masing variable kasus kita tuliskan di masing-masing geom_line().

ggplot(data = dailynational, mapping = aes(x = dates)) + 
  geom_line(aes(y = daily_cases, color = "New")) + 
  geom_line(aes(y = daily_recovered, color = "Recovered")) + 
  geom_line(aes(y = daily_deaths, color = "Deaths")) +
  theme_minimal()

Selanjutnya kita akan menyesuaikan warna untuk masing-masing kasus agar lebih cocok terhadap jenis kasusnya. Misalnya untuk kasus New kita gunakan warna kuning (yellow), Recovered dengan warna hijau (green), dan Deaths dengan warna merah (red). Tebalnya garis juga akan kita sesuaikan sehingga lebih tebal.

# Change color & line size
ggplot(data = dailynational, mapping = aes(x = dates)) + 
  geom_line(aes(y = daily_cases, color = "New"), size = 0.8) + 
  geom_line(aes(y = daily_recovered, color = "Recovered"), size = 0.8) + 
  geom_line(aes(y = daily_deaths, color = "Deaths"), size = 0.8) +
  theme_minimal() + 
  scale_color_manual(name = "Cases", # judul legend
                     values = c("New" = "yellow", 
                                "Recovered" = "green", 
                                "Deaths" = "red"))

Agar visualisasinya lebih baik selanjutnya kita tambahkan judul chart dan judul masing-masing sumbu.

# Add title and axis title
ggplot(data = dailynational, mapping = aes(x = dates)) + 
  geom_line(aes(y = daily_cases, color = "New"), size = 0.8) + 
  geom_line(aes(y = daily_recovered, color = "Recovered"), size = 0.8) + 
  geom_line(aes(y = daily_deaths, color = "Deaths"), size = 0.8) +
  theme_minimal() + 
  scale_color_manual(name = "Cases", # judul legend
                     values = c("New" = "yellow", 
                                "Recovered" = "green", 
                                "Deaths" = "red")) + 
  labs(title = "Indonesia Daily Cases Trend",
       x = "Dates",
       y = "Daily Cases")

Berikutnya kita rubah format label pada sumbu x (Dates) agar kita bisa mengetahui tanggalnya. Format yang akan digunakan adalah “dd/mm” karena tahunnya sama, yaitu 2020, maka tidak perlu ditampilkan. Karena nilai pada sumbu x adalah tanggal, maka kita dapat gunakan fungsi scale_x_date(). Kemudian argumen date_breaks = "2 weeks" artinya antar tanggal yang ditampilkan (major ticks) berjarak 2 minggu, sehingga minor ticks adalah 1 minggu.

# Change x-axis format
ggplot(data = dailynational, mapping = aes(x = dates)) + 
  geom_line(aes(y = daily_cases, color = "New"), size = 0.8) + 
  geom_line(aes(y = daily_recovered, color = "Recovered"), size = 0.8) + 
  geom_line(aes(y = daily_deaths, color = "Deaths"), size = 0.8) +
  theme_minimal() + 
  scale_color_manual(name = "Cases", # judul legend
                     values = c("New" = "yellow", 
                                "Recovered" = "green", 
                                "Deaths" = "red")) + 
  labs(title = "Indonesia Daily Cases Trend",
       x = "Dates",
       y = "Daily Cases") + 
  scale_x_date(date_breaks = "2 weeks", date_labels = "%d/%m")

Selanjutnya kita akan menambahkan sebuah garis trend pada chart di atas yang menunjukkan pola penambahan kasus baru harian di Indonesia. Yang kita butuhkan adalah data tanggal (dates) dan jumlah kasus penambahan harian (daily_cases). Untuk membuat data trend-nya kita gunakan Kernel Smoothing dengan fungsi ksmooth dari package {stats} yang sudah menjadi bagian dari base R ketika kita install R. Tidak ada alasan khusus yang saya gunakan sebagai pedoman ketika memilih fungsi ini. Ada beberapa metode lain yang dapat digunakan untuk membuat trend seperti yang ditulis di artikel ini.

# Add trend line
ks <- ksmooth(x = dailynational$dates, 
              y = dailynational$daily_cases, 
              kernel = "normal", 
              bandwidth = 10, 
              x.points = dailynational$dates)
dailynational$daily_trend <- ks$y

Kita tambahkan data trend tersebut menggunakan geom_line().

ggplot(data = dailynational, mapping = aes(x = dates)) + 
  geom_line(aes(y = daily_cases, color = "New"), size = 0.8) + 
  geom_line(aes(y = daily_recovered, color = "Recovered"), size = 0.8) + 
  geom_line(aes(y = daily_deaths, color = "Deaths"), size = 0.8) +
  theme_minimal() + 
  scale_color_manual(name = "Cases",
                     values = c("New" = "yellow", 
                                "Recovered" = "green", 
                                "Deaths" = "red", 
                                "Trend" = "grey")) + 
  labs(title = "Indonesia Daily Cases Trend",
       x = "Dates",
       y = "Daily Cases") + 
  scale_x_date(date_breaks = "2 weeks", date_labels = "%d/%m") + 
  geom_line(aes(y = daily_trend, color = "Trend"), size = 0.8)

Untuk visualisasi ini saya pribadi lebih suka jika legend-nya diletakan di atas chart. Karena itu saya tambahkan fungsi theme(legend.position = "top").

# Custom legend & position
ggplot(data = dailynational, mapping = aes(x = dates)) + 
  geom_line(aes(y = daily_cases, color = "New"), size = 0.8) + 
  geom_line(aes(y = daily_recovered, color = "Recovered"), size = 0.8) + 
  geom_line(aes(y = daily_deaths, color = "Deaths"), size = 0.8) + 
  theme_minimal() + 
  scale_color_manual(name = "Cases",
                     values = c("New" = "yellow", 
                                "Recovered" = "green", 
                                "Deaths" = "red", 
                                "Trend" = "grey")) + 
  labs(title = "Indonesia Daily Cases Trend",
       x = "Dates",
       y = "Daily Cases") + 
  scale_x_date(date_breaks = "2 weeks", date_labels = "%d/%m") +
  geom_line(aes(y = daily_trend, color = "Trend"), size = 0.8) + 
  theme(legend.position = "top")