Öppna data 1 – SuperSOM med Python och Pandas

I en ‘post factual’ demokrati tror vissa att öppen vetenskap kan ge människor förnuftet tillbaka. Om detta vet vi mycket lite, eftersom vi inte har så mycket fakta att gå på (vilket kan betraktas som lite ironiskt).

Men, något kan man ändå veta. För att öppen vetenskap ska bli användbar av både forskare och allmänhet (framförallt då öppna data) så måste den göras begripbar. När det kommer till kvantitativa data så underlättar det om man antingen har kunskap och licenspengar att köpa och lära sig Excel eller SPSS. Men, om man istället vill vara öppen hela vägen så är det mycket bättre att köra helt med open source. Och om man dessutom vill slå flera flugor i en smäll tar man och lär sig ett programmeringsspråk. Då kan man göra tusen andra saker än bara analysera data.

Ett bra programmeringsspråk för statistik är R eftersom det i standardutförande gör allt man behöver för att använda sig av de datastrukturer som är bra för att analysera statistisk data.

Själv använder jag Python. Det är ett mera generellt språk, men med biblioteket Pandas får man enkla färdiga datastrukturer som är väldigt lätta att använda.

I denna lilla anteckning tänkte jag lägga ut lite kodsnuttar för att analysera SOM-institutets långa tidsserie 1986-2014, även kallad ”SuperSOM”, med Python/Pandas. Det är en guldgruva för samhällsvetare och det är svårt att hitta enkätdata med högre kvalitet i Europa. Dock innehåller denna datamängd mer än 93 tusen enkätsvar, så man får ha lite tålamod och RAM-minne till hands.

SuperSOM1 är öppen data, men den är inte superöppen. Man måste beställa den från Svensk Nationell Datatjänst och tillgängligheten bestäms av ”primärforskaren”. Så man måste ange vad man ska ha datamängden till och sedan får man en nedladdningslänk. Man får inte sprida den vidare.

Tyvärr levererar inte SND i csv-format utan bara i statistikprogramformat. Så jag beställde Excel-format (som smidigt kan konverteras till csv, se nedan).

Först konvertering från Excel till csv med två enkla rader:

import pandas as pd
df = pd.read_excel('data/SND 0905-004 Super-Riks-SOM 1986-2014.xlsx') # öppna Excelfil
df.to_csv("SuperSOM1986-2014.csv", sep='\t', encoding='utf-8') # skriv till csv-fil

 

Observera att det kan ta lite tid att konvertera. Excel-formatet är lite långsamt av sig. Men när det är klart kan man läsa in csv-filen på några sekunder (tar ca. 2 Gb i minne, så bör funka på vanlig dator).

df = pd.read_csv('data/SuperSOM1986-2014.csv', sep='\t', encoding='utf-8')

Nu är hela SuperSOM inläst som en ”dataframe” och allokerad som variabeln df. För att kolla att all data är med i leken kan vi göra en enkel graf år för år med det lättanvända biblioteket Seaborn.

import seaborn as sns
years = sns.factorplot('year', data=df, kind='count', size=8, aspect=2)
years.set_xticklabels(rotation=30)

Japp, SOM-enkäten blir mycket större med åren. Däremot minskar svarsfrekvensen, något man får ha i bakhuvudet.

Därefter kan man ta fram kodboken man får med och sedan börja analysera datan. Som exempel tänker jag ta fråga md10 som frågar hur nöjd man är med livet (det man kallar för ”Life satisfaction” i internationell forskning). Om man vill få ut data i procent för varje år i en tabell får man göra lite handpåläggning (tar gärna emot tips på hur man kan göra detta med mindre kod).

Så här gjorde jag (med lite hjälp av biblioteket tabulate)

from tabulate import tabulate

def lstimeseries():
    tsresults = []
    for y in range(1996, 2015):
        year = y
        md10table = df['md10'].loc[df['year'] == year]
        #md10table = df['md10']
        md10total = 0
        mycketnojd = 0
        ganskanojd = 0
        intesarskiltnojd = 0
        inteallsnojd = 0 
        nan = 0
        for t in md10table:
            if pd.isnull(t):
                nan += 1
            elif t == 98:
                nan += 1
            elif t == 99:
                nan += 1
            elif t == 96:
                nan += 1
            elif t == 1:
                mycketnojd += 1
                md10total += 1
            elif t == 2:
                ganskanojd += 1
                md10total += 1
            elif t == 3:
                intesarskiltnojd += 1
                md10total += 1
            elif t == 4:
                inteallsnojd += 1
                md10total += 1
        # Append the data to a lis of lists that can be tabulated:
        # add , 2 to the round method to get some decimals e.g. round((mycketnojd/md10total * 100), 2)
        tsresults.append([year, round((mycketnojd/md10total * 100)),   
                           round((ganskanojd/md10total * 100)),
                           round((intesarskiltnojd/md10total * 100)),
                           round((inteallsnojd/md10total * 100))])
    return(tsresults)

