Ekonomi alanında, işletmelerde pazarlama-satış alanında, ve son Corona salgınında gördüğümüz gibi sağlık alanında coğrafi yerler ile ilişkili veri setleriyle sık sık karşılaşılır. Bu verilerin harita üzerinde görselleştirilmesi analitik çalışmalarında önemli bir yer tutar.

Coğrafi haritalar ve R’da kullanımı

Haritaların diğer geometrik şekillerden temel farkı Dünya’nın enlem ve boylam koordinat sistemi üzerinde ifade edilmiş olmalarıdır. Bunun dışında haritalar temel olarak bir veya birden fazla poligon ve onların sabitlendiği noktalar olarak ifade edilirler.

R’da pek çok konuda olduğu gibi haritalar konusunda da haritaların nasıl bir veri yapısıyla temsil edileceğine ilişkin birbirine alternatif yaklaşımlar ve bunlara karşılık gelen yazılım kitaplıkları bulunuyor. Bunun dışında harita verilerinin temin edilmesi için de bazı alternatifler var.

Haritaların temsili konusunda temel iki kitaplık görece daha eski olan sp (Spatial Polygon-Uzaysal Polygon) ve görece daha yenive popüler olan sf (Simple Features) paketi ise coğrafi harita veri yapılarını basitleştirmek ve dolayısıyla R’da işlenmesini kolaylaştırmak iddiasıyla ortaya çıkmış. sf harita verileri standart R veri çerçeveleri (data frame); dolayısıyla işlenmeleri daha kolay. Burada bu ikinci kitaplığa odaklanacağız. Bunların dışında bir de rgeos kitaplığı var ki o da coğrafi geometri işlemlerinin yapılmasını sağlayan temel bir kitaplık, ve diğer kitaplıklarla beraber kullanılıyor. Buraya kadar bahsettiklerimiz harita bigisinin temsili ve işlenmesi ile ilgili kitaplıklar. Haritaların görselleştirilmesi konusundaki kitaplıklara ise aşağıdaki bölümlerde ayrıca değineceğiz.

Bir de harita bilgilerinin bulunması gerekiyor elbette. Bu konuda farklı yöntemler var, yeri geldikçe değineceğiz. İlk kullanacağımız yöntem bu konuda bir açık veri kaynağı olan http://www.naturalearthdata.com/. Bu site tarafından sağlanan ve sitenin sağladığı verilerin R’dan doğrudan kullanımına izin veren rnaturalearth kitaplığı. Bu kitaplığın kurulumu ve çalıştırılması sırasında da onu verilerle destekleyen rnaturalearthdata ve onun yüksek çözünürlük (high resolution) olanı rnaturalearhhires paketleri otomatik olarak kuruluyor veya kurulması için sistem uyarı veriyor.

Öncelikle rnaturalearth paketini kullanarak Türkiye harita bilgisini elde edeceğiz. Paketin ne_countries() fonksiyonu istenilern harita bilgisini R’daki iki ana harita kitaplığı olan sp veya sf ile uyumlu olarak verebiliyor. Bir ileride sf kullanacağımız için o tipte bir dönüş isteyeceğiz:

require("rnaturalearth")
turkiye <- ne_countries(country="Turkey", 
                        scale = "medium", 
                        returnclass = "sf")

Elde edilen veriyi incelerseniz ülke ile ilgili nüfusu, ekonomi türü, vb. birçok alt bilgi göreceksiniz. Bunlardan bir tanesi de coğrafi harita üzerinde ülke poligonunu veren geometry bilgisi. Bunuşağıdaki gibi incelerseniz bir multipoligon olduğunu göreceksiniz. Bir ülkeyi oluşturan birden fazla poligon (adalar, denizle bölünmüş kısımlar) ancek bu şekilde ifade edilebilir:

turkiye$pop_est #tahmini nüfus
## [1] 76805524
class(turkiye$geometry) # ülke harita poligonu
## [1] "sfc_MULTIPOLYGON" "sfc"

Bu aşamada isterseniz tam olarak bir harita değil ama poligon olarak görselleştirebilirsiniz:

plot(turkiye$geometry)

Harita görselleştirmede daha doğru yöntem Dünya’nın enlem-boylam koordinat sistemine uygun görselleştirme paketlerini kullanmaktır. Buradaki tercihimiz R’ın yetkin görselleştirme paketi ggplot2 ve onun sf veri yapılarına özel geometrisi geom_sf() olacak:

require(ggplot2)
ggplot(data=turkiye) +
  geom_sf()

Burada üretilen görselin enlem-boylam koordinat sistemine nasıl oturtulduğunu görebiliyoruz.

