Stilfull plotting i Python

I dei fleste vitskapelege samanhengar oppstår det behov for grafisk framstilling av resultat. Omtrent like ofte sit ein gjerne kvelden før presentasjon og produserer desse framstillingane. Her vil eg kort presentere nokon "kjappe" tips for å gjere plotting litt lettare i ein hektisk kvardag.

 

Data og python-modular

Eksempeldata er henta frå SSB sine API, og inneheld informasjon om folketal i regionar kvart år mellom 1986 og 2021. Vidare brukar vi pandas til databehandling, og matplotlib.pyplot for plotting. All informasjonen her, og langt meir, kan du og finne på matplotlib sine sider om justering av plott  (på engelsk).

Hente og forberede data

Vi byrjar med å laste ned datasettet frå SSB. Pandas (og SSB sitt API) gjer dette på kurant vis, sidan pandas kan ta ei vanlig lenke som argument.

import matplotlib.pyplot as plt
import pandas as pd

# Hent data frå SSB
full_data = pd.read_csv("https://data.ssb.no/api/v0/dataset/26975.csv?lang=no",
                        encoding="ISO-8859-1", sep=';', decimal=',')

print(full_data.head(3))

Vi printar ut dei første tre radene i datasettet for å få eit bilete av kva vi har å jobbe med.

|    | region        |   år | statistikkvariabel   |   07459: Befolkning, etter region, år og statistikkvariabel |
|---:|:--------------|-----:|:---------------------|------------------------------------------------------------:|
|  0 | K-3001 Halden | 1986 | Personer             |                                                       25844 |
|  1 | K-3001 Halden | 1987 | Personer             |                                                       25807 |
|  2 | K-3001 Halden | 1988 | Personer             |                                                       25848 |

Sidan region ikkje berre er kommunenamn, har eg juksa litt og funne regionkoden for dei relevante regionane på førehand. Alternativt kunne eg laga ei ordbok (dictionary) for å omsetje frå regionnamn til verdiane i kolonnen for region.

Eg grupperer først datasettet basert på region, og lagar variabelen 'personkolonne', sidan eg ikkje har tenkt å skrive "07459: Befolkning, etter region, år og statistikkvariabel" meir enn høgst naudsynt.

grouped = full_data.groupby('region')

regionar = ["K-0301 Oslo", "K-4601 Bergen", "K-5001 Trondheim", "K-5401 Tromsø"]
personkolonne = "07459: Befolkning, etter region, år og statistikkvariabel"

Plotting

Vi startar med eit plott utan noko ekstra formatering.

# Plott data frå kva region av interesse
plt.figure(figsize=(9,6)) # Sett størrelse på figur

for region in regionar:
    data = grouped.get_group(region)
    plt.plot(data['år'], data[personkolonne], label=region)

plt.title("Standard pyplot")
plt.xlabel("År")
plt.ylabel("Folketal")
plt.legend()
plt.show()

 

Denne varianten er heilt OK, men teksten har ein tendens til å vere litt liten når figuren blir mindre, til dømes om du skal ha to plott side om side (sjå under). Det er fleire måtar å justere dette på, avhengig av kva tekst som skal justerast. Eg har googla for deg, og attgjer svaret her (med litt omsetjing).

# Justering av skriftstørrelse
plt.rc('axes', titlesize=24)        # tittel på plottet "Standard pyplot"
plt.rc('axes', labelsize=20)        # aksenamn, "Folketal og År"
plt.rc('xtick', labelsize=16)       # tala ved punkt på x-aksa
plt.rc('ytick', labelsize=16)       # tala ved punkt på y-aksa
plt.rc('legend', fontsize=16)       # tekst i forklaringsboksen

# Plott data frå kvar region av interesse
plt.figure(figsize=(9,6)) # Sett størrelse på figur

for region in regionar:
    data = grouped.get_group(region)
    plt.plot(data['år'], data[personkolonne], label=region)
    
plt.title("Justert pyplot")
plt.xlabel("År")
plt.ylabel("Folketal")
plt.legend()
plt.show()