print("Nöjd med livet, nationella SOM-undersökningen\n")
# For html, add , tablefmt="html"
print(tabulate(lstimeseries(), headers=["Year", "Mycket nöjd", "Ganska nöjd", 
                                        "Inte särskilt nöjd", "Inte alls nöjd"])) 

Vilket ger:

Year Mycket nöjd Ganska nöjd Inte särskilt nöjd Inte alls nöjd
1996 30 64 6 0
1997 30 61 8 2
1998 28 63 8 1
1999 29 62 8 1
2000 31 61 7 1
2001 32 60 8 1
2002 29 62 8 1
2003 33 58 8 1
2004 33 59 7 1
2005 32 59 7 1
2006 35 57 7 1
2007 35 58 7 1
2008 34 58 7 2
2009 36 57 7 1
2010 36 57 6 1
2011 37 56 6 1
2012 35 58 6 1
2013 36 57 7 1
2014 37 56 6 1

För säkerhets skull kan man kolla att man har räknat rätt genom att kontrollera mot någon av alla de rapporter som SOM-institutet publicerat. Exempelvis en intressant studie av Brülde och Nilsson från 2010.

Det verkar alltså som att vi blir allt mer ”intensivt nöjda” med livet för varje år som går. Missnöjdheten är däremot stabilt låg.

Således har vi tagit fram lite data som vi kan visa för alla kommentatorer  ”känner” det ena och det andra i samtiden. Samtidigt har vi replikerat SOM-institutets mätningar, något som är viktigt för att öka den vetenskapliga kvaliteten. Ju fler ögon desto färre buggar.


  1. Göteborgs universitet, SOM-institutet. (2016). Super-Riks-SOM 1986-2014. Version 1.0. Svensk Nationell Datatjänst. http://dx.doi.org/10.5878/002826 

Mårdhunden, Judasdjuret och Människan

Racoondogskull

I helgen har jag testat att analysera biodiversitetsdata från Analysportalen från Svenska Lifewatch. Analysportalen innehåller enorma mängder artobservationer från bland annat det stora medborgarforskningsprojektet Artportalen.

Mårdhunden är en invasiv art. Den introducerades under perioden 1928-1950 i Ukraina för att jagas för pälsens skull. Människans fåfänga har ofta ekologiska konsekvenser och så även i detta fall. Mårdhunden etablerade sig och spred sig, från post-Sovjet hela vägen över Finland, till Sverige.

För att få bukt med spridningen av mårdhundarna använder man sig av ”Judasdjur”. I Jägareförbundets rapport 2015 kan man läsa följande mycket poetiska stycke:

Sändarmärkta mårdhundar (Judasdjur) letar dygnet runt efter  nya  mårdhundar  som de sedan  leder  oss  till.  Mårdhunden  är  monogam  och  dess  första  prioritet  förutom  egen  överlevnad är att finna en partner som den sedan aldrig lämnar förrän en av dem dör, då den  återigen  börjar leta en ny  partner.  Sterilisering av  sändardjuren  utförs  innan de släpps så att de inte ska kunna reproducera sig om vi skulle förlora dem genom sändarfel.Båda  könen  fungerar  lika  bra  som  Judasdjur.  Sändardjuren  fungerar  effektivare  ju  lägre  täthet det är eftersom det då är mycket svårt för oss människor att ens hitta ett spår. En annan  mårdhund  kommer  dock  att  förr  eller  senare  finna  sin  artfrände.  De  ägnar  all  sin  tid åt detta och vandrar över mycket stora områden (ibland långt över 100 000 hektar) i sitt  sökande.  I  figur  1  ser  vi  området  en  mårdhund  har  täckt  under  två  år  i  sitt  sökande  efter  en  partner,  området  är  större  än  de  flesta  län  i  landet.  När  de  finner  en  partner  avslutas  vandringen  och  paret  stannar  därefter  inom  ett  mycket  begränsat  område,  det  är också så vi ser att de har funnit en partner.

Framtiden ser inte ljus ut för mårdhunden.

screen-shot-2016-12-05-at-09-53-15

Mårdhundsobservationer per län, se källkod.

Men vad har vi för data på mårdhundens utbredning? Analysportalen är ett fantastiskt verktyg för öppna data, och man kan med enkla medel ladda hem alla observationer. Med lite helghackande lyckades jag få till lite kod som kan användas på lite olika sätt. All kod och data som behövs för att replikera figurerna finns här.

Med lite kod kan man skapa en karta över de inrapporterade observationerna (klicka för att få fram info om varje observation).

Se kartan i större format.

