Jobbe med data

Når vi oppretter et dapla-team så får vi tildelt et eget området for lagring av data. For å kunne lese og skrive data fra Jupyter til disse områdene må vi autentisere oss, siden Jupyter og lagringsområdet er to separate sikkerhetsoner.

Figur 1 viser dette klarer skillet mellom hvor vi koder og hvor dataene ligger på Dapla1. I dette kapitlet beskriver vi nærmere hvordan du kan jobbe med dataene dine på Dapla.

Bilde som viser forskjellen mellom kodemiljø og hvor data lagres.
Figur 1: Tydelig skille mellom kodemiljø og datalagring på Dapla.

SSB-biblioteker

For å gjøre det enklere å jobbe data på tvers av Jupyter og lagringsområdet er det laget noen egne SSB-utviklede biblioteker for å gjøre vanlige operasjoner mot lagringsområdet. Siden både R og Python skal brukes på Dapla, så er det laget to biblioteker, en for hver av disse språkene. fellesr er biblioteket for R, og dapla-toolbelt er biblioteket for Python.

dapla-toolbelt

dapla-toolbelt er en en pakke som lar deg enkelt lese og skrive til lagringsområdet uten å måtte autentifisere deg manuelt. Den har en Pandas-aktig syntaks som forhåpentlig er gjenkjennbar for de fleste. Pakken er installert i alle Python-kernels på Dapla, så du trenger ikke å installere den selv hvis du åpner en notebook med Python3 for eksempel. For å importere hele biblioteket i en notebook skriver du bare

notebook
import dapla as dp

dapla-toolbelt bruker en pakke som heter gcsfs for å kommunisere med lagringsområdet. gcsfs er en pakke som lar deg bruke Google Cloud Storage (GCS) som om det var en filsystem. Det betyr at du kan bruke samme syntaks som du bruker for å lese og skrive til filer på din egen maskin. Du kan lese mulighetene i gcsfs her. Et eksempel på hvordan de to pakkene kan brukes sammen ser du her:

notebook
from dapla import FileClient
fs = FileClient.get_gcs_file_system()

# Example of how you can use gcsfs and dapla-toolbelt together
fs.touch("gs://my-bucket/my-folder/")

I koden over brukte jeg kommandoen touch fra gcsfs og FileClient fra dapla-toolbelt for å opprette en mappe i lagringsområdet.

I kapitlene under finner du konkrete eksempler på hvordan du kan bruke dapla-toolbelt til å jobbe med data i SSBs lagringsområdet.

fellesr

R-pakken fellesr er under utvikling og gir mye av den samme funksjonaliteten som dapla-toolbelt gir for Python. I tillegg til å kunne lese og skrive til lagringsområdet på Dapla, så har fellesr også funksjoner for å jobbe med metadata på Dapla.

fellesr er installert på Dapla og funksjoner kan benyttes ved:

notebook
library(fellesr)

Hvis du benytte en renv miljø, må pakken installeres en gang. Dette kan gjøres ved:

notebook
renv::install("statisticsnorway/fellesr")

Vanlige operasjoner

I denne delen viser vi hvordan man gjør veldig vanlige operasjoner når man koder et produksonsløp for en statistikk. Flere eksempler på nyttige systemkommandoer finner du her.

Liste ut innhold i mappe

Eksempeldata

Det finnes et område som alle SSB-ansatte har lese- og skrivetilgang til. Det er
gs://ssb-dapla-felles-data-delt-prod/ i prod-miljøet på Dapla, og
gs://ssb-dapla-felles-data-delt-test/ i staging-miljøet. Eksemplene under bruker førstnevnte i koden, slik at alle kan kjøre koden selv.

Kode-eksemplene finnes for både R og Python, og du kan velge hvilken du skal se ved å trykke på den arkfanen du er interessert i.

Å liste ut innhold i et gitt mappe på Dapla er ganske enkelt. Under ser du hvordan du kan liste ut innholdet i følgende mappe:

gs://ssb-dapla-felles-data-delt-prod/felles/veiledning/python/eksempler/purchases

Vi bruker modulen FileClient fra dapla-toolbelt for å liste ut innholdet i en mappe.

notebook
from dapla import FileClient

# Set path to folder
bucket = "gs://ssb-dapla-felles-data-delt-prod"
folder = "felles/veiledning/python/eksempler/purchases"

FileClient.ls(f"{bucket}/{folder}")

Med kommandoen over får du listet ut alle filnavn i mappen. Hvis du vil ha mer informasjon om filene så kan du bruke ls-kommandoen med detail = True, som under:

