Vi har fram til nå bearbeidet forholdsvis små datasett. Det vil si, data som vi kan også enkelt behandle med excel eller lignende verktøy. R kan være nyttig å behandle sånne datasett, men noen ganger er det lettere å kombinere R med excel. Bruk av excel til å formattere og R til å plotte og kjøre regresjon, for eksempel.

Men når vi har en stor datasett, blir det fort for kompleks å bruke excel til databehandling og analyse. Det er da at R er virkelig nyttig som data-behandlingsverktøy.

##Split-Apply-Combine

Vi har allerede brukt noen av pakkene i det som kalles “The Tidyverse”. Dette inkluderer ggplot2, reshape2, tidyr, og dplyr.

I denne labben skal vi fokusere på å bruke funksjonene i dplyr til å behandle og analysere en stor datasett.

Vi begynner med å laste inn pakkene vi skal bruke:

library(tidyverse)
## ── Attaching packages ───────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.0     ✓ purrr   0.3.4
## ✓ tibble  3.0.3     ✓ dplyr   0.8.5
## ✓ tidyr   1.1.0     ✓ stringr 1.4.0
## ✓ readr   1.3.1     ✓ forcats 0.5.0
## ── Conflicts ──────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
library(zoo)
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric

Fra databasen Proff Forvalt, som BI studenter har tilgang til, har jeg laget en regnskapsdatasett over alle norsk organisasjoner med flere enn 20 ansatte mellom 2009-2015. Du kan laste dette ned fra kurswebsiden:

adf  =   read_csv("https://jmaurit.github.io/anvendt_macro/data/adf.csv")
str(adf)

Her ser vi at datafilen har ca. 130.000 observasjoner og 47 variabler.

Vi begynner med å omformattere litt: vi vil sette dato-variablen til Date formatt, og NACE_bransjekode fra tall til character, eller tekst.

adf["stift_dato"] = as.Date(adf$stift_dato)
adf["NACE_bransjekode"] = as.character(adf$NACE_bransjekode)

NACE er den europeiske standarden for næringsbransjer. Koden består av to deler. En hovedbransje og en delbransje. For eksempel. En kode 6.2 har en hovedbransje 6: olje og gass-utvinning. Mens 6.2 er utvinning av naturgass.

Vi kan laste inn data om definisjonene til disse kodene

NACE_def = read_csv2("https://jmaurit.github.io/anvendt_macro/data/NACE_koder.csv")
## Using ',' as decimal and '.' as grouping mark. Use read_delim() for more control.
## Parsed with column specification:
## cols(
##   code = col_character(),
##   parentCode = col_character(),
##   level = col_double(),
##   name = col_character(),
##   shortName = col_character(),
##   notes = col_character(),
##   validFrom = col_character(),
##   validTo = col_character()
## )
## Warning: 1811 parsing failures.
## row col  expected    actual                                                          file
##   1  -- 8 columns 6 columns 'https://jmaurit.github.io/anvendt_macro/data/NACE_koder.csv'
##   2  -- 8 columns 6 columns 'https://jmaurit.github.io/anvendt_macro/data/NACE_koder.csv'
##   3  -- 8 columns 6 columns 'https://jmaurit.github.io/anvendt_macro/data/NACE_koder.csv'
##   4  -- 8 columns 6 columns 'https://jmaurit.github.io/anvendt_macro/data/NACE_koder.csv'
##   5  -- 8 columns 6 columns 'https://jmaurit.github.io/anvendt_macro/data/NACE_koder.csv'
## ... ... ......... ......... .............................................................
## See problems(...) for more details.

Spørsmål: hvorfor har jeg brukt read_csv() når jeg importerer regnskapsdata, men brukt read_csv2() når jeg importerer NACE-kodene?

Vi kan deretter søke opp koder

filter(NACE_def, code=="70")
## # A tibble: 1 x 8
##   code  parentCode level name       shortName     notes        validFrom validTo
##   <chr> <chr>      <dbl> <chr>      <chr>         <chr>        <chr>     <chr>  
## 1 70    M              2 "Hovedkon… "Hovedkontor… "Inkluderer… <NA>      <NA>
filter(NACE_def, code=="06")
## # A tibble: 1 x 8
##   code  parentCode level name      shortName     notes         validFrom validTo
##   <chr> <chr>      <dbl> <chr>     <chr>         <chr>         <chr>     <chr>  
## 1 06    B              2 "Utvinni… "Utvinning a… "Inkluderer:… <NA>      <NA>

Vi vil splitte variablen NACE_bransjekode inn i to: hovedkode, som vi skal kalle NACE1, og delkode, som vi skal kalle NACE2:

Du kan lese mer om separate koden her.

adf =  separate(adf, NACE_bransjekode, into=c("NACE1", "NACE2"), sep="\\.", remove=FALSE)
## Warning: Expected 2 pieces. Missing pieces filled with `NA` in 278 rows [134,
## 135, 136, 137, 138, 139, 140, 1191, 1192, 1193, 1194, 1195, 1196, 1197, 5482,
## 5483, 5484, 5485, 5486, 5487, ...].

Spørsmål: Hvilken funksjon har parameteren remove=FALSE?

Nå bruker vi filter funksjonen fra dplyr til å kvitte oss med data der NACE1 kode er 0 (NACE kodene begynner med 1), og vi kvitter oss med data fra år 2016 (som mangler).

adf = filter(adf, NACE1!="0") 
adf = filter(adf, year!=2016)

Spørsmål: Hva betyr symbolen !=?

Det betyr “ikke likt” - så her får vi alle radene der år ikke er likt 2016.

Nå er vi klar til å gjøre litt analyse. Det som vi ofte vil gjøre med en stor datasett er å gruppere dataene og beregne tall basert på en sum eller gjennomsnitt i disse gruppene.

