Näytetään tekstit, joissa on tunniste Python. Näytä kaikki tekstit
Näytetään tekstit, joissa on tunniste Python. Näytä kaikki tekstit

torstai 6. maaliskuuta 2014

Python hoitaa, eli kuinka Plus-deski kokeili screen scrapingia

Kun keskustelu Keva-johtaja Merja Ailuksen työsuhde-eduista oli jatkunut jo jonkin aikaa, Yle:n toimittaja Jarno Liski alkoi pyöritellä mielessään kysymyksiä: kuinka kannattava työsuhdeasunnossa asuminen on, kuka siitä todella hyötyy ja millä tavalla työsuhdeasuminen otetaan verotuksessa huomioon. Hänen lähtöoletuksensa oli, että työsuhdeasuntojen verotusarvo vastaisi huonosti asunnoista vapailla markkinoilla maksettavaa vuokraa.

Liski otti yhteyttä Plus-deskin tuottaja Juho Salmiseen, joka lähti yhdessä Teemo Tebestin kanssa viemään asiaa eteenpäin. Lopputulos on nähtävissä täällä.

Verottajan väite syyniin

Verottajan mukaan asuntojen verotusarvo vastaa keskimäärin 90-prosenttisesti asunnosta pyydettyä vuokraa. Esimerkiksi 700 eurolla vuokrattavan asunnon verotusarvon pitäisi olla noin 630 euroa. Tämä tuntui kuitenkin pitävän huonosti paikkansa. Ideana olikin tutkia, minkä suuruisia verotusarvoja asunnot todellisuudessa saavat ja kuinka hyvin verotusarvo vastaa asunnoista pyydettävää vuokraa.

Verottaja määrittelee työsuhdeasunnoille laskukaavan, jonka avulla verotusarvo määritellään asuntokohtaisesti. Verotusarvon laskennassa vaikuttavat asunnon sijainti (pääkaupunkiseutu/muu maa), pinta-ala sekä valmistumisvuosi. Työntekijä saa asuntoedusta lisähyötyä silloin, kun verotusarvo on todellista markkina-arvoa selkeästi alhaisempi, koska tällöin hänen verotuksensa on keveämpi kuin sen tulisi olla.

Teemo ja Juho pohtivat, miten asiaan pääsisi käsiksi. Verottajan kaavan pitäisi tuottaa arvo, joka on hyvin lähellä markkinahintaa. Jarno Liskin idea oli kerätä suuri määrä olemassa olevien vuokra-asuntojen tietoja ja laskea asunnoille verottajan kaavan avulla verotusarvo.

Suomessa on muutama suuri vuokra-asuntoja listaava verkkopalvelu, joiden dataa olisi mahdollista käyttää verottajan kaavan testaamiseen. Käsityönä verotusarvon ja markkina-arvon kattava vertailu ei kuitenkaan onnistuisi, sillä vuokra-asuntoja on sivustoilla tuhansia.

Oli siis kirjoitettava koodinpätkä, jonka avulla pystyttäisiin keräämään tietyn verkkopalvelun vuokra-asuntoja koskeva data, laskea niiden perusteella asunnoille verotusarvo ja analysoida saatuja arvoja. Datan keräämiseen käytettävää menetelmää kutsutaan ruudunraavinnaksi (engl. screen scraping, tuttavallisesti screippaus). Asuntojen tiedot voisi poimia miltä tahansa vuokra-asuntoja listaavalta sivustolta. Teemo käytti tässä tapauksessa Alma Median ylläpitämän Vuokraovi.com-palvelun tietoja.

Koodi ja tietokanta valmiiksi

Ruudunraapijan kirjoittamiseen Teemo päätti käyttää Python-kieltä. Screipatessa kirjoitettu koodi käy järjestelmällisesti läpi verkossa olevia nettisivuja, poimii sivulta halutut elementit ja siirtää ne tietokantaan. Tietokannaksi Teemo valitsi ennestään tutun MongoDB:n. MongoDB sopi käyttötarkoitukseen hyvin, koska noSQL-tietokannassa datalle ei tarvitse määrittää tarkkaa rakennetta etukäteen, vaan sitä voidaan täydentää ja muokata tarpeen mukaan.

Kun Teemo laittoi homman aluilleen 29. marraskuuta, Vuokraovi-palvelussa oli tarjolla noin 6 000 vuokra-asuntoa eri puolilta Suomea. Yhdelle hakutulossivulle saa näkyviin kerrallaan 10–30 vaihtoehtoa, joten tarvittavien kyselyjen määrän minimoimiseksi Teemo sääti määrän maksimiinsa. Näin screipattavaksi jäi hieman yli 200 erillistä sivua, jolla jokaisella oli 30 asuntoa. (Kuva 1)

Helmikuussa 2014 Vuokraovi-palvelussa oli tarjolla n. 8 100 vuokra-asuntoa. (Kuva 1)
Datan keräämistä mutkisti se, että kaikki tarvittavat tiedot eivät löytyneet tältä hakutulossivulta. Koodin tuli käydä läpi myös jokaisen asunnon oma sivu, sillä asunnon rakennusvuotta ei mainita kaikkien asuntojen listauksessa. Kyselyjä piti siis lopulta tehdä 200 hakutulossivuille ja 6 000 yksittäisten asuntojen omille sivuille.

Teemon ensimmäinen ajatus oli kokeilla ScraperWiki -palvelua, jota hän oli kuullut käytettävän screippaukseen. Palvelu on ollut etenkin toimittajien suosiossa ja Teemo ajatteli, että palvelun testaamisen jälkeen hän voisi opettaa sen käyttöä muille toimittajille.

ScraperWiki mahdollistaa esimerkiksi Python-koodin kirjoittamisen ilman erillisten ohjelmointiympäristöjen asentamista. Siksi palvelu sopii esimerkiksi journalisteille, joilla ei usein ole mahdollisuutta tai taitoa asentaa kaikki tarvittavia ohjelmistoja. Teemo päätyi kuitenkin tekemään toteutuksen omalla koneella omassa ympäristössä, koska tämä oli lopulta hänelle kätevämpää.

Scraper käy läpi HTML-koodia ja tallentaa määritellyt osiot talteen myöhempää käyttöä varten. (Kuva 2)
HTML-merkkauksen siivouksessa ja käsittelyssä Teemo käytti hyväkseen BeautifulSoup-nimistä Python-kirjastoa. BeautifulSoup on kirjasto, jonka avulla verkkosivuilla käytetty HTML-merkkaus voidaan muuttaa koneelle ymmärrettävään muotoon. Lopulta koodi ryömi Vuokraovi.comin sivuja muutaman tunnin ajan. Ajo tehtiin vielä uudelleen lopullista uutista varten 2. joulukuuta.>

Trouble shooting vei aikaa

Kun Teemo testasi koodia ensimmäisiä kertoja, koodi ei hakenut dataa halutulla tavalla. Vian etsintään menikin hetki. Lopulta Teemo tajusi tutkiessaan HTTP-pyyntöä, että Vuokraovi.com -sivusto asetti Internet-selaimessa tukun evästeitä, eli cookieita, joiden puute esti scraperin toiminnan. Hän päätteli, että asian korjaamiseksi samaiset evästeet tulisi asettaa myös tietokoneohjelman tekemään HTTP-pyyntöön. Tällöin HTTP-pyyntö vaikuttaisi Vuokraovi.comin näkökulmasta täysin samanlaiselta kuin jos sen tekisi esimerkiksi Google Chrome -selaimella. (Kuva 3)

HTTP-pyyntöihin liittyvät evästeet näkee Google Chrome -selaimessa Inspect Element -valikon takaa. (Kuva3)
Joskus screipatessa voi käydä niin, että jos pyyntöjä tehdään suuri määrä, palvelin voi tulkita ne vihamielisiksi ja estää ne. Vuokraoven tapauksessa reilut 6 000 pyyntöä hukkui luultavasti normaalin verkkoliikenteen sekaan. Screippausta suunnitellessa kannattaa kuitenkin miettiä, voiko rajoituksista tulla ongelmia. Esimerkiksi hakukoneyhtiö Google tunnistaa tehokkaasti koneellisesti tehtävät haut ja estää ne, koska koneellisesti tehtävät haut ovat osa heidän maksullista palveluaan.

Toteutus on ladattavissa avoimena lähdekoodina (CC-BY-SA 4.0) Ylen Github-sivulta.

Yhden päivän työ

Ennen julkaisua aineistosta siivottiin pois sellaiset kohteet, joista puuttui jokin tarvituista tiedoista, kuten rakennusvuosi, sekä autotallit ja kalustetut asunnot. Näin aineistosta saatiin vertailukelpoista. Karsimisen jälkeen pohja-aineistoksi jäi noin 5 000 vuokra-asunnon tiedot. MongoDB:stä data tulostettiin .csv-muodossa ja vietiin Googlen Spreadsheets-taulukkolaskentaohjelmaan, jossa lopullinen verotusarvolaskenta tehtiin. Aineisto on ladattavissa Ylen sivuilta Excel-muodossa.