Birçok durumda ülke düzeyi değil de şehir/eyalet düzeyinde harita detayına ihtiyaç olacaktır. NaturalEarth sistemi bunu yüksek çözünürlük harita bilgisi paketi ile sağlayabilir. Bunun için ne_countries() fonksiyonu yerine şehir veya eyalet detayı getiren ne_states() fonksiyonunu kullanacağız. Bu fonksiyonu çalıştırdığınızda ilgili paketi otomatik kurmaya çalışacaktır. Ama kimi durumlarda (bunun gibi yeni gelişen ve R CRAN deposuna henüz girmemiş paketlerde) bu başarısız olabilir. O zaman öncelikle paketi kurmalısınız:

devtools::install_github("ropenscilabs/rnaturalearthhires") 

Sonrasında şehir düzeyi haritayı bir sf veri nesnesi olarak indirip daha önceki gibi görselleştirebilirsiniz. Bu kez görselleştirmede il adlarını da görmek için geom_sf_text() geometrisini ekliyoruz:

require(rnaturalearthhires)
turkiyeDetay <- ne_states(country="Turkey", 
                        returnclass = "sf")
ggplot(data=turkiyeDetay) +
   geom_sf() +
   geom_sf_text(aes(label=name),size = 3)
## Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
## give correct results for longitude/latitude data