For eksempel, kanskje vi vil beregne summen av inntekter, resultat, kostnader og lønnskostnader for alle hovednæringer (NACE1) og alle år.

For å gjøre dette bruker vi verktøy fra tidyverse som går under kategorien Split-apply-combine.

Vi skal bruke kommandoene group_by og summarise som du kan lese mer om her.

sum_adf = adf %>% group_by(NACE1, year) %>% 
  summarise(
    total_inntekt = sum(driftsinnt, na.rm=TRUE),
    total_res = sum(driftsres, na.rm=TRUE),
    total_kost = sum(driftskostn, na.rm=TRUE),
    total_loenn = sum(loennskostnader, na.rm=TRUE)
  )

Jeg bruker her %>% som heter pipe og som du kan lese mer om her.

Dette virker kanskje litt kompleks, men det er egentlig ganske elegant. Vi leser koden fra venstre til høyre.

Vi kan se resultatene:

sum_adf
## # A tibble: 574 x 6
## # Groups:   NACE1 [82]
##    NACE1  year total_inntekt total_res total_kost total_loenn
##    <chr> <dbl>         <dbl>     <dbl>      <dbl>       <dbl>
##  1 1      2009       1862945     20103    1842846     1209569
##  2 1      2010       2048207      5557    2042654     1332220
##  3 1      2011       2296387     -2771    2299162     1540094
##  4 1      2012       2473189     74522    2398665     1618666
##  5 1      2013       2614102     31576    2582531     1758487
##  6 1      2014       2706554     51567    2654988     1776993
##  7 1      2015       2874254     40825    2833426     1883250
##  8 10     2009     105911476   4696117  101215367    15539230
##  9 10     2010     109992232   6070385  103921844    15452894
## 10 10     2011     116709503   5814757  110894746    15972804
## # … with 564 more rows

La oss først se kun på data fra 2015:

sum_adf15 = filter(sum_adf, year==2015)

Her sorterer vi NACE1 kategoriene (“factors”) etter total inntekt:

sum_adf15["NACE1"] = fct_reorder(sum_adf15$NACE1, sum_adf15$total_inntekt, .desc=TRUE)

Nå kan vi omformattere dette til long formatt, ved å bruke pivot_longer

sum_adf15_long = sum_adf15 %>% pivot_longer(-c("NACE1", "year"), names_to="variabel", values_to="sum")

Vi begynner med å kun se på inntekt

inntekt15 =filter(sum_adf15_long, variabel == "total_inntekt")

Vi kan plotte en barplott over total inntektene av alle industriene i norge. Legg merke til bruken av reorder() funksjonen for å sortere industriene fra høyest til lavest inntekt.

ggplot(inntekt15, aes(x= NACE1, y=sum)) + 
  geom_col()

Nå kan vi plotte inntekt sammen med resultat, kostander og lønnskostnader.

ggplot(sum_adf15_long, aes(x=NACE1, y=sum)) + 
  geom_col() +
  facet_wrap(~variabel, ncol=2, scales="free")

Det vi ser er at industrien med den høyeste inntekt, ikke nødvendigvis er industrien med den høyeste resultatet.

La oss se litt nærmere på de 10 industriene i norge med høyeste inntekt. Her bruker jeg arange.

topp10_inntekt = arrange(inntekt15, desc(sum))$NACE1[1:10]
print(topp10_inntekt)
##  [1] 70 6  46 65 47 64 10 84 86 45
## 82 Levels: 70 6 46 65 47 64 10 84 86 45 41 35 9 20 24 30 52 43 62 71 61 ... 19

Vi ville kanskje tenkt at olje og gass ville være #1, men fra: http://stabas.ssb.no/virksomheter-foretak-og-regnskap/_attachment/85513?_ts=13b2d1c7168, kan vi se at # er 70: hovedkontortjenester, administrativ rådgivning. Den største industrien i norge er ikke olje og gass, men kontorarbeid!

La oss se på hvem tjente mest profitt:

resultat15 = filter(sum_adf15_long, variabel == "total_res" )

topp4_res = arrange(resultat15, desc(sum))$NACE1[1:4]
print(topp4_res)
## [1] 64 70 35 6 
## 82 Levels: 70 6 46 65 47 64 10 84 86 45 41 35 9 20 24 30 52 43 62 71 61 ... 19

Her ser vi at det er faktisk 64, finansieringsvirksomhet som tjener mest. Kontorarbeid er på andre plass, og på tredje plass er Elektrisitets-, Gass, Damp, og varmtvannsforsyning. Olje og gass er på fjerde plass.

Nå, la oss ta disse fire industriene og se på utviklingen i resultat over tid:

topp4_data <- filter(sum_adf, NACE1 %in% topp4_res)

ggplot(topp4_data, aes(x=year, y=total_res)) +
  geom_line() +
  facet_wrap(~NACE1, scales="free")

Det ser ut som det har vært gode tider for finansverden de siste årene. Hvorfor det?

Noen ganger vil man ikke aggregere dataene men heller skape nye variabler innenfor hver gruppering. I så fall kan vi bruke funksjonen mutate isteden for summarize.

La oss si at vi vil lage en lønnsomhetsrangering innen hver hovednæring. Da kan vi bruke mutate sammen med funksjonen min_rank og desc(“descending”: det vil si at 1 er høyest"),

adf_new = adf %>% group_by(NACE1, year) %>% 
  mutate(
    rangering = min_rank(desc(driftsres))
  )

Nå kan vi filtrer ut de mest lønnsomme bedriftene i år 2015:

mest_lønnsomt = filter(adf_new, rangering==1 & year == 2015)
print(c(mest_lønnsomt["navn"], mest_lønnsomt["NACE_beskrivelse"]))

##Oppgaver