Hopp til innhold

Verktøy og mal

Behandling av store datamengder i Python

Med modulen Pandas kan vi bruke programmeringsspråket Python til å håndtere store datasett, og modulen gjør at vi kan lese innholdet i store datafiler inn i et Python-program.

Nedlasting av datafiler fra Statistisk Sentralbyrå

Når vi skal jobbe med store sett med data, er programmering et nyttig verktøy. Mye kan også gjøres i regneark, men her skal vi se på hvordan vi kan bruke Python til å behandle store datasett.

Når vi skal behandle data i Python, er det første vi må gjøre, å laste ned ei fil med dataene og lagre den i et hensiktsmessig format. Gode formater å jobbe med er .txt og .csv. Når vi laster ned filer med stort tallmateriale fra ulike statistikker, er det ofte lurt å bruke .csv.

Vi skal se på noen data om mediebruk fra Statistisk sentralbyrå (SSB). Du finner datasettet som ei nedlastbar fil nederst på denne siden, men prøv helst å laste ned fila selv, fra nettsiden https://www.ssb.no/statbank/table/04495/.

Datasettet som er brukt i eksempelet, er hentet fra Statistikkbanken til SSB. Datasettet inneholder opplysninger om hvor mange minutter en gjennomsnittsnordmann brukte på ulike medier per dag de siste årene. Følg framgangsmåten nedenfor for å velge ut data fra nettsiden:

  • Gå til nettsiden (se over).
  • Sørg først for at "Minutter brukt til ulike medier en gjennomsnittsdag" er valgt i boksen "Statistikkvariabel".
  • Velg deretter alle medietypene i boksen "Medietype" ved å klikke på knappen med grønn hake (eller velg alle manuelt i menyen).
  • Velg årene 2010 til 2019 i boksen "År".
  • Trykk på knappen "Vis tabell".

Når du har gjort dette, vil nettsiden vise en tabell som du kan se utdrag fra nedenfor:

Utdrag av datatabell

Medium

2010

2011

2012

Papiravis

23

24

21

Fjernsyn

152

157

131

Radio

81

86

88

Spørsmål

Hva betyr tallet 131 i ruta for fjernsyn i året 2012?

Svar

Tabellen viser minutter brukt på ulike medier en gjennomsnittsdag. Tallet 131 for fjernsyn i året 2012 viser at en nordmann i gjennomsnitt brukte 131 minutter per dag til å se på fjernsyn i 2012.

For å laste ned ei fil med disse dataene gjør du følgende:

  • Velg "Lagre data som..." til venstre for tabellen.
  • Velg "Semikolonseparert med overskrift (csv)".
  • Trykk "Lagre".
  • Kopier fila over til samme mappe på pc-en som Python-programmene dine ligger i. Det gjør det lettere å referere til fila i programmet som skal bruke den.
  • Endre filnavnet til "ssbmedier.csv".

Innlesing av datafila

Python-modulen Pandas bruker vi både til å lese datafila inn i Python og til å behandle dataene etterpå. Pandas organiserer dataene i en såkalt dataframe (dataramme), som er en tabell med kolonne- og radoverskrifter, ikke ulikt et regneark.

Før vi leser datafila inn i Python, kan vi kort se på den med et regnearkprogram. Bruk datafila du har lastet ned (ssbmedier.csv), eller last ned datafila nederst på denne siden. Åpne fila i et regneark. Datafila ser slik ut (ikke alle kolonner er tatt med):

Regneark som viser antall minutter brukt på ulike medier, blant annet papiraviser, fjernsyn og internett, på en gjennomsnittsdag. Tallene dekker årene 2010 til og med 2019. Skjermutklipp.

Det er tre problemer med denne datafila:

  1. De to første radene må fjernes.
  2. Det mangler tall flere steder. På slike steder står det enten ett eller to punktum, avhengig av hvorfor det mangler tall (se forklaring på nettsida med tabellen).
  3. Kolonneoverskriftene er veldig lange. På bildet over har vi utvidet kolonne B slik at vi får med hele overskriften. Også de andre tallkolonnene har den samme lange overskriften. Vi, derimot, vil bare ha årstallene.

De to første problemene ordner vi under selve innlesingen. Det siste fikser vi etterpå.

Kode for innlesingen

Kopier koden nedenfor og lim den inn i programmeringseditoren du vanligvis bruker. Pass på at fila har samme navn i filstrukturen på pc-en som i programkoden. Kjør programmet. Får du utskrift av dataene? Du får ganske sikkert en dårlig lesbar utskrift, men dette kan du ordne senere.

