Diagram med genomsyn

Postad 2018-01-28 av Karl Pettersson. Taggar: ,

I förra inlägget diskuterade jag några saker som tas upp av Frans (2017), däribland problematiken med missvisande diagram. Som jag skrev, och som jag även tog upp den 11 december 2016 är det inte ett trivialt problem vad som är relevant baslinje. Att bara följa börja på 0 blir många fall orimligt eller omöjligt för en given skala, samtidigt som nollpunkten i sig kan manipuleras genom transformation av skalan. För att ta Frans exempel med Alliansens tidsserie över andelen godkända Göteborgselever hade Alliansen lätt kunnat konstruera ett diagram där graflinjen hade samma utseende men där skalan började på 0 och visade avvikelsen från den aktuella periodens minimum. Hade det varit mindre missvisande än det ursprungliga diagrammet för en person som kastade en hastig blick på det? Om ett diagram har logaritmisk skala, där \(y\)-värdena visas med krympande avstånd, är det matematiskt omöjligt att ha med 0, men det går utmärkt om det är en skala för ett diagram med identisk graflinje som visar \(\log(y)\), då \(\log(1)=0\). Diagram kan också ge intryck av mer eller mindre dramatisk utveckling beroende på hur \(y\)-axelns övre gräns varieras. Här är det ännu orimligare att söka ange någon allmängiltig konstant gräns. Sannolikheter varierar på \([0,1]\), men andra typer av värden kan matematiskt variera på \([0,\infty)\), eller \((-\infty,+\infty)\).

Problemen diskuteras även av Tufte, som rekommenderar att använda en baslinje som visar ens data, inte 0, om nollvärden inte med någon rimlighet kunde förekomma bland värdena, och han påtalar att hans rekommendation överensstämmer med praxis i vetenskapliga publikationer. Det är bra att sätta in data i ett sammanhang, men det bör, hävdar han, göras genom att visa mer data horisontellt, inte genom att lägga tomt vertikalt utrymme ned till 0. Jo, det som kunde kallas kapad \(x\)-axel är nog i varje fall en värre form av förvillande än kapad \(y\)-axel i linjediagram: om det är så att \(x\)-axeln bara visar en liten del av t.ex. en lång tidsserie, på ett sätt som ger snedvriden uppfattning av övergripande trender. I de exempel som jag sett med diagram som påståtts vara missvisande genom kapad \(y\)-axel har inga värden inom intervallet på \(x\)-axeln fallit bort, och det går att enkelt förstå diagrammet genom att läsa skalan. Om det däremot saknas värden, krävs det att den som försöker tolka diagrammet går till bakomliggande datakällor, vilket kan vara omständligt eller i vissa fall rent omöjligt.

Nu i veckan upptäckte jag annars något som kan vara av viss relevans när det gäller konstruktion av icke vilseledande diagram. Den 30 december 2015 skrev jag litet om användning av språket Haskell för analys av data. Det hör inte till de generellt mest använda språken, men det har använts för att utveckla högst användbara program, som Pandoc och Hakyll, som jag använder för att generera denna blogg. Språket har vissa egenskaper som skiljer det från vanligare språk, som referentiell transparens, vilket innebär att uttryck kan ersättas med sina värden utan ett programs beteende förändras. Vanliga programspråk har inte denna egenskap: det går exempelvis att ha en metod som läser in en textfil och returnerar dess innehåll som en sträng. Inläsningen av filen är en sidoeffekt: det är inte möjligt att byta ut referenser till metoden mot referenser till någon specifik sträng den skulle kunna returnera utan att programmets beteende förändras. I Haskell skulle programmeraren i stället definiera en handling med en typ som IO String, en I/O-handling som levererar en sträng som inte är känd på förhand.

Språk med referentiell transparens borde vara attraktiva för dataanalys och andra matematiska tillämpningar, eftersom beteendet hos deras funktioner kommer nära beteendet hos matematiska funktioner. I mitt förra inlägg skrev jag att det nog i stor utsträckning är bristen på färdiga bibliotek som gör att Haskell inte används så flitigt för detta. Det finns bibliotek för att rita diagram, men de är inte lika utvecklade som exempelvis Matplotlib, som är standard i Python. Nu såg jag dock Barbu (2017), som är ett paket med bindningar till Matplotlib från Haskell, med samma koncept som PyPlot, som används för att anropa Matplotlib från Julia. Jag testade att använda det för att reproducera ett diagram baserat på data från Socialstyrelsen (2024), liknande de jag hade med i inläggen den 23 december och 26 december förra året. Resultatet syns i fig. 1: det visar genomsnittligt antal vårdtillfällen över tid för tre grupper av luftvägsinfektioner: övre luftvägsinfektioner (ICD-10 J00–J06), influensa och lunginflammation (J09–J18) och andra nedre luftvägsinfektioner (J20–J22).

Genomsnittligt antal vårdtillfällen i relation till folkmängden för luftvägsinfektioner Sverige 2009–2016.
Figur 1: Genomsnittligt antal vårdtillfällen i relation till folkmängden för luftvägsinfektioner Sverige 2009–2016.

Kod för diagrammet med data finns i ZIP-fil. Den centrala handlingen som läser in filen, anropar hjälpfunktioner för bearbetningar och ritar upp diagrammet är femaPlot i Main.hs.

femaPlot :: IO ()
femaPlot = do
    sxsasir <- sexesagesIncRecs
    let ufli = sexLogIncs "F" sxsasir
    let umli = sexLogIncs "M" sxsasir
    let fmpl = plot (fst ufli) (snd ufli) @@ [o1 "o-", o2 "label" "Kvinnor"]
         % plot (fst umli) (snd umli) @@ [o1 "o-", o2 "label" "Män"]
         % title "Sjukhusvårdade luftvägsinfektioner Sverige 2009–16"
         % legend
         % xlabel "Ålder"
         % ylabel "log(incidens, vårdtillfällen)"
         % grid True
    onscreen fmpl

Funktionen plot, som definierar diagrammet, är knappast snårigare än motsvarande funktionalitet i Julia, med PyPlot, eller Python.

Referenser

Barbu, Andrei. 2017. ”The matplotlib package”. https://hackage.haskell.org/package/matplotlib-0.6.0.
Frans, Emma. 2017. Larmrapporten : Att skilja vetenskap från trams.
Socialstyrelsen. 2024. ”Statistikdatabas för diagnoser i sluten vård”. https://sdb.socialstyrelsen.se/if_par/val.aspx.
Tufte, Edward. ”baseline for amount scale”. https://www.edwardtufte.com/bboard/q-and-a-fetch-msg?msg_id=00003q.