Men hur ska vi förstå mårdhunden och dess relation till territoriet, människan och ett processfilosofiskt tänkande?

Deleuze och Guattari’s berömda passage i Tusen platåer ger oss ett tankeuppslag:

Orkidén avterritorialiseras genom att skapa en bild, en kalkering av en geting; men getingen återterritorialiseras på denna bild. Getingen avterritorialiseras emellertid, i det att den själv blir en del av orkidéns reproduktionsapparat; den återterritorialiserar orkidén genom att transportera pollen. Getingen och orkidén bildar ett rhizom just därför att de är heterogena. (s. 27)

Människans fåfänga skapade en bild av mårdhundens päls i relation till hur människans semiotiska signaler (pälskläder) territorialiserar ett socialt stratum (jag är rik, jag har ”stil”). Detta ledde till att fåfängans logik översattes en kapitalistisk utbyteslogik av de Sovjetiska(!) aktörerna som gjorde vad de kunde för att introducera mårdhunden i östra Europa och västra Asien. Det var främst i Baltikum som mårdhunden kunde återterritorialisera sig i de födorika skogarna. Men den skulle sakta men säkert börja vandra norrut genom Karelen, samtidigt som den avterritorialiserade de djupa skogarna och kastade om den ekologiska balansen. I mårdhundarnas magar skapade dvärgbandmaskar och parasiter kalkeringar av en mårdhund och gjorde denna plats till sin reproduktionsapparat. I detta moment skapade människan en ny bild av mårdhunden som en invasiv art. Man uppfann Judasdjuret, som med avsaknad av reproduktionsapparat (steriliserad) kunde röra sig över hundratals hektar av skog i jakt på en förvildad mårdhund.

Människan, Judasdjuret, mårdhunden och dvärgbandmasken bildar ett rhizom, vars utbredning är en ständig kamp om territorium.

Recension: Digital politik: Sociala medier, deltagande och engagemang

Idag publiceras en recension av boken Digital politik: Sociala medier, deltagande och engagemang (Carlsson, Eric, Bo Nilsson & Simon Lindgren (red.) (2015). Göteborg: Daidalos) som jag har skrivit i tidskriften Human IT, som är helt och hållet open access.

Jag fick fritt fram att skriva långt, så recensionen är förhållandevis utförlig. Utrymmet gjorde även att jag fick möjlighet att driva ett antal metodologiska och teoretiska resonemang som visar hur boken med sitt diskursanalytiska perspektiv (tung inspirerat av Laclau och Mouffe) på många sätt missar den digitala politiken. Detta är dock mer än en metodfråga. Det får bland annat som konsekvens att man helt bortser från konsekvenserna av internets plattformisering, hur hypertext fungerar, vad meta-data betyder osv.

Men det finns även en annan brist i perspektivet. Genom att författarna i huvudsak analyserar hur man har talat om digital politik i konventionella massmedier, så missar man helt och hållet framväxten av ”alternativmedier” och deras (i många fall negativa) påverkan på det offentliga samtalet. Ur ett svenskt perspektiv känns det omöjligt att tala om digital politik utan att nämna Flashback, Avpixlat osv.

Eller vad tänker ni om detta?

 

Lite mer om felmarginalsuträkningar

Sitter och uppdaterar det lilla hobbyprojektet felmarginalen. Eftersom jag precis håller på att lära mig javascript är jag inte helt säker på att jag får de olika matematiska formlerna rätt när jag översätter dem till kod.

För att räkna ut felmarginalen har jag kodat:

Math.sqrt(((procentsats * (100 - procentsats)) / urval)) * 1.96

och, för att räkna baklänges, alltså för att få ut hur stort urval man behöver, har jag vänt på ekvationen så här:

Math.pow(1.96, 2) * (procentsats * (100 - procentsats)) / Math.pow(felmarginal, 2)

Ni som är bra på statistik och javascript, visst funkar detta?

Nästa steg blir att göra så att man kan välja säkerhetsnivåer (90, 95, 99 procent) samt eventuellt göra en graf i D3.js. Förslag och kommentarer mottages varmt.

Felmarginalen

I förra inlägget råkade jag klaga på bristande kunskaper bland journalister för att räkna felmarginaler. Det var inte så konstruktivt och jag kände mig lite ”gnällig”.

Så med lite javascript och lite snabbt sammanrafsad html ordnades felmarginalen.se, en ”lathund” som även försöker vara lite folkbildande.

Det är mest ett test än så länge. Kommentera gärna olika saker man kan göra för att förbättra sidan.

Vetenskap, Folkbildning och Sifokratin