Jutun teknisten osien toteuttaminen järjesteltävine taulukoineen kesti noin työpäivän verran, joskin homma olisi sujunut nopeammin, jos eväste-ongelman selvittämiseen ei olisi mennyt aikaa. Jatkossa vastaavanlaisia toteutuksia tehtäessä evästeet osataan ottaa paremmin huomioon.

Eräs aineistosta tehty havainto oli, että erot markkinavuokrassa suhteessa verotusarvoon olivat alueellisia. Niinpä juttu sai otsikon: Asuntoedusta hyötyy eniten Helsingissä ja kehyskunnissa – katso oman kuntasi tilanne. Jutun yhteydessä käyttäjä sai tutkia asuntojen markkinavuokran ja verotusarvon eroa sekä kunnan että postinumeroalueen perusteella.

Aineisto myös paljasti, että verotusarvon ja markkina-arvon välinen vastaavuus oli keskimäärin 73 prosenttia, siis huomattavasti verottajan edustajan ilmoittamaa 90 prosenttia pienempi.

Juttu ei ollut varsinainen yleisömenestys. Asuntoedusta nauttii Suomessa alle 20 000 henkilöä, joten kohdeyleisökään ei ollut järin suuri. Vaikuttavuutta ei voida kuitenkaan mitata pelkästään lukijamäärän perusteella.

Screippaus-kokeiluna juttu oli Plus-deskille uusi aluevaltaus ja on hyvin todennäköistä, että tulevaisuudessa tällaisia omaan datan hankintaan perustuvia "skuuppeja" tulee enemmän.

Laita siis Python töihin!

Heidi Kähkönen
Kirjoittaja on vapaa toimittaja ja datajournalismikouluttaja
Twitter:  @heidikahkonen

maanantai 9. joulukuuta 2013

Datajournalistin veropäivä

Tarkoitan veropäivällä tässä yhteydessä marraskuun ensimmäistä, jolloin suomalaisten veronmaksajien edellisen kalenterivuoden verotiedot tulevat julkisiksi. Marraskuussa julkistettiin siis kaikkien suomalaisten pääoma- ja ansiotulotiedot vuodelta 2012. Tiedot ovat julkisia ja meidän jokaisen tulotiedot ovat saatavilla verotoimistoista kysyttäessä.

Kerron aluksi hieman veropäivän taustoja ja sitten tarkemmin mitä tein itse veropäivänä.

Tiedotusvälineissä veropäivä näkyy merkittävällä tavalla. Etenkin iltapäivälehdet tekevät paljon juttuja, koskien erilaisia ryhmiä ja heidän parhaiten tienaavia yksilöitä. Esimerkiksi iltapäivälehdet tekevät veropäivänä paljon juttuja otsikkorakenteella: "Katso kuka tienasi eniten ryhmässä X". Helsingin Sanomissa verotietoihin on myös panostettu ja HS:llä onkin erityinen Verokone, josta verotietoja on mahdollista tarkastella useamman vuoden ajalta. Maakuntalehdet taas tekevät juttuja omista lähtökohdistaan keskittyen etenkin oman alueensa parhaiten tienaaviin.

Yle Uutisissa tendenssi tuntuu kulkevan aina vain vähemmän retostelevaan suuntaan. Tämä näkyy esimerkiksi niin, että yksittäisten ihmisten tuloja nostetaan aina vähenevässä määrin esille. Näiden sijaan pyritään nostamaan enemmän esille ilmiöitä, joita tulojen takaa on nähtävissä kuten tuloerojen muutokset ja alueelliset erot tuloissa.

Viime vuonna tämä näkyi siten, että julkaisimme veropäivänä jutun "Näin moni tienaa vähemmän kuin sinä – kokeile itse", josta tuloja oli mahdollista tarkastella, ei yksilötasolla, vaan alueellisesti ja demograafisesti oman kiinnostuksen mukaan. Tämä vuonna samaa teemaa jatkettiin veropäivää edeltävänä päivänä julkaisemalla vastaavanlainen juttu "Kuinka moni naapurisi tienaa vähemmän kuin sinä? Kokeile itse laskurilla". (Kuva 1)

Rakentamallamme työkalulla tuloeroja saattoi verrata postinumeroalueittain. Kuvassa Vantaalla sijaitsevan Mikkolan tulojakauma (Kuva 1)
Lisäksi olemme tietoisesti pienentäneet "katso kuka tienasi eniten" -listoille päätyvien ihmisten määrää.

Viime vuonna koko maan listalla oli 1000 nimeä ja alueiden listoilla 200. Tänä vuonna näillä listoilla esitettyjen nimien määrät putosivat koko maan osalta 100 nimeen ja alueiden osalta 30 nimeen. Määrät poikkeavat selkeästi useista muista mediataloista ja heidän julkaisemistaan määristä.

Toki Yle:lläkin tehdään edelleen myös henkilöihin kohdistuvia juttuja.

Datajournalistin veropäivä


Itse veropäivänänä 1.11.2013 minun vastuullani oli tuottaa Yle Uutisten sivuille edellä mainitut "katso kuka tienasi eniten" -listat koko maan ja alueiden (maakunnat) osalta. Vastasin siis siitä, että veropäivän aamuna verotiedot olivat luettavissa Yle Uutisten sivuilta mahdollisimman nopeasti (käytännössä muutaman minuutin varoitusajalla) niiden julkistamisesta. Tiedotusvälineet saivat ennakkoon tilaamansa aineistot verottajalta samanaikaisesti veropäivän aamuna kello 8.00.

Yle Uutisten näkökulmasta kyse on siitä, että uutistilanteessa halutaan olla ensimmäisiä tai ensimmäisten joukossa. Ja ensimmäisinä mahdollisimman hyvin lukijoita palvelevalla tavalla. Myös maakuntatasolla halusimme olla uutiskilpailussa rintarinnan maakuntalehtien kanssa. Esimerkiksi niin, että Yle Tampere saisi nettiin samaan aikaan jutun verotiedoista ja eniten tienanneista kuin Aamulehti.

Tämä mahdollisimman hyvä ja nopea tapa oli käytännössä nettiuutiseen upotettava taulukko, jota olisi mahdollista järjestää sarakkeiden perusteella ja josta tietoja olisi mahdollista etsiä.

Kantava ajatus tämän saavuttamiseksi oli, että rakennamme putken verotoimistosta nettiin siten, että tiedot kulkisivat minun kauttani. Minun tehtäväkseni siis tuli muokata alkuperäistiedostot sellaiseen muotoon, että ne on mahdollista julkaista netissä ja sitten julkaista ne.

Sain haltuuni edellisen vuoden vastaavat aineistot, jotka Yle Uutiset oli saanut ja lähdin rakentamaan ratkaisumallia niiden pohjalta. Verohallinto toimittaa tiedot .csv-tiedostoina. Tiedostot on nimetty kryptisesti numerokoodeilla, mutta tiedostot avaamalla selviää, että tiedostonimiin on koodattu maakuntanumero (.xls) sekä tieto siitä minkä tyyppistä tietoa tiedosto sisältää. (Kuva 2)

Verohallinto toimittaa verotiedot toimitusvälineille .csv-muodossa. (Kuva 2)

Olimme tilanneet tänä vuonna samat aineistot kuin aikaisempinakin vuosina eli 1000 eniten tienannutta koko maan osalta sekä 200 eniten tienannutta maakuntien osalta (julkaisimme siis vain osan). Nämä tiedot oli pyydetty sekä ansio-, pääomatulojen sekä kokonaisansioiden mukaan järjestettyinä. Yhteensä tiedostoja oli siis 60 kpl ((19 maakuntaa + koko maa) * 3 tiedostoa / alue).

Ajatukseni siis oli, että tuottaisin tästä .csv-materiaalikasasta mahdollisimman automaattisesti html-muotoiset järjestettävät listat, jotka voitaisiin julkaista Yle Uutisten sivuilla artikkeleihin upotettuina.

Lähdin toteuttamaan tätä automatisointia Python-scriptillä. Apuna käytin MondoDB-tietokantaa, joka toimi tietojen väliaikaisena tallennuspaikkana. Scriptin toimintalogiikka on seuraavanlainen:

  1. Lue kansion kaikki tiedostot (60 kpl).
  2. Avaa kansiosta yksi tiedosto kerrallaan käsiteltäväksi.
  3. Selvitä tiedostonimen perusteella mitä maakuntaa tämä tiedosto edustaa sekä sisältääkö tiedosto ihmiset järjestettynä ansiotulojen (1), pääomatuloje (2) vai kokonaistulojen (3) mukaan.
  4. Tallenna tiedot MongoDB-tietokantaan sarakkeiden mukaisesti.
  5. Kirjoita uusi .csv-tiedosto mihin on otettu alkuperäistiedoihin verrattuna mukaan vain kiinnostavat sarakkeet ja johon on lisäksi lisätty uusia kiinnostavia sarakkeita kuten henkilöiden veroprosentti. Lisäksi tässä kohtaa poistettiin tiedoista kaikkien mahdollisesti alle 18-vuotiaiden nimet.
  6. Toista prosessi aineiston seuraavalla tiedostolle.

