oniki maymun

2015 Genel Seçimleri

Bu ilk yazıyı beton, kuru kalabalık ve rant threesome'ının meyvesi olan İstanbul trafiği hakkında yazmayı düşünmüştüm. Artık iyice saçmalayan trafik hayatımızdan ne kadar yiyor, ne zaman yiyor ve Lufthansa bu işin neresinde soruları ile yola çıkıp büyük oyunu bozma yolunda 3-5 paragraf kadar yazı bile yazmıştım. Yazım sürecinde farkettiğim ilk şey yazı yazmayı beceremiyor olduğumdu (bunu siz de farkedeceksiniz). Bir diğeri ise çelik gibi irademe rağmen ülke gündeminden istesem de uzak kalamadığımdı.

Gündemde 'sandıktan ne çıktı?', 'seçmen ne demek istedi?' gibi anlamlı sorular varken trafik yazısı yazılmazdı. Kendimi gündeme kaptırdığım bu sıralarda, ofiste eşzamanlı devam eden çoklu goygoylardan birisinde, tarafıma 'oy verdiğin sandıkta kime kaç oy çıkmış baktın mı?' sorusu yöneltildi. O an öğrendim ki CHP'nin sandık takip sistemi (STS) diye bir sitesi var ve vatandaş oy kullandığı sandıktan ne çıkmış görebiliyor. Hemen oy verdiğim sandığı kontrol ettim ve 'bu millet adam olmaz abi, Aziz Nesin haklıydı' diyerek çevremdekileri etkisiz hale getirdim.

Kalabalık kendine gelip dağıldıktan sonra siteyi tekrar açıp Nişantaşı, Cihangir ve Moda'daki sandıklara baktım ve hamuğa koduklarımın entelleri diyerek çayımı yudumlamaya devam ettim. Çay içerken genel popülasyona küfretmek en sevdiğim vakit geçirme aktivitelerinden birisidir ama bir süre sonra tek tek sandık bakmak sıkıcı olmaya başlıyor. Bütün bu sonuçları bir harita üzerinde görebilsem ve ona göre Yozgat veya Sivas'taki en uygun sandığın yanına taşınsam diye düşünürken CEO'muz bizzat geldi ve toplantı var diyerek düşünce zincirimi böldü. Toplantıda üst üste çay içip masadaki tüm kurabiyeleri götürürken aklımdaki tek şey sandık bazında seçim verisini nasıl ele geçirebilirim düşüncesiydi.


Akşam iş çıkışından sonra doğruca eve gitmem 3 saat sürdü. Evdeki temaslarımda bu şehirde yaşanmaz konusunda görüş birliğine vardıktan sonra bilgisayarın başına geçtim ve CHP'nin sitesini hack ederek verileri indirdim. Şaka şaka. CAPTCHA koymuşlar indirilmesin diye. Sorsam büyük ihtimalle verirler (kerizler) ama insanlarla muhattap olmam gerekecek ve mail'lerde beyler, hanımlar, saygılar havada ucuşacak. Daha kolay bir hedef bulmalıydım. Dakikalar süren araştırmalarım sonucunda Yüksek Seçim Kurulu (YSK)'nın sandık bazında seçim verilerini bizlerle paylaştığı sitesini gözüme kestirdim.

Kullanışsız olması için emek sarfedilmiş, gereksiz javascript kullanımı konusunda trendsetter bir site (10/10).

YSK sonuç sitesinin HTML kaynak koduna baktığımda ilk olarak Oracle backend'ine Brainfuck ile frontend yazmışlar helal olsun diye düşündüm. Tüm veriyi indirmek için HTML kodunu parse ederek scrape etmek söz konusu bile olamazdı. Sayfayla her etkileşimimde javascript kodunun 'bi sn. abi' demesini de sitenin artıları hanesine yazdığım not edilsin. Böyle diyorum ama ilk bir gazla HtmlAgilityPack zorlamadım değil. Bilenler bilir: HtmlAgilityPack, sayfa DOM yapısını Stack Overflow eşliğinde işlemeye yarayan bir yazılım kütüphanesidir. Bu iş böyle olmaz demem çok uzun sürmedi ve HtmlAgilityPack ile yollarımı ayırıp kendimi tekrar Türk interneti çöllerine verdim. En başta yapmam gereken işi (google search), kesin hiç birşey yoktur diye sallayarak vakit kaybetmiştim (kendime not: google.com/search adresini favorilere ekle).

Acaba birisi/birilerinin benim gibi dertleri olmuş muydu? Sihirli sorgu cümlesi "secim sonucu verileri csv" yi bulana kadar bir iki boş atış yaptıktan sonra ikinci sonuç sayfasının ortalarında aradığımı buldum: Talha Öz'ün github repo'su.

