Spørre mot to ulike Statistikkbanktabeller og vise resultatet i en figur

#!/usr/bin/env python
# coding: utf-8

#  Kombinere tall fra to tabeller i Statistikkbanken i en figur.
#  Eksempel Konsumprisindeksen: KPI og KPI-JAE
#  Kombinerer data fra Statistikkbanktabellene 03013 (KPI) og 05327 (KPI-JA)
# Dette ligger også som Jupyter notebook på https://github.com/janbrus/ssb-api-python-examples
# Her importerer vi ulike pakker

import matplotlib.pyplot as plt
import requests
import pandas as pd
from pyjstat import pyjstat  # pyjstat er for behandling av JSON-stat


### start funksjoner ###
#  Funksjon for å konvertere til datoformat og sette dette som en Pandas PeriodIndex,
#  Returnerer i tillegg navnet på frekvens-kolonnen


def dateConv(dataframe):
    frekvens, frek_no, frek_en, fmt = findFrequency(dataframe)
    setPeriodIndex(frekvens, frek_no, frek_en, fmt, dataframe)

    #funksjon for for å finne frekvenskolonne
def findFrequency(dataframe):
    frekvenser = ['måned', 'kvartal', 'uke', 'år', 'year', 'quarter', 'month', 'week']
    frek_no = ''
    freq_en = ''
    fmt = ''
    for w in frekvenser:
        if w in dataframe.columns:
            if w in ['måned', 'month']:
                frek_no = 'M'
                frek_en = 'M'
                fmt = '%YM%m'
            elif w in ['kvartal', 'quarter']:
                frek_no = 'K'
                frek_en = 'Q'
                fmt = '%YK%q'
            elif w in ['uke', 'week']:
                frek_no = 'U';
                frek_en = 'W'
                fmt = '%YW%W-%w'
            else:
                frek_no = ''
                frek_en = 'A'
            return w, frek_no, frek_en, fmt

# funksjon for å sette index som PeriodIndex,
def setPeriodIndex(frekvens, frek_no, freq_en, fmt, df):
    if frekvens in ['kvartal', 'quarter']:
        # erstatter K med Q, konverterr til datoformat og setter frekensen til Pandas PeriodIndex
        df.index = pd.PeriodIndex(pd.to_datetime(df[frekvens].str.replace(frek_no, freq_en), errors='coerce'), freq='Q-DEC')
    elif frekvens in ['uke', 'week']:
        df.index = pd.PeriodIndex(pd.to_datetime(df[frekvens].str.replace(frek_no, freq_en).add('-0'), format= fmt, errors='coerce'), freq='W-MON')
    else:
        df.index =  pd.PeriodIndex(pd.to_datetime(df[frekvens], format= fmt, errors='coerce'), freq=freq_en)
    return frekvens

### slutt funksjoner ###

# URL til tabellenes metadata i PxWebApi, som vi skal poste spørringene mot

URL1 = 'https://data.ssb.no/api/v0/no/table/05327' # KPI-jae
URL2 = 'https://data.ssb.no/api/v0/no/table/03013' # KPI total

# API-spørring i JSON mot tabell 05327 - siste 5 år

sp1 = {
  "query": [
    {
      "code": "Konsumgrp",
      "selection": {
        "filter": "item",
        "values": ['JA_TOTAL', 'JAE_TOTAL', 'JE_TOTAL', 'JEL_TOTAL']
      }
    },
    {
      "code": "ContentsCode",
      "selection": {
        "filter": "item",
        "values": ["KPIJustIndMnd"]
      }
    },
    {
      "code": "Tid",
      "selection": {
        "filter": "top",
        "values": [ "60" ]
      }
    }
  ],
  "response": {
    "format": "json-stat2"
  }
}

# JSON-spørring mot tabell 03013 siste 5 år

sp2 = {
  "query": [
    {
      "code": "Konsumgrp",
      "selection": {
        "filter": "item",
        "values": ["TOTAL"]
      }
    },
    {
      "code": "ContentsCode",
      "selection": {
        "filter": "item",
        "values": ["KpiIndMnd"]
      }
    },
    {
      "code": "Tid",
      "selection": {
        "filter": "top",
        "values": ["60"]
      }
    }
  ],
  "response": {
    "format": "json-stat2"
  }
}


# Poster spørringene sp1 og sp2 mot metadatas url'er. Resultatene lagres som res1 og res2
res1 = requests.post(URL1, json=sp1)
res2 = requests.post(URL2, json=sp2)

# Leser resultatet med JSON-stat biblioteket pyjstat
ds1 = pyjstat.Dataset.read(res1.text)
ds2 = pyjstat.Dataset.read(res2.text)

# Skriver dette til to Pandas dataframes, df1 og df2.
df1 = ds1.write('dataframe')
df2 = ds2.write('dataframe')

df1.head(7)

df1.tail() # slutten av datasettet

# Kaller funksjonen for datokonvertering for df1. *Kan sløyfes*.
dateConv(df1)

df1.index

df1.info()

df1.tail(15)

dateConv(df2) # kan sløyfes eller kommenteres ut.

# Plot av df2 blir riktig, for her er det bare en serie
df2.plot()


# Standard plot av df1 gir alle 4 intervallene. Resultat er avhengig av om det er dato eller kategori på x-aksen.
df1.plot() # standard plot gir alle 4 intervallene og resultat er avhengig av om det er dato eller kategori på x-aksen

# ### slår sammen de to "dataframene"  df1 og df2 med en enkel concat til datasettet "sammen"
sammen = pd.concat([df1,df2])

# Viser topp og bunn for det sammeslåtte datasettet
sammen.head()
sammen.tail()

# ### Omstrukturerer (pivoterer) tabellen for å få en bedre visning
#
# ["tidy data" Hadley Wickham](https://vita.had.co.nz/papers/tidy-data.pdf)
#
df3 = sammen.pivot(columns='konsumgruppe', values='value')

# uten kall til dateconv() bruk i stedet måned som index slik:
# df3 = sammen.pivot(index='måned', columns='konsumgruppe', values='value')

df3.head(3)
df3.index
df3.tail(3)

# ### Figur med Pandas innebygde plot-funksjon
df3.plot()

# Plot med flere parametre
df3.plot(marker="o", markersize=3, figsize=(12, 8))

# Lagrer figurvisningen som en funksjon. Her er også SSBfarger definert. 
# Du kan gi høyde og bredde som parametre. Her bruker vi Matplotlib og ikke Pandas plot.
def visfigur(bredde=12, hoyde=6):
    fig, ax = plt.subplots(figsize=(bredde, hoyde))
    #definerer ssb farger på figurene
    ssbCol = ['#1a9d49', '#075745', '#1d9de2', '#0f2080', '#c78800', '#471f00', '#c775a7', '#a3136c', '#909090', '#000000']
    plt.xlabel('måned')
    plt.ylabel('index')
    ax.set_title('Figur som viser KPI total og undeliggende KPI serier')
    df3.plot(ax=ax, color=ssbCol)
    plt.show()

visfigur()

# ### Plot med ulike stiler
# I Matplotlib kan du prøve ut ulike ferdige stiler. Vi kan evt. lage vår egen etter SSBs designmal.
#
# Prøv ut: 'Solarize_Light2',  'bmh', 'classic', 'dark_background' 'fivethirtyeight', 'seaborn-talk'
with plt.style.context('Solarize_Light2'):
    visfigur()

with plt.style.context('fivethirtyeight'):
    visfigur(16, 8)