Ennen tätä prosessia muunsin alkuperäistiedostojen merkistön UTF-8-muotoon (alunperin ISO-8859-1). Tein tämän, että pystyisin olemaan varma, että skandinaaviset kirjaimet eivät korruptoituisi prosessin aikana. UTF-8 on merkistönä tässä suhteessa parempi. Rakensin tähän erillisen Python-scriptin, joka teki muunnoksen puolestani.

Harjoittelin prosessin läpi edellisen vuoden aineistoilla läpi noin kymmenen kertaa ja sain ajettua prosessin läpi sähköpostista julkaistuksi jutuksi rauhallisessa tahdissa edeten alle kahdessa minuutissa. Eli käytännössä lukijoille hyvin merkityksettömässä ajassa.

Veropäivän aamuna tulin töihin hieman ennen seitsemää aamulla. Verotiedot julkaistiin siis kahdeksalta. Toimittajamme Mikko Naalisvaara oli Haapaniemenkadulla verotoimistossa hakemassa tilaamamme aineistoa ja hänen oli määrä lähettää se minulle sähköpostilla heti aineisto saatuaan. Konkreettisesti Mikko sai verotoimistolta muistitikun, jossa tiedot olivat, jonka hän kiinnitti omaan mukana olleeseen koneeseensa. (Kuva 3)

Verotoimistosta tilatut verotiedot sai nimetyissä muistitukuissa. Tämä Yle:n kappale löysi tiensä työpöydälleni myöhemmin iltapäivällä. (Kuva 3)

Tulin töihin ajoissa ensinnäkin, että kyseisenä päivän HSL:n junat olivat lakossa ja halusin olla erityisen varma, että pääsisin paikalle, mutta toisaalta myös, että pystyin rauhassa käymään vielä ajatustasolla läpi sen mitä olimme tekemässä.

Aamukahdeksan aikaan aloitin sähköpostini "päivitä"-painikkeen aktiivisen naputtamisen. Lopulta kello 8.09 aineisto tippui sähköpostiini. Latasin tiedostot koneelleni ennalta luotuun kansioon ja ajoin tarvittavat Python-scriptit tiedostoille. Tiesin, että hätiköinti ei tulisi säästämään kuin puoli minuuttia kun taas epäonnistuminen johtaisi helposti moninkertaiseen viivästymiseen kun koko prosessi pitäisi aloittaa alusta.

Eniten jännitystä näin koodarin toimesta aiheutti epäilys siitä, että verohallinto olisi voinut mennä muuttamaan tiedostojensa formaattia tai merkistöä jollain tavalla. Tämä luonnollisesti johtaisi joko siihen, että scriptit eivät toimisi ollenkaan tai ne toimisivat loogisesti väärin. Tätä mahdollisuutta silmällä pitäen olin kehittänyt varasuunnitelmana manuaalisesti täytettävän, mutta etukäteen rakennetun html-listan, jonka alueet heti tiedot saatuaan saattoivat itse täyttää. Tämä varasuunnitelma varmasti siis sen, että meillä olisi heti aamulla aiheesta netissä kuitenkin jotain.

Tämä varasuunnitelma oli kuitenkin lopulta etenkin alueiden näkökulmasta enemmän tilannetta sekoittava, koska koko aineiston kattava toteutukseni valmistui alueiden käsin tuottamaa listaa nopeammin. Alueet siis edelleen täyttivät etukäteen rakennettua html-listaa kun he olisivat voineet ottaa käyttöön automaattisesti tuotetun taulukon. Tämän osalta täytyy kommukaatiota ensi vuodeksi parantaa.

Scriptit siis toimivat ja ennaltaharjoiteltu prosessi mahdollisti sen, että meillä oli verkossa kello 8.14 artikkeli johon oli upotettu järjestettävä taulukko, josta oli mahdollista tarkastella eniten tienanneiden tietoja koko maan sekä alueiden osalta. Työ oli siis valmis viidessä minuutissa (josta käytimme ehkä 1–2 minuuttia tietojen tarkistamiseen) tuotettua sähköpostiin tulleista .csv-tiedostoista interaktiivisen uutisapplikaation, joka oli käytettävissä koko maan ja jokaisen yksittäisen

Toteutin scriptin pääosin maanantaina (veropäivä oli perjantai) ja käytin välissä olleet päivät lähinnä hienosäätöön. Käytännössä käytin toteutukseen siis yhden työpäivän, jonka kautta veropäivänä lukijat saivat palvelua 15 minuutissa. Tämä oli merkittävä parannus verrattuna viime vuoteen jolloin listoja on tehtaillut useampi ihminen ja viimeiset listat valmistuivat alueille käsittääkseni vasta puolen päivän aikoihin. Jo sellaisenaan varasuunnitelmana käyttämämme etukäteen rakennettu html-lista oli kehitysaskel edelliseen vuoteen, jolloin alueilla ei ollut mitään yhtenäistä tapaa toimia.

Scriptit ovat ladattavissa Yle Uutisten Github:sta. Annoin projektille nimeksi tax-data-organizer. Scripti toimii niin, että sille annetaan kansio, jossa ovat verottajalta saadut tiedostot. Scripti tallentaa sitten uuteen erilliseen kansioon muokatut versiot sille annetuista tiedostoista. Järjestettävä html-taulukko näistä muokatuista .csv-tiedoista on mahdollista tehdä esimerkiksi mukaillen niin ikään Yle Uutisten Githubista löytyvää Sorttable-projektia.

tiistai 8. lokakuuta 2013

Social network analysis of one's Facebook friends

This blog post is in English as you may have noticed. It is made for a training session held at Södertörn University for the Swedish media organizations.

Objective:
Create a network visualization of one's Facebook friends.

Requirements:
Gephi, Python, MongoDB, Google Chrome, Scraper, Google Account

0. Network visulialization examples 


First here are some of examples where network analysis has given insigth to the given story.
  1. Social network analysis of a shooter suspect's Facebook friends
  2. Probing the murky web of foreign ownership in Finnish firms
  3. Spotlight - Party of the True Finns and islamophobia
As you can see network visualizations can be used in various ways. They can be used both in the background and also as an interactive end user application.

 1. Get your Facebook friends


Your own Facebook friends data is available through Facebook API. To access your Friends list through Facebook API browse to address:
https://graph.facebook.com/me/friends?access_token={place_valid_access_token_here}

A valid access token can be acquired from Graph API Explorer. (Pic 1)

You can get your own Facebook friends data from Facebook API in JSON format fairly easily. (Pic 1)

But only your own Facebook friends data is available through the API. So if you desire to get the friends data of any other profile it is vital to know that it is not available through Facebook API. So if you want to make a social network analysis of your friend's Facebook friends you need to make a little detour.

The detour here is called screen scraping. Screen scraping is a method where we use the computer to read the data what we see on our screen. We do this while computers are much faster doing it and make less mistakes. At simplest screen scraping is done from one page but it can be done from any number of pages.

A tool called Scraper is a very good for doing this when we just need to scrape data from one to two pages. Scaper is Google Chrome addon. if you desire to know more see a tutorial by Jens Finnäs.

For scraping we will use Facebook's mobile interface while it has much simpler outlook. This is nice when doing screen scraping because we have to deal with less code.

The following steps allow you to screen scrape your own friends list (that was also available through the API).
  1. Go to your Friends list. It is located at https://m.facebook.com/{place_your_username_here}?v=friends
  2. First scroll down as many times you need to get all of your friends to show on the screen. To achieve this it is quite handy to used the pagedown functionality. You may experience problems if you have more than 500 friends or so.
  3. Next open the context menu while clicking one of your friend and from the context menu select scrape similar. You need to have the Scraper plugin installed and enabled. (Pic 2)
  4. After clicking scrape similar you'll get a new window. On the left side you can define what you are scraping for and on the right side you see what you get. (Pic 3)
  5. To define what you are looking for you need to use either XPath or jQuery. The default selection is XPath and that suits us. Enter the following code without the quotes into the available field "//div[@class='_4mn c']/a" and click Scrape. What the code says is that we want to get the anchor elements (aka. links) which are located under a <div>-element which has class attribute value "_4mn c". In the section columns set XPath to "@href". That tells the code to fetch the @href parameter from those anchors.
  6. Export the dataset to Google Docs and open the document in Google Docs. Remove unnecessary columns B to E.
  7. Download the data as a .csv-file to your local computer. File -> Download as -> Comma separated values (.csv, current sheet)

(Pic 2)

(Pic 3)

 2. Create the connections between friends


Now we have a .csv-file of all your friends. From this we could make a network visualization which would show that all your friends are connected to you.

What we want to do is to figure out how your friends are connected which each other. This can be accomplished via Facebook API's Friend property which allows us to ask wheater to Facebook profiles are friends with each other. This is nice while you can do this for any two Facebook profiles regardless of their privacy settings.