Python

1import pandas as pd
2data = pd.read_csv("ssbmedier.csv", index_col = 0, skiprows = (0, 1), \
3    sep =";", na_values=[".", ".."], encoding = "latin-1")
4print(data)

Forklaring av koden

Først må vi importere modulen Pandas. Ved å importere den som "pd" slipper vi å skrive hele modulnavnet når vi skal referere til den.

Kommandoen vi bruker for å lese inn fila, er "read_csv()". Variabelen data blir da en dataframe. Vi må sette inn mange såkalte attributter i kommandoen for at innlesingen skal gå smertefritt:

  • ssbmedier.csv: Her skriver vi filnavnet (og eventuell adresse) på datafila som skal leses.
  • index_col = 0: (Attributtet "index_col" får verdien 0.) Her forteller vi Python at første kolonne skal være overskrifter.
  • skiprows = (0, 1): Dette betyr at de to første radene (rad nummer 0 og 1) i fila skal hoppes over. (Husk at Python begynner nummerering på null.)

  • Vi deler opp den lange kodelinja med tegnet \. Husk at neste linje må rykkes inn.
  • sep = ";": Dersom vi ser på datafila med en enkel editor, som for eksempel Notisblokk i Windows, vil vi finne at alle dataene er atskilt fra hverandre med semikolon. Dette attributtet forteller programmet at semikolon betyr "nytt tall".
  • na_values = [".", ".."]: Her lister vi opp det som skal betraktes som manglende data eller "ikke-data". Akkurat i denne fila er manglende data symbolisert ved enten ett eller to punktum, men hvilke(t) tegn som brukes, kan variere fra fil til fil. Attributtet na_values sørger for at tegnet blir oversatt til "NaN", eller "Not_a_Number" i den interne datatabellen. Da skaper det ikke mer krøll under den videre behandlingen.

  • encoding = "latin-1": Dersom vi legger til dette attributtet, tar programmet høyde for at datafila inneholder skandinaviske bokstaver som æ, ø eller å. Dersom dette ikke fungerer, prøv verdien "utf-8".

Til slutt skriver vi ut innholdet av variabelen data på vanlig måte med kommandoen print(data).

Nye kolonneoverskrifter

Dessverre blir utskriften dårlig lesbar på grunn av de lange kolonneoverskriftene ("Minutter brukt til ..."). Vi skal erstatte overskriftene med bare årstallene. Det gjør vi ved å lage ei liste med årstallene og rett og slett skrive inn de nye kolonneoverskriftene i variabelen data. Siden vi har statistiske data for hvert år, kan vi bruke ei for-løkke til å generere lista, som vi her har kalt K for kolonneoverskrift:

Python

1K = []   # lager ei tom liste K 
2startVerdi = 2010   # lager variabel for det første årstallet
3for i in range(0, 10):
4    K.append(startVerdi + i)
5data.columns = K  # setter radoverskriftene i variabelen data lik innholdet av lista K 

Kopier inn koden over rett før print()-kommandoen. Det kan hende at det blir med noe "rusk" under kopieringen; dette må i så fall fjernes i programmeringseditoren. I for-løkka blir det for hver runde lagt til et tall i lista K som er 1 større enn det forrige. I programkoden har vi lagt til kommentarer ved hjelp av tegnet #.

Får du en mer lesbar tabell nå, dersom du legger til koden over før du skriver ut innholdet av variabelen data? Det kan hende at du bare får utskrift av tallene for de 3–4 første årene og for de 3–4 siste. Det er helt normalt. Nå er dataene klar for mer behandling!

Statistiske data med kommandoen describe()

Modulen Pandas har en egen kommando for å hente ut ulike statistiske mål i en dataframe. Vi skriver følgende:

Python

1print(data.describe())

Resultatet skal ligne på tabellen nedenfor, der vi bare har skrevet inn tallene for året 2010:

Statistiske data med describe()

2010

count

11.000000

mean

44.727273

std

48.203923

min

1.000000

25%

8.000000

50%

23.000000

75%

78.500000

max

152.000000

Forklaring på dataene