Häromdagen publicerade Vetenskap och Folkbildning (VoF) en opinionsundersökning som mäter i vilken utsträckning folk tror på ”pseudovetenskaper”. Frågorna som man har ställt är ibland ganska problematiska, men själva undersökningen, som är genomförd av Demoskop, saknar även urval och bortfallsanalys, så man kan inte veta vad svarsfrekvensen varit. Men detta är ganska vanligt bland opinionsundersökningar som beställs av pollsters, något som jag skrev om i min avhandling (fast historiskt om en kontrovers på 80-talet).

Vi lever i en samtid genomsyrad av strategisk kommunikation, och VoF är ju en del av denna postmoderna trend (även om de säger sig ogilla postmodernism, vem gör inte det!). I detta tillstånd kan man inte säga att opinionen finns; den ”konstrueras”, vilket leder till att opinionsundersökingar är viktigare som ”konstruktiva instrument” än som vetenskapliga instrument.

Anledningen till att opinionsundersökningar fungerar så bra för opinionsarbete är att vetenskapsjournalister och journalister i allmänhet oftast saknar tid och ibland saknar kunskaper i att granska alla de mätningar som produceras. Att sätta sig ned och räkna felmarginaler, värdera urval och bortfall, granska frågorna som ställts blir en övermäktig uppgift. Som vetenskapsjournalist är det lättare att hylla vetenskapen till skyarna än att granska vad de säger och varför de säger vad de säger. Några tusen tecken, några tusen kronor i arvode, oftast blir det inte mer än så.

Detta tillstånd är inte optimalt ur en demokratisk synvinkel. En brännande fråga, som borde vara prioriterad bland folkbildare, är avsaknaden av kritik när det kommer till alla dessa opinionsundersökningar som cirkulerar i samhället.

Vi tar en titt på mediereceptionen. På DN-debatt presenterade VoF resultaten med fokus på huruvida folk tror på ”raser”. Här är partipolitikvinkeln central. Sverigedemokrater visar sig tro på raser i hög utsträckning, vänsterpartister i mindre. Ur en PR-synvinkel kan man här ställa priset för opinionsundersökningen mot kostnaden för en helsidesannons. Jag vet inte vad någon av de båda kostar, så jämförelsen är bara hypotetisk.

Men partipolitikspåret visade sig vara ett vinnande koncept. Vetenskapsjournalisten Karin Bojs lyckades på 2800 tecken pressa in en ambitiös sammanfattning av rapporten som till och med fick Folkpartister att se ut som Freudianer.

bojs

Per Gudmundsson skriver sedan ett rafflande referat i SvD om hur miljöpartister tror att månlandningen var en bluff och hur moderater oftare än andra tror på chemtrails. Gudmundsson skriver också, lite reflexivt att:

Någon fråga om tron på opinionsmätningar som utgår från webbpaneler och bryter ner svaren från drygt tusen respondenter, varav en stor del sannolikt inte har någon partisympati alls, på detaljnivå har dock inte ställts. Man kan utgå från att slumpen har spelat viss roll för mätningens resultat. Vilket är en smula ironiskt, när föreningen bakom annars – med den äran – bekämpar vidskepelse om slagrutor, homeopati och själavandring.

Intressant. En skepsis över en undersökning genomförd av skeptikerna själva. Även Sam Linderoth på ETC väljer en klickbetesrubrik, fast mot Sverigedemokraterna, med rubriken ”SD-väljare tror på raser och UFO-besök”.

Partifokuset verkar vara ett givande koncept. När internetreklam framstår som mindre effektiv för varje dag som går känns detta som vägen framåt när det kommer till påverkansarbete.

Men håller verkligen partikorrelationerna? Det finns 176 Sverigedemokrater i totalurvalet (n=1113). När 48% av dessa tror på raser har vi en felmarginal på 7.4 procent (95% signifikans). Det kan alltså likaväl röra sig om 41.6% eller 55.4% (vilket inte gör något om man bara är ute efter att utse det mest ”rastroende” partiet, eftersom avståndet är långt till de andra partierna). Men tar vi och kollar samtliga resultat så skulle felmarginalerna bara hålla måttet i vissa fall.

\\

Som vetenskapsteoretiker tillhör jag ju fältet ”science studies”. VoF beskriver detta fält som tillhörande traditionen ”postmodernism”:

En typiskt postmodern forskningsriktning är s.k. science studies, där man studerar forskare, forskningsinstitutioner och forskningprocesser ur socialantropologiskt perspektiv.

Detta eko till 90-talets science wars känns nästan retro. VoF borde arkivera den för framtiden! Men samtidigt låser man nog in sig i ett hörn vars bäst-före-datum har gått ut, inte för att man ska följa trender, utan för att de positioner som var möjliga i slutet av 90-talet nog är omöjliga idag. Det finns längre ingen som intar de bisarra former av postmodernistiska positioner som hade ett visst manöverutrymme på 90-talet. ”Science studies” har aldrig varit en del av denna tradition. Den har bara på ett väldigt enkelt sätt undersökt villkoren för objektivitet inom vetenskaperna. Hur objektivitet och kunskap konstrueras (för de växer inte på träd av sig själva), av människor, maskiner, planeter, atomer, begrepp, fantasier – och vilken central roll vetenskaplig kunskap har spelat i den moderniseringsfront som pressat våra samhällen till den punkt som vi står vid idag.