notebook
FileClient.ls(f"{bucket}/{folder}", detail = True)

Syntaksen med ls er veldig lik det man kjenner fra en Linux-terminal. Men når vi bruker detail = True blir metadata om filene returnert som en Python-liste med dictionaries. Det kan være svært nyttig når du f.eks. trenger å vite dato og tidspunkt for når en fil ble opprettet, eller når den sist ble oppdatert.

notebook
# Loading functions into notebook
library(fellesr)

# Path to folder
bucket <- "ssb-dapla-felles-data-delt-prod/"
folder <- "felles/veiledning/python/eksempler/purchases"

# List files in folder 
list.files(paste0(bucket, folder))

Merknad: Når du spesifisere bøtter i R, trenger du ikke “gs://” foran.

Skrive ut filer

Å skrive filer til et lagringsområde på Dapla er også ganske enkelt. Det ligner mye på den syntaksen vi er kjent med fra vanlige R- og Python-pakker, med noen små unntak.

Parquet

Under lager vi en dataframe i en notebook og skriver den ut til en parquet-fil.

Når vi leser en Parquet-fil med dapla-toolbelt så bruker den pyarrow i bakgrunnen. Dette er en av de raskeste måtene å lese og skrive Parquet-filer på.

notebook
import dapla as dp
import pandas as pd
import numpy as np

# Set path to folder
bucket = "gs://ssb-dapla-felles-data-delt-prod"
folder = "felles/veiledning/python/eksempler/purchases"

# Create pandas dataframe
purchases = pd.DataFrame(np.random.randn(10, 5),
                        columns=["A", "B", "C", "D", "E"])

# Write pandas dataframe as parquet to GCS bucket
dp.write_pandas(df = purchases,
                gcs_path = f"{bucket}/{folder}/data.parquet",
                file_format = "parquet",)

Når vi kalte write_pandas over så spesifiserte vi at filformatet skulle være parquet. Dette er default, så vi kunne også ha skrevet det slik:

notebook
dp.write_pandas(df = purchases,
                gcs_path = f"{bucket}/{folder}/data.parquet")

Men for de andre filformatene må vi altså spesifisere dette.

Når vi jobber med Parquet-fil i R, bruker vi pakken arrow. Dette er en del av fellesr pakken så du trenger kun å kalle inn dette. Pakken inneholder funksjonen write_SSB som kan brukes til å skrive data til bøtte på Dapla.

notebook
library(fellesr)

# Set stien til hvor data skal lagres
bucket <- "ssb-dapla-felles-data-delt-prod"
folder <- "felles/veiledning/r/eksempler/purchases"

# Lage en eksempel dataframe
purchases = data.frame(A = runif(10), B= runif(10), C=runif(10))

# Skrive data til bøttet som en parquet
write_SSB(purchases, file.path(bucket, folder, "purchases.parquet"))

Merknad: Når du spesifisere bøtter i R, trenger du ikke “gs://” foran.

Tekstfiler

Noen ganger ønsker vi å lagre data i andre formatter slik som CSV, JSON og XML.

dapla-toolbelt kan skrive ut json, csv og posisjonsfiler (fixed-width-files/fwf) til lagringsområdet. Måten den gjør det på er å bruke Pandas sine funksjoner read_json, read_csv, read_fwf under panseret. Dette kan være nyttig å vite for skjønne hvordan dapla-toolbelt håndterer ulike strukturer i (spesielt hierarkiske) tekstfiler. Under ser du hvordan du kan skrive ut en dataframe til en json-fil.

notebook
import numpy as np
import pandas as pd
from dapla import FileClient

# Set path to folder
bucket = "gs://ssb-dapla-felles-data-delt-prod"
folder = "felles/veiledning/python/eksempler/purchases"

# Create a dataframe with Pandas
df = pd.DataFrame(np.random.randn(10, 5), columns=["A", "B", "C", "D", "E"])

# Save dataframe as json with dapla-toolbelt
dp.write_pandas(df = df,
                gcs_path = f"{bucket}/{folder}/test.json",
                file_format = "json")

Som vi ser at syntaksen over så kunne vi skrevet ut til noe annet enn json ved å endre verdien i argumentet file_format.

Pakken fellesr kan også brukes til å skrive andre type filer, for eksempel csv, til bøtter. Dette gjøres med funksjonen write_SSB og spesifisere ønsket filtype i filnavn.

Først kaller vi biblioteket og lage noe test data ved:

notebook
library(fellesr)