Key point here is that one can't ask Facebook API who are all the friends of a Facebook profile. But one can ask one-by-one wheather to profiles are friends. So if you have a list of Facebook profiles it is possible to make the network. And as you may have realized we have list while we have screen scraped it. This also means that sometimes you can't get the profile list you deside due user's privacy settings. For example users can hide their friends list from users that they have not friended.

But how to use the Friend property. One can access it via Graph API Explorer which we already used to acquire the proper access token.

With Graph API Explorer we could make the network by hand by entering all the queries to Graph API one-by-one. For example the query:

"SELECT uid1 FROM friend WHERE uid2 = 635279474 AND uid1 = 732028610"

tells us whether these to user id's are friends with each other. But for any larger network this would be a pain in the ass while you would have to make hundreds and hundreds of queries. (Pic 4)

(Pic 4)
Fortunately this is not the case. I have written a Python script that does the work for us. But unfortunately I'm quite sure that the script won't work out of the box for most users.

Download the script.

Place the script file in some good place where you can access it. Copy also the .csv-file that same folder. Next open terminal, browse to that folder and run:

python facebook_network.py {filename} ({use_existing = True|False})

for example:

python facebook_network.py teemo_tebest.csv False

When you are asked for an access token please refer to Graph Api Explorer to get one. You may phase several problems while running the script. First check that you have Python installed. Secondly you need a running MongoDB. Thirdly you need to have atleast two Python libraries installed called pyfacegraph and pymongo. (Pic 5)

The script need to be run in terminal environment. (Pic 5)
The script runs for a while. First it fetches all the metadata available from the profiles. After that the script forms the connecions between the users. After all is done the script outputs the data into a network file format.

3. Visualize the data with Gephi


Now that we have the data in a network format (.gexf) we can visualize it with available tools. One great and free tool is called Gephi.

If you failed to form your own dataset you can download and example dataset.

First open Gephi and use the basic file opening functionality to open the .gexf-file created in the previous step. You'll see a pop up window that gives you an overview of the data. After Gephi has loaded the data you'll see a junk of data. (Pic 6)

The data is layouted randomly when the file is loaded for the first time. (Pic 6)

Without adjusting any settings we don't get much out of the data. We can zoom into it and point single nodes to see the edges leaving from it but that is just it.

What we need to do is to define the layout algorithm, the node size and the color values for the nodes to get more out of the data.

Just to make clear:
Node: balloon
Edge: connection between nodes.

For social networks a layout algorithm called ForceAtlas2 is a proper choise so we will use that. Check that your settings are as in the picture. (Pic 7)

ForceAtlas is great for social networks. Other available layout algorithms include for example Geo Layout. (Pic 7)
After you hit run you'll see the magic happening in just a few seconds. The nodes are settled in the network based on the edges between them. Meaning that the nodes which have more connections between each other are grouped closer to each other and the nodes that have less connections to each other are torn apart.

Next you want to define the colors and the node sizes. But before then you need to count some figures. Mainly you want to run Modularity and Eigenvector centrality. Modularity will divide the nodes into groups and eigenvector centrality will allow us to define the node size. (Pic 8)

Statistics can be calculated from the right side. (Pic 8)
Now we can use these figures to define the colors. (Pic 9)

You can change the colors if you want. (Pic 9)
And the node sizes. (Fig 10)

You may define the min and the max node size. (Pic 10)
After doing so your network could look example like this. (Pic 11)

The separate groups are in different colors defined by Modularity and the node sizes tell how central the node is in the network. (Pic 11) 
You can zoom into the groups and point out single nodes to see the edges. To enable the labels click the small arrow icons below the network. (Pic 12)

You may enable the node labels. (Pic 12)
You are all done. You may adjust your colors and nodes sizes. You can test for example to use the gender information to define the colors. You can also try other layout algorithms to see what is available.

See also Olli Parviainen's great slides about visualizing your Twitter network with Gephi. Slides 8 to 17 show you the main visualizing steps inside Gephi that we've also used here.

4. Summary


Using network visualizations to show the connections between your Facebook friends or social media connections in general is a great way to make insight.

There are also several Javascript tools available that enable you to publish your network online as an interactive visualization:
Network visualizations are not that familiar way of visualization for a common user. Not like bar charts or maps. People tend not to understand what they are about and call them these odd spyderweb visualizations.  From this perspective I am not that eager to publish them online. But for professionals like journalists and programmers network visualizations can give real good insigth into the data. Knowing how to do network visualization gives you most often an unique perspective that no one else have thought of.

perjantai 24. toukokuuta 2013

Karttatoteutuksista ja koordinaatistoista

Olemme PlusDeskin ensimmäisten neljän kuukauden aikana toteuttaneet muiden muassa suuret määrät erilaisia karttavisualisointeja. (Kuva 1)

Olemme tehneet niin yksittäisiin pisteisiin perustuvia Google Maps -pohjaisia karttoja kuten:
kuin myös erilaisia aluisiin perustuvia Google Maps -karttoja kuten:

Kartat ovat oiva visualisointikeino kun halutaan kuvata datan spatiaalista merkitystä kuten tässä esimerkissä, jossa ilmiön voidaan keskittynyt rannikolle. (Kuva 1)
Toisinaan olemme myös piirtäneet kartalle yhtäaikaisesti yksittäisiä pisteitä ja alueita kuten Bostonin tapahtumat -jutun yhteydessä, jossa maratonin reitti piirrettiin yhtenäisenä viivana kun taas räjähdykset ja muut tapahtumapaikat yksittäisinä pisteinä.

Google Maps:n lisäksi olemme hyödyntäneet kartoissa OpenStreetMap-palvelua kuten esimerkiksi jutussa, jossa kerroimme mistä löytyy keskimäärin paras sää hiihtolomakaudella.

Mainittujen palveluiden lisäksi olemme toteuttaneet karttoja räätälöidyillä SVG-pohjaisilla ratkaisuilla kuten näissä esimerkeissä:
Räätälöidyissä toteutuksissa olemme hyödyntäneet Raphaël-nimistä JavaScript-kirjastoa, joka sopii hyvin tarpeisiimme, koska se toimii myös vanhemmilla Internet Explorer -selaimilla. Tavoitteemme kun on, että Yle Uutiset ja sen sisällöt toimivat mahdollisimman laajasti kaikilla laitteilla.

Interaktiivisten ja klikattavien karttojen lisäksi olemme julkaisset staattisia kuviin perustuvia karttoja kuten tässä euroviisupisteiden jakautumista maiden välillä kuvaavassa jutussa.

Se mitä työkalua ja ratkaisua kulloinkin hyödynnämme ohjaa ensisijaisesti se minkälaista dataa olemme visualisoimassa. Joskus toiset työkalut soveltuvat paremmin toisille datoille. Esimerkiksi käsiteltäessä suuria datamassoja on Google Maps usein toimiva työkalu.

Välillä eteen tulee tilanteita, jolloin meillä on olemassa tarvittava karttapohja (kuten sairaanhoitopiirien rajat) valmiina vain SVG-toteutusta varten, joka rajaa meitä käyttämästä Google Maps:n kaltaisia palveluita, koska ne perustuvat SVG:n käyttämien pikseleiden sijaan koordinaatistotietoon.



Julkaisimme viime maaliskuussa jutun koskien lakkautettuja kouluja Suomessa vuosina 2006–2012. Jutun taustalla on Tilastokeskukselta tilaamamme kouluaineisto, johon olimme pyytäneet mukaan koulujen paikkatiedot. Ajatuksenamme oli visualisoida lakkautettavat koulut kartalle käyttäen OpenStreetMap-palvelua. (Kuva 2)

Toteutuksesta oli mahdollista tarkastella lakkautettuja kouluja Suomen kartalta valitulla aikajaksolla ja oppilasmäärällä. (Kuva 2)
Saimme datan Excel-tiedostona, johon oli listattu kaikki lakkautetut koulut vuosittain yhdessä koordinaattitietojen kanssa. Ongelma oli, että koordinaattitiedot olivat kartastokoordinaattijärjestelmässä (KKJ), joka on erityisesti Suomeen kehitetty koordinaattijärjestelmä, jota edellä mainitut Google Maps ja OpenStreetmap-palvelut eivät tue.

KKJ jakaa Suomen neljään projektiokaistaan ja projektiokaistoilla on oma koordinaatistonsa. Projektiokaistojen tehtävä on käsittääkseni poistaa maan kaarevuudesta aiheutuvaa vääristymää. (Kuva 3)

KKJ:ssa Suomi on jaettu neljään projektiokaistaan. (Kuva 3)
KKJ-järjestelmän eri projektiokaistojen käyttäminen on siinä määrin monimutkaista etenkin rajakohdissa, että siitä on kehitetty yksinkertaistettu versio nimeltään yhtenäiskoordinaatistojärjestelmä (YKJ).

YKJ tunnetaan myös nimellä KKJ3, koska se vastaa kartastokoordinaattijärjestelmän projektiokaistaa kolme jatkettuna itään ja länteen niin, että se kattaa koko Suomen. YKJ-koordinaateille on ominaista, että kolmoskaistalle sijoittuviin koordinaatteihin ei lisätä alkuun projektiokaistan merkitsevää numeroa kolme.

