Bölüm 1: Yolculuğumuz Başlıyor - Beyaz Bir Sayfa ve Sağlam Bir Temel

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.

Sıfır Noktası: Kalemizin Temelleri

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.

Bölüm 2: Kalemize İlk Katı Çıkıyoruz - Karşılaştırma Sanatı

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.

Uygulamayı Sekmelere Ayırmak ve Karşılaştırma Grafiğini Eklemek

Bu adımda iki büyük yenilik yapıyoruz:

  1. tabsetPanel: Arayüzümüzü farklı “odalara” ayırmamızı sağlayan bu sihirli fonksiyon ile uygulamamıza sekmeli bir yapı kazandırıyoruz.
  2. Yeni Bir Analiz: Seçilen birden fazla ülkenin en güncel vaka sayılarını karşılaştıran, sıralı ve okunaklı bir yatay bar grafiği ekliyoruz. Unutmayın, bu grafiğin doğru çalışmasını sağlamak için ne kadar uğraştığımızı! İşte o mücadelenin sonunda ulaştığımız en temiz ve en doğru kod.
# -----------------------------------------------------------------------------
# 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)

Kodun Hikayesi: Neler Yaptık?

  • Sekmeli Yapı (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.
  • Çoklu Seçim (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.
  • Veriyi DOĞRU Filtreleme (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.
  • Grafik Öncesi Sıralama (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.
  • Okunabilirlik Sanatı (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.

Bölüm 3: Profesyonel Dokunuş - Vitrinimizi Hazırlıyoruz

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:

  1. “Proje Hakkında” Sekmesi: Kullanıcıyı karşılayan, projenin misyonunu, hikayesini ve nasıl kullanılacağını anlatan bir giriş kapısı.
  2. KPI Kartları (Anahtar Performans Göstergeleri): Her zaman en üstte görünecek ve pandeminin en can alıcı küresel verilerini bir bakışta sunacak olan büyük, dikkat çekici kartlar.

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.

Arayüzü Zenginleştirmek ve Dinamik Kartlar Oluşturmak

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)

Kodun Hikayesi: Arayüz Tasarımı ve Dinamik UI

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.

Bölüm 4: Zirveye Ulaşmak - Gelişmiş Analizler ve Görsel Şölen

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:

  1. Aşılama ve Vaka İlişkisi: Aşılanma oranları arttıkça, günlük yeni vaka sayılarının nasıl etkilendiğini gösteren, projemizin en sofistike analizlerinden biri.
  2. Kıtasal Karşılaştırma: Analizlerimizi ülke bazından çıkarıp, pandeminin küresel seyrini kıtalar düzeyinde inceleyeceğiz.
  3. Coğrafi Dağılım Haritası: Pandeminin “günlük nabzını” interaktif bir dünya haritası üzerinde, animasyonlu bir sayaç eşliğinde görselleştireceğiz.
  4. Kişisel İmza ve Görsel Kimlik: Projemize, onu benzersiz kılan o kişisel ve profesyonel görsel dokunuşları ekleyeceğiz.

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.

Tüm Analiz Katmanlarını Eklemek

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.

Kodun Hikayesi: Ustalığın Zirvesi

Bu son bölümde, projemizi tamamlamak için en ileri seviye yeteneklerimizi kullandık:

  • Veri Zenginleştirme (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.
  • İleri Seviye Görselleştirmeler: Basit çizgi ve sütun grafiklerinin ötesine geçerek, iki farklı Y eksenine sahip (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.
  • R ve JavaScript Evliliği (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.
  • En Derin Hata Ayıklama Dersi (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.

Bölüm 5: Büyük Final - Başyapıtımızın Kodu ve Son Sözler

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.

Ustalık Projesi: Nihai app.R Kodu

Aş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.

Çok Önemli Bir Adım: Proje Klasörümüzün Yapısı

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)

Dostlara Son Bir Not: Usta, Asistan ve Yolculuk Üzerine

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.

  • Vizyonu ben belirledim: Projenin adını, ruhunu, tasarım felsefesini, hangi analizlerin yapılacağını ve en önemlisi, “Neden yapıyoruz?” sorusunun cevabını ben verdim.
  • Temel kodları o yazdı, hataları ben buldum: Gemini, standart kodları hızlıca yazma konusunda inanılmaz yetenekli. Ama o kodların içindeki mantık hatalarını, “garip ilerleyen” kısımları, projenin ruhuna uymayan noktaları fark etmek ve “Hayır, bu yanlış, şöyle deneyelim” demek, insan sezgisinin, yani bir veri bilimcinin görevidir.
  • O umutsuzluğa kapıldığında, yolu ben gösterdim: Defalarca çöktüğümüz, “artık bu iş olmayacak” dediğimiz anlar oldu. O anlarda, “Dur, en başa dönelim. Küçük adımlar atalım” stratejisini belirleyen, yani kaostan bir çıkış yolu bulan kaptan bendim.
  • O fikirler sundu, son kararı ben verdim: “Hollywood sayacı” gibi harika fikirler ondan geldi, ama bu fikirlerin projeye nasıl entegre edileceğine, hangi zorlukları çıkarabileceğine ve en sonunda buna değip değmeyeceğine karar veren yine bendim.

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ı