count
tallet på observasjoner vi har i kolonnen
mean
gjennomsnittet av verdiene i kolonnen
std
standardavviket i kolonnen. Standardavviket forteller oss noe om hvor stor spredning det er i datamaterialet. Hvis standardavviket er stort, betyr det at vi har mange observasjoner som ligger langt unna gjennomsnittet. Hvis vi beveger oss ett helt standardavvik opp og ned fra gjennomsnittsverdien, skal vi ha cirka 2/3 av alle observasjonene i dette intervallet.
min
minimumsverdien i kolonnen
25%
den nederste kvartilen. Dette tallet forteller oss at 25 prosent av observasjonene i kolonnen er lavere enn denne verdien.
50%
medianen. Dette tallet forteller oss at 50 prosent av observasjonene i kolonnen er lavere enn denne verdien.
75%
den øverste kvartilen. Dette tallet forteller oss at 75 prosent av observasjonene i kolonnen er lavere enn denne verdien (og at 25 prosent av observasjonene er høyere enn denne verdien).
max
maksimumsverdien i kolonnen

Grafisk framstilling av dataene

Modulen Pandas har sin egen plot()-kommando, som i sin tur bruker modulen Pyplot sin plot()-kommando (se teorisiden "Tegne grafer med Pyplot") til å lage framstillingen.

Vi ønsker å lage en framstilling der vi ser hvordan utviklingen har gått for de ulike medietypene. Det betyr at vi ønsker å ha årstallene på x-aksen.

Vi skriver følgende for å lage et linjediagram av tallene i variabelen data:

Python

1data.plot()

Da får vi følgende grafiske framstilling:

Eksempel på feil ved grafisk framstilling. På x-aksen skulle det ha vært årstall, men i stedet er det medietypene som er plassert der. Hver dataserie skulle ha vært en medietype, ikke årstall som her. Skjermutklipp.
Tips om du ikke får fram den grafiske framstillingen

Dersom du bruker Jupyter Notebook:

  • Skriv %matplotlib inline øverst i koden.

Dersom du bruker Spyder:

  • Grafiske framstillinger dukker opp i vinduet "Object", som ofte ligger over konsollvinduet til høyre ved standard oppsett i Spyder. Objektvinduet viser som standard hjelpeinformasjon, du må derfor velge "Plots" nederst i dette vinduet.

Generelt tips:

  • Skriv import matplotlib.pyplot as plt øverst i koden.
  • Skriv plt.show() nederst i koden.

Spørsmål

Hva viser denne grafiske framstillingen?

Forklaring

Vi har fått en grafisk framstilling som viser hvordan brukstallene for de ulike mediene varierer innenfor det enkelte året. For eksempel starter året 2019 med tallet 9 for papiravis, fortsetter med tallet 78 for fjernsyn og så videre. (Vi kunne iallfall ha lest denne informasjonen ut av framstillingen om de enkelte medietypene hadde stått oppført langs x-aksen.)

Vi ønsker oss uansett det motsatte, nemlig å se for eksempel at tallene for papiravis starter på 23 i 2010 og øker til 24 i 2011. Problemet er at årstall ikke ble oppfattet som den kategorien som skal være på x-aksen.

Framstillingen blir feil. Vi kan fikse dette ved å bytte om på rader og kolonner i variabelen data før vi lager plottet. Dette gjør vi med kommandoen transpose, se linje 1 i koden nedenfor. I tillegg ønsker vi å unngå at forklaringen kommer oppå grafikkfeltet. Det fikser vi ved å legge til metoden "legend" til plot()-kommandoen og la attributtet "bbox_to_anchor" få verdien "(1, 1)", som er koordinatene til øvre høyre hjørne av plottet. Se linje 2.

Python

1data = data.transpose()
2data.plot().legend(bbox_to_anchor = (1, 1))

Da får vi følgende figur:

Linjediagram som viser bruken av ulike medier, for eksempel papiraviser, fjernsyn og internett, i årene 2010 til 2019. Skjermutklipp.

Nå ble framstillingen riktig fordi vi kjørte kommandoen "transpose()" som transponerer tabellen ved å la radene bli kolonner. Forklaringen la seg dessuten pent på sida av grafikkfeltet.

Spørsmål

Hvorfor slutter grafen for hjemme-pc i året 2014, mens grafen for digitale spill starter i 2016?

Forklaring

Grafene er avkortet når det ikke er tall for årene før eller etter i tabellen. På den originale nettsiden til SSB er det brukt ett eller to punktum for å markere år uten tall.

Plotting av enkeltkolonner