YKJ-koordinaatit ovat siis muotoa:
  • p=6700000, i=3500000
tai lyhyemmin muotoa:
  • 6700000, 3500000.
Itäinen koordinaatti lasketaan metreinä keskimeridiaanille ja pohjoinen koordinaatti kertoo matkan päiväntasaajalta pohjoiseen.

Kuten sanottua ongelma KKJ- ja YKJ-koordinaatistoissa on, etteivät kartastopalvelut tue niitä. Esimerkiksi Google Maps tukee GPS:lle kehitettyä WGS84-koordinaatistojärjestelmää, ja tämän vuoksi meidän täytyi muuntaa koordinaattien järjestelmä.

Googlen avulla löysin Luonnontieteellisen keskusmuseon ylläpitämän palvelun nimeltä Luomus, joka mahdollistaa koordinaattijärjestelmien välisen muunnoksen. Palveluun on mahdollista syöttää muunnettavat YKJ:n mukaiset itä- ja pohjoiskoordinaatit, jonka jälkeen palvelu palauttaa muunnoksen tuloksen.

Esimerkiksi kysely http://www.luomus.fi/projects/coordinateservice/?orig_system=ykj&north=6982696&east=3541961 palauttaa vastauksen:

<CoordinateData>
<result>
<new_system>etrs89</new_system>
<north>62.94538003831</north>
<east>27.823170453332</east>
</result>
<orig_system>ykj</orig_system>
<version>2.0</version>
<disclaimer>
This is coordinate translation system provided by Finnish Museum of Natural History (FMNH). Copyright FMNH and National Land Survey of Finland. FMNH makes best effort to keep the data and conversions as accurate as possible.
</disclaimer>
</CoordinateData>


Vastauksen kohdista <north>62.94538003831</north> ja <east>27.823170453332</east> ovat luettavissa annettuja YKJ-koordinaatteja vastaavat WGS84-koordinaatiston mukaiset koordinaatit.

Muunnettavia koordinaattitietoja minulla oli 26 185 Excel-rivin edestä, joten rakensin Python-ohjelmointikielellä yksinkertaisen koodinpätkän, joka automatisoi tämän muunnostyön. Toteutus on saatavilla kirjoituksen lopusta. (Kuva 4)

Tekemäni koodi muunsi YKJ-koordinaatistossa olleet tasokoordinaatit WGS84-koordinaatissa oleviksi maantieellisiksi koordinaateiksi. (Kuva 4)
Mikä on kuitenkin myös luettavissa vastauksesta on kohta <new_system>etrs89</new_system>. Eli kohdejärjestelmä ei tässä siis olekaan WGS84 vaan ETRS89. Tästä seuraavaksi.



Käsiteltäessä karttatietoa koordinaatistojärjestelmät eivät ole ainoa huomioitava asia vaan koordinaatit voidaan eri järjestelmien lisäksi esittää myös eri formaateissa yhden järjestelmän sisällä. Yleisiä esitysformaatteja ovat ainakin tasokoordinaatit ja maantieteelliset koordinaatit. YKJ- ja KKJ-koordinaatit ilmoitetaan kokemuksieni mukaan yleisimmin tasokoordinaatteina kun taas Google Maps tukee maantieteellisiä koordinaatteja.

Ero tasokoordinaattien ja maantieteellisten koordinaattien välillä on esitystapa. Tasokoordinaatit kertovat matkan pohjoiseen ja itään kun maantieteelliset koordinaatit kertovat leveys- ja pituuskoordinaatit. Tasokoordinaatit ovat siis kokonaislukuja ja maantieteelliset koordinaatit asteita.

Koordinaatistoformaatteihin ja niiden muunnoksiin pääsin tutustumaan kun teimme äskettäin jutun painorajoitettavista silloista Suomessa. Saimme Liikennevirastolta tiedot 391:stä tulevaisuudessa uuden painorajoituksen saavasta sillasta ja mukana olivat siltojen paikkatiedot. Paikkatiedot olivat saamassamme Excel-tiedostossa ETRS89-koordinaatiston suomalaisen EUREF-FIN-nimisen projektion mukaisia. ETRS89-koordinaatit vastaavat käytännössä WGS84-koordinaatteja (ero on alle metrin).

Koordinaattitiedot olivat kuitenkin tasokoordinaatistoformaatissa, jotka eivät tietääkseni ole tuettuja Google Maps -työkalussa. Tässä tapauksessa minun ei kuitenkaan tarvinnut muuntaa koordinaattijärjestelmää toiseen vaan kuten edellä vaan vaihtaa vain koordinaattien esitystapa. Luomus.fi-palvelusta ei harmikseni löytynyt työkaluja formaattimuunnoksiin, mutta löysin ratkaisun Karttapaikka.fi-palvelusta Juho Salmisen avustuksella.

Karttapaikka.fi-palvelusta löytyvän Karttapaikka-nimisen työkalun avulla on mahdollista tehdä monipuolisesti erilaisia koordinaatteihin liittyviä muunnoksia. Palveluun voi syöttää koordinaatteja niin taso- kuin maantieteellisessä formaatissa, jonka jälkeen palvelu palauttaa tiedot kattavasti. Tarjolla ovat kaikki Suomessa yleisesti käytetyt koordinaatistojärjestelmät ja -formaatit.

Tein myös tässä tapauksessa samanlaisen yksinkertaisen koodinpätkän, jolla automatisoin formaattimuunnoksen tekemisen kaikille hallussani olleille koordinaateille. Tämäkin toteutus on saatavilla kirjoituksen lopusta. (Kuva 5)

Tekemäni koodi muunsi kaikki painorajoitettujen siltojen tasokoordinaatit maantieteellisiksi koordinaateiksi. (Kuva 5)
Itseasiassa ensimmäisessäkin esimerkissä lähdeaineistomme oli aluksi tasokoordinaatistoformaatissa. Eli Luomus.fi-palvelu teki itseasiassa muunnoksen niin koordinaattijärjestelmän kuin myös koordinaattiformaatin osalta.



Yhteenvetona voitaneen todeta, että paikkatieto ja sen esittäminen on melko moninaista, mutta kokemuksieni mukaan Suomessa paikkatietodata julkaistaan useimmin tasokoordinaatteina joko YKJ- tai KKJ-järjestelmässä, jotka siis ovat hankalia käytössä olevien karttapalveluiden kannalta.

2000-luvulla Suomessa on pyritty siirtymään ETRS89-järjestelmään, mutta esimerkiksi Tilastokeskuksen ruututietokanta on siirtynyt siihen vasta vuonna 2011. Suunta on kuitenkin kohti ETRS89:ää, joka vastaa siis melkein WGS84-järjestelmää. Eli ETRS89-koordinaatteja on mahdollista käyttää esimerkiksi Google Maps:ssä kunhan ne vain ovat maantieteellisessä formaatissa.



Tämän kirjoituksen myötä julkistan Yle Plus -GitHub-repositorion. Repositorion tarkoitus on toimia julkaisukanavana Yle Uutisten ja ainakin PlusDeskin tekemille avoimille lähdekoodille. (Kuva 6)

Tulemme käyttämään Yle Plus-repositoriota toteuttamiemme koodien julkaisemiseen avoimena lähdekoodina. (Kuva 6)
Ensimmäisinä työkaluina julkaisemme tässä kirjoituksessa mainittujen koordinaattimuunnosten lähdekoodit, jotka siis mahdollistavat YKJ-tasokoordinaattien muuntamisen WGS84-järjestelmän maantieteellisiksi koordinaateiksi sekä ETRS89-tasokoordinaattien muuntamisen maantieteellisiksi koordinaateiksi.

Lähdekoodit julkaistaan CC-BY-SA-lisenssillä, joka mahdollistaa koodien hyödyntämisen niin yksityisessä kuin kaupallisessakin käytössä. Ainoat rajoitteet ovat, että käytettäessä alkuperäinen lähde (Yle Uutiset) on mainittava ja että kaikki mahdolliset jatkototeutukset on jaettava samalla lisenssillä.

tiistai 26. kesäkuuta 2012

Twiittien kertomaa: #Juhannusjuna

Päivitys 28.6. klo 20:30: Twiittien aikaleimatietoja oli tulkittu väärin, koska ne olivat GMT+0 aikaa eivätkä Suomen aikaa. Tämä on korjattu tekstiin, mutta jaettu data on edelleen GMT+0.

Sain hyvää palautetta edellisestä viestistäni #SuomiTop100-ilmiö ja osittain sen myötä päädyin tällä kertaa tutkimaan #juhannusjuna Twitter-keskustelua.

Juhannusjuna oli Yleisradion tuotanto, joka seurasi juhannusyönä 2012 Helsinki-Rovaniemi-junan matkaa suorana televisiossa, radiossa ja netissä. Lähestys oli 13 tunnin mittainen ja se televisioitiin Suomen lisäksi myös Saksaan, Ranskaan sekä Norjaan. Tuotanto noudatti ns. Slow TV -formaattia, joka määritelmänsä mukaan seuraa maratoonin omaisesti arkipäiväistä tapahtumaa suorana alusta loppuun.

