Før man kan begynde at lave analyser i R er man nødt til at have noget data, at arbejde med. Første skridt er, at vi skal have data ind i programmet - og herefter skal man sikre sig, at data er korrekt formatteret i forhold til de analyser, man ønsker at lave, og at data ikke indeholder fejl.
Første skridt er at importere data fra en fil til R.
De nemmeste formatter at arbejde med er *.csv (comma separated values) og *.txt (ren tekst). Begge disse formatter er ren tekst (plain text) uden formatteringer, hvor den primære forskel er at *.csv bruger tegn (komma eller semikolon) til at adskille værdierne og *.txt bruger mellemrum eller tabulering til at adskille værdierne.
R har en indbygget funktion til at læse disse filer:
read.csv() (Bruges hvis værdier er adskilt med komma
eller semikolon)read.delim() (Bruges hvis værdier er adskilt med
tabulation)Før man importerer data fra csv eller txt er man nødt til at vide noget om formatteringen. R har nogle default indstillinger, som den antager når den indlæser filen. Men hvis filen er formatteret anderledes vil indlæsningen fejle.
Default indstillingen for csv-filer er fx at værdierne er adskilt af kommaer og decimaler er adskilt med punktum (fordi det gør man på engelsk). Men hvis csv-filen er gemt i en dansk version af Excel vil værdierne være afskilt af semikolon og decimaler afskilt med komma. I det tilfælde vil vi være nødt til at fortælle R, hvordan filen er formatteret.
Vi skal derfor undersøge om:
Hvis første linje i filen er navnene på variablene, bliver disse
brugt til at navngive variablene i dataframen (Dette er default
indstillingen). Hvis først linje ikke er navne, men data, skal vi
definere argumentet header = FALSE.
Default indstillingen for separateren (tegnet der adskiller
værdierne) er et komma (,). Hvis værdierne er adskilt med semi-kolon
skal vi definere argumentet sep = ";" – hvis det er
mellemrum sep = " " og hvis det er tabulation
sep = "\t".
R vil per default læse værdien NA som en manglende værdi. Men hvis
manglende værdier er angivet på andre måder skal dette defineres med
argumentet na.strings. Hvis fx manglende værdier er angivet
med punktumer bliver argumentet na.strings = ".".
Default indstillingen for decimal-separateren er punktum. Hvis der
bruges andet, oftest komma, defineres argumentet
dec = ",".
I denne guide bruges et datasæt med simulerede data (hvor nogle af variablene indeholder fejl, som skal rettes). Man se nederst i guiden hvordan de simulerede data er genereret.
# Indlæsning af fil fra et fælles drev
testDataRaw <- read.csv("L:\\AuditData\\testData\\testData.csv")Hvis filen ligger i samme mappe på computen, som R-filen behøver man kun at skrive fil-navnet
Hvis filen ligger i en anden mappe, fx på et fælles drev, skal vi specificere hele fil-stien
# Indlæsning af fil fra et fælles drev
strokedf <- read.csv("L:\\AuditData\\ApoData\\strokedata.csv")Bemærk at der bruges dobbelt back slash til adskillelse af niveauerne. Dette er den mest stabile metode på Windows computere, men det fungerer nogle gange anderledes på andre styresystemer.
Hvis data er tilgængeligt på internettet kan vi bruge http-adressen som filnavn
Indlæsning af plain text formatter er klart det letteste i baseR.
Plaintext filer kan også indlæses med pakken {readr} (som
er en del af Tidyverse), som giver nogle flere muligheder for at
specificere parametre, fx styring af dataformater for hver variabel.
Hvis man har brug for at indlæse andre filformatter findes der også
pakker, som kan håndtere dette. Fx findes pakken {readxl}
til indlæsning af Excel-formatter, som fungerer rimeligt. Ligeledes
findes der også pakker, der kan importere data fra andre
statistikprogrammer, fx SPSS, SAS eller STATA.
Data kan have forskellige typer og det er vigtigt at data har den rigtige type, da det har betydning for hvordan data håndteres. Mange af de fejl man får i R handler om, at data har det forkerte format i forhold til den funktion man bruger.
De basale typer er:
I de fleste tilfælde kan R godt genkende den korrekte type, men ellers kan man ‘tvinge’ et format igennem.
Hvis man vil vide hvilke format variablene er indlæst i, kan man få
en liste med funktionen str().
## 'data.frame': 100 obs. of 17 variables:
## $ id : int 1 2 3 4 5 6 7 8 9 10 ...
## $ hospital : num 2 4 3 1 4 3 3 3 5 2 ...
## $ admission_date: chr "2024-01-04" "2024-01-18" "2024-01-20" "2024-01-22" ...
## $ admission_time: chr "15:02" "13:50" "13:56" "14:17" ...
## $ discharge_date: chr "2024-01-12" "2024-01-25" "2024-01-24" "2024-01-31" ...
## $ discharge_time: chr "14:23" "09:31" "19:24" "14:10" ...
## $ gender : chr "F" "M" "M" "F" ...
## $ age : num 85 72 61 65 54 72 72 79 72 77 ...
## $ height : num 163 162 176 166 167 180 174 175 183 170 ...
## $ weight : num 62 55.2 44 65.8 39.9 ...
## $ allocation : chr "Control" "Control" "Control" "Control" ...
## $ sbp_pre : num 139 114 138 151 152 152 147 140 132 135 ...
## $ sbp_post : num 138 109 138 149 151 148 148 136 130 136 ...
## $ gender_err : num 0 1 1 1 1 0 1 1 2 1 ...
## $ age_err : num 85 72 61 65 54 72 72 79 72 77 ...
## $ weight_err : chr "62,04" "55,23" "44,03" "65,76" ...
## $ height_err : chr "163" "162" "176" "166" ...
Talvariable bliver kun sjældent indlæst forkert. Men der kan være
tilfælde, hvor man vil tvinge en variabel til at være tal eller heltal,
hvilket gøres med funktionerne as.numeric() og
as.integer():
Variable, som indeholder andet end tal vil som udgangspunkt blive
formatteret som character, men ellers kan den tvinges med
funktionen as.character():
## [1] "2" "4" "3" "1" "4" "3" "3" "3" "5" "2" "5" "4" "3" "1" "4" "2" "1" "5"
## [19] "5" "2" "4" "5" "4" "1" "2" "5" "5" "3" "2" "2" "3" "3" "4" "5" "4" "2"
## [37] "5" "2" "2" "1" "1" "1" "1" "1" "2" "4" "4" "4" "2" "4" "4" "3" "5" "5"
## [55] "1" "2" "1" "5" "4" "2" "5" "2" "3" "1" "4" "1" "3" "4" "2" "5" "1" "3"
## [73] "1" "4" "4" "5" "5" "1" "5" "3" "3" "3" "2" "3" "5" "4" "5" "5" "3" "1"
## [91] "3" "1" "3" "2" "1" "2" "4" "2" "3" "1"
Nogle typer af analyser virker kun med kategoriske variable, som i R
kaldes for factors. Det er derfor vigtigt at kategoriske
variable bliver formatteret korrekt. (Bemærk at man både kan bruge
funktionerne as.factor() og factor())
Når man indlæser data med read.csv() sker denne
formattering ikke automatisk (hvis data er tal indlæses de som numeric
og hvis data er ord indlæses de som character). Nogle funktioner har en
indbygget funktion, der kan få den til at indlæse tekst-variable som
factors med argumentet stringsAsFactors - der er dog lidt
delte meninger om det er en god ide, fordi R ‘gætter’ sig til om en
variabel er en factor, hvilket kan påvirke reproducerbarheden. Det mest
sikre er at indlæse variablen som character og derefter formattere til
factor, så hele arbejdesgangen er dokumenteret.
I eksemplet indlæses variablen gender som en character variabel og skal derefter formatteres som factor med de to niveauer “M” og “F”. Når en character-variabel formatteres som factor vil alle unikke værdier blive lavet til en kategori og automatisk blive tildelt en tilsvarende label.
## [1] F M M F F M F M M M F F F M M M F M M F F F F F F F M M M M M F F M M M F
## [38] M F M M M M M F F F M M M F F F M F M F F M M F F M M F F F M F F F F M F
## [75] M F M F M M F M M F F F F M M F F F F M F M M M F M
## Levels: F M
Hvis variablen i csv-filen er indtastet som tal vil den blive indlæst som numeric eller integer. Disse kan sagtens formatteres som factor, hvor tallene så bliver brugt som labels
## [1] 2 4 3 1 4 3 3 3 5 2 5 4 3 1 4 2 1 5 5 2 4 5 4 1 2 5 5 3 2 2 3 3 4 5 4 2 5
## [38] 2 2 1 1 1 1 1 2 4 4 4 2 4 4 3 5 5 1 2 1 5 4 2 5 2 3 1 4 1 3 4 2 5 1 3 1 4
## [75] 4 5 5 1 5 3 3 3 2 3 5 4 5 5 3 1 3 1 3 2 1 2 4 2 3 1
## Levels: 1 2 3 4 5
Alternativt kan man tildele labels ved at tilføje argumenterne levels
og labels - bemærk at man tilføjer flere værdier med funktionen
c(). Levels argumentet definerer rækkefølgen af niveauerne
og labels definerer navnene på niveauerne i samme rækkefølge:
# Formattering af factor fra heltalvariabel
testData$hospital <- as.factor(testDataRaw$hospital,
levels = c(1,2,3,4,5),
labels = c("NOH", "BBH", "HGH",
"AHH", "RH"))## [1] BBH AHH HGH NOH AHH HGH HGH HGH RH BBH RH AHH HGH NOH AHH BBH NOH RH
## [19] RH BBH AHH RH AHH NOH BBH RH RH HGH BBH BBH HGH HGH AHH RH AHH BBH
## [37] RH BBH BBH NOH NOH NOH NOH NOH BBH AHH AHH AHH BBH AHH AHH HGH RH RH
## [55] NOH BBH NOH RH AHH BBH RH BBH HGH NOH AHH NOH HGH AHH BBH RH NOH HGH
## [73] NOH AHH AHH RH RH NOH RH HGH HGH HGH BBH HGH RH AHH RH RH HGH NOH
## [91] HGH NOH HGH BBH NOH BBH AHH BBH HGH NOH
## Levels: NOH BBH HGH AHH RH
## [1] "2024-01-04" "2024-01-18" "2024-01-20" "2024-01-22" "2024-01-28"
## [6] "2024-02-10" "2024-02-25" "2024-02-25" "2024-03-03" "2024-03-03"
## [11] "2024-03-08" "2024-03-08" "2024-03-13" "2024-03-19" "2024-03-20"
## [16] "2024-03-20" "2024-03-21" "2024-03-25" "2024-03-28" "2024-03-28"
## [21] "2024-03-30" "2024-03-30" "2024-04-02" "2024-04-05" "2024-04-06"
## [26] "2024-04-06" "2024-04-06" "2024-04-10" "2024-04-13" "2024-04-15"
## [31] "2024-04-23" "2024-04-24" "2024-04-27" "2024-05-08" "2024-05-09"
## [36] "2024-05-09" "2024-05-12" "2024-05-15" "2024-05-16" "2024-05-21"
## [41] "2024-05-22" "2024-05-24" "2024-05-24" "2024-05-26" "2024-05-28"
## [46] "2024-05-29" "2024-05-29" "2024-05-30" "2024-06-05" "2024-06-09"
## [51] "2024-06-11" "2024-06-19" "2024-06-23" "2024-06-24" "2024-06-24"
## [56] "2024-06-30" "2024-07-02" "2024-07-06" "2024-07-12" "2024-07-13"
## [61] "2024-07-16" "2024-07-23" "2024-07-25" "2024-07-27" "2024-07-28"
## [66] "2024-07-31" "2024-08-04" "2024-08-07" "2024-08-13" "2024-08-13"
## [71] "2024-08-14" "2024-08-17" "2024-08-25" "2024-08-28" "2024-08-31"
## [76] "2024-09-03" "2024-09-07" "2024-09-11" "2024-09-12" "2024-09-15"
## [81] "2024-09-22" "2024-09-24" "2024-09-27" "2024-09-29" "2024-10-26"
## [86] "2024-10-30" "2024-11-02" "2024-11-03" "2024-11-05" "2024-11-11"
## [91] "2024-11-15" "2024-11-17" "2024-11-25" "2024-11-29" "2024-11-30"
## [96] "2024-12-01" "2024-12-08" "2024-12-16" "2024-12-17" "2024-12-25"
## [1] "2024-01-04 CET" "2024-01-18 CET" "2024-01-20 CET" "2024-01-22 CET"
## [5] "2024-01-28 CET" "2024-02-10 CET" "2024-02-25 CET" "2024-02-25 CET"
## [9] "2024-03-03 CET" "2024-03-03 CET" "2024-03-08 CET" "2024-03-08 CET"
## [13] "2024-03-13 CET" "2024-03-19 CET" "2024-03-20 CET" "2024-03-20 CET"
## [17] "2024-03-21 CET" "2024-03-25 CET" "2024-03-28 CET" "2024-03-28 CET"
## [21] "2024-03-30 CET" "2024-03-30 CET" "2024-04-02 CEST" "2024-04-05 CEST"
## [25] "2024-04-06 CEST" "2024-04-06 CEST" "2024-04-06 CEST" "2024-04-10 CEST"
## [29] "2024-04-13 CEST" "2024-04-15 CEST" "2024-04-23 CEST" "2024-04-24 CEST"
## [33] "2024-04-27 CEST" "2024-05-08 CEST" "2024-05-09 CEST" "2024-05-09 CEST"
## [37] "2024-05-12 CEST" "2024-05-15 CEST" "2024-05-16 CEST" "2024-05-21 CEST"
## [41] "2024-05-22 CEST" "2024-05-24 CEST" "2024-05-24 CEST" "2024-05-26 CEST"
## [45] "2024-05-28 CEST" "2024-05-29 CEST" "2024-05-29 CEST" "2024-05-30 CEST"
## [49] "2024-06-05 CEST" "2024-06-09 CEST" "2024-06-11 CEST" "2024-06-19 CEST"
## [53] "2024-06-23 CEST" "2024-06-24 CEST" "2024-06-24 CEST" "2024-06-30 CEST"
## [57] "2024-07-02 CEST" "2024-07-06 CEST" "2024-07-12 CEST" "2024-07-13 CEST"
## [61] "2024-07-16 CEST" "2024-07-23 CEST" "2024-07-25 CEST" "2024-07-27 CEST"
## [65] "2024-07-28 CEST" "2024-07-31 CEST" "2024-08-04 CEST" "2024-08-07 CEST"
## [69] "2024-08-13 CEST" "2024-08-13 CEST" "2024-08-14 CEST" "2024-08-17 CEST"
## [73] "2024-08-25 CEST" "2024-08-28 CEST" "2024-08-31 CEST" "2024-09-03 CEST"
## [77] "2024-09-07 CEST" "2024-09-11 CEST" "2024-09-12 CEST" "2024-09-15 CEST"
## [81] "2024-09-22 CEST" "2024-09-24 CEST" "2024-09-27 CEST" "2024-09-29 CEST"
## [85] "2024-10-26 CEST" "2024-10-30 CET" "2024-11-02 CET" "2024-11-03 CET"
## [89] "2024-11-05 CET" "2024-11-11 CET" "2024-11-15 CET" "2024-11-17 CET"
## [93] "2024-11-25 CET" "2024-11-29 CET" "2024-11-30 CET" "2024-12-01 CET"
## [97] "2024-12-08 CET" "2024-12-16 CET" "2024-12-17 CET" "2024-12-25 CET"
testData$admission <- as.POSIXct( paste(
testDataRaw$admission_date,
testDataRaw$admission_time),
format = "%Y-%m-%d %H:%M")## [1] "2024-01-04 15:02:00 CET" "2024-01-18 13:50:00 CET"
## [3] "2024-01-20 13:56:00 CET" "2024-01-22 14:17:00 CET"
## [5] "2024-01-28 20:31:00 CET" "2024-02-10 14:16:00 CET"
## [7] "2024-02-25 07:37:00 CET" "2024-02-25 08:41:00 CET"
## [9] "2024-03-03 15:39:00 CET" "2024-03-03 10:41:00 CET"
## [11] "2024-03-08 17:31:00 CET" "2024-03-08 17:25:00 CET"
## [13] "2024-03-13 19:12:00 CET" "2024-03-19 12:19:00 CET"
## [15] "2024-03-20 09:37:00 CET" "2024-03-20 11:19:00 CET"
## [17] "2024-03-21 23:28:00 CET" "2024-03-25 06:19:00 CET"
## [19] "2024-03-28 14:29:00 CET" "2024-03-28 11:40:00 CET"
## [21] "2024-03-30 13:47:00 CET" "2024-03-30 06:07:00 CET"
## [23] "2024-04-02 07:38:00 CEST" "2024-04-05 10:09:00 CEST"
## [25] "2024-04-06 07:16:00 CEST" "2024-04-06 13:17:00 CEST"
## [27] "2024-04-06 05:39:00 CEST" "2024-04-10 17:50:00 CEST"
## [29] "2024-04-13 16:41:00 CEST" "2024-04-15 05:54:00 CEST"
## [31] "2024-04-23 15:10:00 CEST" "2024-04-24 23:48:00 CEST"
## [33] "2024-04-27 14:43:00 CEST" "2024-05-08 11:01:00 CEST"
## [35] "2024-05-09 23:18:00 CEST" "2024-05-09 11:12:00 CEST"
## [37] "2024-05-12 11:12:00 CEST" "2024-05-15 03:30:00 CEST"
## [39] "2024-05-16 15:29:00 CEST" "2024-05-21 07:50:00 CEST"
## [41] "2024-05-22 16:21:00 CEST" "2024-05-24 08:29:00 CEST"
## [43] "2024-05-24 14:09:00 CEST" "2024-05-26 02:15:00 CEST"
## [45] "2024-05-28 02:04:00 CEST" "2024-05-29 18:35:00 CEST"
## [47] "2024-05-29 08:49:00 CEST" "2024-05-30 16:32:00 CEST"
## [49] "2024-06-05 11:15:00 CEST" "2024-06-09 13:27:00 CEST"
## [51] "2024-06-11 21:48:00 CEST" "2024-06-19 05:11:00 CEST"
## [53] "2024-06-23 07:52:00 CEST" "2024-06-24 09:27:00 CEST"
## [55] "2024-06-24 10:01:00 CEST" "2024-06-30 13:49:00 CEST"
## [57] "2024-07-02 20:11:00 CEST" "2024-07-06 06:46:00 CEST"
## [59] "2024-07-12 22:50:00 CEST" "2024-07-13 10:52:00 CEST"
## [61] "2024-07-16 16:05:00 CEST" "2024-07-23 07:15:00 CEST"
## [63] "2024-07-25 08:57:00 CEST" "2024-07-27 15:14:00 CEST"
## [65] "2024-07-28 00:18:00 CEST" "2024-07-31 11:58:00 CEST"
## [67] "2024-08-04 09:02:00 CEST" "2024-08-07 10:45:00 CEST"
## [69] "2024-08-13 20:32:00 CEST" "2024-08-13 07:55:00 CEST"
## [71] "2024-08-14 21:32:00 CEST" "2024-08-17 15:47:00 CEST"
## [73] "2024-08-25 15:10:00 CEST" "2024-08-28 18:35:00 CEST"
## [75] "2024-08-31 08:55:00 CEST" "2024-09-03 07:43:00 CEST"
## [77] "2024-09-07 08:44:00 CEST" "2024-09-11 15:09:00 CEST"
## [79] "2024-09-12 07:41:00 CEST" "2024-09-15 05:23:00 CEST"
## [81] "2024-09-22 23:27:00 CEST" "2024-09-24 06:38:00 CEST"
## [83] "2024-09-27 12:17:00 CEST" "2024-09-29 10:16:00 CEST"
## [85] "2024-10-26 04:19:00 CEST" "2024-10-30 13:58:00 CET"
## [87] "2024-11-02 16:08:00 CET" "2024-11-03 11:52:00 CET"
## [89] "2024-11-05 07:10:00 CET" "2024-11-11 09:43:00 CET"
## [91] "2024-11-15 01:07:00 CET" "2024-11-17 13:49:00 CET"
## [93] "2024-11-25 09:04:00 CET" "2024-11-29 13:28:00 CET"
## [95] "2024-11-30 10:49:00 CET" "2024-12-01 16:05:00 CET"
## [97] "2024-12-08 12:09:00 CET" "2024-12-16 01:25:00 CET"
## [99] "2024-12-17 12:08:00 CET" "2024-12-25 11:10:00 CET"
testData$admission <- lubridate::ymd_hm( paste(testDataRaw$admission_date, testDataRaw$admission_time))## [1] "2024-01-04 15:02:00 UTC" "2024-01-18 13:50:00 UTC"
## [3] "2024-01-20 13:56:00 UTC" "2024-01-22 14:17:00 UTC"
## [5] "2024-01-28 20:31:00 UTC" "2024-02-10 14:16:00 UTC"
## [7] "2024-02-25 07:37:00 UTC" "2024-02-25 08:41:00 UTC"
## [9] "2024-03-03 15:39:00 UTC" "2024-03-03 10:41:00 UTC"
## [11] "2024-03-08 17:31:00 UTC" "2024-03-08 17:25:00 UTC"
## [13] "2024-03-13 19:12:00 UTC" "2024-03-19 12:19:00 UTC"
## [15] "2024-03-20 09:37:00 UTC" "2024-03-20 11:19:00 UTC"
## [17] "2024-03-21 23:28:00 UTC" "2024-03-25 06:19:00 UTC"
## [19] "2024-03-28 14:29:00 UTC" "2024-03-28 11:40:00 UTC"
## [21] "2024-03-30 13:47:00 UTC" "2024-03-30 06:07:00 UTC"
## [23] "2024-04-02 07:38:00 UTC" "2024-04-05 10:09:00 UTC"
## [25] "2024-04-06 07:16:00 UTC" "2024-04-06 13:17:00 UTC"
## [27] "2024-04-06 05:39:00 UTC" "2024-04-10 17:50:00 UTC"
## [29] "2024-04-13 16:41:00 UTC" "2024-04-15 05:54:00 UTC"
## [31] "2024-04-23 15:10:00 UTC" "2024-04-24 23:48:00 UTC"
## [33] "2024-04-27 14:43:00 UTC" "2024-05-08 11:01:00 UTC"
## [35] "2024-05-09 23:18:00 UTC" "2024-05-09 11:12:00 UTC"
## [37] "2024-05-12 11:12:00 UTC" "2024-05-15 03:30:00 UTC"
## [39] "2024-05-16 15:29:00 UTC" "2024-05-21 07:50:00 UTC"
## [41] "2024-05-22 16:21:00 UTC" "2024-05-24 08:29:00 UTC"
## [43] "2024-05-24 14:09:00 UTC" "2024-05-26 02:15:00 UTC"
## [45] "2024-05-28 02:04:00 UTC" "2024-05-29 18:35:00 UTC"
## [47] "2024-05-29 08:49:00 UTC" "2024-05-30 16:32:00 UTC"
## [49] "2024-06-05 11:15:00 UTC" "2024-06-09 13:27:00 UTC"
## [51] "2024-06-11 21:48:00 UTC" "2024-06-19 05:11:00 UTC"
## [53] "2024-06-23 07:52:00 UTC" "2024-06-24 09:27:00 UTC"
## [55] "2024-06-24 10:01:00 UTC" "2024-06-30 13:49:00 UTC"
## [57] "2024-07-02 20:11:00 UTC" "2024-07-06 06:46:00 UTC"
## [59] "2024-07-12 22:50:00 UTC" "2024-07-13 10:52:00 UTC"
## [61] "2024-07-16 16:05:00 UTC" "2024-07-23 07:15:00 UTC"
## [63] "2024-07-25 08:57:00 UTC" "2024-07-27 15:14:00 UTC"
## [65] "2024-07-28 00:18:00 UTC" "2024-07-31 11:58:00 UTC"
## [67] "2024-08-04 09:02:00 UTC" "2024-08-07 10:45:00 UTC"
## [69] "2024-08-13 20:32:00 UTC" "2024-08-13 07:55:00 UTC"
## [71] "2024-08-14 21:32:00 UTC" "2024-08-17 15:47:00 UTC"
## [73] "2024-08-25 15:10:00 UTC" "2024-08-28 18:35:00 UTC"
## [75] "2024-08-31 08:55:00 UTC" "2024-09-03 07:43:00 UTC"
## [77] "2024-09-07 08:44:00 UTC" "2024-09-11 15:09:00 UTC"
## [79] "2024-09-12 07:41:00 UTC" "2024-09-15 05:23:00 UTC"
## [81] "2024-09-22 23:27:00 UTC" "2024-09-24 06:38:00 UTC"
## [83] "2024-09-27 12:17:00 UTC" "2024-09-29 10:16:00 UTC"
## [85] "2024-10-26 04:19:00 UTC" "2024-10-30 13:58:00 UTC"
## [87] "2024-11-02 16:08:00 UTC" "2024-11-03 11:52:00 UTC"
## [89] "2024-11-05 07:10:00 UTC" "2024-11-11 09:43:00 UTC"
## [91] "2024-11-15 01:07:00 UTC" "2024-11-17 13:49:00 UTC"
## [93] "2024-11-25 09:04:00 UTC" "2024-11-29 13:28:00 UTC"
## [95] "2024-11-30 10:49:00 UTC" "2024-12-01 16:05:00 UTC"
## [97] "2024-12-08 12:09:00 UTC" "2024-12-16 01:25:00 UTC"
## [99] "2024-12-17 12:08:00 UTC" "2024-12-25 11:10:00 UTC"
Formatteringen af hele datasættet vil så se således ud:
testData <- data.frame(id = 1:length(testDataRaw$ id)) # Løbenummer
testData$ hospital <- factor(testDataRaw$hospital,
levels = c(1,2,3,4,5),
labels = c("NOH", "BBH", "HGH",
"AHH", "RH"))
testData$ admission <- lubridate::ymd_hm( paste(testDataRaw$admission_date,
testDataRaw$admission_time))
testData$ discharge <- lubridate::ymd_hm( paste(testDataRaw$discharge_date,
testDataRaw$discharge_time))
testData$ gender <- factor(testDataRaw$gender)
testData$ age <- testDataRaw$age
testData$ height <- testDataRaw$height
testData$ weight <- testDataRaw$weight
testData$ allocation <- factor(testDataRaw$allocation)
testData$ sbp_pre <- testDataRaw$sbp_pre
testData$ sbp_post <- testDataRaw$sbp_post
str(testData)## 'data.frame': 100 obs. of 11 variables:
## $ id : int 1 2 3 4 5 6 7 8 9 10 ...
## $ hospital : Factor w/ 5 levels "NOH","BBH","HGH",..: 2 4 3 1 4 3 3 3 5 2 ...
## $ admission : POSIXct, format: "2024-01-04 15:02:00" "2024-01-18 13:50:00" ...
## $ discharge : POSIXct, format: "2024-01-12 14:23:00" "2024-01-25 09:31:00" ...
## $ gender : Factor w/ 2 levels "F","M": 1 2 2 1 1 2 1 2 2 2 ...
## $ age : num 85 72 61 65 54 72 72 79 72 77 ...
## $ height : num 163 162 176 166 167 180 174 175 183 170 ...
## $ weight : num 62 55.2 44 65.8 39.9 ...
## $ allocation: Factor w/ 2 levels "Control","Treatment": 1 1 1 1 1 2 1 2 1 1 ...
## $ sbp_pre : num 139 114 138 151 152 152 147 140 132 135 ...
## $ sbp_post : num 138 109 138 149 151 148 148 136 130 136 ...
Hvis data indeholder fejl eller ikke er formatteret korrekt vil der være risiko for, at resultaterne af ens analyser bliver forkerte. Det er derfor vigtigt at gennemgå alle variable, så man er sikker på at alt er korrekt.
Fejl kan både opstå ved indlæsningen i af data i R (det er typisk kun formatteringsfejl), men ellers vil mange fejl være indtastningsfejl eller fejl der opstår i senere håndtering af data (fx er Excel kendt for automatisk at formattere data til andre formatter - fx vil den lave alt der minder om datoer om til datoer).
I numeriske variable vil den typiske fejl være indtastningsfejl. Mindre fejl kan være svære at se, men vil tilgengæld ikke påvirke analyser i særlig høj grad (det har lille betydning om man har tastet 66 i stedet for 68). Til gengæld kan ekstreme værdier have stor betydning for resultatet af ens analyser. I de fleste forskningsdatabaser vil man have defineret grænse for hvad der kan indtastes, som forhindre de fleste ekstreme værdier. Men hvis man skal arbejde med lidt ældre data skal man være mere påpasselig. Noget man også skal være opmærksom på med ældre data er at databaserne ofte ikke kunne håndterer andre datatyper end det definerede - derfor var det normalt at man indtastede ‘ukendte’ værdier som en talværdier uden for det mulige spektrum - ofte enten 9, 99, 999 eller 9999.
En måde at undersøge for ekstreme værdier er med funktionen
summmary(), som returnere de mest almindelige deskriptive
estimater:
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 40.00 64.00 71.00 97.99 77.00 999.00
Her kan vi både se at den maksimale værdi i variablen ‘age’ er 999, samt at gennemsnittet er betydeligt højere end medianen.
En anden måde man kan undersøge for ekstreme værdier er at tegne et plot:
Her bliver det tydeligt at der er tre værdier på 999 - og som altså afviger betydeligt fra resten af data.
Når man opdager ekstreme værdier, må man overveje om de potentielt
kunne være sande - og derfor skal blive i datasættet - eller om de skal
fjernes. I nogle tilfælde kan man prøve at finde den sande værdier fra
andre kilder. Men ofte vil den eneste mulighed være, at fjerne værdier
ved at udskifte dem med NA. En metode er at bruge funktionen
ifelse(b, T, F), hvor b er en betingelse,
der testes; T er handlingen hvis betingelsen er sand og
F er handlingen hvis betingelsen er falsk. I eksemplet
testes om værdier ikke er 999 (!= betyder ‘ikke
lig med’); og hvis betingelsen er sand bevares værdien, men hvis
betingelsen er falsk indsættes NA:
## [1] 85 72 61 65 54 72 72 79 72 77 64 64 68 87 68 73 59 73
## [19] 84 67 81 62 62 63 55 64 89 68 NA 71 67 73 62 81 59 78
## [37] 72 71 77 61 58 67 82 76 93 60 71 101 70 79 78 86 71 82
## [55] 53 62 71 64 72 53 NA 65 65 68 64 53 76 70 40 81 63 72
## [73] 73 59 55 85 89 76 72 71 73 NA 80 77 64 71 70 71 80 65
## [91] 76 72 70 80 71 59 57 67 63 58
Det første vi kan undersøge er hvor mange unikke værdier variablen
har. Umiddelbart vil vi kun forvente, at variablen ‘gender’ har to
mulige værdier - med mindre man har givet mulighed for yderligere
muligheder, men i så fald vil der være meget få af dem. Hvis man skal
undersøge hvilke unikke værdier variablen har bruger man funktionen
unique():
## [1] 0 1 2
Her kan man se at der er tre unikke værdier.
Derefter kan man bruge funktionen table() til at
undersøge antallet af hver værdi:
##
## 0 1 2
## 29 63 8
Her kan vi se at der er en lidt påfaldende fordeling af værdier. Selv hvis der var en mulighed, som hed ‘Andet’ ville man kun forvente få besvarelser i denne værdi. En anden mulighed kunne være, at data ikke er blevet kodet ens i hele datasættet. Det kan nogengange forekomme, når man samkører data indsamlet fra forskellige kilder (fx hvis der rekrutteres deltagere fra forskellige behandlingssteder).
Derfor kan vi prøve at opdele efter behandlingssted:
##
## 1 2 3 4 5
## 0 8 8 5 8 0
## 1 12 12 15 12 12
## 2 0 0 0 0 8
Her kan man se at fire af behandlingsstederne har kodet køn som 0/1, mens et enkelt behandlingssted tilsyneladende har kodet variablen som 1/2. Her kan vi igen bruge funktionen ifelse() til at rette de fejlagtige værdier, men bevare de korrekte værdier. Men i dette tilfælde bruges yderligere en ifelse() til at rette påvirkede værdier:
testData$gender_err2 <- ifelse(testDataRaw$hospital == 5,
ifelse(testDataRaw$gender_err == 1, 0, 1) ,
testDataRaw$gender_err)
table(testDataRaw$gender_err2, testDataRaw$hospital)##
## 1 2 3 4 5
## 0 8 8 5 8 12
## 1 12 12 15 12 8
En fejl man nogle gange kommer til er at importere en numerisk variabel, hvor der er brugt kommer som decimal-tegn. R forventer i per default at der bruges punktum - så hvis det ikke specificeres at der er komma - vil R fortolke variablen som en tekst variabel.
## [1] "62,04" "55,23" "44,03" "65,76" "39,85" "76,87" "65" "80,54"
## [9] "79,32" "63,22" "67,78" "70,99" "77,44" "74,76" "86,23" "80,57"
## [17] "91,48" "44,81" "60,17" "64,82" "71,47" "92,29" "57,92" "60,64"
## [25] "56,2" "85,14" "82,63" "46,6" "77,45" "95,8" "92,66" "73,26"
## [33] "51,66" "53,28" "75,04" "85,57" "61,17" "97,88" "70,68" "65,34"
## [41] "66,76" "67,09" "59,32" "89,62" "53,94" "68,81" "83,77" "100,32"
## [49] "91,84" "81,23" "62,58" "59,52" "46,18" "102,72" "69,16" "84,74"
## [57] "81,02" "66,79" "91,59" "89,86" "71,99" "79,13" "92,99" "96,42"
## [65] "92,92" "81,12" "51,67" "91,93" "58,35" "78,23" "88,5" "67,98"
## [73] "58,39" "66,87" "85,9" "59,29" "71,54" "60,14" "83,99" "89,72"
## [81] "105,98" "79,69" "70,14" "73,08" "38,79" "71,63" "97,26" "46,18"
## [89] "99,33" "89,55" "67,96" "59,24" "69,99" "88,47" "77,52" "80,35"
## [97] "84,63" "70,03" "74,5" "94,45"
Hvis man prøver at lave den om til tal med funktionen
as.numeric() vil den returnere NA værdier:
## Warning: NAs introduced by coercion
En løsning vil være at importere variablen igen med en specifikation om at decimal-tegnet er “,”.
Men man kan også rette variablen ved at udskifte kommaerne med
punktumer - hvilket kan gøres med funktionen gsub():
Eller med funktionen str_replace() fra pakken
{stringr}:
Begge funktioner retter komma til punktum, som om det var et stykke
tekst - men det gør at R efterfølgende fortolker tallet korrekt, når man
bruger as.numeric().
Nogle gange få man data, hvor manglende værdier er kodet som noget andet end NA - fx med et punktum eller teksten “Missing value”. Her vil R i første omgang importere data som character variabel.
## [1] "163" "162" "176" "166" "167" "180" "." "175" "183" "170" "173" "197"
## [13] "184" "172" "154" "169" "179" "168" "171" "189" "191" "185" "188" "177"
## [25] "186" "194" "165" "195" "157" "201" "181" "161" "159" "203" "153" "193"
## [37] "178" "182"
I de fleste tilfælde kan man tvinge variablen til at blive tal med
as.numeric() - og så vil R automatisk lave alle værdier,
som ikke er tal, om til NA:
## Warning: NAs introduced by coercion
## [1] 163 162 176 166 167 180 NA 175 183 170 NA NA NA 173 197 184 172 154
## [19] NA 169 179 170 NA 168 NA 172 171 171 183 189 197 173 166 166 171 191
## [37] 163 185 167 188 179 173 173 180 177 186 186 194 170 186 165 166 172 195
## [55] 157 201 168 165 188 177 181 NA 183 180 177 169 NA 186 NA NA NA NA
## [73] NA 161 191 159 179 NA NA NA 203 189 168 179 153 168 180 157 NA NA
## [91] 162 168 193 173 171 178 182 197 162 NA
Eksemplerne i denne guide er baseret på et simuleret datasæt med tilfældige værdier.
Herunder kan man se koden der er brugt til at lave datasættet.
# Generering af test datasæt
set.seed(123) # Seed for randomisering
n <- 100 # Antal observationer
testDataRaw <- data.frame(id = 1:n) # Observations id
# Tilfældig trækning af 1 til 5; replace = FALSE sikre at der er lige mange
# hver gruppe
testDataRaw$ hospital <- sample(c(rep(1,n/5),rep(2,n/5),rep(3,n/5),
rep(4,n/5),rep(5,n/5)), n, replace = FALSE)
# Tilfælde datoer i 2024; sort() gør at datoer er kontinuerlige
testDataRaw$ admission_date <- sort( as.Date(
runif(n, min = as.numeric(as.Date("2024-01-01")),
max = as.numeric(as.Date("2024-12-31"))),
origin = "1970-01-01"))
# Tilfældigt indlæggelsestidspunkt
# Timerne er normalfordelt omkring kl 11
testDataRaw$ admission_time <- format(as.POSIXct(
paste0((round( rnorm(n, mean = 11, sd = 5)) %% 24),":",
sample(0:59,n,replace = TRUE)), format="%H:%M"), "%H:%M")
# Udskrivelsesdatoen er indlæggelsesdatoen plus et tilfældigt antal dage
testDataRaw$ discharge_date <- testDataRaw$ admission_date +
sample(2:10, 100, replace = TRUE)
# Datoerne formateres som character i stedet for posix
testDataRaw$ admission_date <- as.character(testDataRaw$ admission_date)
testDataRaw$ discharge_date <- as.character(testDataRaw$ discharge_date)
# Udskrivelses tidspunktet er tilfældigt efter en normalfordeling omkring kl 14
testDataRaw$ discharge_time <- format(as.POSIXct(
paste0((round( rnorm(n, mean = 14, sd = 2)) %% 24),":",
sample(0:59, n, replace = TRUE)), format="%H:%M"), "%H:%M")
# Tilfældigt køn
testDataRaw$ gender <- sample(c("F","M"), n, replace = TRUE)
# Tilfældig alder | normalfordelt omkring 70 år
testDataRaw$ age <- round( rnorm(n, mean = 70, sd = 10) )
# Tilfældig højde betinget af køn, så mændene har en gennemsnitshøjde på 180 cm
# og kvinder en gennemsnitshøjde på 170 cm
testDataRaw$ height <- unlist(lapply(testDataRaw$ gender,
function(x){if(x == "M"){
return(round( rnorm(1, mean = 180, sd = 10) ))}
else {
return(round( rnorm(1, mean = 170, sd = 10) ))}
}))
# Vægt udregnes ud fra højde med et tilfældigt BMI; BMI er udregnet som en
# kvadradisk funktion af en normalfordeling med mean 600 (BMI = ca 24.5)
testDataRaw$ weight <- sqrt(rnorm(n, mean = 600, sd = 180)
)*(testDataRaw$ height/100)^2
# Tilfældig allokering til behandling eller kontrol
testDataRaw$ allocation <- sample(c("Treatment","Control"), n, replace = TRUE)
# Tilfældigt normalfordelt blodtryk før behandling
testDataRaw$ sbp_pre <- round( rnorm(n, mean = 140, sd = 10) )
# Tilfældigt normalfordelt blodtryk efter behandling hvor en betingelse gør at
# "Treatment" har et gennemsnitligt fald i blodtryk og "Control" er uændret
testDataRaw$ sbp_post <- testDataRaw$ sbp_pre+unlist(
lapply(testDataRaw$ allocation,
function(x){if(x == "Treatment"){
return(round( rnorm(1, mean = -5, sd = 3) ))}
else {
return(round( rnorm(1, mean = 0, sd = 2) ))} }))
# Køns-variabel hvor en betingelse laver en anden indtastning hvis "hospital"
# er lig med 5
testDataRaw$ gender_err <- unlist(lapply(testDataRaw$ hospital,
function(x){if(x == 5){
return(sample(c(1,2), 1, replace = TRUE))}
else {
return(sample(c(0,1), 1, replace = TRUE))} }))
# Alders-variabel hvor 3 tilfældige værdier udskiftes med værdien 999
testDataRaw$ age_err <- testDataRaw$age
testDataRaw$ age_err[sample(1:n, 3, replace = FALSE)] = 999
# Vægt-variabel hvor decimal-tegnet udskiftes med komma pg formateres til tekst
testDataRaw$ weight_err <- stringr::str_replace(as.character(
round(testDataRaw$ weight,2)),"[.]", ",")
# Højde-variabel hvor 20 tilfældige værdier udskiftes med punktum og formateres
# som tekst
testDataRaw$ height_err <- as.character(testDataRaw$ height)
testDataRaw$ height_err[sample(1:n, 20, replace = FALSE)] = "."Sekvenser af tal kan laves på to måder enten ved
angive starttaller og sluttallet, adskilt af et enkelt kolon, fx vil
koden 1:10 give en vektor fra 1 til 10:
## [1] 1 2 3 4 5 6 7 8 9 10
Funktionen seq(min, max) giver samme output:
## [1] 1 2 3 4 5 6 7 8 9 10
Hvis man vil have en sekvens med større spring, fx 10, bruges koden
seq(min, max, by), hvor argumentet by angiver
størrelsen på springet:
## [1] 10 20 30 40 50 60 70 80 90 100
Funktionen rep(x, n) (replicate) bruges til at lave en
vektor med en bestemt værdi et antal gange (x angiver
værdien og n angiver antallet af gentagelser):
## [1] 1 1 1 1 1 1 1 1 1 1
Funktionen sample(x, n, replace) bruges til at lave
tilfældige trækninger af en vektor (x),
n antal gange, enten med eller uden tilbagelægning
(replace defineres som entel TRUE eller
FALSE, hvor default er FALSE). Hvis
replace defineres som FALSE skal vektoren være
mindst samme længde som n.
## [1] "Treatment" "Treatment" "Control" "Treatment" "Control" "Treatment"
## [7] "Treatment" "Control" "Treatment" "Treatment"
Funktionen rnorm(n, mean, sd) bruges til at lave en
vektor med tilfældige værdier efter en normalfordeling, hvor
n angiver antallet af trækninger, mean
angiver gennemsnittet for normalfordelingen og sd
angiver standarddeviationen for normalfordelingen:
## [1] 68.04207 75.64337 82.57325 63.65407 74.24233 71.80031 67.70405 69.26792
## [9] 63.17828 69.15111
Funktionen runif(n, min, max) bruges til at lave en vektor med tilfældige værdier mellem to værdier, hvor n angiver antallet af trækninger, min angiver den laveste værdi og max angiver den højeste værdi. Trækningen har ingen fordeling og alle værdier har derfor samme sandsynlighed:
## [1] 94.3224738 37.4545655 0.8041536 98.0524503 37.7540056 98.5867555
## [7] 13.9616391 56.2218103 79.4360194 19.3901369
Funktionen lapply(x, FUN) bruges til at anvende en
funktion på en liste af værdier, hvor x angiver listen
og FUN angiver funktionen, der skal anvendes (Bruges på
samme måde som et for-loop). I eksemplet er x en sekvens fra 1 til 10 og
FUN er en funktion, som returnerer kvadrattallet af x:
## [[1]]
## [1] 1
##
## [[2]]
## [1] 4
##
## [[3]]
## [1] 9
##
## [[4]]
## [1] 16
##
## [[5]]
## [1] 25
##
## [[6]]
## [1] 36
##
## [[7]]
## [1] 49
##
## [[8]]
## [1] 64
##
## [[9]]
## [1] 81
##
## [[10]]
## [1] 100
Selvom lapply() tager en vektor som input vil den
returnere en liste af resultater, som ikke har den samme datastruktur
som inputtet. Hvis man skal bruge lapply() på en variabel i
en data.frame til at transformere til en ny variabel, skal man bruge
funktionen unlist() til at lave outputtet til en
vektor:
## [1] 1 4 9 16 25 36 49 64 81 100
I datasimulationen bruges lapply() til at lave variabel
med højde, som er betinget af køn. Mænd (gender == "M")
gives en højde en gennemsnit på 180 cm og en standarddeviation på 10 cm,
mens kvinder gives en højde med et gennemsnit på 170 cm og en
standarddeviation på 10 cm:
testDataRaw$ height <- unlist(lapply(testDataRaw$ gender,
function(x){if(x == "M"){
return(round( rnorm(1, mean = 180, sd = 10) ))}
else {
return(round( rnorm(1, mean = 170, sd = 10) ))}
}))| id | gender | height |
|---|---|---|
| 1 | F | 176 |
| 2 | M | 157 |
| 3 | M | 179 |
| 4 | F | 196 |
| 5 | F | 171 |
| 6 | M | 193 |
| 7 | F | 173 |
| 8 | M | 194 |
| 9 | M | 175 |
| 10 | M | 184 |
| 11 | F | 169 |
| 12 | F | 175 |
| 13 | F | 147 |
| 14 | M | 167 |
| 15 | M | 178 |
Jacob Liljehult
Klinisk sygeplejespecialist
cand.scient.san, PhD
Neurologisk afdeling Nordsjællands Hospital
Dyrehavevej 29 3400 Hillerød
E-mail: jacob.mesot.liljehult@regionh.dk
Website: https://jacobliljehult.github.io/research/
QR-kode til denne side: