Dynamiska visualiseringar av citeringsnätverk – ett test och metodgenomgång

Jag har länge försökt hitta ett bra arbetsflöde för att skapa visualiseringar av dynamiska nätverk, dvs. nätverk som förändras över tid. Alla nätverk förändras ju, även om hastigheterna kan vara väldigt annorlunda. Ett nätverk av vägar, tågräls eller gasledningar kan se ganska oförändrade ut över flera år, även om de hela tiden måste underhållas. Ett nätverk av vänner, kolleger, politiker, lobbyister eller studiekamrater kan uppstå och försvinna inom ungefär samma tidsrymd.

Det är dock ett metodologiskt problem att de flesta sätt som vi visualiserar nätverken på består av statiska representationer. Vi skapar bilder och ”kartor” som hela tiden missar att nätverken aldrig egentligen är helt statiska. Jag och min kollega Gustaf Nelhans försökte råda bot på detta i vår artikel om lyckoforskningens framväxt genom att skapa statiska bilder för olika tidsintervaller. Med hjälp av lite hermeneutisk ifyllnade går det att ställa sig i ett processontologisk modus och få en uppfattning om själva flödet av citeringar.

Men som för de flesta metoder är det önskvärt att öka detaljrikedomen om man vill genomföra en antropologi av det moderna. Jag tänkte här lägga ut en lösning på hur man kan skapa dynamiska nätverksvisualiseringar av publikations(meta-)data hämtad från Web of Science. Exemplet utgörs av min pågående forskning om citizen science och mitt syfte är här att visualisera framväxten av ett citeringsnätverk för forskning som använder sig av medborgarforskning som metod.

Således följer en väldigt teknisk beskrivning. Med tanke på att det troligtvis bara finns en handfull av forskare som gör så här (på svenska) lägger jag ut resultated direkt för er som tänkte sluta läsa nu:

Vad kan man säga om detta nätverk? Kommentarer uppskattas som presenter på julafton!

\\

Nu över till själva metoden. Vad som följer är en ganska omständig process och det är verkligen inte den optimala metoden. Tanken är att jag framöver ska skriva några små skript som automatiserar denna process hela vägen, men jag måste först lära mig lite mera programmering. Men det är ändå bra att lägga ut hela processen så att man vet vad man ska göra.

Således. Ta fram datorkörkortet och se till att du har följande mjukvara:

  • Python (inkl. modulerna networkx och community),
  • find
  • git
  • Gephi
  • ffmpeg
  • (för bonus-features: imagemagick och perl)

Python, find och git borde följa med MacOS/Linux, medan Gephi och ffmpeg får hämtas separat. På Windows måste man (tror jag) installera separat, men var försiktig med de terminalkommandon som jag skriver ut, vissa saker skiljer sig åt. Dessa mjukvaror behövs för de tre stegen i arbetsflödet: 1) förprocessera data, 2) applicera nätverksalgoritmer, 3) rendera video utifrån statiska bilder.

1. Separera datamängder årsvis

När man har hämtat en datamängd från Web of Science består den av en enda textfil. För att Gephi ska kunna bygga dynamiska nätverk måste man antingen ha byggt en färdig .gexf-fil, eller så instruerar man Gephi att ta uppstyckade datamängder och ge dem en tidsstämpel. Den förra metoden är det som saknas rent skriptmässigt (min ambition är att skapa ett sådant skript i framtiden), den senare metoden är den som jag presenterar här (den är mera omständig, men funkar).

Först och främst behöver vi ladda hem en knippe skript som jag hittade på Github skrivna av Reid McIlroy-Young för John McLevey.


git clone https://github.com/mclevey/web_of_science_isi.git

Det är oklart varför dessa skrevs, men de är väldigt användbara, även om de måste modifieras för att breddas i användningsområde. Forskare skriver ju ofta specialiserade verktyg för en viss forskningsfråga. För att sedan anpassa dessa till den fråga man själv ställer måste man koda om dem. Men för detta exempel kommer jag använda dem ganska rakt av.

Ta datamängden från Web of Science och lägg txt-filen i katalogen ISIcollectors. Om du har klistrat ihop flera dataset är det viktigt att du tar bort alla gånger som strängen VR 1.0 förekommer, förutom på andra raden, precis i början av filen. Om denna rad kommer mitt i datamängden kommer skriptet att stoppa.

Kör sedan bara:


python ISISplitter.py

Om allt går vägen kommer du nu få massor med nya textfiler, en för varje år. År är ju den högsta ”upplösningen” vi kan få ur Web of Science, så allt är väl än så länge.

Nu skulle man kunna stanna här och sedan ta varje fil till Sci2 för för-processering och sedan vidare till Gephi. Men det optimala är ju att automatisera detta steg, alltså direkt skapa nätverksfiler från Web of Science-filerna. Det intressanta skriptet co-Citemaker.py gör precis detta. Men, det är inställt på att bara arbeta med en fil åt gången. Som tur är går det att köra det på massor av filer med det finurliga programmet find. Flytta först den ursprungliga filen ur katalogen och kör sedan:


find *.txt -exec python co-citeMaker.py {} \; -exec mv co-CiteNetwork.graphml {}.graphml \;

Vad jag gör övan är att 1) hitta alla .txt-filer 2) köra pythonskriptet på varje fil 3) döpa om oputpufilen till ursprungsfilnamnet + filändelsen graphml. Visst är datorer finurliga!

Nu har vi en .graphml-fil för varje tidsintervall (år). Nu vill man få in detta till Gephi. Den lösning som man borde jobba med på sikt är att direkt jobba mot Gephi’s java-bibliotek, men det får bli ett framtida projekt. Istället jobbade jag mot Gephis grafiska gränssnitt. Här har vi mitt skrivbord avfilmat:

Först generera jag bara ett dynamiskt nätverk som jag genast tar bort. Sedan öppnar jag första filen som jag vill ha in och väljer Time frame som jag sedan definierar årsvis. På så sätt får man fram en tidslinje som man kan laborerar med. Här är min gephi-fil

Nu kan det uppstå ett problem. Kanske är det så att vi vill ha en ”kumulativ” visualisering. Alltså, att noderna inte försvinner för varje år, utan att de ”varar” längre. Detta är i mindre utsträkning en teknisk fråga, utan i första hand en teoretisk: Hur långt ska vi sträcka ut ”relationismen” i vår analys? Finns bara det som hela tiden upprätthålls? I Så fall ska vi låta noderna förfalla när de inte längre är aktiva. Eller, tillåter vi en viss ”eko-effekt” på grund av pragmatisk-epistemiska överväganden? I detta exempel lutar jag åt det senare.

För att göra så att alla noder permanenteras vill vi att de ska vara till och med slutet av vår tidslinje (här 2015). Om man först exporterar från Gephi till .gexf-formatet kan man enkelt jobba mot rådatan.

Så här ser rådatan i gexf-filen ut:


<node id="CIRIACYWANTRUP SV 1975" label="CIRIACYWANTRUP SV 1975" start="1991.0" endopen="1992.0">

Vad som behöver göras är att byta ut alla endopen mot årtalet 2015. Det är bara att söka och ersätta med din favoriteditor, eller så kör man bara följande lilla rad perl (perl är redan inbyggt i vettiga operativsystem och det är smidigare att använda för stora filer):


perl -p -i -e 's/endopen="\d\d\d\d.\d"/endopen="2015.0"/g' filnamn.gexf

Gexf-filen kan nu importeras tillbaka till Gephi. Lite omständigt, jag vet. Men i framtiden kommer jag förhoppningsvis kunna knåpa ihop lite python-skript som fixar till .gexf-filerna som man vill ha dem redan från början.

Nu kommer man till sista steget. Jag hoppar över hur man skapar fina visualiseringar i Gephi, det är ett annat ämne. Men när man har fått det snyggt måste man exportera en bild för varje år. Detta gör man i preview-fönstret och det går bra att bara exportera png-filer till en katalog och numrera dem 1.png, 2.png, 3, 4 osv.

Sedan kan man bara ställa sig i katalogen och köra:


ffmpeg -r 1 -pattern_type glob -i '*.png' -c:v libx264 -pix_fmt yuv420p out.mp4

Så får man en liten filmfil.

För att göra en animerad gif kan man använda sig av smidiga imagemagic och snabbt baka ihop en fil med:


convert -delay 100 -loop 0 *.png animation.gif

För att göra den lite mindre:


convert animation.gif -coalesce temporary.gif; convert -size 1024x1024 temporary.gif -resize 300x300 animationsmall.gif

Så får man:

Det finns flera saker att förbättra här. Bland annat har noderna statiska storlekar (trots att deras ”degree” ändras över tid). Sedan har jag inte kört någon community detection på detta ännu. Men man kan tänka sig att det uppstår och försvinner små (algoritmdefinierade) communities av citeringar. Jag har heller inte kollat mera exakt hur co-citeringen går till i python-skriptet jag använde. Men det är något jag ska jobba med framöver.

Nu ska jag åter dyka in i mitt sommarprojekt. Kommentera gärna!

Kommentera

E-postadressen publiceras inte. Obligatoriska fält är märkta *

Time limit is exhausted. Please reload CAPTCHA.