Tarkoituksenani oli ilmentää Juhannusjunan tekijöille heidän pyynnöstään #juhannusjuna-hashtagin alla käytyä keskustelua.

Teknisen toteutuksen kuvaus


Twitter-rajapinta eli API on hyvin monipuolinen ja helppokäyttöinen. Rajapinnan kautta on mahdollista hakea kaikki Twitter:ssä muutenkin julkisesti saatavilla oleva data (tietyin rajoituksin). Esimerkiksi search-metodi mahdollistaa viimeisimpien twiittien hakemisen hakuehtoihin perustuen. Rajapinta on laajasti dokumentoitu ja tarjolla on myös runsaasti esimerkkejä siitä miten rajapintaa kutsutaan.

Normaalisti rajapintoja käytetään ohjelmallisesti, mutta koska Twitter:n osalla kyse on REST-rajapinnasta on kyselyitä mahdollista tehdä myös suoraan selaimen avulla. Esimerkiksi yksittäinen #juhannusjuna-hashtagin sisältävä twiitti ja sen tiedot on mahdollista hakea syöttämällä selaimen osoiteriville kysely:
http://search.twitter.com/search.json?q=%23juhannusjuna&rpp=1,

missä q=%23juhannusjuna rajaa hakua niin, että haetaan vain twiitit joissa on sana #juhannusjuna ja rpp=1 rajaa tulosten määrän yhteen sivua kohden (results per page). Kuten huomataan erotellaan useat parametrit osoiterivillä &-merkillä. Huomioitavaa on myös, että risuaita (#) on merkittävä osoiterivillä sen koodatussa muodossa "%23", koska sillä on erityismerkityksensä URL-osoitteissa.

Rajapinta palauttaa JSON-muotoista dataa, jota on helppo tulkita koneellisesti. Ihmiselle Twitter:n palauttama vaste on hankalalukuista etenkin, koska tietoja ei ole rivitetty. Vaste on kuitenkin mahdollista kopioida esimerkiksi JSONLint-nimiseen palveluun, joka muuntaa datan helpommin luettavaan muotoon (joskin edelleen on kyse koodista).

Kuten sanottua on rajapintoja usein mielekästä käsitellä ohjelmallisesti. Itse kirjoitin yksinkertaisen, n. 10 riviä pitkän, Python-koodipätkän, jonka avulla hain kaikki #juhannusjuna sekä #midsummertrain -aiheiset twiitit Twitter-rajapinnasta (n. 1600 kpl) ja tallensin ne MondoDB -tietokantaan. Lokiikaltaan kyse on siis hyvin suoraviivaisesta ohjelmasta, joka hakee twiittejä rajapinnasta yksi kerrallaan ja tallentaa uudet vielä hakemattomat twiitit lokaaliin tietokantaan, josta niiden käsittely ei enää riipu Twitter:stä.

Tulokset


#SuomiTop100-hashtagin tapauksessa kiinnostavia näkökulmia olivat se ketkä asiasta keskustelivat ja miten he linkittyivät toisiinsa sekä toisaalta millaisia lopulliselle listalle päässeet olivat Twitter-käyttäjinä. #Juhannusjuna:n tapauksessa kiinnostavia kysymyksiä taas olivat etenkin se milloin twiittejä lähetettiin, koska kyse oli suorasta lähetyksestä sekä se ketkä olivat aktiivisimpia keskustelijoita. Slow TV:n luonteesta johtuen (pitkä yön yli kestävä lähetys) erityisen kiinnostavaa ajallisesti oli se miten twiitit jakautuivat tunneittain yön aikana.

Onnistuin tallentamaan twiittejä ajalta 18.6. - 26.6. yhteensä hieman vajaa 1600 kpl:tta. Tässä luvussa olivat mukana #juhannusjuna ja #midsummertrain hashtagin sisältäneet twiitit. Yhteensä hashtagejä käytti 318 käyttäjää ja heitä oli kaikilta kielialueilta, joilla lähetys näkyi (FR: 2, EN: 150, NO: 5, DE: 5, SV: 18, DE: 3, IN: 2, FI: 1388). Huomioitavaa on, että kielialue määritellään siitä mitä kieltä käyttäjä käyttää palvelussa eli suuri osa EN-kielikoodin omaavista twiittaajista on varmasti suomalaisia.

Aktiivisin twiittausjakso koettiin juhannusjunan lähdön aikaan kun 19:00-20:00 välisenä aikana Twitter:iin lähetettiin yhteensä 401 juhannusjuna-aiheista twiittiä. Iltaa ja yötä myöden twiittien määrät tippuivat ensin 268:een tunnissa ja sitten 171:een tunnissa. Kuitenkin vielä aamulla klo 01:00-02:00 välillä hashtagit keräsivät 86 twiittiä, joka on keskimäärin enemmän kuin yksi twiitti minuutissa. Koko data on saatavilla tästä .csv-muodossa (avattavissa Excelissä).

Aktiivisimmaksi twiittaajaksi ylsi @MikaPirhonen 173:lla juhannusjuna-aiheisella twiitillään. Kakkoseksi kipusi @digikim ennen Juhannusjunan virallista Twitter-tiliä @Juhannusjuna. Lähetettyjen twiittien keskiarvo käyttäjää kohden oli 4.9 (1573/318). Twiittimäärien mediaani oli 1 eli suurin osa käyttäjistä lähetti vain yhden #juhannusjuna tai #midsummertrain -aiheisen twiitin. Koko data on saatavilla tästä .csv-muodossa.

Lisäys 27.6.2012 klo 11:30:

Twitterissä heräsi kysymys siitä ovatko mukana myös retwiitit. Tutkittuani dataa selvitin, että kyllä ovat ja eniten retwiittejä eli 12 sai @Iirorantala:n twiitti:
@IiroRantala: Spread this link of #juhannusjuna #midsummertrain. The show is available world wide live from a night train in Finland. Starts in one hour.
Kakkoseksi twiittien suosituimmuudessa retwiittien osalta pääsi @TuomasEnbuske, jonka twiittiä:
@TuomasEnbuske: Don't tell mamassa on kuulemma tänä iltana todellinen #juhannusjuna.
jaettiin edelleen 6 kertaa. Suurin osa twiiteistä aiheilla #juhannusjuna ja #midsummertrain ei saanut yhtään retwiittiä, koska vain 82 twiittiä 1573:sta jaettiin eteenpäin kerran tai useammin ja suurin osa näistäkin twiitattiin uudelleen vain kertaalleen.

Ps. jos sinä olet kiinnostunut oman hashtagisi takana käytävästä keskustelusta, mutta tämän toteuttaminen tuntuu ylitsepääsemättömältä ota yhteyttä esimerkiksi Twitterissä niin katsotaan mitä voimme tehdä. Kannattaa kuitenkin ottaa huomioon, että Twitteristä on mahdollista hakea vain n. 1500 viimeisintä twiittiä ja maksimissaaan 6-8 päivän takaa.

Tässä viestissä jaetut datasetit ovat lisensoitu Nimeä-Tarttuva (CC BY-SA) ja data on tarkoituksenmukaisesti jätetty visualisoimatta, jotta datan käyttöönottaminen olisi siten mielekkäämpää kun pääsee tekemään tyhjältä pöydältä.

lauantai 31. maaliskuuta 2012

Facebook-verkostojen visualisointi

SPOILER: tämä postaus käsittelee teknistä sanastoa.

Innostuin tällä viikolla tutkimaan miten Facebook-verkostoja olisi mahdollista kerätä ja visualisoida vastaavalla tavalla kuten olen aikaisemmin visualisoinut Twitter-verkostoja sekä keitä @TuomasEnbuske seuraa. Enemmän tai vähemmän tunnetusti Facebook on kitsaampi jakamaan tietojaan Graph-rajapintansa kautta kuin mitä Twitter oman rajapintansa kautta, mutta rohkaistuin kuitenkin yrittämään tehtyäni ensin Olli Parviaisen kanssa kansanedustajien kaveruusverkoston sekä nähtyäni miten Jens Finnäs oli onnistunut tekemään vastaavanlaisen toteutuksen Ruotsin parlamentista.

Graph-rajapinta


Facebook:lla on siis olemassa Graph-niminen rajapinta, joka mahdollistaa Facebook-datan käsittelyn koneellisesti. Lähinnä rajapinta on varmasti tarkoitettu erilaisille Facebook-applikaatioille, mutta sitä on mahdollista käyttää myös muuten. Yksinkertainen Graph-rajapintakysely, joka palauttaa halutun käyttäjän (tässä tapauksessa allekirjoittaneen) tiedot (kuten ID, sukupuoli, nimi, jne.) voidaan suorittaa syöttämällä seuraava osoite selaimeen:

graph.facebook.com/teelmo.

Rajapintaa voidaan kutsua yhtälailla käyttäjän käyttäjänimellä kuin Facebook:n käyttäjä-ID-arvolla. Toisin sanoen kysely:

