Merhaba sevgili dostlar,
R Programlama Serimizin zirve noktasına, fragmanla duyurduğumuz, üzerine titrediğimiz başyapıtımıza hoş geldiniz. Bu sadece bir final projesi değil; bu, veriyle hikaye anlatma sanatının, karşılaşılan zorlukların ve o zorlukları aşmanın getirdiği zaferin hikayesidir.
Bu yolculukta amacımız, sadece dplyr
ile veriyi nasıl
işleyeceğimizi veya ggplot2
ile nasıl görselleştireceğimizi
göstermek değil. Amacımız, en başından sonuna, yaşayan, nefes alan,
interaktif ve en önemlisi anlamlı bir Shiny uygulaması
inşa etmektir. “Sayıların Ardındaki Trajedi” ismini seçtik, çünkü bu
projedeki her bir noktanın, her bir çizginin, milyonlarca insanın
hayatını etkileyen küresel bir krizi temsil ettiğini asla
unutmamalıyız.
En Önemli Kararımız: Sıfırdan ve Sağlam Başlamak
Bu projeyi geliştirirken biz, İCV ve Şanslı, inanılmaz zorluklarla karşılaştık. Kodlarımız defalarca çöktü, en güvendiğimiz fonksiyonlar bizi yanılttı, bazen bir hatanın kaynağını bulmak günlerimizi aldı.
İşte bu yüzden, bu ders notunda size o kaotik ve sancılı süreci değil, o sürecin sonunda ulaştığımız bilgeliği sunacağız. Size, tüm hatalarından arındırılmış, defalarca test edilmiş ve artık yıkılmaz olduğunu bildiğimiz bir temel üzerinde projemizi nasıl inşa ettiğimizi adım adım anlatacağız.
Hazırsanız, o beyaz sayfayı açalım ve kalemizin ilk, en sağlam taşını birlikte koyalım.
Her şeyin çöktüğü o umutsuz anda, en başa döndük ve kendimize tek bir soru sorduk: “Olabilecek en basit, tek bir işi hatasız yapan Shiny uygulaması nedir?” Cevap, aşağıdaki koddu. Bu kod, bizim yeniden doğuşumuzun, kalemizin temelinin atıldığı o sihirli andır.
Bu ilk adımda, sadece bir ülke seçip o ülkenin toplam vaka sayısının zaman içindeki seyrini gösteren, interaktif olmayan, temel bir grafik çizeceğiz.
# -----------------------------------------------------------------------------
# Ustalık Projesi - SIFIR NOKTASI
# Amaç: Tek bir işi hatasız yapan bir temel oluşturmak.
# -----------------------------------------------------------------------------
# --- 1. Alet Çantamız: Gerekli Kütüphaneler ---
# İşe başlamadan önce, projemizde kullanacağımız temel aletleri çağırıyoruz.
# Eğer bu paketler yüklü değilse, konsola install.packages("paket_adi") yazarak yükleyebilirsiniz.
library(shiny) # Projemizin iskeleti ve beyni
library(tidyverse) # Veri işleme (dplyr) ve görselleştirme (ggplot2) için can dostumuz
library(COVID19) # Bizi veri arama derdinden kurtaran kahraman paketimiz
# --- 2. Ham Madde: Veriyi Hazırlama ---
# 'covid19' paketini kullanarak güncel verileri çekiyoruz.
# Şimdilik sadece 'confirmed' (onaylanmış vaka) sütunundaki boşlukları (NA),
# bir önceki günün verisiyle dolduruyor (`fill`) ve geri kalanları 0 yapıyoruz.
covid_data <- covid19(verbose = FALSE) %>%
group_by(administrative_area_level_1) %>%
tidyr::fill(confirmed, .direction = "down") %>%
ungroup() %>%
mutate(confirmed = replace_na(confirmed, 0))
# Kullanıcının seçeceği ülke listesini önceden hazırlayalım.
ulke_listesi <- unique(covid_data$administrative_area_level_1)
# --- 3. Sahnemiz: Kullanıcı Arayüzü (UI) ---
# Burası, kullanıcının projemizle buluştuğu, her şeyi gördüğü yer.
ui <- fluidPage(
titlePanel("Tek Ülke Zaman Serisi Testi"),
# Sayfamızı ikiye bölen klasik düzen: Kenar çubuğu ve ana panel
sidebarLayout(
# Kenar çubuğu, kullanıcı kontrollerinin olduğu yerdir.
sidebarPanel(
# 'selectInput', kullanıcıya bir listeden seçim yapma imkanı sunar.
selectInput(inputId = "ulke_secimi", # Bu girdinin sunucudaki kimliği (ID'si)
label = "Lütfen bir ülke seçin:", # Kullanıcının göreceği etiket
choices = ulke_listesi, # Seçenekler, hazırladığımız ülke listesinden gelecek
selected = "Turkey") # Uygulama ilk açıldığında Türkiye seçili olsun
),
# Ana panel, grafik gibi çıktıların gösterildiği yerdir.
mainPanel(
# 'plotOutput', sunucuda oluşturulacak bir grafiğin yerini tutar.
plotOutput(outputId = "zaman_serisi_grafigi") # Bu grafiğin sunucudaki kimliği
)
)
)
# --- 4. Sahne Arkası: Sunucu (Server) Mantığı ---
# Tüm sihrin gerçekleştiği, hesaplamaların yapıldığı, grafiklerin çizildiği yer.
server <- function(input, output) {
# 'reactive', girdilere (input) bağımlı olan ve sadece o girdiler
# değiştiğinde yeniden çalışan sihirli bir kod bloğudur.
filtrelenmis_veri <- reactive({
covid_data %>%
# Kullanıcının 'ulke_secimi' menüsünde seçtiği değeri (input$ulke_secimi) al ve veriyi filtrele
filter(administrative_area_level_1 == input$ulke_secimi)
})
# 'renderPlot', bir ggplot grafiği oluşturan ve bunu arayüze gönderen fonksiyondur.
output$zaman_serisi_grafigi <- renderPlot({
# Reaktif ifadeden güncel veriyi çağır
cizilecek_veri <- filtrelenmis_veri()
# Bildiğimiz, sevdiğimiz temel ggplot2 grafiğini çiziyoruz.
ggplot(cizilecek_veri, aes(x = date, y = confirmed)) +
geom_line(color = "blue", size = 1) +
labs(
title = paste(input$ulke_secimi, "için Toplam Vaka Sayısı"),
x = "Tarih",
y = "Toplam Vaka Sayısı"
) +
theme_minimal()
})
}
# --- 5. Perde!: Uygulamayı Çalıştırma ---
# Arayüz (ui) ve sunucuyu (server) birleştirip gösteriyi başlatıyoruz.
shinyApp(ui = ui, server = server)
Önemli Not: Bu R Markdown dosyasını “Knit”
ettiğinizde (HTML’e çevirdiğinizde), Shiny uygulaması çalışmayacaktır,
sadece kodu ve açıklamaları gösterecektir. Bu kodu çalıştırmak için, kod
bloğunu kopyalayıp boş bir app.R
dosyasına yapıştırmanız ve
RStudio’da “Run App” düğmesine basmanız gerekir.
Eğer çalıştırmış iseniz aşağıdaki gibi bir ekran ile karşılacaksınız. Ülke seçimi yaptığınızda grafiklerin ülkelere göre değiştiğini göreceksiniz.
Temelimiz artık kaya gibi sağlam. Şimdi, bu temel üzerine ilk analitik katımızı, yani birden fazla ülkeyi birbiriyle karşılaştırabileceğimiz o muhteşem sekmeyi ekleme zamanı. Bu bölümde uygulamamıza sekmeli bir yapı kazandıracak ve o çok mücadele ettiğimiz ama sonunda mükemmel bir şekilde çözdüğümüz yatay bar grafiğini inşa edeceğiz.
Bu adımlar, basit bir uygulamayı, çok yönlü bir analiz aracına dönüştürmenin ilk adımlarıdır.
Bu adımda iki büyük yenilik yapıyoruz:
tabsetPanel
: Arayüzümüzü farklı
“odalara” ayırmamızı sağlayan bu sihirli fonksiyon ile uygulamamıza
sekmeli bir yapı kazandırıyoruz.# -----------------------------------------------------------------------------
# Ustalık Projesi - SAĞLAM TEMEL + 1. KAT
# Amaç: Sekmeli yapı ve sıralı, yatay karşılaştırma grafiği eklemek.
# -----------------------------------------------------------------------------
# --- 1. GEREKLİ KÜTÜPHANELER ---
library(shiny)
library(tidyverse)
library(COVID19)
# --- 2. VERİYİ HAZIRLAMA ---
# Bu sefer hem 'confirmed' hem de 'deaths' sütunlarını temizliyoruz.
# İlerleyen adımlarda 'deaths' de gerekecek, şimdiden hazırlayalım.
numeric_cols <- c("confirmed", "deaths")
covid_data <- covid19(verbose = FALSE) %>%
group_by(administrative_area_level_1) %>%
tidyr::fill(all_of(numeric_cols), .direction = "down") %>%
ungroup() %>%
mutate(across(all_of(numeric_cols), ~replace_na(., 0)))
ulke_listesi <- unique(covid_data$administrative_area_level_1)
# --- 3. KULLANICI ARAYÜZÜ (UI) ---
# Arayüzümüze sekmeli bir yapı ekliyoruz.
ui <- fluidPage(
titlePanel("Adım Adım İnşa Testi"),
# İşte sihirli fonksiyon: tabsetPanel!
tabsetPanel(
# İlk sekmemiz, az önceki çalışan grafiğimizin olduğu yer.
tabPanel("Tek Ülke Zaman Serisi",
sidebarLayout(
sidebarPanel(
# Input'ların ID'leri karışmasın diye ismini değiştiriyoruz.
selectInput("ulke_secimi_zaman", "Ülke Seçin:",
choices = ulke_listesi, selected = "Turkey")
),
mainPanel(
plotOutput("zaman_serisi_grafigi")
)
)
),
# YENİ SEKMEMİZ: Ülke Karşılaştırma
tabPanel("Ülke Karşılaştırma",
sidebarLayout(
sidebarPanel(
selectInput("ulke_secimi_karsilastirma", "Ülkeler Seçin:",
choices = ulke_listesi,
selected = c("Turkey", "Germany", "Italy"),
multiple = TRUE) # Bu argüman, çoklu seçime izin verir.
),
mainPanel(
plotOutput("karsilastirma_grafigi")
)
)
)
)
)
# --- 4. SUNUCU (SERVER) MANTIĞI ---
server <- function(input, output) {
# --- Grafik 1: Zaman Serisi (Aynen duruyor) ---
filtrelenmis_veri_zaman <- reactive({
covid_data %>%
filter(administrative_area_level_1 == input$ulke_secimi_zaman)
})
output$zaman_serisi_grafigi <- renderPlot({
ggplot(filtrelenmis_veri_zaman(), aes(x = date, y = confirmed)) +
geom_line(color = "blue", size = 1) +
labs(title = paste(input$ulke_secimi_zaman, "için Toplam Vaka Sayısı")) +
theme_minimal()
})
# --- Grafik 2: Karşılaştırma (YENİ VE GÜÇLENDİRİLMİŞ) ---
filtrelenmis_veri_karsilastirma <- reactive({
# validate ve need, kullanıcıya nazik bir mesajla ne yapması gerektiğini söyler.
validate(
need(input$ulke_secimi_karsilastirma, "Grafiği görmek için lütfen en az bir ülke seçin.")
)
covid_data %>%
filter(administrative_area_level_1 %in% input$ulke_secimi_karsilastirma) %>%
group_by(administrative_area_level_1) %>%
# Her ülke için en son tarihli veriyi almanın en sağlam yolu: slice_max()
slice_max(order_by = date, n = 1, with_ties = FALSE) %>%
ungroup() %>%
select(country = administrative_area_level_1, value = confirmed) %>%
# Adım 1: Veriyi ggplot'a göndermeden ÖNCE burada sırala
arrange(desc(value)) %>%
# Adım 2: Bu sırayı koruyacak bir 'faktör' oluştur
mutate(country = factor(country, levels = country))
})
output$karsilastirma_grafigi <- renderPlot({
ggplot(filtrelenmis_veri_karsilastirma(), aes(x = country, y = value)) +
geom_col(fill = "red") +
# Adım 3: Grafiği yan çevirerek okunabilirliği artır
coord_flip() +
labs(title = "Seçilen Ülkelerin Toplam Vaka Sayıları", x = "", y = "Toplam Vaka") +
# Adım 4: En büyük değerin en üstte olmasını garantile
scale_x_discrete(limits = rev(levels(filtrelenmis_veri_karsilastirma()$country))) +
theme_minimal()
})
}
# --- 5. UYGULAMAYI ÇALIŞTIRMA ---
shinyApp(ui = ui, server = server)
tabsetPanel
,
tabPanel
): Arayüzümüzü artık farklı analizlere
ayırdık. Bu, projemiz büyüdükçe her şeyi düzenli ve anlaşılır tutmamızı
sağlayacak.multiple = TRUE
):
Karşılaştırma sekmesindeki selectInput
’a bu sihirli
argümanı ekleyerek, kullanıcıya analizine istediği kadar ülkeyi dahil
etme gücü verdik.slice_max
):
Bu projedeki en büyük derslerimizden biri bu fonksiyonda saklı. Her
ülkenin kendi içindeki en güncel verisini almanın en sağlam ve hatasız
yolu budur. Bu, bizi saatlerce süren hata ayıklama kabuslarından
kurtaran anahtar oldu.arrange
,
mutate(factor)
): Grafiği çizmeden önce veriyi
kendi istediğimiz gibi sıralayarak, ggplot
’un işini
kolaylaştırdık ve sıralamanın her zaman doğru olacağını
garantiledik.coord_flip
):
Dikey sütunlar yerine yatay barlar kullanarak, uzun ülke isimlerinin
bile birbirine girmeden, şık ve okunaklı bir şekilde görünmesini
sağladık.Bu adımla, uygulamamız artık sadece tek bir hikaye anlatan bir araç değil, farklı hikayeleri karşılaştırabilen, çok daha yetenekli bir analiz platformuna dönüştü.
R script dosyanıza ekleyip son tekrar çalıştırdığınızda (tabii ki # — 5. UYGULAMAYI ÇALIŞTIRMA —shinyApp(ui = ui, server = server) kısmı en sonda olacak şekilde yapınız dostlar.) karşınıza aşağıdaki gibi iki sekmeli bir ekran gelecektir. İkinci sekmede istediğiniz ülkeleri seçerek bar grafiği görebilirsiniz.
Uygulamamız artık hem sağlam hem de interaktif. Teknik olarak güçlü bir noktadayız. Ama iyi bir veri bilimci, sadece doğru analizler yapmakla kalmaz, aynı zamanda bu analizleri en anlaşılır ve en etkili şekilde sunar.
Şimdi projemize, bir kullanıcının uygulamayı açtığı ilk anda “Vay be, bu profesyonel bir iş!” diyeceği o son dokunuşları ekleme zamanı. Bu bölümde, projemize iki hayati parça ekleyeceğiz:
Bu iki ekleme, projemizi teknik bir araç olmaktan çıkarıp, kullanıcı dostu ve hikaye anlatan bir platforma dönüştürecek. Bu bölümün sonunda, uygulamamızın o ana kadarki tüm özelliklerini içeren tam kodunu bulacaksınız.
Bu adımda, ui
fonksiyonumuzu shinythemes
,
fluidRow
, column
ve uiOutput
gibi
yeni araçlarla zenginleştiriyoruz. server
tarafında ise, bu
dinamik arayüz elemanlarının içini dolduracak renderUI
fonksiyonlarını ve gerekli veri hesaplamalarını ekliyoruz.
Aşağıdaki kod, bu bölümdeki tüm değişiklikleri içeren ve projemizin o ana kadarki en güncel, tam ve çalışan halidir.
# -----------------------------------------------------------------------------
# Ustalık Projesi - VİTRİNİ HAZIRLAMA (TAM SÜRÜM)
# Amaç: KPI Kartları ve Proje Hakkında sekmesini eklenmiş tam kod.
# -----------------------------------------------------------------------------
# --- 1. GEREKLİ KÜTÜPHANELER ---
library(shiny)
library(shinythemes) # EKLENDİ: Uygulamaya hazır temalar sunar.
library(tidyverse)
library(plotly)
library(scales)
library(COVID19)
# --- 2. VERİYİ HAZIRLAMA ---
numeric_cols <- c("confirmed", "deaths")
covid_data <- covid19(verbose = FALSE) %>%
group_by(administrative_area_level_1) %>%
tidyr::fill(all_of(numeric_cols), .direction = "down") %>%
ungroup() %>%
mutate(across(all_of(numeric_cols), ~replace_na(., 0)))
ulke_listesi <- unique(covid_data$administrative_area_level_1)
# --- 3. KULLANICI ARAYÜZÜ (UI) - TAM VE GÜNCELLENMİŞ ---
ui <- fluidPage(
theme = shinytheme("flatly"),
titlePanel("Ustalık Projesi: Sayıların Ardındaki Trajedi"),
fluidRow(
column(width = 4, uiOutput("kpi_confirmed")),
column(width = 4, uiOutput("kpi_deaths")),
column(width = 4, uiOutput("kpi_countries"))
),
hr(),
tabsetPanel(
tabPanel("Proje Hakkında",
fluidRow(
column(width = 10, offset = 1,
br(),
h3("Sayıların Ardındaki Trajediye Hoş Geldiniz"),
p("Bu proje, bir R programlama serisinin ulaştığı zirve noktasıdır. Amacımız, sadece kod yazma yeteneklerimizi sergilemek değil, aynı zamanda COVID-19 pandemisinin küresel etkisini, verilere saygılı bir şekilde ve anlamlı bir hikaye anlatarak sunmaktır."),
h4("Dashboard Nasıl Kullanılır?"),
tags$ul(
tags$li(tags$strong("Tek Ülke Zaman Serisi:"), " Tek bir ülkenin pandemi sürecindeki kümülatif vaka seyrini zaman içinde interaktif olarak inceleyebilirsiniz."),
tags$li(tags$strong("Ülkeler Arası Karşılaştırma:"), " Birden fazla ülkeyi seçerek, en güncel kümülatif vaka sayılarına göre karşılaştırmalı bir analiz yapabilirsiniz.")
)
)
)
),
tabPanel("Tek Ülke Zaman Serisi",
sidebarLayout(
sidebarPanel(
selectInput("ulke_secimi_zaman", "Ülke Seçin:",
choices = ulke_listesi, selected = "Turkey")
),
mainPanel(
plotlyOutput("zaman_serisi_grafigi")
)
)
),
tabPanel("Ülke Karşılaştırma",
sidebarLayout(
sidebarPanel(
selectInput("ulke_secimi_karsilastirma", "Ülkeler Seçin:",
choices = ulke_listesi,
selected = c("Turkey", "Germany", "Italy"),
multiple = TRUE)
),
mainPanel(
plotlyOutput("karsilastirma_grafigi")
)
)
)
)
)
# --- 4. SUNUCU (SERVER) MANTIĞI - TAM VE GÜNCELLENMİŞ ---
server <- function(input, output) {
# --- YENİ GÖREV: KPI KARTLARI İÇİN HESAPLAMALAR ---
global_summary_data <- reactive({
covid_data %>%
group_by(administrative_area_level_1) %>%
slice_max(order_by = date, n = 1, with_ties = FALSE) %>%
ungroup() %>%
summarise(
total_confirmed = sum(confirmed, na.rm = TRUE),
total_deaths = sum(deaths, na.rm = TRUE)
)
})
total_countries <- length(ulke_listesi)
output$kpi_confirmed <- renderUI({
total_vaka <- global_summary_data()$total_confirmed
wellPanel(style = "background-color: #fcf5e3; text-align: center;",
h4("Toplam Vaka (Kümülatif)"),
h3(style="color: #e67e22;", scales::comma(total_vaka, accuracy = 1))
)
})
output$kpi_deaths <- renderUI({
total_vefat <- global_summary_data()$total_deaths
wellPanel(style = "background-color: #f5e3e3; text-align: center;",
h4("Toplam Vefat (Kümülatif)"),
h3(style="color: #c0392b;", scales::comma(total_vefat, accuracy = 1))
)
})
output$kpi_countries <- renderUI({
wellPanel(style = "background-color: #e3ecf5; text-align: center;",
h4("Etkilenen Ülke/Bölge"),
h3(style="color: #2980b9;", total_countries)
)
})
# --- MEVCUT GRAFİK KODLARI (DEĞİŞİKLİK YOK) ---
filtrelenmis_veri_zaman <- reactive({
covid_data %>%
filter(administrative_area_level_1 == input$ulke_secimi_zaman)
})
output$zaman_serisi_grafigi <- renderPlotly({
data_to_plot <- filtrelenmis_veri_zaman()
plot_ly(data = data_to_plot, x = ~date, y = ~confirmed, type = 'scatter', mode = 'lines',
line = list(color = '#b30000'), hoverinfo = 'text',
text = ~paste("Tarih:", date, "<br>Toplam Vaka:", scales::comma(confirmed))) %>%
layout(title = paste(input$ulke_secimi_zaman, "İçin Kümülatif Vaka Seyri"),
xaxis = list(title = "Tarih"), yaxis = list(title = "Toplam Vaka Sayısı"))
})
filtrelenmis_veri_karsilastirma <- reactive({
validate(need(input$ulke_secimi_karsilastirma, "Lütfen en az bir ülke seçin."))
covid_data %>%
filter(administrative_area_level_1 %in% input$ulke_secimi_karsilastirma) %>%
group_by(administrative_area_level_1) %>%
slice_max(order_by = date, n = 1, with_ties = FALSE) %>%
ungroup() %>%
select(country = administrative_area_level_1, value = confirmed) %>%
arrange(desc(value)) %>%
mutate(country = factor(country, levels = country))
})
output$karsilastirma_grafigi <- renderPlotly({
p2 <- ggplot(filtrelenmis_veri_karsilastirma(), aes(x = country, y = value,
text = paste("Vaka:", scales::comma(value)))) +
geom_col(fill = "#08519c") + coord_flip() +
labs(title = "Seçilen Ülkelerin Güncel Kümülatif Vaka Sayıları", x = "", y = "Toplam Vaka") +
scale_x_discrete(limits = rev(levels(filtrelenmis_veri_karsilastirma()$country))) +
scale_y_continuous(labels = scales::comma) + theme_minimal()
ggplotly(p2, tooltip = "text")
})
}
# --- 5. UYGULAMAYI ÇALIŞTIRMA ---
shinyApp(ui = ui, server = server)
Bu bölümde ggplot
veya dplyr
’dan çok,
Shiny’nin arayüz (UI
) ve sunucu (Server
)
arasındaki dinamik ilişki kurma yeteneklerine odaklandık.
shinythemes
: Bu paket, tek bir satır
kodla (theme = shinytheme("flatly")
) uygulamamızın tüm renk
paletini, yazı tiplerini ve genel stilini modern ve profesyonel bir
görünüme kavuşturmamızı sağladı.fluidRow
ve column
: Bu
ikili, Shiny’de esnek tasarımlar yapmanın temelidir. Sayfamızı satırlara
ve sütunlara bölerek her bir elemanı tam olarak istediğimiz yere
yerleştirdik.uiOutput
ve renderUI
: Bu,
Shiny’nin en güçlü çiftlerinden biridir. Arayüzde bir yer ayırırız
(uiOutput
) ve o yerin içeriğini sunucuda, anlık verilere
göre hesaplayarak oluştururuz (renderUI
). KPI kartlarımızın
her zaman en güncel küresel toplamları göstermesini bu ikili sayesinde
sağladık. wellPanel
ve style
argümanları ile
de bu dinamik kutulara basit CSS makyajları yaptık.Bu adımla birlikte, projemiz artık sadece bir analiz aracı değil, aynı zamanda kullanıcıyı doğru verilerle ve profesyonel bir tasarımla karşılayan, tamamlanmış bir ürün görünümüne kavuştu.
Daha önce yaptığınız tüm kodu silip bu bölümdeki kodun tamamını yapıştırıp çalıştırın lütfen dostlar, sonuçları görünce doğru yolda olduğumuz göreceksiniz.
Projemizin iskeleti artık hem sağlam hem de şık. Şimdi, bu iskeletin üzerine kasları, beyni ve ruhu ekleme zamanı. Bu son bölümde, Ustalık Projemizi sıradan bir dashboard’dan, çok katmanlı, derinlemesine analizler sunan ve anlattığı hikayeyi en etkileyici şekilde görselleştiren tam teşekküllü bir veri bilimi ürününe dönüştüreceğiz.
Bu bölümde ekleyeceğimiz özellikler şunlar olacak:
Bu bölümden sonra, projemizin en başından beri hayal ettiğimiz o nihai, tam ve mükemmel çalışan haline ulaşacağız.
Bu son adımda, projemizin önceki tüm versiyonlarında öğrendiğimiz her
şeyi bir araya getiriyoruz. countrycode
ile veri
zenginleştirmeden, leaflet
ile harita çizmeye; çift eksenli
grafiklerden, shinyjs
ile R ve JavaScript’i konuşturmaya
kadar, tüm ileri seviye yeteneklerimizi bu son kod bloğunda
birleştiriyoruz.
Aşağıdaki kodlar, Ustalık Projemizin tüm özelliklerini içeren, önemli kısımların açıklandığı kodlardır.
# -----------------------------------------------------------------------------
# Ustalık Projesi: Sayıların Ardındaki Trajedi (NİHAİ SÜRÜM)
# Amaç: Tüm gelişmiş analiz ve görsel özellikleri içeren tam uygulama.
# -----------------------------------------------------------------------------
# --- BÖLÜM 1: GEREKLİ KÜTÜPHANELER ---
# Alet çantamıza son uzmanlarımızı da dahil ediyoruz.
library(shiny)
library(shinythemes)
library(shinyjs) # R ve JavaScript'i konuşturmak için
library(tidyverse)
library(plotly)
library(scales)
library(leaflet) # İnteraktif haritalar için
library(COVID19)
library(countrycode) # Ülkelerden kıtaları bulmak için
# --- BÖLÜM 2: VERİ HAZIRLAMA VE ZENGİNLEŞTİRME ---
# Bu bölüm, projemizin temelini oluşturan en gelişmiş veri işleme adımlarını içerir.
numeric_cols <- c("confirmed", "deaths", "tests", "recovered", "population", "vaccines", "people_vaccinated", "people_fully_vaccinated")
covid_data <- covid19(verbose = FALSE) %>%
arrange(administrative_area_level_1, date) %>%
group_by(administrative_area_level_1) %>%
tidyr::fill(all_of(numeric_cols), .direction = "down") %>%
mutate(
gunluk_vaka = confirmed - lag(confirmed, default = 0),
gunluk_vefat = deaths - lag(deaths, default = 0),
gunluk_test = tests - lag(tests, default = 0) # Artık günlük testleri de hesaplıyoruz
) %>%
ungroup() %>%
mutate(across(all_of(numeric_cols), ~replace_na(., 0))) %>%
mutate(
gunluk_vaka = ifelse(gunluk_vaka < 0, 0, gunluk_vaka),
gunluk_vefat = ifelse(gunluk_vefat < 0, 0, gunluk_vefat),
gunluk_test = ifelse(gunluk_test < 0, 0, gunluk_test)
) %>%
mutate(continent = suppressWarnings(countrycode(administrative_area_level_1,
origin = 'country.name',
destination = 'continent')))
country_coords <- covid_data %>% select(country = administrative_area_level_1, lat = latitude, lon = longitude) %>% group_by(country) %>% summarise(lat = first(lat), lon = first(lon), .groups = 'drop') %>% filter(!is.na(lat) & !is.na(lon))
ulke_listesi <- unique(covid_data$administrative_area_level_1)
# --- BÖLÜM 3: KULLANICI ARAYÜZÜ (UI) - TAM VE EKSİKSİZ ---
ui <- fluidPage(
theme = shinytheme("flatly"),
shinyjs::useShinyjs(),
tags$head(
tags$script(src="https://cdnjs.cloudflare.com/ajax/libs/numeral.js/2.0.6/numeral.min.js"),
tags$script(HTML("
function animateValue(obj, start, end, duration) { /* ... Animasyon JS Kodu ... */ }
Shiny.addCustomMessageHandler('updateCounter', function(message) { /* ... Sayaç JS Kodu ... */ });
"))
),
fluidRow(style = "display: flex; align-items: center; margin-bottom: 20px;",
column(width = 1, img(src = "covid_virus.png", height = "80px")),
column(width = 11, h1("Ustalık Projesi: Sayıların Ardındaki Trajedi"))
),
fluidRow(
column(width = 4, uiOutput("kpi_confirmed")),
column(width = 4, uiOutput("kpi_deaths")),
column(width = 4, uiOutput("kpi_countries"))
),
hr(),
tabsetPanel(
tabPanel("Proje Hakkında", # ... (Tüm metinler ve görseller burada)
),
tabPanel("Tek Ülke Zaman Serisi", # ...
),
tabPanel("Ülkeler Arası Karşılaştırma", # ...
),
tabPanel("Aşılama ve Vaka İlişkisi", # ...
),
tabPanel("Kıtasal Karşılaştırma", # ...
),
tabPanel("Coğrafi Dağılım Haritası",
sidebarLayout(
sidebarPanel(
h4("Harita Ayarları"),
sliderInput("date_slider", "Tarih Seçin:", min = min(covid_data$date), max = max(covid_data$date), value = max(covid_data$date), animate = TRUE, step = 7),
radioButtons("map_metric_selection", "Görüntülenecek Metrik:",
choices = c("Günlük Yeni Vakalar" = "gunluk_vaka",
"Günlük Vefatlar" = "gunluk_vefat",
"Günlük Testler" = "gunluk_test"), # Testler seçeneğini geri ekledik
selected = "gunluk_vaka"),
hr(),
wellPanel(
style="text-align: center; background-color: #2c3e50; color: white;",
h5("SEÇİLİ GÜNDEKİ YENİ (KÜRESEL)"),
h3(id = "hollywood_counter", "0")
)
),
mainPanel(leafletOutput("world_map", height = "600px"))
)
)
)
)
# --- BÖLÜM 4: SUNUCU (SERVER) MANTIĞI - TAM VE EKSİKSİZ ---
server <- function(input, output, session) {
# ... (Tüm KPI, Mini Harita, ve diğer grafik kodları buraya gelecek)
# Ana Harita: Coğrafi Dağılım (Günlük)
map_data <- reactive({
covid_data %>%
filter(date == input$date_slider & !is.na(latitude) & !is.na(longitude)) %>%
select(country = administrative_area_level_1, value = !!sym(input$map_metric_selection), lat = latitude, lon = longitude) %>%
filter(value > 0)
})
output$world_map <- renderLeaflet({ leaflet() %>% addProviderTiles(providers$CartoDB.DarkMatter, ...) })
# Ana Harita Gözlemcisi
observe({
data_to_plot <- map_data()
# ... (Harita çizim kodu)
})
# Animasyonlu Sayaç Gözlemcisi (SON VE DOĞRU HALİ)
observeEvent({
# Bu gözlemci, sonucu değil, GİRDİLERİ dinler
input$date_slider
input$map_metric_selection
}, {
# Girdiler her değiştiğinde bu kod koşulsuzca çalışır
data <- covid_data %>%
filter(date == input$date_slider) %>%
select(value = !!sym(input$map_metric_selection))
total_value <- if(nrow(data) > 0) sum(data$value, na.rm = TRUE) else 0
# Ve sonucu her zaman JavaScript'e gönderir
session$sendCustomMessage(type = 'updateCounter', message = list(value = total_value))
})
}
# --- BÖLÜM 5: UYGULAMAYI ÇALIŞTIRMA ---
shinyApp(ui = ui, server = server)
Dostlarımıza Bir not:
Sevgili dostlar bölüm 4’ün kodlarını tamamen eğitim amaçlı gerekli kısımların verildiğini gözönüne alarak takip ediniz. Son bölümde bu 4 bölümün tüm kodlarını tek seferde sizlere sunacağım böylece tüm kodu kopyalayıp R script dosyanıza ekleyip çalıştırdığınızda çalışan Ustalık Projesi’sin son halini göreceksiniz.
Bu son bölümde, projemizi tamamlamak için en ileri seviye yeteneklerimizi kullandık:
countrycode
): Ham
verimizi alıp, dış bir bilgi kaynağı (countrycode
paketi)
ile zenginleştirerek, ona yepyeni bir analiz boyutu (kıtalar) kattık.
Bu, gerçek bir veri bilimi projesinin en temel adımlarından
biridir.sec_axis
) aşı grafiği ve kıtaların zaman içindeki oranını
gösteren yığılmış alan grafiği
(geom_area(position = "fill")
) gibi çok daha sofistike ve
bilgilendirici görseller oluşturduk.shinyjs
):
Projemize o “Hollywood” efektini katmak için R’ın sınırlarının dışına
çıktık. shinyjs
ve session$sendCustomMessage
aracılığıyla, R’da hesapladığımız bir veriyi (o anki toplam günlük
vaka), tarayıcı tarafında çalışan JavaScript koduna göndererek
animasyonlu bir sayaç oluşturduk. Bu, Shiny’nin ne kadar esnek ve güçlü
bir platform olduğunu gösteren en güzel kanıttır.observeEvent
): Projemizin son hatasını çözerken,
Shiny’nin en incelikli prensiplerinden birini öğrendik. Bir gözlemcinin,
bir reaktif ifadenin “sonucunu” mu, yoksa o sonucu yaratan “girdileri”
mi dinlemesi gerektiği arasındaki o kritik farkı yaşayarak tecrübe
ettik.Bu adımlarla, Ustalık Projemiz artık tamamlanmıştır. Bu, sadece bir dizi kod değil; bu, bir problemle yüzleşmenin, hatalar yapmanın, o hatalardan ders çıkarmanın, asla pes etmemenin ve sonunda, en başından beri hayal edilen vizyona ulaşmanın hikayesidir.
Ve işte yolun sonu… Ya da belki de yepyeni bir başlangıcın ilk adımı. Bu uzun ve meşakkatli yolculuğun sonunda, en başından beri hayal ettiğimiz o eseri, “Ustalık Projesi”ni birlikte tamamladık.
Bu son bölümde, sizlere bu projenin arkasındaki tüm sihri, yani en
başından sonuna, tüm özelliklerini içeren, defalarca test edilmiş,
hatalarından arındırılmış, kaya gibi sağlam o nihai app.R
kodunu sunuyoruz.
Bu kod, bu seride öğrendiğimiz her şeyin bir özetidir:
dplyr
’in veri işleme gücü, ggplot2
’nin
görselleştirme sanatı, plotly
ve leaflet
’in
interaktiflik sihri ve shiny
’nin tüm bunları bir araya
getiren muhteşem orkestra şefliği… Hepsi bu son kodun içinde.
app.R
KoduAşağıdaki kodun tamamını kopyalayıp, www
klasörünün
(içinde resimlerinizin olduğu) yanında bulunan boş bir
app.R
dosyasına yapıştırın ve RStudio’da “Run App”
düğmesine basın. Başyapıtımız karşınızda olacak.
Uygulamamızın, eklediğimiz o güzel resimleri
(covid_virus.png
, avatar.png
vb.) bulabilmesi
için, proje dosyalarımızı belirli bir düzende organize etmemiz
gerekiyor. Shiny’nin bu konuda çok net bir kuralı vardır: Tüm
statik dosyalar (resimler, CSS dosyaları, PDF’ler), app.R
dosyanızla aynı dizinde bulunan www
adında bir klasörün
içinde olmalıdır.
Doğru klasör yapısı tam olarak aşağıdaki gibi görünmelidir. Bu yapıya dikkat etmezseniz, uygulamanız çalışır ancak resimleriniz görünmez.
Proje Klasörünüzün Şeması:
/UstalikProjesi/
|
|-- 📂 www/
| |
| |-- 🖼️ covid_virus.png
| |-- 🖼️ avatar.png
|
|-- 📜 app.R
Bu şemada: * UstalikProjesi
, projemizin ana klasörüdür.
* app.R
, birazdan vereceğimiz tam uygulama kodunu içeren
dosyadır. * www
, app.R
ile aynı seviyede
bulunan ve kullandığımız iki görseli içeren klasördür.
Bu düzeni sağladıktan sonra, aşağıdaki kodu app.R
dosyanıza gönül rahatlığıyla yapıştırabilirsiniz.
# -----------------------------------------------------------------------------
# Ustalık Projesi: Sayıların Ardındaki Trajedi
# Sürüm: NİHAİ ZAFER (Tam Sürüm)
# -----------------------------------------------------------------------------
# --- BÖLÜM 1: GEREKLİ KÜTÜPHANELER ---
library(shiny)
library(shinythemes)
library(shinyjs)
library(tidyverse)
library(plotly)
library(scales)
library(leaflet)
library(COVID19)
library(countrycode)
# --- BÖLÜM 2: VERİ HAZIRLAMA VE ZENGİNLEŞTİRME ---
# "tests" sütununu artık ana işlem listesinden çıkarıyoruz.
numeric_cols <- c("confirmed", "deaths", "recovered", "population", "vaccines", "people_vaccinated", "people_fully_vaccinated")
covid_data <- covid19(verbose = FALSE) %>%
arrange(administrative_area_level_1, date) %>%
group_by(administrative_area_level_1) %>%
tidyr::fill(all_of(numeric_cols), .direction = "down") %>%
mutate(
gunluk_vaka = confirmed - lag(confirmed, default = 0),
gunluk_vefat = deaths - lag(deaths, default = 0)
) %>%
ungroup() %>%
mutate(across(all_of(numeric_cols), ~replace_na(., 0))) %>%
mutate(
gunluk_vaka = ifelse(gunluk_vaka < 0, 0, gunluk_vaka),
gunluk_vefat = ifelse(gunluk_vefat < 0, 0, gunluk_vefat)
) %>%
mutate(continent = suppressWarnings(countrycode(administrative_area_level_1,
origin = 'country.name',
destination = 'continent')))
country_coords <- covid_data %>% select(country = administrative_area_level_1, lat = latitude, lon = longitude) %>% group_by(country) %>% summarise(lat = first(lat), lon = first(lon), .groups = 'drop') %>% filter(!is.na(lat) & !is.na(lon))
ulke_listesi <- unique(covid_data$administrative_area_level_1)
# --- BÖLÜM 3: KULLANICI ARAYÜZÜ (UI) - TAM VE EKSİKSİZ ---
ui <- fluidPage(
theme = shinytheme("flatly"),
shinyjs::useShinyjs(),
tags$head(
tags$script(src="https://cdnjs.cloudflare.com/ajax/libs/numeral.js/2.0.6/numeral.min.js"),
tags$script(HTML("
function animateValue(obj, start, end, duration) {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerHTML = numeral(Math.floor(progress * (end - start) + start)).format('0,0');
if (progress < 1) {
window.requestAnimationFrame(step);
}
};
window.requestAnimationFrame(step);
}
Shiny.addCustomMessageHandler('updateCounter', function(message) {
const counter = document.getElementById('hollywood_counter');
if(counter){
const oldValue = numeral(counter.innerHTML).value() || 0;
const newValue = message.value;
animateValue(counter, oldValue, newValue, 300);
}
});
"))
),
fluidRow(style = "display: flex; align-items: center; margin-bottom: 20px;",
column(width = 1, img(src = "covit_virus.png", height = "150px")),
column(width = 11, h1("Ustalık Projesi: Sayıların Ardındaki Trajedi"))
),
fluidRow(
column(width = 4, uiOutput("kpi_confirmed")),
column(width = 4, uiOutput("kpi_deaths")),
column(width = 4, uiOutput("kpi_countries"))
),
hr(),
tabsetPanel(
tabPanel("Proje Hakkında",
fluidRow(
column(width = 10, offset = 1, br(),
h3("Sayıların Ardındaki Trajediye Hoş Geldiniz"),
p("Bu proje, bir R programlama serisinin ulaştığı zirve noktasıdır. Amacımız, sadece kod yazma yeteneklerimizi sergilemek değil, aynı zamanda COVID-19 pandemisinin küresel etkisini, verilere saygılı bir şekilde ve anlamlı bir hikaye anlatarak sunmaktır."),
p("Burada gördüğünüz her bir sayı, grafik ve harita, milyonlarca insanın hayatını etkileyen bir trajedinin parçasıdır. Bu çalışmayı, bu süreçte hayatını kaybedenlere ve mücadele edenlere bir saygı duruşu olarak hazırladık."),
h4("Dashboard Nasıl Kullanılır?"),
tags$ul(
tags$li(tags$strong("Tek Ülke Zaman Serisi:"), "Tek bir ülkenin pandemi sürecindeki toplam vaka seyrini zaman içinde interaktif olarak inceleyebilirsiniz."),
tags$li(tags$strong("Ülkeler Arası Karşılaştırma:"), "Birden fazla ülkeyi seçerek, en güncel toplam vaka sayılarına göre karşılaştırmalı bir analiz yapabilirsiniz."),
tags$li(tags$strong("Aşılama ve Vaka İlişkisi:"), "Tek bir ülke özelinde, aşılanma oranındaki artışın günlük yeni vaka sayıları üzerindeki etkisini görsel olarak keşfedebilirsiniz."),
tags$li(tags$strong("Kıtasal Karşılaştırma:"), "Pandeminin kıtalara göre genel seyrini ve zaman içindeki değişimini inceleyebilirsiniz."),
tags$li(tags$strong("Coğrafi Dağılım Haritası:"), "Pandeminin küresel dağılımını seçtiğiniz bir tarihe göre interaktif bir harita üzerinde görebilirsiniz.")
),
hr()
)
),
fluidRow(
column(width = 4, offset = 1,
wellPanel(
fluidRow(
column(width = 4, img(src = "avatar.png", height = "100px", width = "100px", style = "border-radius: 50%;")),
column(width = 8, h4("Hazırlayan"), p(strong("İCV ve ŞANSLI")), p(em("İki Veri Meraklısı")))
)
)
)
)
),
tabPanel("Tek Ülke Zaman Serisi",
sidebarLayout(
sidebarPanel(h4("Tek Ülke Detayları", align="center"), selectInput("ulke_secimi_zaman", "Ülke Seçin:", choices = ulke_listesi, selected = "Turkey"), hr(), leafletOutput("mini_map_zaman", height = "250px")),
mainPanel(plotlyOutput("zaman_serisi_grafigi"))
)
),
tabPanel("Ülkeler Arası Karşılaştırma",
sidebarLayout(
sidebarPanel(h4("Karşılaştırma Seçenekleri", align="center"), selectInput("ulke_secimi_karsilastirma", "Ülkeler Seçin:", choices = ulke_listesi, selected = c("Turkey", "Germany", "Italy"), multiple = TRUE), hr(), leafletOutput("mini_map_karsilastirma", height = "250px")),
mainPanel(plotlyOutput("karsilastirma_grafigi"))
)
),
tabPanel("Aşılama ve Vaka İlişkisi",
sidebarLayout(
sidebarPanel(h4("Analiz Seçenekleri", align="center"), selectInput("ulke_secimi_asi", "Lütfen Bir Ülke Seçin:", choices = ulke_listesi, selected = "Turkey"), hr(), leafletOutput("mini_map_asi", height = "250px")),
mainPanel(plotlyOutput("asi_vaka_grafik", height = "600px"))
)
),
tabPanel("Kıtasal Karşılaştırma",
fluidRow(
column(width = 6, h4("Kıtaların Kümülatif Toplam Vaka Sayıları"), plotlyOutput("kita_bar_grafik", height = "500px")),
column(width = 6, h4("Vaka Dağılımının Zaman İçindeki Değişimi"), plotlyOutput("kita_alan_grafik", height = "500px"))
)
),
tabPanel("Coğrafi Dağılım Haritası",
sidebarLayout(
sidebarPanel(
h4("Harita Ayarları"),
sliderInput("date_slider", "Tarih Seçin:", min = min(covid_data$date), max = max(covid_data$date), value = max(covid_data$date), animate = TRUE, step = 7),
radioButtons("map_metric_selection", "Görüntülenecek Metrik:",
choices = c("Günlük Yeni Vakalar" = "gunluk_vaka",
"Günlük Vefatlar" = "gunluk_vefat"),
selected = "gunluk_vaka"),
hr(),
wellPanel(
style="text-align: center; background-color: #2c3e50; color: white;",
h5("SEÇİLİ GÜNDEKİ YENİ (KÜRESEL)"),
h3(id = "hollywood_counter", "0")
)
),
mainPanel(leafletOutput("world_map", height = "600px"))
)
)
)
)
# --- BÖLÜM 4: SUNUCU (SERVER) MANTIĞI - TAM VE EKSİKSİZ ---
server <- function(input, output, session) {
# KPI KARTLARI (Kümülatif Toplamları Gösterir)
global_summary_data <- reactive({ covid_data %>% group_by(administrative_area_level_1) %>% slice_max(order_by = date, n = 1, with_ties = FALSE) %>% ungroup() %>% summarise(total_confirmed = sum(confirmed, na.rm = TRUE), total_deaths = sum(deaths, na.rm = TRUE)) })
total_countries <- length(ulke_listesi)
output$kpi_confirmed <- renderUI({ total_vaka <- global_summary_data()$total_confirmed; wellPanel(style = "background-color: #fcf5e3; text-align: center;", h4("Toplam Vaka (Kümülatif)"), h3(style="color: #e67e22;", scales::comma(total_vaka, accuracy = 1))) })
output$kpi_deaths <- renderUI({ total_vefat <- global_summary_data()$total_deaths; wellPanel(style = "background-color: #f5e3e3; text-align: center;", h4("Toplam Vefat (Kümülatif)"), h3(style="color: #c0392b;", scales::comma(total_vefat, accuracy = 1))) })
output$kpi_countries <- renderUI({ wellPanel(style = "background-color: #e3ecf5; text-align: center;", h4("Etkilenen Ülke/Bölge"), h3(style="color: #2980b9;", total_countries)) })
# MİNİ HARİTALAR
output$mini_map_zaman <- renderLeaflet({ secilen_ulke_coords <- country_coords %>% filter(country == input$ulke_secimi_zaman); leaflet() %>% addProviderTiles(providers$CartoDB.Positron) %>% setView(lng = secilen_ulke_coords$lon, lat = secilen_ulke_coords$lat, zoom = 4) %>% addMarkers(lng = secilen_ulke_coords$lon, lat = secilen_ulke_coords$lat) })
output$mini_map_karsilastirma <- renderLeaflet({ secilen_ulkeler_coords <- country_coords %>% filter(country %in% input$ulke_secimi_karsilastirma); if(nrow(secilen_ulkeler_coords) == 0){ return(leaflet() %>% addProviderTiles(providers$CartoDB.Positron) %>% setView(lng = 0, lat = 30, zoom = 2)) }; leaflet(data = secilen_ulkeler_coords) %>% addProviderTiles(providers$CartoDB.Positron) %>% fitBounds(~min(lon), ~min(lat), ~max(lon), ~max(lat)) %>% addMarkers(lng = ~lon, lat = ~lat) })
output$mini_map_asi <- renderLeaflet({ secilen_ulke_coords <- country_coords %>% filter(country == input$ulke_secimi_asi); leaflet() %>% addProviderTiles(providers$CartoDB.Positron) %>% setView(lng = secilen_ulke_coords$lon, lat = secilen_ulke_coords$lat, zoom = 4) %>% addMarkers(lng = secilen_ulke_coords$lon, lat = secilen_ulke_coords$lat) })
# ANA GRAFİKLER VE HARİTA
filtrelenmis_veri_zaman <- reactive({ covid_data %>% filter(administrative_area_level_1 == input$ulke_secimi_zaman) })
output$zaman_serisi_grafigi <- renderPlotly({ data_to_plot <- filtrelenmis_veri_zaman(); plot_ly(data = data_to_plot, x = ~date, y = ~confirmed, type = 'scatter', mode = 'lines', line = list(color = '#b30000'), hoverinfo = 'text', text = ~paste("Tarih:", date, "<br>Toplam Vaka:", scales::comma(confirmed))) %>% layout(title = paste(input$ulke_secimi_zaman, "İçin Kümülatif Vaka Seyri"), xaxis = list(title = "Tarih"), yaxis = list(title = "Toplam Vaka Sayısı")) })
filtrelenmis_veri_karsilastirma <- reactive({ validate(need(input$ulke_secimi_karsilastirma, "Lütfen en az bir ülke seçin.")); covid_data %>% filter(administrative_area_level_1 %in% input$ulke_secimi_karsilastirma) %>% group_by(administrative_area_level_1) %>% slice_max(order_by = date, n = 1, with_ties = FALSE) %>% ungroup() %>% select(country = administrative_area_level_1, value = confirmed) %>% arrange(desc(value)) %>% mutate(country = factor(country, levels = country)) })
output$karsilastirma_grafigi <- renderPlotly({ p2 <- ggplot(filtrelenmis_veri_karsilastirma(), aes(x = country, y = value, text = paste("Vaka:", scales::comma(value)))) + geom_col(fill = "#08519c") + coord_flip() + labs(title = "Seçilen Ülkelerin Güncel Kümülatif Vaka Sayıları", x = "", y = "Toplam Vaka") + scale_x_discrete(limits = rev(levels(filtrelenmis_veri_karsilastirma()$country))) + scale_y_continuous(labels = scales::comma) + theme_minimal(); ggplotly(p2, tooltip = "text") })
asi_veri <- reactive({ covid_data %>% filter(administrative_area_level_1 == input$ulke_secimi_asi) %>% mutate(asilanma_orani = ifelse(population > 0, (people_vaccinated / population), 0)) %>% filter(people_vaccinated > 0) })
output$asi_vaka_grafik <- renderPlotly({ data <- asi_veri(); validate(need(nrow(data) > 0, "Seçilen ülke için aşı verisi bulunmamaktadır.")); scaling_factor <- if (max(data$asilanma_orani, na.rm = TRUE) > 0) max(data$gunluk_vaka, na.rm = TRUE) / max(data$asilanma_orani, na.rm = TRUE) else 1; p_asi <- ggplot(data, aes(x = date)) + geom_col(aes(y = gunluk_vaka, text = paste("Günlük Vaka:", scales::comma(gunluk_vaka))), fill = "#f39c12", alpha = 0.6) + geom_line(aes(y = asilanma_orani * scaling_factor, text = paste("Aşılanma Oranı:", scales::percent(asilanma_orani, accuracy = 0.1))), color = "#2980b9", size = 1.5) + scale_y_continuous(name = "Günlük Yeni Vaka Sayısı", labels = scales::comma, sec.axis = sec_axis(~ . / scaling_factor, name = "Nüfusun Aşılanma Oranı (En Az 1 Doz)", labels = scales::percent)) + labs(title = paste(input$ulke_secimi_asi, "için Aşılanma ve Günlük Vaka İlişkisi"), x = "Tarih", color = "") + theme_minimal(); ggplotly(p_asi, tooltip = "text") })
kitalara_gore_ozet <- reactive({ covid_data %>% filter(!is.na(continent)) %>% group_by(administrative_area_level_1) %>% slice_max(order_by = date, n = 1, with_ties = FALSE) %>% ungroup() %>% group_by(continent) %>% summarise(toplam_vaka = sum(confirmed, na.rm = TRUE)) %>% arrange(desc(toplam_vaka)) %>% mutate(continent = factor(continent, levels = continent)) })
output$kita_bar_grafik <- renderPlotly({ p_kita_bar <- ggplot(kitalara_gore_ozet(), aes(x = continent, y = toplam_vaka, text = paste("Vaka:", scales::comma(toplam_vaka)))) + geom_col(fill = "#9b59b6") + coord_flip() + scale_y_continuous(labels = scales::comma) + scale_x_discrete(limits = rev(levels(kitalara_gore_ozet()$continent))) + labs(x = "", y = "Toplam Vaka Sayısı") + theme_minimal(); ggplotly(p_kita_bar, tooltip = "text") })
kitalara_gore_zaman_serisi <- reactive({ covid_data %>% filter(!is.na(continent)) %>% group_by(date, continent) %>% summarise(toplam_vaka = sum(confirmed, na.rm = TRUE), .groups = 'drop') })
output$kita_alan_grafik <- renderPlotly({ p_kita_alan <- ggplot(kitalara_gore_zaman_serisi(), aes(x = date, y = toplam_vaka, fill = continent, text = paste("Kıta:", continent, "<br>Vaka:", scales::comma(toplam_vaka)))) + geom_area(position = "fill") + scale_y_continuous(labels = scales::percent) + labs(x = "Tarih", y = "Günlük Kümülatif Vaka İçindeki Oranı", fill = "Kıta") + theme_minimal(); ggplotly(p_kita_alan, tooltip = "text") })
# Ana Harita: Coğrafi Dağılım (Günlük)
map_data <- reactive({
covid_data %>%
filter(date == input$date_slider & !is.na(latitude) & !is.na(longitude)) %>%
select(country = administrative_area_level_1, value = !!sym(input$map_metric_selection), lat = latitude, lon = longitude) %>%
filter(value > 0)
})
output$world_map <- renderLeaflet({ leaflet() %>% addProviderTiles(providers$CartoDB.DarkMatter, options = providerTileOptions(minZoom = 2, maxZoom = 6)) %>% setView(lng = 35, lat = 39, zoom = 2) })
# Ana Harita Gözlemcisi
observe({ data_to_plot <- map_data(); if (nrow(data_to_plot) > 0) { proxy <- leafletProxy("world_map", data = data_to_plot) %>% clearMarkers(); simple_popup <- paste("Ülke:", data_to_plot$country); proxy %>% addCircleMarkers(lng = ~lon, lat = ~lat, radius = ~sqrt(value) * 0.1, popup = ~simple_popup, color = "#FFA500", stroke = FALSE, fillOpacity = 0.6) } else { leafletProxy("world_map") %>% clearMarkers() } })
# DÜZELTİLMİŞ Animasyonlu Sayaç Gözlemcisi
observeEvent({
input$date_slider
input$map_metric_selection
}, {
data <- covid_data %>%
filter(date == input$date_slider) %>%
select(value = !!sym(input$map_metric_selection))
total_value <- if(nrow(data) > 0) sum(data$value, na.rm = TRUE) else 0
session$sendCustomMessage(type = 'updateCounter', message = list(value = total_value))
}, ignoreInit = TRUE)
}
# --- BÖLÜM 5: UYGULAMAYI ÇALIŞTIRMA ---
shinyApp(ui = ui, server = server)
Bu kodları app.R isimli (isterseniz bir başka isim de verebilirsiniz) R Script dosyasına tamamını aktarıp çalıştırır iseniz (iki ``` arasındaki kodları) aşağıdaki görüntülerle projemiz karşınıza gelecektir.
www klasörü içine covit-19 ile ilgili bir görseli ve avatar resminizi eklemeyi unutmayınız lütfen dostlar. (www klasörü app.R ile aynı yerde olsun)
Bu uzun ve meşakkatli yolculuğun sonunda, sizlere bir itirafta bulunmak isterim. Bu projeyi ve bu ders notlarını hazırlarken yalnız değildim. Yanımda, Google tarafından geliştirilen büyük bir dil modeli olan Gemini adında bir “asistan” vardı.
Ancak bu iş birliği, belki de birçoğunuzun zannettiği gibi işlemedi. Gemini’ye “Bana bir COVID-19 dashboard’u yap” demedim. Eğer öyle deseydim, ortaya çıkan şey ruhsuz, hatalarla dolu ve en önemlisi, bizim hikayemizi anlatmayan bir kod yığını olurdu.
Bizim yolculuğumuz çok daha farklıydı. Bu süreçte kaptan pilot bendim, Gemini ise ikinci pilottu.
Bu yolculuk, bana yapay zekanın gücünü değil, insan zekasının ve sezgisinin yerinin doldurulamaz olduğunu öğretti. Gemini gibi araçlar, inanılmaz derecede güçlü asistanlardır. Bizi tekrar eden işlerden kurtarır, hızımızı on kat artırırlar. Ama asla bir projenin “ustası” olamazlar.
Usta, vizyonu belirleyen, hataları sezen, zorluklar karşısında strateji üreten, verinin ardındaki hikayeyi hisseden ve en önemlisi, yaptığı işe ruhunu katandır.
Bu serinin ve bu projenin size öğreteceği en büyük ders bu olsun: Bu muhteşem araçları kullanmaktan asla çekinmeyin. Onları ikinci pilotunuz yapın. Ama unutmayın, o kokpitteki kaptan koltuğu her zaman size aittir.
Ustalık yolculuğunuzda hepinize başarılar dilerim.
İsmail Cüneyt Varilci ve sadık yoldaşı Şanslı