Diagram med genomsyn
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).
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.