graph.facebook.com/635279474,

tuottaa saman vastauksen. Tiedot ovat haettavissa Graph-rajapinnan kautta kenestä tahansa Facebook-profiilista huolimatta siitä miten suljettu tai avoin profiili on. Rajapinnalta on mahdollista kysyä myös monia muita asioita, mutta esimerkiksi satunnaisen käyttäjän Facebook-kavereiden hakeminen ei ole mahdollista edes vaikka olisit kyseisen käyttäjän kaveri. Muiden kuin itsensä kavereiden hakeminen onnistuu rajapinnan kautta jos käyttäjä on sallinut tietojensa jakamisen applikaatiolle, jonka ylläpitäjä olet. Omat Facebook-kaverit rajapinnan kautta saa haettua.

Python + MongoDB = voitto


Kuten edellä näytettiin on Graph-rajapintaa mahdollista selata käsipelillä selaimen avulla, mutta käsiteltäessä suurempia datajoukkoja tulee tiedon keräämisestä tällä tavoin raskasta. Näin ollen erinäiset ohjelmointikielet ja niillä rakennetut scriptit eli lyhyet kertaalleen ajettavat ohjelmakoodit tulevat apuun. Käytetyllä ohjelmointikielellä ei ole suurta merkitystä vaan valinta kannattaa tehdä sen pohjalta, joka on itselle tutuin ja luontevin. Hyviä vaihtoehtoja kuitenkin ovat ainakin Python, Ruby ja Facebook:n tapauksessa etenkin PHP, koska suurin osa Facebook:n koodiesimerkeistä on kirjoitettu sillä. Itse valitsin kuitenkin Pythonin, koska halusin jatkojalostaa omaa Python osaamistani.

Pythonille löytyy useampia Facebook Graph API -kirjastoja, mutta ainakin pyFaceGraph-nimisen kirjaston avulla Graph-rajapinnan käyttö on todella yksinkertaista. Tässä yksinkertainen koodin pätkä, joka hakee käyttäjän tiedot kirjaston avulla jos käyttäjän tietoja ei ole jo aikaisemmin haettu ja tallennettu tietokantaan.

>>> from facegraph import Graph
>>> g = Graph()
>>> ...
>>> username = 'teelmo'
>>> # Check that username is not already in MongoDB
>>> if not users.find({"data.username": fusername}).count():
>>>    user = g[username]()
>>>    print user.id
'635279474'

Tietojen tallentamisessa hyödynsin MongoDB:tä, joka on NoSQL-tietokanta. MongoDB soveltuu käytettäväksi tämänkaltaisissa scripteissä, jotka hakevat tietoa netistä, koska tiedon eheydestä eikä rakenteesta tarvitse välittää samalla tavalla kuin käytettäessä esimerkiksi MySQL:ää tai muuta SQL-pohjaista tietokannanhallintajärjestelmää. MongoDB hyödyntää tiedon tallentamisessa JSON-formaattia, joka on myös formaatti, jossa Graph-rajapinta palauttaa hakutulokset.

Kuvassa 1 nähdään miten scripti hakee annettujen käyttäjien tietoja Graph-rajapinnalta. Tässä tapauksessa suurin osa haettavista käyttäjistä on jo aikaisemmin noudettu lokaaliin tietokantaan, mutta yhden käyttäjän osalta tiedot haetaan Graph-rajapinnasta.

Pythonilla ja MongoDB:llä toteutettu keräin, joka hakee annettujen käyttäjien tiedot Facebookin Graph-rajapinnasta. (Kuva 1)
Facebook:n Graph API ei rajoita tietojen hakemista samalla tavalla kuin Twitter (150 pyyntöä tunnissa), mutta satunnaisesti joitain virheitä esiintyy. Ongelmatilanteissa auttaa kun tiedot tallennetaan lokaalisti MongoDB:hen eikä tietojen hakemista tarvitse siten aloittaa ongelmatilanteissa alusta.

Käyttäjien tiedot kyllä, mutta miten yhteydet?


Graph-rajapinnan tarjoamat toiminnallisuudet on listattu täällä. Rajapinnan avulla saadaan siis kysyttyä suoraan mm. käyttäjien, tapahtumien ja sivujen tietoja. Käyttäjien välisiä yhteyksiä, tapahtumaan osallistujia tai sivujen tykkääjiä ei rajapinnan perustoiminnallisuuksilla kuitenkaan saa haettua.

Apuun tulee kuitenkin Graph-rajapintaa laajentava Facebook Query Language (FQL). FQL mahdollistaa Graph-rajapinnan kautta saatavien tietojen hakemisen SQL:n kaltaisella kyselykielellä. Esimerkiksi Facebook:n käyttäjä-ID:tä vastaava käyttäjänimi on mahdollista hakea seuraavalla kyselyllä:

SELECT username FROM user WHERE uid = 635279474

Käyttäjien välisten kaveruussuhteiden kyseleminen on mahdollista friend-taulusta. On huomioitavaa, että kaveruussuhteita on mahdollista kysyä myös sellaisten käyttäjien välillä, jotka muuten ovat piilottaneet profiilinsa. FQL:ää on mahdollista kokeilla erityisellä Graph API Explorer -työkalulla. FQL:n käyttäminen edellyttää OAuth-tunnistautumista ja ns. "Access token" täytyy välittää kyselyiden mukana.

Toteutin myös yhteyksien hakemisen Python-scriptillä. Apuna käytin Pythonille löytyvää requests-kirjastoa. Tässä yksinkertaisuudessaan ja lyhykäisyydessään FQL-kyselyn toteuttava koodinpätkä:

>>> import requests
>>> ...
>>> fql_query = "SELECT uid2 FROM friend WHERE uid1 = 635279474 and uid2 = %d" % uid2
>>> fql = "/fql?q=%s&access_token=%s" % (fql_query, access_token)
>>> try:
>>>    r = requests.get(domain + fql)
>>> except:
>>>    print "Error occured"


FQL-kysely palauttaa ne käyttäjä-ID-arvot, jotka täsmäävät annettuun hakuun. Eli hakiessani olenko kaveri käyttäjien A, B ja C kanssa palauttaa Facebook niiden käyttäjien ID:t, joiden kanssa haetuista olen kaveri.

Tulokset kirjoitin .gexf-muodossa ja visualisoinnin tein Gephi-nimisellä verkostojen visualisointiohjelmalla. Gephi:n käyttöön en mene tässä sen syvällisemmin, koska olen käsitellyt sitä jo aiemmin tässä blogissa ja tyydynkin vain viittaamaan Olli Parviaiseen luomiin ohjeistuksiin.

Summa summarum


Yhteenvetona voitaneen todeta, että Facebook:sta on mahdollista muodostaa kaveruusverkostoja vastaavasti kuin aikaisemmin olen näyttänyt Twitter:stä. Facebook:n kohdalla joutuu hieman enemmän tekemään käsityötä datan keräämisessä, mutta lopputulos on täysin vastaava. Erona Twitter-verkostoihin on, että Facebook:sta kerätyistä kaveruusverkostoista muodostuu paljon rypästeisempiä (Kuva 2). Eri ryhmät kuten lapsuuden ystävät, koulukaverit, työkaverit ja sukulaiset ovat hyvin vähän tekemisissä toistensa kanssa, mutta ovat linkittyneet keskenään hyvin paljon.

Facebook-kaveriverkostoni. Eri ryhmät erottuvat toisistaan erittäin selkeästi. Solmuja verkostossa on 206 ja yhteyksiä 1878. (Kuva 2)
Twitter:ssä taas ihmiset ovat enemmän verkostoituneet keskenään yli sosiaalisten tai fyysisten rajojen (Kuva 3). Twitter:n demografia poikkeaa toki hyvin paljon Facebook:sta, mutta mielestäni tämä kertoo, että Twitter:ssä seurataan kiinnostavia henkilöitä ja itse asiaa, joka johtaa samoista asioista kiinnostuneiden ihmisten tiheisiin verkostoihin. Toisaalta voidaan vetää myös se johtopäätös, että julistan Twitter:ssä vain yhden aihealueen ilosanomaa ja jos aihealueita olisi useampia nähtäisiin näistä eri aihealueista kiinnostuneet ihmiset omina ryppäinään.

Twitter-seuraajaverkostoni. Ihmiset ovat verkostoituneet keskenään hyvin tiheästi. Solmuja verkostossa 149 ja yhteyksiä 1209. (Kuva 3)
Voidaan kuitenkin tehdä sivistynyt arvaus, että Facebook:ssa seurataan ihmisiä, jotka ovat tuttuja fyysisestä maailmasta kun taas Twitter:ssä seurataan ihmisiä, jotka ovat kiinnostavia. Tässä toki kyseessä vain yhden ihmisen Facebook-kaverit ja Twitter-seuraajat, joten kovin pitkälle meneviä johtopäätöksiä ei voida tehdä, mutta olen havainnut samankaltaisen trendin esiintymisen myös muissa testitapauksissa.

Stay tuned!