Det er sjølvsagt ikkje naudsynt å endre alle små bitar, men moglegheita er der. Dersom du i staden for å skrive "plt.rc(...)" brukar "matplotlib.rc(...)" vil størrelsane påverke alle plott du produserer med same skript (hugs "import matplotlib" først).
Med eit godt auge kan du sjå at litt av aksetittelen for y-aksa manglar på plottet til høgre. Vi skal sjå korleis vi kan fikse dette litt lenger nede.

Andre stilar

Det finst langt fleire justeringsmoglegheiter i pyplot enn skriftstørrelsar, men dei skal vi la ligge, enn så lenge. Den kjappaste måten å justere plott på er å låne stilen frå nokon andre. Om du har for vane å følgje med på presidentval i Sambandsstatane (USA), har du kanskje sett statistikk frå nettstaden "fivethirtyeight". Matplotlib med pyplot har støtte for "styles", og stilen "fivethirtyeight" er eit forsøk på å etterlikne nettopp desse plotta.

Om du har tatt steget over til Python frå R, men ikkje vil sei det til venene dine endå
kan du til dømes bruke stilen "ggplot".

For å bruke desse stilane trengs det ikkje mykje justering av koden som produserte det opphavlege plottet:

# Ta i bruk plotstil
plt.style.use('fivethirtyeight')

plt.figure(figsize=(9,6)) # Sett størrelse på figur
for region in regionar:
    data = grouped.get_group(region)
    plt.plot(data['år'], data[personkolonne], label=region)

# osv...

Lagre stilen for seinare bruk

Innstillingane for det justerte plottet over kan vi lagre i ei eiga fil, og bruke igjen seinare. Kanskje du skriv på ei større oppgåve? I så fall kan det vere særs kurant at plotta dine har lik utforming i heile oppgåva. Og om du treng å endre på stilen er det berre éin stad du må gjere det. Her går eg ut frå at vi vil ha litt forskjellig stil for kvar type plott, og lagrar innstillingane for det justerte plottet i fila "./plotstilar/linjeplott.mplstyle". Her har eg og laga ei eiga mappe for stilane, "plotstilar".

Innhaldet i fila ser slik ut:

axes.titlesize : 24
axes.labelsize : 20
xtick.labelsize : 16
ytick.labelsize : 16
legend.fontsize : 16

Du brukar stilen på same måte som 'ggplot', men refererer til filstien i staden for namnet, slik:

plt.style.use("./plotstilar/linjeplott.mplstyle")

Kombinere stilar

Du kan og kombinere stilar, så om du f.eks vil bruke 'ggplot', men med justeringane vi  gjorde over kan du gi kommandoen ei liste med stilar:

plt.style.use(['ggplot', './plotstilar/linjeplott.mplstyle'])

Her vil vår eigenproduserte stil overstyre ggplot med dei spesifikke attributtane vår stil har verdiar for. Lenger oppe såg du kanskje at "ggplot"-stilen hadde litt lita skrift, men med våre justeringar ser den ganske bra ut side om side.

Ein uheldig konsekvens av å justere på skriftstørrelsar, er at f.eks. tittel på aksane kan ende opp utanfor området matplotlib teiknar i figuren. Dette er synleg i til dømes "Justert pyplot" lenger oppe. Her har vi fiksa det ved å stille inn tala på y-aksa slik at dei brukar vitskapeleg notasjon i staden for å skrive ut heile talet.

plt.ticklabel_format(axis='y', style='sci', scilimits=(3, 3))

Dette gjer at alle tal på y-aksen som er av størrelsesorden 3 eller større vil få denne notasjonen.

Bonus

For fans av teikneserien xkcd har pyplot faktisk ein funksjon for å gi plott ein stil som minner om å teikne for hand, slik som plott i xkcd-serien. Den brukar ein spesifikk font som må installerast separat (tilgjengeleg på github). Uheldigvis har den ikkje Æ, Ø eller Å inkludert. Så om plottet ditt skal illustrere eit poeng heller enn eit presist bilete av data, kan kanskje dette være eit friskt pust i presentasjonen?

 

Av Geir Tore Ulvik
Publisert 11. feb. 2022 17:11 - Sist endret 4. jan. 2023 16:15
sju mennesker

En blogg for deg som er interessert i IT-verktøy og datahåndtering, samt informasjon om, og erfaringer fra, forskningsprosjekter hvor bruk av IT inngår som en sentral del, enten det dreier seg om kvalitative eller kvantitative metoder/forskningsspørsmål.