scrape.py: Scrapes Turkish local elections, 2014 results from ysk.gov.tr (official election institute).

Talha'nın bu süreçte neler yaşadığını ve veri ile ne yaptığını (hayır öyle birşey yapmamış, terbiyesizleşmeyin) buradan okuyabilirsiniz. Yeri gelmişken kendisine teşekkür ediyorum.

scrape.py koduna bakınca splinter, splinter neymiş diye bakınca da Selenium diye bir kütüphanenin varlığından haberdar oldum. Hemen atlamayıp önce Talha'nın blog postunu okusaymışım da haberdar olabilirmişim. Neyse. Selenium, web developer'ların kod çorbası ucubelerini test edebilmeleri için geliştirilmiş faydalı bir eser (8.5/10). DOM ile boğuşmak yerine sayfada şu butona tıkla, burayı kopyala vs diye takılabiliyorsunuz.

scrape.py kodunun 2014 yerel seçimleri için olmasının dışında bir eksiği de python ile yazılmış olması (python kurmam gerekecek çünkü). Eninde sonunda kuracağım ama hazır kurulu Visual Studio ile ne yapabilirim diye Manage Nuget Packages diyor ve Selenium için bir Nuget paketi buluyorum. 2015, yapısal olarak 2014 kodunun aynısı. Sayfadaki elemanların id'leri ve .NET ile çalıştığım için bazı fonksiyonlarin isimleri değişiyor sadece. Browser'da F12 ile developer tools'a geçip eleman id'lerini bulup gerekli yüzeysel düzeltmeleri yaptıktan sonra run forest run diyor ve izliyorum.

Bu işlerin en zevkli tarafı hamaliye kısmının otomatik yapılışını izlemek. Çamaşır makinesi izlemek gibi. Tüm veriyi indirmek iki saat civarında sürüyor. Bir çaydanlık çayı daha tek başıma içiyorum.


İşimiz tabi ki bitmedi, hatta yeni başlıyoruz. Tüm illeri/ilçeleri tek tek indirdik ama derli-topluluk açısından bunları birleştirmek gerekli. Talha birleştirme işini xlrd ile yapmış ama benim COM Interop belasına bulaşmam lazım zira dosyalar xls formatında (xlsx olaydı iyiydi). Neyse ki hangi satır hangi kolon diye çok uğraşmadan basit copy/paste ile halledilebilecek bir yapı var. Hızlı ve kirli dedikleri cinsten birşeyler yazıp kod çalışırken biraz daha çay içiyorum.

Birleştirme işinden sonra elimde her il için farklı header'ı olan bir excel dosyası var. (Her ilde farklı bağımsız adaylar olmasının yanında (duh), gelen verilerde parti sıraları il il değişkenlik gösteriyor.) Çalışmak için csv daha iyi bir format ama onun için header sayısının sabit olması lazım (sabit olması tercih ediliyor demek daha doğru aslında). Bağımsız adaylarla daha sonradan dalga geçebilmek için bu şekilde bırakıyorum. Türkiye'de 50015 mahalle/köy (ve muhtar) var bu arada (kesin bilgi).


Bu kadar uğraşmamın sebebi Yozgat veya Sivas civarlarında tam olarak nereye yerleşeceğimi bir çırpıda görmek istememdi hatırlarsanız. Kafamdaki şey aşağıdaki haritanın benzerini Türkiye için yapmak.

Artık ırkçılık daha kolay! (11/10)

Böyle bir harita, mahalle/köy sınırlarını bilmeyi gerektiriyor ve burası zurnanın zırt dediği yer. Tüm Türkiye için mahalle/köy sınırlarının çekilebileceği açık bir kaynak yok. Olsa bile kimsede tam verinin olduğunu da sanmıyorum. Openstreetmap'te kesinlikle yok onu biliyorum. Avanos'ta yaşayan Almanya doğumlu katpatuka neler demiş okuyalım:

İl sınırları haritaları

Posted by katpatuka on 3 November 2010 in Turkish (Türkçe)

OSM'de geri kaldığı için son günlerde internette il ve ilçe sınır haritaları arıyorum - bir bok çıkmıyor! Türkiye CBS konusunda baya geride kalıyor sanarım! Resmi dairelerde bile güzel veri bulunmaz! Haritacılık halen askeri parmak altında kaldığı için açık kaynak kodlu doysaları zaten bulunmaz; ne .shp dosyalar ne de AutoCAD, sadece dandırık dündürük küçük çözünürlükte olan WGS84 dayalı olmayan .jpg ve .gif dosyaları kaymakamlık, özel idare ya da belediye sitelerinde buldum...