Eğer Türkiye haritası için ilçe düzeyinde geometri verisine ihtiyaç duyuyorsanız bunun için farklı bir kaynak gerekir. ABD Kaliforniya Üniversitesi tarafından sağlanan ücretsiz servis linki (https://gadm.org sitesinden) bu amaçla kullanılabilir. Aşağıdaki örneğimiz online dosyayı doğrudan okuyup geçici bir dosyaya atacaktır. Örnekteki linki ihtiyaçlarınıza göre uyarlarken GADM sitesinde (1)sf tipi veri seçmeli, ve (2) ilçe detayı istiyorsanız level-2 veri istemelisiniz:

#harita dosyasını indir
require(curl)
tmp <- tempfile()
curl_download("https://biogeo.ucdavis.edu/data/gadm3.6/Rsf/gadm36_TUR_2_sf.rds", tmp)

#dosyayı harita nesnesine dönüştür
require(sf) # harita bilgilerini okuyan  paket
turkiyeDetayIlce <- readRDS(tmp)

İsterseniz dosyayı manuel olarak indirip yapabilirsiniz.

require(sf) 
turkiyeDetayIlce <- readRDS("../assets/veri-setleri/gadm36_TUR_2_sf.rds")

Sonrasında ilçe düzeyinde de aynı bir önceki örnekteki gibi görselleştirme yapabilirsiniz. Ancak farklı veri kaynaklarından gelen veriler farklı standartta olacaktır. Örneğin NaturalEarth’den gelen verile yerleşim yeri ismi name değişkeninde iken GADM kaynaklı veride ilçe düzeyi isimler NAME_2 değişkeninde, il düzeyi ise NAME_1 değişkenindedir. Aşağıdaki örnekte NaturalEarth il düzeyi veri ile GADM ilçe düzeyi veriyi birlikte kullanıyoruz. geom_sf_text() fonksiyonunu bir kez ilçe isimlerini görsele koymak için, bir kez de farklı bir veri setinden il isimlerini koymak için çağırıyoruz:

ggplot(data=turkiyeDetayIlce) +
  geom_sf() +   
  geom_sf_text(aes(label=NAME_2),size = 2)+
  geom_sf_text(data=turkiyeDetay, aes(label=name), color="red",size = 3)
## Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
## give correct results for longitude/latitude data

## Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
## give correct results for longitude/latitude data

Fiziki harita üzerinde infografiler

Fiziki harita üzerinde infografiler üretilmesi analitik alanında önemli ihtiyaçlardan biridir. Esasen yukarıda açıklanan şekilde haritayı görselleştirdikten sonra infografi biçin bir adım kalıyor: veri noktalarını harita ile aynı koordinat sistemini, yani enlem-boylam koordinatlarını kullanacak şekilde yerleştirmek. İlke olarak basit olmasına rağmen burada sıklıkla temel bir veri sorunu yaşanır: infografide kullanılacak veri setlerinde genellikle yerleşimlerin (şehirler) adı ve yanında onunla ilgili bazı veriler (yerleşimdeki göç, suç oranı, satış hacmi, vb.) gelir. Ancak enlem-boylam koordinatları bulunmaz. Aşağıdaki örneklerde öncelikle bu güçlüğü aşmak için infografiye kaynak olan veri seti ile koordinat bilgisinin bulunduğu harita veri setini bir şekilde eşleştirme konusunu halledeceğiz

Örnek: Türkiye iller arası göç

Bu bölümde Türkiye ili ilgili bir infografi örneği yapacağız. Veri olarak ta TUİK’ten edinilebilen il düzeyi göç verilerini kullanacağız. Ne var ki bu veri setini harita üzerinde görselleştirmeden önce biraz veri dönüştürme gerekiyor. Meraklı okurlar aşağıdaki bölümde bu veri dönüştürmelerinin nasıl yapıldığını okuyabilirler. Ancak isterseniz bir sonraki bölüme atlayabilir ve doğrudan temizlenmiş veri setini kullanarak devam edebilirsiniz

Veri temizliği

Öncelikle bu veri tablosunu sadeleştirerek, sütun isimleri geçerli değişken isimleri olacak şekilde bir CSV dosyasına dönüştürdük. Bu ara formata dönüştürülmüş veriyi linkten alabilirsiniz. Bu analizde amacımız hem şehirlerin göç miktarını göstermek hem de göç alan veya veren şehirleri ayrıştırmak oalcak. Bu yüzden şehirlerin net göç miktarının pozitif veya negatif oluşuna göre bir veri sütunu ekleyeceğiz. Bunun için sign() fonksiyonundan ve mutlak değer bulan abs() fonksiyonundan yararlanabiliriz. Elde ettiğimiz sonuçları mutlak göç miktarını belirten mutlakGoc ve net göçün içer veya dışa doğru oluşunu belirten yon değişkenlerinde tutacağız:

goc <- read.csv("../assets/veri-setleri/tuik-ic-goc.csv")
goc$netgoc <- goc$aldigiGoc - goc$verdigiGoc
goc$yon <- as.factor(sign(goc$netgoc))
goc$mutlakGoc <- abs(goc$netgoc)

Şimdi bu göç verisini harita verisi ile birleştirmemiz gerekiyor. Ancak burada ciddi bir güçlük var çünkü yabancı kaynaklardan gelen harita bilgisinde il/ilçe isimleri İngilizce alfabesindeki harflerle yazılmış, oysa TUIK verilerinde il isimleri Türkçe. Ne yazık ki Türkiye harita verilerini sağlıklı bir formatta bize sağlayacak bir kurum Türkiye’de bulunmuyor. Bu yüzden aşağıdaki manipülasyonlar gerekiyor. chartr() fonksiyonunu her şehir isminde istediğimiz harfleri bulup başka harfler koymak için kullanacağız, ancak bunu tüm harfler için tekrarlamak üzere bir for döngüsü kuracağız:

harflistesitr    <- strsplit("ğışçÜĞİŞÖ","")[[1]] #Türkçe harfler, "Ç", "ö" ve "ü" hariç
harflistesiascii <- strsplit("giscUGISO","")[[1]] # İngilizce karşılıkları
goc$ilAscii<-goc$il
for (i in 1:length(harflistesitr)) {
  goc$ilAscii<-chartr(harflistesitr[i],harflistesiascii[i],goc$ilAscii)
}

Üstelik bu kadarı da yeterli değil. NaturalEarth veya GDA verilerine dikkatli bakarsanız çoğu Türkçe harf yerine İngilizce alfabesinin kullanıldığını, ancak ilkinde “Ç”nin, “ö”‘nün ve “ü”nün Türkçe olduğunu, ikisinde de “Zonguldak” yerine “Zinguldak”, “Kirikkale” yerine “Kinkkale” yazıldığını, ve “Kahramanmaraş” yerine “K. Maras” yazıldığını görürsünüz. Bu yüzden haritada hala bazı eksiklikler olacak. Bunları teker teker düzelteceğiz:

goc$ilAscii[goc$il=="Kahramanmaraş"]="K. Maras"
goc$ilAscii[goc$il=="Zonguldak"]="Zinguldak"
goc$ilAscii[goc$il=="Kırıkkale"]="Kinkkale"

Bu kadar temizlikten sonra temizlenmiş veriyi bir dosyada saklamak iyi olabilir:

write.csv(goc,"../assets/veri-setleri/il-goc-temiz.csv")

Veriyi harita ile eşleştirme ve görselleştirme

Yukarıdaki veri temizliğini yapmak istemiyorsanız temizlikten sonra elde edilen veri dosyasını doğrudan indirip kullanabilirsiniz:

goc <- read.csv("../assets/veri-setleri/il-goc-temiz.csv")

Görselleştirme yapabilmek için önce elimizde infografiye kaynak olan göç verisetindeki şehir isimleri ile harita veri setindeki şehir isimleri ve enlem-boylam bilgisini eşleştirmek gerekiyor. Bunun için dplyr paketindeki inner_join() , fonksiyonu şehir isimlerini eşleştirerek iki veri tablosunu birleştirmek için idealdir:

require(rnaturalearth)
turkiyeDetay <- ne_states(country="Turkey", 
                        returnclass = "sf")
require(dplyr)
tmp<-inner_join(turkiyeDetay,goc,by=c("name"="ilAscii"))
## Warning: Column `name`/`ilAscii` joining character vector and factor, coercing
## into character vector

Nihayet görselleştirmeye geçelim. Görselimizde nokta büyüklükleri net göçü gösterirken artı ve eksi göç durumu için farklı renkler kullanacağız. Şehir isimlerini son katman olarak yazdırıyoruz ki biraz da büyükçe olan infografik noktaların altında kalmasınlar:

require(ggplot2)
ggplot(data=tmp) +
  geom_sf() +
  geom_point(aes(x=longitude, y=latitude, size=mutlakGoc, color=yon))+
  geom_sf_text(aes(label=il),size = 3) + 
  ggtitle("İllerin aldığı-verdiği göç")
## Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
## give correct results for longitude/latitude data

Örnek: Korona virüs vaka haritası

Güncel bir örnek olarak Korona virüs vakasını ele alalım. Dünya’nın farklı ülkelerinde onaylanmış Korona virüsü vakaları ile ilgili bir veri seti Kaggle tarafından paylaşılmaktadır. Bu verisetini indirerek aşağıdakine benzer bir kod ile yükleyebilirsiniz. Örneğimizde aynı ülkede farklı zamanlarda tespit edilen vakalar aggregate() fonksiyonu ile birleştirilmiştir:

covid <- read.csv("../assets/veri-setleri/covid_19_data.csv")
covidCountry<-aggregate(. ~ Country.Region, data=covid, FUN=sum)

Bu veri setini Dünya ülkelerinin coğrafi bilgileri veri seti ile eşleştirerek tek bir veri seti elde edeceğiz. Ne var ki birleştirme aşamasında iki farklı kaynaktan gelen veri tablolarındaki ülke isimleri tam olarak eşleşmiyor. Örneğin Kaggle verisinde “Mainland China” diye yazarken harita verisinde “China” olarak, ilkinde “US” ikincisinde “USA” olarak geçiyor.

Bu yüzden biz Kaggle verisinin harita verileriyle eşleşecek şekilde elle değiştirilmiş bir versiyonunu kullandık. Siz de bu veri tablosunu indirip kullanabilirsiniz:

covidCountry <- read.csv("../assets/veri-setleri/covid_19_data-duzeltilmis-toplu.csv")

Ayrıca Dünya haritasının iki versiyonuna ihtiyacımız olacak. rnaturalearth kitaplığı ülke seviyesi haritada enlem-boylam bilgisi içermiyor, ancak il düzeyinde veri sorgulandığında bu bilgileri içeriyor. Çizim için ilkine, infografik verilerin eşleştirilmesi için ikincisine ihtiyacımız olacak. İlk haritada her ülkeden bir koordinat yeterli olduğundan ve başka sorunları engellemek için dplyr kitaplığından distinct() fonksiyonu ile mükerrer kayıtları temizleyeceğiz (bunun küçük bir sakıncası her ülkenin enlem-boylam’ı olarak o ülkeden herhangi bir şehrin konumunu alıyor olmamız):

dunya<-ne_states(returnclass = "sf")#ggplot2::map_data("world")
require(dplyr)
dunya<-distinct(as_tibble(dunya),admin, .keep_all = TRUE)
dunyaSadeceUlkeler<-ne_countries(returnclass = "sf")

Eşleştirme için yine dplyr kitaplığından inner_join() kullanıyoruz:

require(dplyr)
birlesik<-inner_join(covidCountry,dunya,by = c( "Country.Region" = "admin"))
## Warning: Column `Country.Region`/`admin` joining factor and character vector,
## coercing into character vector

Şimdi bu veri setini görselleştirebiliriz. Her ülke üzerindeki noktaların büyüklüğü için onaylı vaka sayısını kullanıyoruz. geom_sf() fonksiyonunda ayrı bir veri seti belirttiğimize dikkat edin:

ggplot(data=dunya) +
  geom_sf(data=dunyaSadeceUlkeler) +
  geom_point(data=birlesik,
             aes(x=longitude, y=latitude,size=Confirmed), color="blue")+
  geom_sf_text(data=dunyaSadeceUlkeler,aes( label=admin),size = 1.3) + 
  ggtitle("Dünya ülkelerindeki Korona Virüs Vakaları")
## Warning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
## give correct results for longitude/latitude data