Dersom vi ønsker å sammenlikne bruken av fjernsyn med bruken av internett, kan vi lage en ny dataframe som bare inneholder de kolonnene vi er interessert i. Først velger vi ut radene med fjernsyn og internett fra dataframe-variabelen data, så legger vi dem i en ny dataframe, og til slutt transponerer vi denne. Vi skriver følgende:

Python

1utdrag = data.iloc[[1, 10]]
2utdrag = utdrag.transpose()
3utdrag.plot().legend(bbox_to_anchor = (1, 1))

Her lager vi en ny dataframe-variabel utdrag som bare inneholder rad nummer 1 og 10 fra variabelen data, ved hjelp av metoden iloc.

NB: Husk å fjerne tidligere kommandolinjer med "transpose()" fra programmet ditt når du setter inn linjene over.

Bruken av fjernsyn og internett i antall minutter per dag i årene 2010 til 2019, framstilt som linjediagram. Internettbruken øker kraftig, mens fjernsynsbruken synker tilsvarende. Skjermutklipp.

Spørsmål

Hvorfor brukte vi tallene 1 og 10 i argumentet til metoden iloc?

Svar

Tallene for fjernsyn står i andre rad, og tallene for internett står i 11. rad. Siden radnummereringen begynner på 0, må vi bruke 1 og 10 i kommandoen for å få vist disse radene.

Spørsmål

Er det tilfeldig at tallene 1 og 10 er argumenter både i range-funksjonen i for-løkka og i kommandoen med iloc?

Svar

Ja. Hvis vi velger andre medietyper enn fjernsyn og internett, påvirker ikke dette for-løkka, men i iloc-kommandoen må vi sette inn nye tall.

Forbedring av den grafiske framstillingen

Vi kan forbedre den grafiske framstillingen ved hjelp av modulen Pyplot.

Spørsmål

Hva kan vi gjøre dersom vi vil forbedre den grafiske framstillingen av tallene for fjernsyn og internett? Hva mangler i figuren?

Forslag
  • Det mangler aksetitler.
  • Det kunne vært en tittel på diagrammet øverst.
  • Statistisk sentralbyrå har gitt dataene sine til allmennheten, men på det vilkåret at de blir oppgitt som kilde. Det bør altså stå "Kilde: Statistisk sentralbyrå" ett eller annet sted på framstillingen.
  • Det kunne ha vært rutenett, for da blir det lettere å lese verdier ut av diagrammet.

Oppgave

Kommandoene nedenfor endrer den grafiske framstillingen i samsvar med forslaget vårt. Skriv dem inn og observer hva som skjer. Prøv å forklare hva hver av de fire kommandoene gjør.

Skriv øverst i programkoden (om du ikke alt har gjort det):

Python

1import matplotlib.pyplot as plt 

Dette må du gjøre for å kunne bruke funksjonene i Pyplot i programmet. Skriv så nederst i programkoden:

Python

1plt.xlabel("Årstall")
2plt.ylabel("Minutter per dag i gjennomsnitt")
3plt.title("Tid brukt på fjernsyn og internett 2010–2019")
4plt.suptitle("Kilde: Statistisk sentralbyrå", fontsize = 8)
5plt.grid(True)
Løsningsforslag
Bruken av fjernsyn og internett i årene 2010 til 2019, framstilt som linjediagram. Figuren har tittelen "Årstall" på x-aksen og tittelen "Minutter per dag i gjennomsnitt" på y-aksen. Hovedtittelen er "Tid brukt på fjernsyn og internett 2010–2019", og over tittelen står det at kilden er Statistisk sentralbyrå.  Skjermutklipp.
  • plt.xlabel lager tittel på x-aksen.

  • plt.ylabel lager tittel på y-aksen.

  • plt.title lager tittelen over diagrammet.

  • plt.suptitle lager kildehenvisningen til Statistisk sentralbyrå og plasserer den over hovedtittelen. (Det er mulig du må justere fontstørrelsen til noe annet enn 8.)

  • plt.grid slår på rutenett.

Det kan hende du må skrive plt.show() for å få fram figuren.

Se også teorisiden "Tegne grafer med Pyplot" om du vil lese mer om forbedret framstilling av grafer.

Filer

Oppgave

Lag en tilsvarende grafisk framstilling der du sammenlikner to eller tre ulike medietyper som du kan tenke deg å studere nærmere.

CC BY-SASkrevet av Tove Annette Holter og Bjarne Skurdal.
Sist faglig oppdatert 01.06.2022

Læringsressurser

Grafer og store datasett i Python