Tek İstanbul Rehber Haritası iyi yapılmış durumda fakat o veriler hayatta vermezler...

Türkiye openstreetmap'deki olan imkanları henüz pek tanımıyor, gözü açılmadı - anlatamam yav...!

Mahalle/köy sınır poligonları işine ileride tekrar bulaşmayı düşünüyorum ancak şimdilik noktasal koordinatlar ile devam etmek dışında bir seçeneğim yok. Yapmaya çalıştığım şeye, yani isimden enlem/boylam tahmini işine Geocoding diyorlar. Google ve Facebook kelimenin tam anlamıyla isminizden kıçınızı son koyduğunuz noktanın koordinatını santimetresine kadar söyleyebilir mesela, ama bu başka bir yazının konusu. Evet.

Google, Yandex, Bing ve Openstreetmap dörtlüsünün hepsinin Geocoding API desteği var. En dandiği Openstreetmap tabi ki. Diğerlerinin hepsi de Türkiye verisini büyük ihtimalle aynı yerden alıyorlar. Biraz da Ruski'lerin gönlü olsun diyerek Amerikan sermayesi yerine Yandex'i seçiyorum. Yandex, bu işi günde en fazla 25000 defa bedavaya getirebilirsiniz demiş. 50 bin küsür adres için 2 gün demek. Olsun, acelem yok.

string[] lines = File.ReadAllLines("mahalle2015.csv").Distinct().ToArray();
using (WebClient wc = new WebClient(){ Encoding = Encoding.UTF8 })
{
    foreach (string line in lines)
    {
        if (string.IsNullOrWhiteSpace(line)) continue;
        Console.Write(line);

        // get the geocoder response as xml string
        // limit to one result
        string address = String.Format("http://geocode-maps.yandex.ru/1.x/?geocode={0}&lang=en-US&results=1", line);
        string result = wc.DownloadString(address);

        // extract the values between <pos>...</val>
        // no xml parsing - serenity now, insanity later.
        int pos1 = result.IndexOf("<pos>");
        int pos2 = result.IndexOf("</pos>", pos1 + 1);

        // write to console and append to out file
        string latlonStr = result.Substring(pos1 + 5, pos2 - (pos1 + 5));
        Console.WriteLine(" " + latlonStr);
        File.AppendAllText("out.txt", latlonStr+Environment.NewLine,Encoding.UTF8);
    }
}

Yukarıdaki kodun hata kontrollü ve resume destekli versiyonunu çalıştırıp tüm mahalle/köy koordinatlarını indirmek 6 saatten biraz fazla sürüyor. Resume desteği şart çünkü arada bağlantıdan kaynaklı saçmalama garantisi var. Bu işlemin sonunda Yandex Geocoding API'si hakkında diyeceklerim:

  • İyice abartmadıkça günlük 25000 istek sınırı uygulanmıyor gibi(exhibit A: 6 saatte 50000 küsür istek).
  • Geocoding algoritması İstanbul'u tamamen ayrı ele alıyor. Adres satırında İstanbul varsa bulamadığı adresi, sadece mahalle isminden bulabiliyor mesela. Ankara ve İzmir'in bazı kısımları da benzer şekilde.
  • 2800 küsür kadar mahalle/köy'ün nerede olduğu belirsiz. Bilinmeyenlerin içinde İstanbul Esenyurt'taki bazı mahalleler de var.
  • Toplam bilinmeyen adres sayısı %6 civarında.

En azından istanbul tam olsun diye eksikleri diğer kaynaklardan tamamlamaya uğraştım ama anlaşılan o ki Esenyurt'taki mahalleler çok gıcır. Aşağıdaki mahalleler için yetkilileri göreve davet ediyorum.

ISTANBUL  ESENYURT  ASIK VEYSEL MAH.
ISTANBUL  ESENYURT  BAGLARÇESME MAH.
ISTANBUL  ESENYURT  BALIKYOLU MAH.
ISTANBUL  ESENYURT  ESKINOZ MAH.
ISTANBUL  ESENYURT  KAPADIK MAH.
ISTANBUL  ESENYURT  ORHAN GAZI MAH.
ISTANBUL  ESENYURT  SELAHADDIN EYYUBI MAH.
ISTANBUL  ESENYURT  SULTANIYE MAH.
ISTANBUL  ESENYURT  SÜLEYMANIYE MAH.
ISTANBUL  ESENYURT  TURGUT ÖZAL MAH.
ISTANBUL  ESENYURT  YUNUS EMRE MAH.
ISTANBUL  ESENYURT  ZAFER MAH.
ISTANBUL  ESENYURT  AKEVLER MAH.
ISTANBUL  ESENYURT  BARBAROS HAYRETTIN PASA MAH.
ISTANBUL  ESENYURT  BATTALGAZI MAH.
ISTANBUL  ESENYURT  GÖKEVLER MAH.
ISTANBUL  ESENYURT  HÜRRIYET MAH.
ISTANBUL  ESENYURT  MEHMET AKIF ERSOY MAH.
ISTANBUL  ESENYURT  MEVLANA MAH.
ISTANBUL  ESENYURT  NECIP FAZIL KISAKÜREK MAH.
ISTANBUL  ESENYURT  OSMANGAZI MAH.
ISTANBUL  ESENYURT  PIRI REIS MAH.
ISTANBUL  ESENYURT  SEHITLER MAH.