# Set stien til hvor data skal lagres
bucket <- "ssb-dapla-felles-data-delt-prod"
folder <- "felles/veiledning/r/eksempler/purchases"

# Lage en eksempel dataframe
purchases = data.frame(A = runif(10), B= runif(10), C=runif(10))

# Skrive til csv
write_SSB(purchases, file.path(bucket, folder, "purchases.csv")

xlsx

Det er ikke anbefalt å bruke xlsx-formatet, men her ser du hvordan det kan skrives ut. Mer kommer.

notebook
import pandas as pd
from dapla import AuthClient

# Henter token for å kunne lese fra Dapla
token = AuthClient.fetch_google_credentials()

# Filsti
bucket = "gs://ssb-dapla-felles-data-delt-prod"
folder = "felles/veiledning/python/eksempler/purchases"

df.to_excel(f"{bucket}/{folder}/test.xlsx",
           storage_options={"token": token})

Kommer snart

Lese inn filer

Under finner du eksempler på hvordan du kan lese inn data til en Jupyter Notebooks på Dapla.

Parquet

notebook
import dapla as dp

# Set path to folder
bucket = "gs://ssb-dapla-felles-data-delt-prod"
folder = "felles/veiledning/python/eksempler/purchases"

# Read path into pandas dataframe 
dp.read_pandas(gcs_path= f"{bucket}/{folder}/data.parquet",
               file_format = "parquet",
               columns = None,)

Som vi så med write_pandas så er file_format default satt til parquet, og default for columns = None, så vi kunne også ha skrevet det slik:

dp.read_pandas(gcs_path= f"{bucket}/{folder}/data.parquet")

columns-argumentet er en liste med kolonnenavn som vi ønsker å lese inn. Hvis vi ikke spesifiserer noen kolonner så vil alle kolonnene leses inn.

Pakken fellesr kan brukes til å lese inn data. Funksjonen read_SSB() kan lese inn filer i flere format inkluderende parquet.

Her er et eksempel av å lese inn parquet fil “1987”.

notebook
library(fellesr)

bucket <- "ssb-dapla-felles-data-delt-prod"
folder <- "R_smoke_test"

dt_1987 <- read_SSB(file.path(bucket, folder, "1987.parquet"))

Vi kan også filtrere hvilke variabel vi ønsker å lese inn ved å spesifisere parameter col_select. For eksempel:

dt_1987 <- read_SSB(file.path(bucket, folder, "1987.parquet"),
                    col_select = c("Year", "Month"))

Innlesning av parquet som er kartdata finner du her: Lese kartdata

Tekstfiler

Kommer mer snart. Python-koden under bygger på eksempelet over.

notebook
import dapla as dp

# Path to write to
bucket = "gs://ssb-dapla-felles-data-delt-prod"
folder = "felles/veiledning/python/eksempler/purchases"

# Read in json-file from dapla-storage
df = dp.read_pandas(gcs_path = f"{bucket}/{folder}/test3.json",
               file_format = "json")

Funksjonen read_SSB() kan lese inn flere type av fil-format, slik som csv og json. Du trenger ikke å endre koden, kun spesifisere hele filnavn.

Først kaller vi inn biblioteket fellesr og spesifisere bøtte/mappen:

notebook
library(fellesr)

# Filsti
bucket <- "ssb-dapla-felles-data-delt-prod"
folder <- "R_smoke_test"

# Lese inn CSV-fil
dt_1987 <- read_SSB(file.path(bucket, folder, "1987.csv"))

For å lese inn en json-fil kan skrive følgende:

notebook
dt_1987 <- read_SSB(file.path(bucket, folder, "1987.json"))

xlsx

notebook
import pandas as pd
from dapla import AuthClient

# Hent token
token = AuthClient.fetch_google_credentials()

# Les inn fil
df = pd.read_excel("gs://ssb-prod-arbmark-skjema-data-produkt/test_gcp.xlsx",
    storage_options={"token": token})

Kommer snart

SAS

Her er et eksempel på hvordan man leser inn en sas7bdat-fil på Dapla som har blitt generert i prodsonen.

notebook
import dapla as dp

sti = "gs://ssb-dapla-felles-data-delt-prod/felles/veiledning/sas/statbank_ledstill.sas7bdat"

dp.read_pandas(sti, file_format="sas7bdat", encoding="latin1")

Siden innlesing av sas7bdat-filer ikke er støttet i fellesr, så kan vi bruke R-pakken reticulate for å benytte oss av funksjonaliteten i Python-pakken dapla-toolbelt.

notebook
library(reticulate)
dp <- import("dapla")
sti  <- gs://ssb-dapla-felles-data-delt-prod/felles/veiledning/sas/statbank_ledstill.sas7bdat"
dp$read_pandas(sti, file_format="sas7bdat", encoding="latin1")

Slette filer

Å slette filer fra lagringsområdet kan gjøres på flere måter. I kapitlet om sletting av data viste vi hvordan man gjør det med pek-og-klikk i Google Cloud Console. Under ser du hvordan du kan slette filer med dapla-toolbelt og gcsfs eller fellesr.

notebook
from dapla import FileClient
fs = FileClient.get_gcs_file_system()

bucket = "gs://ssb-dapla-felles-data-delt-prod"
folder = "felles/veiledning/python/eksempler/purchases"

fs.rm(f"{bucket}/{from_folder}/df.json")

Funksjonen gc_delete_object kan brukes til å slette data på lagringsområdet.

notebook
library(fellesr)

bucket <- "ssb-dapla-felles-data-delt-prod"
folder <- "felles/veiledning/r/eksempler/purchases"

gcs_delete_object(file.path(bucket, folder, "purchases.parquet"))

Kopiere filer

Å kopiere filer mellom mapper på et Linux-filsystem innebærer som regel bruke cp-kommandoen. På Dapla er det ikke så mye forskjell. Vi bruker en ligende tilnærming nå vi skal kopiere mellom bøtter eller mapper på lagringsområdet til SSB. Under ser du hvordan du kan kopiere en fil fra en mappe til en annen.

La oss begynne med et eksempel der vi kopierer en fil fra en mappe til en annen i samme bøtte.

notebook
from dapla import FileClient
fs = FileClient.get_gcs_file_system()

# Path to folders
bucket = "gs://ssb-dapla-felles-data-delt-prod"
from_folder = "felles/veiledning/python/eksempler/purchases"
to_folder = "felles/veiledning/python/eksempler"

# Copy file
fs.cp(f"{bucket}/{from_folder}/data.parquet",
      f"{bucket}/{to_folder}/data_copy.parquet")

Det også fungere for å kopiere filer mellom bøtter.

Et annet scenario vi ofte vil støte på er at vi ønsker å kopiere en fil fra vårt Jupyter-filsystem til en mappe på lagringsområdet. Her kan vi bruke fs.put-metoden.

notebook
from dapla import FileClient
fs = FileClient.get_gcs_file_system()

# Create a new file in your home directory called test.txt
with open('/home/jovyan/test.txt', 'w') as f:
    f.write('Create a new text file!')

#Path to folder
bucket = "gs://ssb-dapla-felles-data-delt-prod"
folder = "felles/veiledning/python/eksempler"

# Copy file from local to remote file system
fs.put(lpath=f"/home/jovyan/test.txt", rpath=f"{bucket}/{folder}/test.txt")

Ønsker vi å kopiere en hel mappe fra lagringsområdet til Jupyter-filsystemet, kan vi bruke fs.get-metoden, med opsjonen recursive=True.

notebook
from dapla import FileClient
fs = FileClient.get_gcs_file_system()

# Copy file
fs.get(<from_bucket>,
      "/home/jovyan/sesongjustering/",
      recursive=True)

Kommer snart

Flytte filer

notebook
from dapla import FileClient
fs = FileClient.get_gcs_file_system()

bucket = "gs://ssb-dapla-felles-data-delt-prod"
from_folder = "felles/veiledning/python/eksempler/purchases"
to_folder = "felles/veiledning/python/eksempler"

fs.mv(f"{bucket}/{from_folder}/data.parquet", f"{bucket}/{to_folder}/data.parquet")

Kommer snart

Opprette mapper

Selv om bøtter ikke har mapper med en hierarkisk struktur slik man er kjent med fra klassike filsystemer, så kan man opprette det som ser ut som mapper i objektnavnet. I realiteten blir bare / oppfattet som en del av navnet på objektet. Skulle du likevel ønske å opprette dette så kan du gjøre det følgende måte:

notebook
from dapla import FileClient
fs = FileClient.get_gcs_file_system()

#Path to folder
bucket = "gs://ssb-dapla-felles-data-delt-prod"
folder = "felles/veiledning/python/eksempler"

# Create folder
fs.touch(f"{bucket}/{folder}/testmappe/")

Kommer snart

Fotnoter

  1. I de tidligere systemene på bakken så var det ikke nødvendig med autentisering mellom kodemiljø og datalagringen↩︎