"Ryömin juuri kaikkia @TuomasEnbuske:n seuraajia Keitä me kaikki ollaan Seuraavaksi @alexstubb Siinä taitaakin olla kaikki suom. Twitteristit" - https://twitter.com/#!/teelmo/status/185680267548168192

perjantai 17. helmikuuta 2012

Twitter-verkostoanalyysia Gephi:llä

Innostuin viime viikonloppuna kokeilemaan ensimmäistä kertaa omatoimisesti miten Twitter-dataa on mahdollista visualisoida. Olen aiemmin tutkinut ja työstänyt paljon erilaisia sosiaalisia verkostoja, mutta ennen en ole laittanut todella käsiä rasvaan Twitterin osalta. Osittain tähän on ollut syynä se, että olen mieltänyt Olli Parviaisen jo avanneen kaikki Twitterin salat tämän osalta eikä asiaan vihkiytyminen näin ollen ole tuntunut mielekkäältä. Pisteet tämän vaikutelman luomisesta Ollille :) Sisäinen devaajanluonteeni kuitenkin siis vei voiton ja lähdin kehittelemään jotain omaa.

Twitter tarjoaa monipuoliset mahdollisuudet datansa käsittelyyn rajapintansa kautta. API:lla (Application programming interface) on mahdollista hakea automaattisesti oikeastaan kaikki se tietosisältö, joka on käyttäjälle muutenkin nähtävissä. Tämä on poikkeuksellista esimerkiksi verrattuna Facebook:n Graph API:in, jonka kautta tietoa on mahdollista hakea rajoitetummin. Tästä syystä Facebook-datan hakeminen on omalta osaltani tapahtunut hyvin paljon käsityönä kuten "Kansanedustajat Facebookissa – kuka on kenenkin kaveri?" -tapauksen yhteydessä.

Päädyin rakentamaan Twitter-datan avulla verkoston käyttäjistä/Twitter-tileistä joita seuraan. Henkilökohtaisella Twitter-tilini seurannassa ~90:ntä tiliä, joten tästä seurasi suoraan verkoston solmujen lukumäärä. Yhteydet solmujen välille rakensin sen perusteella miten seuraamani henkilöt seuraavat keskenään toisiaan. Lopputuloksena syntyi sosiaalisen verkosto, joka havainnollistaa seuraamieni henkilöiden keskenäiset suhteet, suhteiden perusteella muodostuvat ryhmät ja näiden ryhmien asemoitumisen toisiinsa nähden.

Keräsin datan käyttäen Python-ohjelmointikieltä ja tein tulostuksen verkostodatalle soveltuvassa GraphML-muodossa, jota visualisointiin käyttämäni visualisointityökalu Gephi ymmärtää. Gephi:ssä käytin tulosten latomiseen ForceAtlas2-algoritmia, jonka olen todennut käytännön kokemusten kautta toimivan hyvin etenkin pienten sosiaalisten verkostojen visualisoimiseen. Värikoodauksen tein dataan käsin määrittelemällä jokaisen solmun sille olennaiseen kategoriaan. Solmun koko kuvastaa astesummaa eli solmuun suuntautuvien yhteyksien määrää. Toisin sanoen solmun koko kuvaa suoraan Twitter-tiliä seuraavien Twitter-tilien määrää verkostossa. Yhteydet solmujen välillä ovat suunnattuja. Annotoinnin ja jälkikäsittelyn tein Paintbrush-nimisellä ohjelmalla. (Kuva 1)

Sosiaalinen verkostoni Twitter:ssä visualisoituna Gephi:llä. (Kuva 1)
Visualisointi vahvistaa hyvin niiden eri ryhmien olemassaolon, jotka todellisuudessakin ovat olemassa verkostossani, mutta lisäksi visualisoinnista voidaan nähdä miten nämä ryhmät sijoittuvat suhteessa toisiinsa. Esimerkiksi TTYläiset erottuvat visualisoinnissa hyvin selkeästi muista ryhmistä erilleen. Tulkitsen tämän johtuvan toisaalta siitä, että seuraamani TTYläiset eivät ole kaikkein aktiivisimpia Twitter-käyttäjiä mutta toisaalta siitä, että heillä ei ole mitään syytä seurata Yleläisiä tai datajournalismi-ihmisiä. Myös Yleläiset erottuvat visualisoinnissa selkeästi omana joukkonaan, mutta he taas asettuvat selkeästi lähemmäksi datajournalismi-ryhmää kuin TTYläiset, jonka voidaan mieltää olevan ihan ammatillisestikin luontevaa. Nämä kolme ryhmää (1. TTY, 2. YLE ja 3. Datajournalismi) ovat verkostoni suurimmat ja selkeimmät, joten näen, että tällä erää on mielekästä tässä tulkita vain ne ja jättää muut pienemmät ryhmät tässä erää huomioimatta.

Kuvissa 2, 3 ja 4 nähdään eräiden muiden käyttäjien Twitter-verkostot. Verkostot on toteutettu samoilla periaatteilla kuin edellä kuvattu oma verkostoni sillä erotuksella, että solmujen väritys on tehty Gephi:n Modularity-luokittelun avulla. Lisäksi näissä verkostoissa esiintyvät Twitter-tilit on anonymisoitu, koska haluan esittää ne vain esimerkin omaisesti enkä pyrkiä sen tarkempaa tulkitsemiseen. Tarkempi tulkinta vaatisi tutustumista verkostoissa oleviin Twitter-tileihin, jotta voitaisiin ymmärtää miksi tietyt ryhmät ovat lähempänä toisiaan kuin toiset.

Kuvien 2, 3 ja 4 verkostoista nähdään miten verkoston ulostulo on hyvin erilainen eri käyttäjille. Toisilla käyttäjillä nähdään hyvin pitkälle keskenään verkostoituneet ryhmät kun taas toiset käyttäjät seuraavat toisistaan täysin riippumattomia Twitter-tileistä koostuvia ryhmittymiä.

Kuvassa 2 nähtävä verkosto on niin tiheä, että tarkkojen tulkintojen tekeminen yleisnäkymästä on vaikeaa. Verkostojen tulkinnassa onkin tärkeää, että analysoinnin yhteydessä dataa voidaan selata mm. "details on demand" -periaatteella.

Tiheiden verkostojen tulkinta on usein vaikeaa jos verkostoa ei ole mahdollista rajata tai zoomata. Gephi mahdollistaa datan rajaamisen erilaisten filtereiden avulla ja verkostoa on mahdollista zoomata. Yleiskuvan merkitys on kuitenkin tärkeä, koska sen avulla nähdään ne alueet, jotka voisivat olla kiinnostavia jatkotutkimuksen kannalta. (Kuva 2)
Kuvassa 3 nähtävän verkoston erityispiirre on kahden sisäisesti tiiviin ryhmän heikko vuorovaikutus keskenään. Jos kyseessä tässä olisi yrityksen kahden eri osaston välinen kommunikaatio olisin hyvin huolestunut yrityksen sisäisen viestinnän toimivuudesta.

Twitteriä on mahdollista käyttää hyvin monella eri tavalla. Tämän kyseisen henkilö seuraa selkeästi kahta erillistä toisistaan riippumatonta ryhmää ihmisiä, jotka voivat poiketa toisistaan esimerkiksi kiinnostuksen  tai spatiaalisesti. (Kuva 3)

Kuvan 4 verkostossa osa ryhmistä on eriytynyt omaan lokeroonsa kun taas toiset ryhmät ovat verkostoituneet keskenään.

Tämän käyttäjän verkostossa tietyt ryhmät erottuvat hyvin selkeästi omiin lokeroihinsa, mutta oikealta löytyvä klusteri on taas hyvin vahvasti verkostoitunut keskenään. (Kuva 4)
Kuten huomataan vaatii verkostoanalyysi aina rinnalleen tuntemusta visualisoitavana olevasta ilmiöstä. Pystyn tulkitsemaan omaa verkostoani melko luotettavasti, koska tunnen minkälainen ilmiö on kyseessä ja tiedän mitä verkostossa olevat solmut ovat. Tulkinnasta tulee kuitenkin heti hankalampaa kun verkostossa esiintyvät solmut ja yhteydet tai ylipäänsä verkostossa nähtävä ilmiö ei ole tuttu. Onkin väärin olettaa, että verkostoanalyysi olisi jonkinlainen Hopea Luoti, joka suoraan ratkaisisi monimutkaisia ongelmia ja vastaisi kysymyksiin. Verkostoanalyysi toimii ongelmien ratkaisun tukena ja toisaalta antaa suuntaa sille  mistä ratkaisua voisi lähteä etsimään.

Seuraavat askeleet Twitter-analyysin osalta ajattelin ottaa siihen suuntaan, että kaivan verkoston ihmisten seuraajista sen sijaan, että katson keitä ihmiset seuraavat. Uskoisin tämän näkökulman tuottavan mielenkiintoisia tuloksia kun analyysi kohdistetaan yhteiskunnallisesti tärkeisiin ihmisiin kuten poliitikkoihin.

Keep on Tweeting...