Sınır poligon verisi olmadığından noktasal koordinatlarla çalışmak durumundayım ancak bunlarla yapılabilecek şeyler sınırlı. Harita üzerinde birşey göstermek adına kolaya kaçıp heatmap çizdirme kararı alıyorum. Bilmeyenler için, heatmap (ısı haritası) aşağıdaki gibi birşey. Resim Alper Dinçer'in blog'undan.

Zelzeleler

Yeri gelmişken sözelciler için bir hatırlatma yapayım: SEÇİM VERİSİ HEATMAP İLE GÖSTERİLMEZ. Lineer değişmeyen, sürekli olmayan değerler heatmap ile gösterilmez, demografik veri hiç gösterilmez. Benim gibi ota boka heatmap çizdirmeyin, adam olun.

Devam edelim... Bir iki gün bakındıktan sonra heatmap için leaflet.js + leaflet-heat.js kombosunu kullanmaya karar verdim. Kullanmak saçmalık derecesinde kolay. Bu kadar kolay olmamalıydı. Aşağıda yirmi küsür satırda sarayı hedef gösterdim mesela. Eskiden olsa hehey.

<html>
<head>
    <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.css" />
    <script src="http://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.js"></script>
    <script src="http://leaflet.github.io/Leaflet.heat/dist/leaflet-heat.js"></script>
</head>
<body>
    <div id="map" style="width: 100%; height: 100%"></div>
    <script type="text/javascript">
    var layer = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://cartodb.com/attributions">CartoDB</a>, <a href="http://www.onikimaymun.org">12maymun</a>'
        });
    var map = L.map('map', { center: [39.930734, 32.798288], zoom: 14 });
    map.addLayer(layer);
    
    values=[[39.930734, 32.798288, 10000000]]; // on milyon 
    
    var heat = L.heatLayer(values, {radius: 10, blur: 5}).addTo(map);
    </script>
</body>
</html>

1 nokta ile performans inanılmaz. 50 bin küsür nokta çizdirirken de pürüzsüz olacak eminim. Her mahalle/köy için (partinin aldığı oy/geçerli oy)*100 hesabını yapıp hemen cehape zihniyeti nerelere yuvalanmış ona bakıyorum.

Türkiye bacon tüketim yoğunluğu haritası

Meclise kapağı atmış tüm partiler için yukarıdaki hesabı tekrarlayıp, hepsini aynı harita üzerinde göstermek işini de hallettikten sonra bir de Saadetx10, Vatanx100 ve LDPx100 için hesap yapmaya girişiyorum. Girişiyorum dediğim, koda üç satır ekliyorum. Veri isimlerinden de anlaşılacağı üzere, harita üzerinde görülecek değerler bu partilerin aldıkları oy oranlarının 10 veya 100 ile çarpılmış halleri. Diğer partileri de üşenmezsem eklerim bir ara.

Ve nihayet en sonunda, herşeyin bir araya geldiği harita (tam ekran bakmanız önerilir):

Tam ekran görüntülemek için tıklayın


Bu harita bize ne gösteriyor sorusunu cevaplamayı siz değerli okuyucularıma bırakıyorum. Ben taşınacağım mahalleyi buldum.

Sonuçta tam istediğim şeyi yapamasam da DOKTORA DÜZEYİNDE bir çalışma oldu bana sorarsanız. Eksikler var, hatalar da vardır kesin. Bariz bir eksik haritada legend olmaması mesela. Uzun iş diye üşendim ama vakit bulursam ekleyeceğim. Tüm seçmenlerin adresini tek tek bilen YSK, ÇOK DAHA HASSAS ve eksiksiz bir harita yapabilir isterse diye de bitireyim.

Tüm veri ve veriyi indirmek/işlemek için yazdıklarım github'da. Yapılabilecek çok şey var ama ben birşey yapana kadar bir seçim daha olacak gibi gözüktüğünden, hepinizi veriyi indirip kurcalamaya davet ediyorum.

Herkese sabırlar.

@onikimaymunn
2015 / Yukarınohutlu Mah. / YOZGAT