Högteknologiska framsteg och
en planet som håller på att kollapsa.
Till och med geologerna är
konstruktivister
.

Vilka universitet nämns i Statens offentliga utredningar?

Jag funderar på ytterligare en dimension i SOUerna som ”kunskapsform”. Vilka universitet och högskolor nämns i utredningarna? Man kan anta att ett sådant omnämnande har två huvudsakliga orsaker. Antingen äger det rum när expertis från universiteten inkorporeras i utredningarna (professor X från Lunds Universitet, forskning vid Malmö högskola), eller när högskoleväsendet som sådant utreds.

Eventuellt skulle detta kunna vara ett alternativt sätt att mäta det som så populärt (och problematiskt) kallas för ”impact” eller ”samverkan”, givet att man kan antingen separera eller vikta de två olika ovan nämnda orsakerna till omnämning

Ambitionen är inte här att skapa ett sådant mått, utan att bara att visa hur mått kan skapas på väg mot en mätkritik av måtten. Måttkonstruktivism helt enkelt, i sin mest bokstavliga betydelse.

\\

Jag tänker att det borde vara ganska enkelt att med grova verktyg kunna göra en första insamling.

Följande lilla uttryck:


\s[A-Z]([^\s]*)\s(universitet|högskola)

Ger sökträffar i stil med:


381:samma universitet C O . Häggquist, dåvarande docenten vid Göteborgs högskola, professorn vid Stockholms högskola S. I. Karling, överbibliotekarien
390:förslag i tre promemorior, betecknade »P. M. angående Uppsala universitets drätselverk», P. M. angående Uppsala universitets kansli» och »P. M.
391:angående organisationen m. m. vid Lunds universitets kansli och räntekammare». Promemoriorna, som äro dagtecknade den 30 juni, den 3 september
398:15 januari 1947 till departementschefen avgivit en promemoria rörande Göteborgs högskolas ekonomiska ställning. Vid handläggningen av denna fråga
399:har beredningen biträtts av sekreteraren vid Göteborgs högskola, filosofie

