DQLab
DQLab merupakan platform belajar data science secara online yang dapat diakses melalui link berikut. Di dalam DQLab — hingga saat tulisan ini dibuat — sudah terdapat 33 Modules, 174 Chapters, dan 1153 Sub-Chapters.
Saya akan membahas salah satu mini project terbaru dari DQLab sebagai tantangan dalam DQWeekend pada minggu ini yaitu tentang Analisis Data COVID-19 di Indonesia.
Data mentor dalam topik bahasan ini adalah Muhammad Aswan Syahputra
Kita akan memanfaatan keterbukaan data publik dan teknik analisis data untuk memahami dinamika pandemi COVID-19. Sehingga outputnya kita akan belajar tentang:
COVID-19 merupakan penyakit yang saat ini telah menjadi pandemi secara global. Kondisi menjadi semakin mengkhawatirkan karena hingga detik ini masih belum ditemukan vaksin yang efektif untuk virus penyebab COVID-19. Pemerintah di berbagai negara umumnya dengan sigap membentuk gugus tugas (task force unit) untuk menangani penyebaran COVID-19 di masyarakat, termasuk pemerintah di Indonesia.
Salah satu bentuk aksi yang dilakukan oleh pemerintah adalah dengan mengumpulkan dan menyediakan data pertumbuhan kasus COVID-19 kepada publik. Data pertumbuhan kasus tersebut tidak jarang juga dilengkapi dengan dasbor dan grafik visualisasi pendukung dengan harapan masyarakat dapat memahami informasi dengan lebih mudah. Sebagai contoh adalah portal covid19.go.id besutan Gugus Tugas Penanganan COVID-19 Nasional dan portal PIKOBAR milik pemerintah Provinsi Jawa Barat. Serta banyak portal data COVID-19 lainnya yang disediakan oleh masing-masing pemerintah daerah.
Data yang digunakan adalah data yang disediakan di portal covid19.go.id. Adapun cara untuk mengambil data dari portal tersebut secara langsung adalah dengan menggunakan Application Programming Interface (API). Secara sederhana, API dapat dipahami sebagai cara komputer untuk berkomunikasi dengan komputer lainnya sehingga dapat terjadi transaksi pertukaran data.
library(httr)
resp <- GET("https://data.covid19.go.id/public/api/update.json")
Keterangan:
GET()digunakan untuk mengakses API yang diambil dari package httr dimana fungsi tersebut membuat sebuah permintaan kepada server penyedia API. Permintaan tersebut selanjutnya diproses dan dijawab oleh server sebagai sebuah respon.Mengapa penting? Karena dengan ini kita dapat mengetahui status permintaan pada API yang ingin diakses.
status_code(resp)
## [1] 200
Keterangan:
status_code() digunakan untuk mengetahui status permintaan. Hasilnya adalah 200, artinya permintaan sukses dipenuhi.Untuk mengetahui jenis-jenis status code lebih lengkap dapat diakses pada melalui link berikut.
Respon API dengan status code 200 menyatakan bahwa permintaan kita berhasil dipenuhi dan konten yang diminta tersedia untuk diekstrak. Konten yang diminta, tersedia dalam bentuk application/json yaitu berkas JSON yang dapat diekstrak dengan fungsi content() dengan argumen tambahan as = "parsed" dan simplifyVector = TRUE.
cov_id_raw <- content(resp, as="parsed", simplifyVector=TRUE)
Selanjutnya kita akan akan melihat terlebih dahulu berapa banyak komponen dalam berkas JSON yang kita ekstrak beserta namanya.
#Banyak komponen
length(cov_id_raw)
## [1] 2
#Nama komponen
names(cov_id_raw)
## [1] "data" "update"
Ekstrak kembali dengan membentuk variabel baru yaitu cov_id_update.
cov_id_update <- cov_id_raw$update
Kita akan fokus pada obyek cov_id_update untuk menjawab pertanyaan-pertanyaan berikut:
Untuk menjawab pertanyaan tersebut kita perlu mengetahui apa saja nama dalam objek dengan memanfaatkan fungsi lapply().
lapply(cov_id_update,names)
## $penambahan
## [1] "jumlah_positif" "jumlah_meninggal" "jumlah_sembuh" "jumlah_dirawat"
## [5] "tanggal" "created"
##
## $harian
## [1] "key_as_string" "key" "doc_count"
## [4] "jumlah_meninggal" "jumlah_sembuh" "jumlah_positif"
## [7] "jumlah_dirawat" "jumlah_positif_kum" "jumlah_sembuh_kum"
## [10] "jumlah_meninggal_kum" "jumlah_dirawat_kum"
##
## $total
## [1] "jumlah_positif" "jumlah_dirawat" "jumlah_sembuh" "jumlah_meninggal"
Keterangan:
names(cov_id_update), karena dalam lapply() kita akan menghasilkan tipe data list dengan melihat keseluruhan nama hingga ke dalam variabel penambahan, harian, total sedangkan apabila menggunakan names() yang kita hasilkan adalah tipe data character dengan hanya melihat nama objek penambahan, harian, total tanpa melihat lebih dalam.cov_id_update$penambahan$tanggal
## [1] "2020-08-01"
cov_id_update$penambahan$jumlah_sembuh
## [1] 2012
cov_id_update$penambahan$jumlah_meninggal
## [1] 62
cov_id_update$total$jumlah_positif
## [1] 109936
cov_id_update$total$jumlah_meninggal
## [1] 5193
Dari data yang kita peroleh, dapat diperoleh suatu kesimpulan sederhana bahwa:
Jumlah masyarakat yang terinfeksi dan meninggal akibat COVID-19 sudah sangat banyak! Kita harus waspada namun tetap tenang dalam menghadapi masa pandemi ini. Ingat 3 langkah sederhana melawan COVID-19 yaitu cuci tangan, pakai masker, dan jaga jarak.
Pada kali ini kita akan berfokus dengan data COVID-19 di Provinsi Jawa Barat. Alamat API untuk Provinsi Jawa Tengah dapat diakses dengan alamat API berikut https://data.covid19.go.id/public/api/prov_detail_JAWA_BARAT.json
resp_jabar <- GET("https://data.covid19.go.id/public/api/prov_detail_JAWA_BARAT.json")
status_code(resp_jabar)
## [1] 200
200 Yap! Artinya permintaan sukses dipenuhi.
Sekarang ekstrak isi konten.
cov_jabar_raw <- content(resp_jabar, as = "parsed", simplifyVector = TRUE)
Sekarang, kita jalankan fungsi names() pada cov_jabar_raw tersebut untuk mengetahui nama-nama elemen utama yang tersedia.
names(cov_jabar_raw)
## [1] "last_date" "provinsi" "kasus_total"
## [4] "kasus_tanpa_tgl" "kasus_dengan_tgl" "meninggal_persen"
## [7] "meninggal_tanpa_tgl" "meninggal_dengan_tgl" "sembuh_persen"
## [10] "sembuh_tanpa_tgl" "sembuh_dengan_tgl" "list_perkembangan"
## [13] "data"
cov_jabar_raw$kasus_total
## [1] 6532
cov_jabar_raw$meninggal_persen
## [1] 3.214942
cov_jabar_raw$sembuh_persen
## [1] 60.71647
Informasi umum mengenai COVID-19 di Jawa Barat telah kita dapatkan. Namun informasi akan lebih lengkap jika memiliki data perkembangan COVID-19 dari waktu ke waktu,
Data historis perkembangan COVID-19 tersebut tersimpan dengan nama list_perkembangan dalam alamat API yang kita buat. Mari kita ekstrak datanya dan lihat struktur datanya.
cov_jabar <- cov_jabar_raw$list_perkembangan
str(cov_jabar)
## 'data.frame': 153 obs. of 9 variables:
## $ tanggal : num 1.58e+12 1.58e+12 1.58e+12 1.58e+12 1.58e+12 ...
## $ KASUS : int 3 0 1 1 0 1 0 0 0 0 ...
## $ MENINGGAL : int 0 0 1 0 0 0 0 0 0 0 ...
## $ SEMBUH : int 0 0 0 0 0 0 0 0 0 0 ...
## $ DIRAWAT_OR_ISOLASI : int 3 0 0 1 0 1 0 0 0 0 ...
## $ AKUMULASI_KASUS : int 3 3 4 5 5 6 6 6 6 6 ...
## $ AKUMULASI_SEMBUH : int 0 0 0 0 0 0 0 0 0 0 ...
## $ AKUMULASI_MENINGGAL : int 0 0 1 1 1 1 1 1 1 1 ...
## $ AKUMULASI_DIRAWAT_OR_ISOLASI: int 3 3 3 4 4 5 5 5 5 5 ...
Selanjutnya lihat 6 observasi teratas.
head(cov_jabar)
## tanggal KASUS MENINGGAL SEMBUH DIRAWAT_OR_ISOLASI AKUMULASI_KASUS
## 1 1.583021e+12 3 0 0 3 3
## 2 1.583107e+12 0 0 0 0 3
## 3 1.583194e+12 1 1 0 0 4
## 4 1.583280e+12 1 0 0 1 5
## 5 1.583366e+12 0 0 0 0 5
## 6 1.583453e+12 1 0 0 1 6
## AKUMULASI_SEMBUH AKUMULASI_MENINGGAL AKUMULASI_DIRAWAT_OR_ISOLASI
## 1 0 0 3
## 2 0 0 3
## 3 0 1 3
## 4 0 1 4
## 5 0 1 4
## 6 0 1 5
Bukankah kita temukan kejanggalan?
Kejanggalan yang ditemukan antara lain, kolom tanggal dan format penulisan kolom yang tidak konsisten. Kita perlu melakukan proses Data Wrangling terlebih dahulu.
Apa itu Data Wrangling? Data Wrangling adalah proses membaca data dari berbagai sumber dan merubah isi serta struktur data sehingga siap untuk dianalisis.
Ada beberapa tahapan yang perlu dilakukan untuk menjinakan data cov_jabar, yaitu:
DIRAWAT_OR_ISOLASIKASUS menjadi kasus_baruMENINGGALSEMBUHlibrary(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
new_cov_jabar <-
cov_jabar %>%
select(-contains("DIRAWAT_OR_ISOLASI")) %>%
select(-starts_with("AKUMULASI")) %>%
rename(
kasus_baru = KASUS,
meninggal = MENINGGAL,
sembuh = SEMBUH
) %>%
mutate(
tanggal = as.POSIXct(tanggal/1000, origin="1970-01-01"),
tanggal = as.Date(tanggal)
)
#Melihat struktur data dari new_cov_jabar
str(new_cov_jabar)
## 'data.frame': 153 obs. of 4 variables:
## $ tanggal : Date, format: "2020-03-01" "2020-03-02" ...
## $ kasus_baru: int 3 0 1 1 0 1 0 0 0 0 ...
## $ meninggal : int 0 0 1 0 0 0 0 0 0 0 ...
## $ sembuh : int 0 0 0 0 0 0 0 0 0 0 ...
ggplot(new_cov_jabar, aes(tanggal, sembuh)) +
geom_col(fill = "olivedrab2") +
labs(
x = NULL,
y = "Jumlah kasus",
title = "Kasus Harian Sembuh Dari COVID-19 di Jawa Barat",
caption = "Sumber data: covid.19.go.id"
) +
theme_ipsum(
base_size = 13,
grid = "Y",
ticks = TRUE
) +
theme(plot.title.position = "plot")
ggplot(new_cov_jabar, aes(tanggal, meninggal)) +
geom_col(fill = "darkslategray4") +
labs(
x = NULL,
y = "Jumlah kasus",
title = "Kasus Harian Meninggal Akibat COVID-19 di Jawa Barat",
caption = "Sumber data: covid.19.go.id"
) +
theme_ipsum(
base_size = 13,
grid = "Y",
ticks = TRUE
) +
theme(plot.title.position = "plot")
Setelah mengamati grafik perkembangan kasus, ternyata terjadi fluktuasi pertambahan kasus harian. Dilandasi hal tersebut, kita ingin mencoba mengamati bagaimana perkembangan kasus dalam rentang waktu pekanan.
Kita dapat dengan mudah bekerja dengan data tanggal apabila menggunakan paket lubridate. Adapun yang akan digunakan untuk mengekstrak informasi pekan dalam satu tahun adalah fungsi week().
library(lubridate)
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
cov_jabar_pekanan <-
new_cov_jabar %>%
count(
tahun = year(tanggal),
pekan_ke = week(tanggal),
wt = kasus_baru,
name = "jumlah"
)
#Melihat struktur data
glimpse(cov_jabar_pekanan)
## Rows: 23
## Columns: 3
## $ tahun <dbl> 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020...
## $ pekan_ke <dbl> 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,...
## $ jumlah <int> 4, 2, 18, 45, 129, 185, 217, 238, 227, 338, 243, 151, 427,...
Setelah melakukan inspeksi terhadap data cov_jabar_pekanan, kita ingin memahami apakah pekan ini lebih baik dari pekan kemarin?
Untuk menjawab hal tersebut kita melakukan kalkulasi sederhana dengan tahapan berikut:
jumlah_pekanlalujumlah_pekanlalu dengan nilai 0jumlah dengan kolom jumlah_pekanlalu. Hasil komparasi ini disimpan dalam kolom baru dengan nama lebih_baik, isinya adalah TRUE apabila jumlah kasus baru pekan ini lebih rendah dibandingkan jumlah kasus pekan lalucov_jabar_pekanan <-
cov_jabar_pekanan %>%
mutate(
jumlah_pekanlalu = dplyr::lag(jumlah,1),
jumlah_pekanlalu = ifelse(is.na(jumlah_pekanlalu),0,
jumlah_pekanlalu),
lebih_baik = jumlah < jumlah_pekanlalu
)
#Melakukan Inspeksi dari cov_jabar_pekanan
glimpse(cov_jabar_pekanan)
## Rows: 23
## Columns: 5
## $ tahun <dbl> 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 20...
## $ pekan_ke <dbl> 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,...
## $ jumlah <int> 4, 2, 18, 45, 129, 185, 217, 238, 227, 338, 243, 1...
## $ jumlah_pekanlalu <dbl> 0, 4, 2, 18, 45, 129, 185, 217, 238, 227, 338, 243...
## $ lebih_baik <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FA...
Keterangan: Penulisan dengan dplyr::lag() digunakan untuk menghindari konflik dengan fungsi lag() dari paket stats.
Dengan menggunakan data hasil perhitungan tersebut maka sekarang kita dapat membuat bar-chat penambahan kasus pekanan yang ditambahkan informasi baru untuk menjawab pertanyaan
Apakah pekan ini lebih baik?
ggplot(cov_jabar_pekanan, aes(pekan_ke, jumlah, fill = lebih_baik)) +
geom_col(show.legend = FALSE) +
scale_x_continuous(breaks = 9:31,expand = c(0,0)) +
scale_fill_manual(values = c("TRUE" = "seagreen3", "FALSE" = "salmon")) +
labs(
x = NULL,
y = "jumlah kasus",
title = "Kasus Pekanan Positif COVID-19 di Jawa Barat",
subtitle = "Kolom hijau menunjukan penambahan kasus baru lebih sedikit dibandingkan satu pekan sebelumnya",
caption = "Sumber data: covid.19.go.id"
) +
theme_ipsum(
base_size = 13,
plot_title_size = 21,
grid = "Y",
ticks = TRUE
) +
theme(plot.title.position = "plot")
Ada yang akhirnya sembuh, namun tak sedikit pula yang meninggal akibat COVID-19. Sementara itu penambahan kasus baru terus terjadi di masyarakat. Hal ini mungkin memicu pertanyaan lain
Hingga saat ini ada berapa kasus yang masih aktif?
Aktif dalam artian sedang dalam perawatan atau isolasi.
Informasi ini telah disediakan di dalam respon API covid19.go.id yang kita minta.
Jumlah kasus aktif dapat dihitung dengan cara mengurangi jumlah akumulasi positif dengan jumlah akumulasi sembuh dan jumlah akumulasi meninggal.
Kita dapat menggunakan fungsi cumsum() untuk menghitung nilai akumulasi dari suatu vektor numerik.
cov_jabar_akumulasi <-
new_cov_jabar %>%
transmute(
tanggal,
akumulasi_aktif = cumsum(kasus_baru) - cumsum(sembuh) - cumsum(meninggal),
akumulasi_sembuh = cumsum(sembuh),
akumulasi_meninggal = cumsum(meninggal)
)
#Melihat 6 data terakhir
tail(cov_jabar_akumulasi)
## tanggal akumulasi_aktif akumulasi_sembuh akumulasi_meninggal
## 148 2020-07-26 3257 2550 201
## 149 2020-07-27 2917 2956 201
## 150 2020-07-28 2444 3563 201
## 151 2020-07-29 2491 3611 203
## 152 2020-07-30 2302 3945 205
## 153 2020-07-31 2356 3962 205
Membuat line-chart pola kasus aktif dengan menggunakan fungsi geom_line().
ggplot(data = cov_jabar_akumulasi, aes(x = tanggal, y = akumulasi_aktif)) +
geom_line()
Kita akan menggunakan fungsi gather() dari paket tidyr untuk mentransformasi data cov_jabar_akumulasi. Data tersebut akan dirubah dari yang semula berformat wide menjadi format long.
Manfaatnya adalah saat kita melakukan grafik komparasi antara kasus aktif, sembuh, dan meninggal.
library(tidyr)
cov_jabar_akumulasi_pivot <-
cov_jabar_akumulasi %>%
pivot_longer(
cols = -tanggal,
names_to = "kategori",
names_prefix = "akumulasi_",
values_to = "jumlah"
) %>%
mutate(
kategori = sub(pattern = "akumulasi_",replacement = "",kategori)
)
glimpse(cov_jabar_akumulasi_pivot)
## Rows: 459
## Columns: 3
## $ tanggal <date> 2020-03-01, 2020-03-01, 2020-03-01, 2020-03-02, 2020-03-0...
## $ kategori <chr> "aktif", "sembuh", "meninggal", "aktif", "sembuh", "mening...
## $ jumlah <int> 3, 0, 0, 3, 0, 0, 3, 0, 1, 4, 0, 1, 4, 0, 1, 5, 0, 1, 5, 0...
Kita akan membuat grafik komparasi antara akumulasi kasus aktif, kasus sembuh, dan kasus meninggal.
Caranya adalah dengan melakukan pivot pada data cov_jabar_akumulasi, kemudian buat satu layer geom_line() dengan menggunakan colour aesthetic.
ggplot(cov_jabar_akumulasi_pivot,aes(tanggal,jumlah,colour=(kategori))) +
geom_line(size=0.9) +
scale_y_continuous(sec.axis = dup_axis(name = NULL)) +
scale_colour_manual(
values = c(
"aktif" = "salmon",
"meninggal" = "darkslategray4",
"sembuh" = "olivedrab2"
),
labels = c("Aktif","Meninggal","Sembuh")
) +
labs(
x = NULL,
y = "Jumlah kasus akumulasi",
colour = NULL,
title = "Dinamika Kasus COVID-19 di Jawa Barat",
caption = "Sumber data: covid.19.go.id"
) +
theme_ipsum(
base_size = 13,
plot_title_size = 21,
grid = "Y",
ticks = TRUE
) +
theme(
plot.title = element_text(hjust = 0.5),
legend.position = "top"
)
Apakah jumlah kasus di pekan ini lebih rendah dibandingkan pekan kemarin dan dua pekan kemarin secara berturut-turut?
cov_jabar_pekanan <-
new_cov_jabar %>%
count(
tahun = year(tanggal),
pekan_ke = week(tanggal),
wt = kasus_baru,
name = "jumlah"
)
cov_jabar_2pekan <-
cov_jabar_pekanan %>%
mutate(
jumlah_pekanlalu = dplyr::lag(jumlah,1),
jumlah_pekanlalu = ifelse(is.na(jumlah_pekanlalu),0,
jumlah_pekanlalu),
jumlah_2pekanlalu = dplyr::lag(jumlah,2),
jumlah_2pekanlalu = ifelse(is.na(jumlah_2pekanlalu),0,
jumlah_2pekanlalu),
lebih_baik = (jumlah < jumlah_pekanlalu) & (jumlah < jumlah_2pekanlalu)
)
ggplot(cov_jabar_2pekan, aes(pekan_ke, jumlah, fill = lebih_baik)) +
geom_col(show.legend = FALSE) +
scale_x_continuous(breaks = 9:31,expand = c(0,0)) +
scale_fill_manual(values = c("TRUE" = "seagreen3", "FALSE" = "salmon")) +
labs(
x = NULL,
y = "jumlah kasus",
title = "Kasus Pekanan Positif COVID-19 di Jawa Barat",
subtitle = "Kolom hijau menunjukan penambahan kasus baru lebih sedikit dibandingkan pekan kemarin dan dua pekan kemarin secara berturut-turut",
caption = "Sumber data: covid.19.go.id"
) +
theme_ipsum(
base_size = 13,
plot_title_size = 21,
grid = "Y",
ticks = TRUE
) +
theme(plot.title.position = "plot")
Demikian bahasan yang dapat saya sajikan. Semoga bermanfaat.
Ucapan terima kasih saya berikan kepada DQLab dan Data Mentor Mas Muhammad Aswan Syahputra yang telah membuat Challenge minggu ini menarik dan juga teman saya yang turut membantu dan memberikan saran kepada saya yaitu Triardy Satria Wibawa serta Ryan Adi Nugroho.