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 skal lage en ny objekt med vår aggregert data, sum_adf.
Vi begynner med vår dataframe, adf. %>% kan tolkes som at vi sender vår dataframe til funksjonen group_by.
I group_by funksjonen, spesifiserer vi at vi vil gruppere med variablene NACE1 og year så at vi får en gruppe for hver NACE1 hovedkode per år.
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
Filtrer ut alle data fra olje- og gassselskaper.
Beregn totale inntekter, resultat og likviditet per år.
Bruk ggplot til å plotte disse resultatene.
Gjør samme analyse, men se på delkodene, oljeutvinning: 6.1 og gassutvinning 6.2, hver for seg.