Uttrycket säger ungefär: ”hitta allt som börjar med ett mellanslag, därefter en stor bokstav, därefter vad som helst förutom ett blanksteg, därefter ett mellanslag och sist antingen ordet ”universitet” eller ”högskola””. Uttrycket är väldigt snällt för att det går enkelt att hämta stadens namn i första parentesen och universitet/högskola i andra parentesen (ex. som $1, $2, Tyvärr ger denna sträng även några anomalier, exempelvis:


För högskolans
I universitetsstaden
En universitetslärare

Dessa går dock att sortera ut med en liten stoppordslista i ett senare skede. Vad man dessutom missar är nya uttryck såsom ”Mittuniversitetet”, ”Högskolan Väst” osv. Funderar vidare på hur dessa kan matchas.

Jag tänkte sedan stoppa in dessa uttryck i några små program. Men först kastar jag bara ut denna lilla anteckning och ni som vill kommentera är mer än välkomna att göra så.

SOUernas geografi

Var är Sverige utrett? Oftast frågar man efter hur och så tänker man sig att svaret skulle vara av kvalitativ art. Men det finns en annan väg, givet att man vet exakt vilka platser som har skrivits ned i alla statliga offentliga utredningar. Rad för rad. I denna bloggpost och instruktion hade jag tänkt att försöka mig på att ta ett steg närmare en sådan empirisk datamängd.

En spännande teoretisk fråga man då kan ställa sig handlar om hur staten ”blir till” (performativt) genom att utreda ort för ort. På samma sätt som ett nätverk av ryska gasledningar kan göra anspråk på ett territorium, kan ett nätverk av utredningar sakta men säkert släppa på och av flöden av ”statsgas” som letar sig ända ut i skogarna och värmer (eller kyler) stugorna med en känsla av att tillhöra ”staten Sverige”.

Nationens gränser finns inte, de måste hela tiden (om)skapas.

Jag tänkte med denna bloggpost lägga ut steg för steg hur jag har lyckats extrahera alla ortnamn ur SOUer 1922–1996. Jag lägger ut all rådata och alla steg i bearbetningen. Det blir kanske inte så läsvänligt, men å andra sidan kan alla mina steg replikeras och kritiseras så att datan blir fullt användbar för vem som helst att använda eller publicera. Dessutom krävde det hela mycket datorkraft, så det är en miljöinsats att använda mina filer.

Med andra ord, det är alltså fritt fram att göra vad ni vill!

\\
Steg 1 – Extrahera ortsnamn
Detta tog en vecka. I skripten som Peter Krantz publicerade finns lista på ortsnamn som man kan matcha mot alla SOUer med hjälp av:


grep -owf features.txt data/30tal/*.txt > trettiotal.txt

Jag körde detta för varje katalog i åtta screens på en riktigt snabb server. I detta läge använder sig grep av Aho Corasick strängmatchning. Den letar alltså efter träffar från en (jättelång) lista av ord och så printar den ut dessa till en fil. Kanske hade jag kunnat distribuera processorlasten över alla 32 processorer i mitt system, men jag ville heller inte krascha maskinen som delas med andra. Den råa datamängden från detta kan laddas ned som:

Men dessa filer innehåller massvis av skräpdata och är bökiga att arbeta med statistiskt. Således måste de bearbetas.

\\

Formatera data

För att göra datamängden statistiskt läsbar började jag karva fram variablerna Årtal, SOU-Nummer och Ortnamn enligt följande trixande med reguljära uttryck (kommentera gärna om ni har smidigare lösningar).

Först gjorde jag en första grov extrahering av Årtal (\d{4}), Nummer (:\d{2}) och sedan Ortsnamen som kommer sist på varje rad (.*):


ag -o '\d{4}:\d{2}.*(?<=:\d{2}.txt:).*' SOU1922-1996.txt > 1.txt

Sedan gjorde jag två olika versioner. Den första filen (2.6Mb)inneåller År, Nummer, Ort och gjordes om till en kommaseparerad fil med ett enkelt perl-handgrepp:


perl -p -i -e 's/.txt:/, /g' 1.txt

Men, för enkelhets skull gjorde jag även (2.4Mb) en version med bara År och Ort ur ovanstående fil:


perl -p -i -e 's/:\d{2}//g' 1.txt

\\

Stoppord

Men nu framträder ytterligare ett problem. Ortnamn, speciellt svenska ortnamn, har ju ibland betydelser även i vardagsspråket. Staden Kista och en sådan där trälåda man läggs i när man dör, stavas likadant. Ännu värre blir det med orten ”Vi” eller ”Gran”. Jag började först med att bläddra igenom filerna induktivt och hittade själv följande lista:

Vi|Bet|När|Bön|Håll|Mark|Böj|Vad|Färjan|Kap|Så|Alla|Verksamheten|Vecka|Källa|Varmvattnet|Hallen|Bo|Gran|Björk|Viken

Men detta var väldigt tidsödande, så jag frågade runt i sociala medier och fick massvis av svar. Här är de jag samlade in i går kväll:

Lyckan|Önska|By|Ön|Holmen|Strand|Vara|Mannaminne|Ensamheten|Bor|Kärleken|Skara|Bro|Viken|Skruv|Tving|Råda|Torp|Näs|Berg|Lugnet|Torp|By|Sand|Slut

För att sortera ut dessa falska positiver använde jag mig av lite perl.


perl -p -i -e 's/^.*\b(Vi|Bet|När|Bön|Håll|Mark|Böj|Vad|Färjan|Kap|Så|Alla|Verksamheten|Vecka|Källa|Varmvattnet|Hallen|Bo|Gran|Björk|VikenLyckan|Önska|By|Ön|Holmen|Strand|Vara|Mannaminne|Ensamheten|Bor|Kärleken|Skara|Bro|Viken|Skruv|Tving|Råda|Torp|Näs|Berg|Lugnet|Torp|By|Sand|Slut|Mom)\b//g' test.txt

Detta tar bort alla rader som innehåller något av ovanstående ord. Däremot lämnar det kvar blanka rader som jag tar bort med:


perl -p -i -e 's/^(\s*|\r|\n)$//mg' test.txt

Av någon anledning fanns fortfarande orten ”Så” med, så jag fick köra en ny vända med:


perl -p -i -e 's/.*Så//g' test.txt

Och sedan ta bort blanka rader igen.

\\
Datamängden

Sedan är det bara att döpa om txt-filen till .csv. Och så har vi datamängden SOU1922-1996artal-ort-filtered.csv.zip (1.5Mb).

Denna kommaseparerade fil borde man kunna läsa in rakt i Excel om man vill. För att göra allt replikerbart har jag dock bara gjort lite enkel statistik i R. För att skapa en enkel lista över orter i SOUer (i fallande ordning), kör jag bara (byt ut sökväg):


mydata <- read.csv("/home/anon/Desktop/SOU1922-1996artal-ort-filtered.csv") sink("Platserfrekvens.txt") df <- as.data.frame(table(mydata$Place)) ; df[order(df$Freq, decreasing=T), ]

Här instruerar jag R att skriva ut hela resultatet till filen Platserfrekvens.txt vars topplista ser ut så här:

Stockholm 73039
Göteborg 28937
Uppsala 26130
Malmö 20032
Danmark 17324
Norrland 15245
Örebro 14191
Lund 14143
Kalmar 10821
Bohus 8124
Frankrike 7372
Umeå 6948
Gävle 6569
Norrköping 6013
Västerås 5777
Luleå 5389
Skåne 5249
Linköping 5125
Karlskrona 5015
Hälsingborg 4695
Växjö 4190
Os 4069

Jag antar att "Os" är ytterligare en sökanomali. Stoppordslistan måsta alltså uppdateras.

Men detta vara bara ett test och ett sätt att få ut datamängden så att ni andra kan börja tinka med den. Det borde vara enkelt att skapa fina grafer över tid, kanske ned till varje enskild rapport. Det skulle även vara helt magnifikt med en sverigekarta som över tid uppdaterades dynamiskt med en "prick" på kartan varje gång en SOU nämnde en ort. Då skulle man, precis som med tekniska nätverk, se precis var någonstans det finns "täckning" och var någonstans det finns "vita fläckar". Tänk en täckningskarta för mobiltelefoner.

Kommentera mer än gärna. Vad finns det mer för idéer?

Ämnen i SOUniversum 3 – Topic Modelling

Det regnar ute och jag funderar på olika sätt som man kan behandla SOU-materialet på. För hundra år sedan skrevs en utredning på en skrivmaskin och nu ska jag behandla den med ett dator som är designad i Kalifornien och byggd i Taiwan. Om vi någonsin har varit moderna går det att snabbspola denna modernitet i gigahertz-hastighet.

En metod som har blivit populär inom digital humaniora är ”topic modeling”, som beskrivs av Journal of Digital Humanities som:

Topic modeling is a form of text mining, a way of identifying patterns in a corpus. You take your corpus and run it through a tool which groups words across the corpus into ‘topics’. Miriam Posner has described topic modeling as “a method for finding and tracing clusters of words (called “topics” in shorthand) in large bodies of texts.”

Mitt ursprungsläge präglas av skepsis. Varför ska vi använda maskininlärning på ett material som redan är systematiserat, kategoriserat och strukturerat av författarna. En SOU har ju en rubrik, innehållsförteckning, sammanfattning, osv., och har noggrant skrivits fram enligt rådande konventioner. Stopp och belägg! Åter till humaniora som det alltid har varit.

Men skepsis är en i grunden reaktiv affekt. Vi har ju redan datorerna i våra knän, varför inte experimentera och se vad som händer?

Jag började med att ladda ned programmet Mallet och följde sedan en välskriven installationsanvisning för samtliga operativsystem. Jag körde sedan några tester på engelska textkorpus för att få lite känsla för vad programmet gjorde. Denna teknologiska hermeneutik präglades av fullständig blackboxing till en början. Ofta är det ju så vi gör med teknologier som vi tror är ”verktyg”. Vi använder dem med en lätthet tills de inte gör som vi vill.

Problemet med SOUerna är ju att de innehåller massa brus och artefakter från inskanning och konvertering till text. Jag har fortfarande inte klurat ut hur detta ska lösas, men så länge kan man ”fuska” sig fram litegrann, för att få en helhetsbild.

Som testobjekt valde jag SOU 1924:35 Ylleindustriens produktionsförhållanden. Inget slår modernitetsångesten när det regnar. Man tänker på de där fabrikerna som nu huserar designbyråers kontor, men som en gång i tiden höll textilarbetare inspärrade vid dånande maskiner. Nu utvecklar man istället appar i dessa fabriker.

För att ta en genväg (som inte håller rent lingvistiskt) runt allt brus som finns i textfilen av denna SOU, valde jag att bara inkludera ord som är längre än fem tecken. Ett exempel på hur man kan göra detta är:


ag -o '\b[\wåäöÅÄÖ]{5}(.*?)\b' Ylleindustriens\ produktionsförhållanden\ -\ SOU\ 1924\:35.txt > ylle.txt

(byt ut ag -o mot grep -Eoi om du bara har det senare installerat)

Jag vet inte om man får göra så här. Om någon lingvist läser, kommentera gärna. Men nu har jag iallafall filen ylle.txt, som består av långa ord från hela SOUn. Här kan man läsa intressanta ord, exempelvis ”vicuñageten”. Här är en bild på ett sådant djur:

Vicugna-ESO

Därefter är det dags att använda Mallet. Jag lägger filen ylle.txt i en ny katalog och börjar med att bygga ett korpus:


./bin/mallet import-dir --token-regex '[\p{L}\p{M}]+' --input /home/anon/Desktop/ylle/ylleord/ --output ylle.mallet --keep-sequence --remove-stopwords

(observera att om ni använder windows ska ni inte skriva ./ i början, samt byta ut katalogstrukturen till motsvarigheten på erat system)

Jag vet inte riktigt vad jag gör här. Men --token-regex '[\p{L}\p{M}]+' ska iallafall göra det lite snällare för icke-engelsk text. Men nu har jag i alla fall korpuset ylle.mallet. Därefter är det dags för själva topic modelingen. Jag kör:


./bin/mallet train-topics --input ylle.mallet --num-topics 10 --output-state topic-state.gz --output-topic-keys ylle_keys.txt --output-doc-topics ylle_composition.txt

Jag väljer här att Mallet ska sammanfatta tio topics. Denna variabel går att öka och minska efter smak. Ur detta får jag dels tio olika topics i filen ylle_keys.txt samt en beräkning på hur hög täckningsgrad varje topic har i filen ylle_composition.txt. Här kan man se att topic 9 har den högsta sammanfattningsgraden (undrar om man kan säga så) på ungefär 12% (vet inte vad det betyder riktigt). Detta ämne lyder:

ylleindustrien bomullsindustrien kombinerade produktionskostnader produktion inventarier förbrukning totalkostnad sydamerikansk kapitalkostnaderna framträdande rörelsekapital importerat tillverkningar kamylleindustri uppdelning spridningen undersökningen sydamerikanska

Nästa ämne är topic 4 som lyder:

sammanlagt kamyllevä kardylleindustrien innehåller utredningen egenskaper produktionsf löngodsfabriker konsumtion tillverkningen kardyllebranchen beräknats resultatet världskriget spinnerier noteringar rentvättad kortulliga verkställd

Maskinen har arbetat. Jag vet inte riktigt vad jag har gjort.

Hur går jag vidare med detta? Skulle det vara intressant att göra en liknande analys av hela SOU-materialet? Finns det något mindre brutalt sätt att göra filerna rena och snygga och anpassade till svenskt alfabet och svenska stoppord? Blir vi bildade av denna kunskap eller är detta ett intellektuellt förfall? Kommentera!

Uppdatering: Sverker Lundin har gjort en liknande analys men med flera topics. Läs här!.

Uppdatering 2: Ändrade och uppdaterade efter att ha anpassat till svenska tecken, se kommentaren nedan.

Relationer i SOUniversum 2 : SOUlab

skriptet

I förra blogginlägget gick jag den långa omvägen till att bygga upp en nätverksfil som man kan visualisera med program som Gephi. Det var aningen krångligt, men ganska roligt.

Peter Krantz har skrivit en knippe pythonskript som gör allt mycket enklare. Bilden ovan (klicka för förstoring) är genererad på alla SOUer från 1922–1993, storleken justerad efter ”indegree”, dvs. ju fler citeringar en SOU får, desto större ”boll”. Exakt hur detta ska analyseras är något jag får återkomma till, så kommentera gärna!

Krantz skript är väldigt användbart men för att det ska gå att metodredovisa i detalj (ex. om man funderar på att publicera artiklar på materialet) måste man analysera enventuella felkällor.

SÅ jag tänkte att jag skriver ut en liten instruktion om hur man får igång skripten, dels för mitt eget minne, dels för om någon vill pröva själv. All kod är släppt under MIT licens av Krantz.

\\

Nu, över till terminalen.

1. Först och främst måste du ha Python installerat på din dator. Kör du Linux är det troligtvis redan installerat, annars är det lätt att hämta via distributionens pakethanterare. På MacOS går det att hämta här.

2. Python behöver några extra moduler. Dessa installeras med kommandot pip install peewee==2.6.1 lxml pygexf (lägg till sudo före pip i Linux).

3. Om du inte redan har gjort det, ladda hem alla SOUer som individuella textfiler och packa upp.

4. Om du har Git installerat kör du sedan bara:

git clone https://github.com/peterk/SOUlab.git

Annars kan du ladda ned och packa upp manuellt från Github-sidan.

5. Nu kan skripten börja köras. Det är tre stycken som körs efter varandra. Tänk på att hela tiden ange rätt sökväg, alltså till den katalog som blev till när du packade upp alla SOU-textfilerna:

python build_db.py /katalog/till/SOUfilerna
python parsesou.py /katalog/till/SOUfilerna
python gen_graph.py filnamn.gexf

Programmet bör då ge följande lilla meddelande om allt funkar


Writing to filnamn.gexf
SOU-grafen directed static
number of nodes : 4894
number of edges : 20943

.gexf-filen går sedan att öppna med Gephi.

Tack Peter Krantz för detta verktyg. Det sparar oändligt med arbete och eftersom det är öppet kan det användas för riktig forskning framöver!