-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path03-strings-factors-dates.Rmd
1162 lines (866 loc) · 48.1 KB
/
03-strings-factors-dates.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
---
output:
html_document: default
pdf_document: default
---
```{r, echo = FALSE}
knitr::opts_chunk$set(
error = TRUE, # do not interrupt in case of errors
warnings = FALSE,
message = FALSE
)
library(tidyverse)
library(nycflights13) #paket s podatki, ki ga bomo sproti uporabljali
library(webshot)
library(htmlwidgets)
library(lubridate)
```
# Nizi, kategorične spremenljivke in datumi
Pri delu s podatki se pogosto srečamo s posebnimi podatkovnimi tipi, kot so nizi, kategorične spremenljivke in datumi. Z nizi smo že delali v prvih dveh poglavjih. Delali smo tudi s kategoričnimi spremenljivkami, čeprav tega nismo poudarili. V tem poglavju bomo spoznali naprednejše operacije nad temi podatkovnimi tipi v okviru zbirke tidyverse ter predstavili praktične primere, dobre prakse in pasti pri delu z njimi.
Konec poglavja pa bomo posvetili branju in pisanju podatkov v različnih oblikah.
## Priprava
#### Nizi {-}
Najprej si poglejmo najbolj uporabne funkcije za delo z nizi.
Nize lahko združujemo na več načinov:
```{r}
crke <- c("A", "B", "C", "D")
stevilke <- c("1", "2", "3", "4")
str_c(crke, stevilke)
str_c(crke, stevilke, sep = "-")
str_c(crke, stevilke, collapse = "-")
```
Obratno jih lahko razčlenimo na več manjših nizov:
```{r}
str_split("A1-B2-C3-D4", pattern = "-")
```
Zelo uporabna je tudi funkcija za zamenjavo podnizov.
```{r}
str_replace("A1-B1-A2-B2", pattern = "1", "(ena)")
str_replace_all("A1-B1-A2-B2", pattern = "1", "(ena)")
str_replace_all("A1-B1-A2-B2", c("1" = "(ena)", "2" = "(dva)"))
```
Poglejmo, kako bi iz zbirke sadežev v že vgrajenem nizu `fruit` poiskali vse nize, ki vsebujejo `berry`.
```{r}
length(fruit)
str_subset(fruit, "berry")
```
Podobno lahko vrnemo logični vektor nizov, ki vsebujejo `berry`.
```{r}
str_detect(fruit, "berry")
```
Funkcija `str_detect()` je zelo uporabna pri izbiranju vrstic glede na nize. Običajno jo uporabljamo v kombinaciji z regularnimi izrazi, ki jih bomo spoznali v jedru tega poglavja.
#### Kategorične spremenljivke {-}
Pri pripravi na kategorične spremenljivke bomo uporabili podatke o uspehu študentov na izpitu:
``` {r}
tib <- tibble(
ime = c("Maja", "Ales", "Tom", "Barbara", "Simon", "Tina"),
spol = c("z", "m", "m", "z", "m", "z"),
ocena = c(10, 10, 6, 8, 8, 7),
dan_izpita = c(8, 8, 16, 16, 8, 23),
mesec_izpita = rep(6, 6),
leto_izpita = rep(2021, 6),
opravljene_vaje = c(1, 1, 1, 1, 0, 1),
procenti_vaje = c(70, 65, 80, 90, 30, 75)
)
tib
```
Spremenimo **spol** v nominalno spremenljivko (brez ureditve) in **oceno** v ordinalno spremenljivko (obstaja vrsti red vrednosti):
``` {r}
tib <- tib %>% mutate(spol = factor(spol),
ocena = factor(ocena, ordered = TRUE))
tib$spol
tib$ocena
```
Nivoje faktorja (levels) lahko uredimo tudi glede na vrednosti v nekem drugem stolpcu. To običajno pride prav pri vizualizaciji.
``` {r}
tib <- tib %>%
mutate(spol = fct_reorder(spol, procenti_vaje, .desc = TRUE))
tib$spol
```
#### Datumi {-}
Za delo z datumi bomo uporabljali paket **lubridate**. Datume lahko z lubridate shranimo na več načinov. Preprosto lahko sestavimo datum s posameznimi komponentami:
``` {r}
library(lubridate)
make_date(year = 2021, month = 6, day = 11)
```
Ali iz niza:
``` {r}
ymd("2021-06-11")
```
Lahko dodamo tudi čas:
``` {r}
make_datetime(year = 2021, month = 6, day = 11, hour = 11, min = 30, tz = "CET")
```
Z datumi lahko tudi računamo:
``` {r}
my_date <- make_date(year = 2021, month = 6, day = 11)
my_date
my_date + days(2)
my_date + months(3)
```
#### Shranjevanje podatkov v csv {-}
V pripravi v 2. poglavju smo že spoznali funkcijo `rear_csv2()` za branje podatkov iz tekstovnih datotek. Podatke lahko tudi shranimo v tekstovno datoteko s funkcijo `write_csv2()`:
```{r}
write_csv2(tib, "./data-raw/studenti.csv")
```
**Naloga 1**: Datoteko *ocene.csv* v mapi *data-raw* preberite s funkcijami paketa **readr** (`read_csv2()` in `write_csv2()`). Shranite jo kot *ocene2.csv* in to datoteko preberite nazaj. Nato naredite enako z baznimi funkcijami v R (`read.csv2()` in `write.csv2()`). Kaj opazite?
Z **readr**:
```{r, echo = F}
ocene <- read_csv2("./data-raw/ocene.csv")
write_csv2(ocene, "./data-raw/ocene2.csv")
read_csv2("./data-raw/ocene2.csv")
```
Z R:
```{r, echo = F}
ocene <- read.csv2("./data-raw/ocene.csv")
write.csv2(ocene, "./data-raw/ocene2.csv")
read.csv2("./data-raw/ocene2.csv")
```
**Naloga 2**: Ponovno preberite *ocene.csv* ter:
- spremenite stolpec `opravljene_vaje` v kategorične vrednosti,
- zamenjajte stolpce `leto_izpita`, `mesec_izpita`, `dan_izpita` z `datum_izpita`, ki vsebuje vse te podatke,
- izberite samo imena, ki se začnejo na 'T' in podatke shranite v datoteko oceneT.csv.
```{r, echo = F}
ocene <- read.csv2("./data-raw/ocene.csv")
ocene <- ocene %>%
mutate(opravljene_vaje = factor(opravljene_vaje)) %>%
mutate(datum_izpita = make_date(year = leto_izpita,
month = mesec_izpita,
day = dan_izpita)) %>%
select(-leto_izpita, -mesec_izpita, -dan_izpita) %>%
filter(str_detect(ime, "T"))
write_csv2(ocene, "./data-raw/oceneT.csv")
ocene
```
### Delo s šumniki {-}
Pri delu z nizi RStudio uporablja privzeto kodiranje vašega operacijskega sistema. Preprost način, da preverite, ali R uporablja pravilno kodiranje, je, da v konzolo napišete "čebula". Nazaj morate dobiti enak niz in ne "cebula" ali celo "პbula".
```{r}
"čebula"
```
Če R ne vrne istega niza, je potrebno nastaviti privzeto kodiranje na svojem operacijskem sistemu. Da vidimo, katero kodiranje je trenutno uporabljeno, lahko napišemo:
```{r}
Sys.getlocale()
```
Pri tem izpisu vidimo, da uporabljamo sloveski nabor znakov CP1250. Če imate težave, lahko to začasno spremenite z ukazom:
```{r}
Sys.setlocale(category = "LC_ALL", locale = "Slovenian_Slovenia.1250")
```
Oziroma lahko na primer v OS Windows nastavite privzeto kodiranje pod "Settings -> Time & Language -> Region -> Regional Format". Tukaj izberite "Slovenian (Slovenia)".
Za slovenski OS Windows: "Nastavitve -> Ura in Jezik -> Regija -> Območne nastavitve" in izberite "Slovenščina (Slovenija)".
## Nizi
Zbirka paketov tidyverse vsebuje paket **stringr**, ki je namenjen delu z nizi. Vsi ukazi v paketu se začnejo s `str_`, kar v kombinaciji s funkcionalnostjo avtomatskega dopolnjevanja omogoča hitro izbiro in pregled vseh funkcij.
V jeziku R lahko za definiranje niza uporabimo enojne (') ali dvojne navednice ("). R bo rezultat sicer vedno vrnil v dvojnih navednicah.
```{r}
niz_dvojne_navednice <- "urejanje podatkov"
niz_enojne_navednice <- 'urejanje podatkov'
niz_dvojne_navednice
niz_enojne_navednice
```
Če želimo znotraj niza uporabiti dvojne navednice, lahko za definicijo uporabljamo enojne in obratno:
```{r}
citat1 <- "Jure je rekel: 'Citiram Janeza'."
citat2 <- 'In Janez je rekel: "Citiram Jureta".'
citat1
citat2
```
Izpis v drugem primeru uporablja tako imenovan **escape character** (\\), ki omogoča tudi vpis tabulaterjev (\\t), znakov za novo vrstico (\\n) in podobno. Vse znake, ki jih je tako možno zapisati, si lahko ogledate z ukazom `?"'"`.
Niz lahko vključuje tudi vse posebne znake, ki jih podpira Unicode. To je standard za konsistentno kodiranje teksta, ki vsebuje preko 100000 znakov. Za vnos znakov unicode uporabite **\\uxxxx**, kjer je **xxxx** koda znaka, ki ga lahko poiščete na strani https://unicode.org/charts/.
```{r}
normalna_porazdelitev <- "Normal(\u03BC, 1)"
normalna_porazdelitev
```
Dolžino nizov poiščemo s funkcijo `str_length()`:
```{r}
str_length(c("tri", "štiri", "", citat1, normalna_porazdelitev))
```
Za združevanje nizov se uporablja `str_c()`, ki mu lahko dodamo tudi niz `sep`, ki ga vrine med vse podane nize:
```{r}
str_c("paradižnik", "bučke", "jabolka", "paprika", "jagode", sep = ", ")
```
Če funkciji podamo vektorje lahko združimo tudi več nizov naenkrat. Pri tem bo število dobljenih nizov enako največjemu vektorju, manjši pa se bodo reciklirali, kar pomeni, da se bodo njihove vrednosti ponavljale, dokler ne dosežejo dolžine najdaljšega vektorja.
```{r}
str_c("Podpoglavje ", c(1, 2, 3, 4), ": ",
c("Nizi", "Faktorji", "Datumi", "Branje podatkov"))
```
Če želimo nize združiti zaporedno uporabimo `collapse`. Za izpis nizov brez escape znakov uporabite funkcijo `cat()`:
```{r}
poglavja <- str_c("Podpoglavje ", c(1, 2, 3, 4), ": ",
c("Nizi", "Faktorji", "Datumi", "Branje podatkov"), collapse = "\n")
poglavja
cat(poglavja)
```
Manjkajoče vrednosti lahko zamenjamo s `str_replace_na()`.
```{r}
str_replace_na(c("ena", "dva", NA, "štiri"), replacement = "tri!")
```
### Podnizi
Velikokrat pri delu z nizi potrebujemo le del niza ali pa želimo niz razdeliti na več manjših nizov. Poglejmo si na primeru niza `" KUPI: paradižnik, bučke, jabolka, paprika, jagode. "`, iz katerega želimo dobiti vektor sadja in zelenjave:
```{r}
listek <- " KUPI: paradižnik, bučke, jabolka, paprika, jagode. "
```
Najprej odstranimo odvečne začetne in končne presledke. To storimo s `str_trim()`:
``` {r}
listek <- str_trim(listek)
listek
```
Funkcija `str_sub()` vrne podniz glede na indekse znakov:
``` {r}
str_sub(listek, 7, 16) # Vrne znake med indeksom 7 do 16.
str_sub(listek, 7) # Vrne znake od sedmega naprej.
str_sub(listek, -7) # Vrne zadnjih sedem znakov.
str_sub(listek, -7, -2) # Vrne predzadnjih šest znakov.
```
S tem znanjem lahko odstranimo prvih šest znakov in zadnjega:
```{r}
listek <- str_sub(listek, 7, -2)
listek
```
Sedaj, ko imamo elemente samo naštete in ločene z vejico, jih lahko preprosto razdelimo s funkcijo `str_split()`, ki ji podamo vzorec za ločevanje:
```{r}
str_split(listek, pattern = ", ")
```
### Iskanje vzorcev z regularnimi izrazi
Regularni izrazi so močno orodje za iskanje poljubnega vzorca v nizih. V tem poglavju bo predstavljena le osnovna funkcionalnost regularnih izrazov, saj gre za zelo obširno orodje. Za prikaz delovanja bomo uporabljali funkciji `str_view()` in `str_view_all()`, ki uporabljata paket **htmlwidgets** za prikaz rezultatov. Prva označi samo prvo ujemanje, druga pa vsa ujemanja.
Regularni izrazi so nizi, ki določijo iskalni vzorec. Z njimi lahko, na primer, iščemo nize, ki se začnejo na določen podniz, končajo na določen podniz, vsebujejo posebne simbole in podobno. Uporabljamo jih lahko v vseh funkcijah paketa **stringr**, ki imajo kot vhod `pattern`.
Sestavimo dva seznama v katerih bomo iskali vzorce. Za začetek poiščimo vse vejice.
```{r}
library(htmlwidgets)
listek1 <- "KUPI: paradižnik, bučke, jabolka, paprika, jagode. Aja, pa banane."
listek2 <- "KUPI: sok(3x), kruh, piškote, pašto(2x), sol in poper."
str_view_all(c(listek1, listek2), ",")
```
Kaj pa pike?
```{r}
str_view_all(c(listek1, listek2), ".")
```
Pika je poseben znak pri regularnih izrazih, ki predstavlja poljuben znak. Zato smo dobili ujemanja z vsemi črkami. Da lahko dejansko poiščemo pike in druge posebne znake moramo uporabiti znak escape `\`, s katerim programu sporočimo, naj simbola za tem ne prebere kot regularni izraz. Tukaj se nam nekoliko zaplete, ker je znak `\` že znak escape v R za tabulatorje (`\t`), nove vrstice (`\n`) itd. Zato, da vzorcu dejansko podamo `\.` moramo zapisati `\\.`. Tukaj si ponovno lahko pomagamo s funkcijo `cat()`, ki nam bo pokazala dejanski niz podan funkciji.
```{r}
cat("\\.") # Poglejmo kakšen niz bo dejansko sprejela funkcija str_view_all().
str_view_all(c(listek1, listek2), "\\.")
```
Regularni izrazi uporabljajo tudi dva posebna znaka, ki označujeta začetek `^` in konec `$` niza. Poiščimo samo pike, ki zaključujejo nize:
```{r}
str_view_all(c(listek1, listek2), "\\.$")
```
V regularnih izrazih so zelo uporabne tudi naslednje sekvence.
- `\d`: Ujemanje s poljubno števko;
- `\s`: Ujemanje s poljubnim presledkom (presledek, tabulator, nova vrstica);
- `\b`: Ujemanje s prehodom iz besednih znakov (črke) na nebesedne (ločila, presledki);
- `[xyz]`: Ujemanje z x, y ali z;
- `[^xyz]`: Ujemanje z vsem razen x, y in z. Strešica znotraj oglatih oklepajev pomeni negacijo;
- `[[:digit:]]`: Ujemanje s poljubnim številom;
- `[[:alpha:]]`: Ujemanje s poljubno črko, uporabite lahko tudi `[[a-z]]` in `[[:lower:]]` za vse male črke ali `[[A-Z]]` in `[[:upper:]]` za vse velike črke;
- `[[:punct:]]`: Ujemanje z ločili.
Poglejmo si uporabo nekaterih:
```{r}
str_view_all(c(listek1, listek2), "\\dx") # Poiščemo vse številke, ki jim sledi x.
str_view_all(c(listek1, listek2), "[čžš]") # Vse šumnike.
str_view_all(c(listek1, listek2), "[aA].[aA]") # Dva a-ja s poljubnim znakom vmes.
str_view_all(c(listek1, listek2), ".[\\s,.]") # Vse konce besed.
str_view_all(c(listek1, listek2), "[a-f]") # Vse črke od a do f.
str_view_all(c(listek1, listek2), "[[:punct:]]") # Vsa ločila.
```
Znotraj oglatih oklepajev je pomen pike dobeseden znak pika. Tako, da ga ni potrebno zapisati kot `\\.` Priporočamo, da sami preizkušate še druge sekvence.
Navpična črta `|` pomeni ali:
```{r}
str_view_all(c(listek1, listek2), "sol|sok")
```
### Ponovitve
Zelo uporabni so tudi ukazi za ponovitve znakov ali sekvenc, ki nam omogočajo iskanje nizov določenih dolžin. Ukazi za delo s ponovitvami so:
- `?`: ponovi 0 ali 1 krat,
- `*`: ponovi 0 ali več krat,
- `+`: ponovi 1 ali več krat,
- `{n}`: ponovi natanko n krat,
- `{n,}`: ponovi n ali več krat,
- `{,m}`: ponovi m ali manj krat,
- `{n,m}`: ponovi med n in m krat.
Če znak za ponovitev uporabimo za enim znakom pomeni to ponovitev prejšnjega znaka. Lahko pa ga uporabimo tudi za oglatimi ali navadnimi oklepaji. Navadni oklepaji ločijo del regularnega izraza, podobno kot smo navajeni pri računanju z oklepaji. Določajo pa tudi skupino, o kateri bomo več povedali v naslednjem poglavju. Oglati oklepaji določajo izbiro, se pravi ponoviti se mora vsaj en od naštetih znakov.
Poiščimo najprej vse podnize, kjer se "an" ponovi enkrat:
```{r}
str_view_all(c(listek1, listek2), "(an){1}")
```
Poiščimo vse besede dolžine 3. Da določimo začetek in konec besede, uporabimo `\\b`, ki določa prehod med besednim in nebesednim znakom. Za tem izberemo poljuben nabor črk, ki se mora ponoviti 3-krat:
```{r}
str_view_all(c(listek1, listek2), "\\b[:alpha:]{3}\\b")
```
Poiščimo vse besede dolžine med 3 ali 5 znakov:
```{r}
str_view_all(c(listek1, listek2), "\\b[:alpha:]{3,5}\\b")
```
Poiščimo vse besede dolžine točno 3 ali 5 znakov. V tem primeru moramo uporabiti "ali":
```{r}
str_view_all(c(listek1, listek2), "\\b([:alpha:]{3}|[:alpha:]{5})\\b")
```
Poskušajmo poiskati besede, ki se začnejo na p in končajo na k ali e.
```{r}
str_view_all(c(listek1, listek2), "\\bp[:alpha:]*[ke]\\b")
```
### Skupine
Kot smo že videli lahko uporabimo tudi oklepaje, da ločimo del regularnega izraza od ostalih simbolov. Oklepaji pa definirajo tudi skupino, na katero se lahko sklicujemo z `\x`, kjer je x zaporedna številka skupine.
Poglejmo si uporabo le tega na že vgrajenem vektorju sadja. Parameter `match = TRUE` nam vrne le nize, pri katerih je prišlo do ujemanja.
```{r}
str_view(fruit, "(..)\\1", match = TRUE)
```
Zgornji regularni izraz z `..` poišče dva poljubna znaka. Z `(..)` definiramo skupino. Z `\1` oziroma `\\1`, pa zahtevamo, da se na tem mestu še enkrat nahaja točno to kar je bilo med oklepaji.
Poiščimo imena sadežev, ki imajo vsaj dve (lahko različni) ponavljajoči si črki:
```{r}
str_view(fruit, "(.)\\1.*(.)\\2", match = TRUE)
```
### Uporaba regularnih izrazov
Uporabo regularnih izrazov si najprej pogljemo na preprostih podatkih, nato pa še na podatkih iz ankete.
Poiščimo sadje in zelenjavo, ki vsebuje šumnike:
```{r}
seznam <- c("paradižnik", "bučke", "jabolka", "paprika", "jagode");
str_detect(seznam, "[čšž]")
```
Če želimo namesto logičnega vektorja dobiti dejanska imena elementov, uporabimo `str_subset()`:
```{r}
str_subset(seznam, "[čšž]")
```
Poglejmo, koliko samoglasnikov vsebuje vsaka beseda v seznamu. Pozor, funkcija `str_count()` ne zazna ponovitev, če se le-te prekrivajo:
```{r}
str_count(seznam, "[aeiou]")
str_count("ababa", "aba") # Ne zazna dveh ponovitev, ker se prekrivata.
```
S funkcijo `str_locate()` vrnemo indekse lokacij najdenih vzorcev, s `str_extract()` pa dobimo ta vzorec oziroma podniz. S `str_match()` dobimo podskupine.
Poiščimo podnize do prvega šumnika.
```{r}
str_locate(seznam, "^.*[čšž]")
str_extract(seznam, "^.*[čšž]")
str_match(seznam, "(^.*)([čšž])")
```
V zadnjem primeru funkcija `str_match()` v prvem stolpcu vrne enako kot `str_extract()`, nato pa v naslednjih stolpcih vrne še vsako skupino posebej.
Preprosta in zelo uporabna pa je funkcija `str_replace()` oziroma `str_replace_all()`, s katero lahko zamenjamo dele nizov.
Prekrijmo šumnike z znaki "x":
```{r}
str_replace(seznam, "[čšž]", "x")
```
Podamo lahko tudi zamenjavo za več ujemanj:
```{r}
str_replace_all(seznam, c("č" = "c", "š" = "s", "ž"= "z"))
```
Poglejmo si sedaj še uporabo na realnih podatkih. Najprej naložimo podatke:
``` {r}
ds_jobs <- read.csv2("./data-raw/DS-jobs.csv")
ds_jobs <- tibble(ds_jobs)
ds_jobs
```
Najbolj uporabna funkcija pri delu z nizi v podatkih je `str_detect()`, ki nam omogoča izbiro podmnožice vrstic. Pogosto jo kombiniramo z regularnimi izrazi. Poiščimo tiste vrstice, kjer imajo anketiranci več službenih nazivov. Ti so ločeni z znakom /:
```{r}
ds_jobs %>%
select(CurrentJobTitle) %>%
filter(str_detect(CurrentJobTitle, "/"))
```
Izberimo samo tiste vrstice, kjer se `CurrentJobTitle` začne z "Data" in se `EmploymentStatus` konča s "full-time":
```{r}
ds_jobs %>%
select(EmploymentStatus, CurrentJobTitle) %>%
filter(str_detect(CurrentJobTitle, "^Data"),
str_detect(EmploymentStatus, "full-time$"))
```
Včasih so nizi v podatkih predolgi, oziroma lahko brez škode za pomen izbrišemo del niza. Najlažje to naredimo tako, da ga zamenjamo s praznim nizom. Izbrišimo podnize "Employed":
```{r}
ds_jobs <- ds_jobs %>%
mutate(EmploymentStatus = str_replace(EmploymentStatus, "Employed ", ""))
ds_jobs %>%
select(EmploymentStatus, CurrentJobTitle)
```
Sedaj imamo eno manjšo nekonsistentnost v podatkih. Nekateri nizi se začnejo z veliko začetnico, nekateri pa z malo. Spremenimo vse na malo začetnico:
```{r}
ds_jobs <- ds_jobs %>%
mutate(EmploymentStatus = str_to_lower(EmploymentStatus))
ds_jobs %>%
select(EmploymentStatus, CurrentJobTitle)
```
Funkcijo `str_count()` lahko uporabimo, da anketirancem dodamo stolpec s številom službenih nazivov:
```{r}
ds_jobs %>%
select(CurrentJobTitle) %>%
mutate(NumberOfJobTitles = str_count(CurrentJobTitle, "/") + 1)
```
Poskušajmo razdeliti CurrentJobTitle na FirstTitle in SecondTitle, pri tem naj se naziva ponovita, če ima anketiranec samo enega.
```{r}
ds_jobs %>%
select(CurrentJobTitle) %>%
mutate(FirstTitle = str_extract(CurrentJobTitle, "^[^/]*"),
SecondTitle = str_extract(CurrentJobTitle, "[^/]*$"))
```
## Kategorične spremenljivke
Kategorične spremenljivke so spremenljivke, ki lahko zavzamejo samo končno mnogo vnaprej določenih vrednosti. Najbolj pogoste so:
- **Nominalne spremenljivke**. To so spremenljivke brez ureditve: tip izdelka ali znamka avtomobila.
- **Ordinalne spremenljivke**. To so spremenljivke, ki imajo smiselno ureditev: stopnja izobrazbe ali šolski uspeh.
V R uporabljamo za delo s kategoričnimi spremenljivkami t. i. **faktorje** (ang. **factor**). Ti se od spremenljivk tipa niz razlikujejo v tem, da se v spremenljivki hrani informacija o vseh možnih vrednostih. Faktorju ni mogoče dodati vrednosti, ki je ni v množici možnih vrednosti, kar služi kot varovalka pred napakami pri vnosu podatkov.
Poglejmo si uporabo faktorja na dveh preprostih primerih, kjer bomo sami ustvarili spremenljivki. Kasneje si bomo pogledali še delo s faktorji na primeru realnih podatkov, kjer bomo ponovno uporabili podatke o zaposlitvah na področju podatkovnih ved.
Kot primer nominalne spremenljivke si oglejmo krvne skupine. Obstajajo 4 možne vrednosti. Ustvarimo vektor krvnih skupin za pet različnih oseb:
```{r}
krvne_skupine <- c("B", "B", "O", "AB", "BA")
```
Sedaj je ta vektor shranjen kot niz. Kaj so slabosti takšnega shranjevanja kategoričnih podatkov? Prvič, nimamo nobenega varovala pred tipkarskimi napakami -- R je zadnji vnos prebral kot *BA* in ga tako tudi shranil, čeprav ta krvna skupina ne obstaja:
```{r}
krvne_skupine
```
Pri ročnem vnosu podatkov hitro pride do tipkarskih napak in načeloma ne vemo, ali je avtor podatkov v tem primeru želel vnesti A, B ali AB.
Drugič, če želimo urediti to spremenljivko, se bodo vrednosti razvrstile po abecedi:
```{r}
sort(krvne_skupine)
```
Morda pa bi bilo bolj smiselno urediti po standardni ureditvi, torej A, B, AB, O.
Da se izognemo tem težavam, je bolje, če spremenljivko, za katero vemo, da bo zasedla eno od vnaprej določenih vrednosti, shranimo kot faktor. V R za to uporabimo funkcijo `factor()`. Naredimo faktor iz spremenljivke `krvne_skupine`:
```{r}
krvne_skupine_fac <- factor(krvne_skupine)
krvne_skupine_fac
```
Opazimo, da je sedaj spremenljivka drugačnega tipa, saj hrani tudi informacijo o možnih vrednostih oziroma ravneh (ang. levels). Ampak v tem primeru so te ravni napačne (ne zajame vseh 4 krvnih skupin, poleg tega pa vsebuje tudi eno napačno vrednost). Funkcija `factor()` privzeto kot ravni nastavi vse vrednosti v podani spremenljivki. Če želimo, ji lahko podamo dodaten argument `levels`, kjer ročno določimo, katere ravni bodo v spremenljivki. V kolikor to vemo vnaprej, je dobra praksa, da podamo tudi ta argument:
```{r}
krvne_skupine_fac <- factor(krvne_skupine, levels = c("A", "B", "AB", "O"))
krvne_skupine_fac
sort(krvne_skupine_fac)
```
Opazimo dvoje: sedaj lahko spremenljivko uredimo glede na standardno notacijo in nesmiselne vrednosti se spremenijo v `NA`. Faktorju torej ne moremo prirediti vrednosti, ki ni enaka eni izmed vrednosti v ravneh. Da dostopamo do vseh ravni faktorja, uporabimo funkcijo `levels()`:
```{r}
levels(krvne_skupine_fac)
```
Včasih imajo kategorične spremenljivke tudi smiselno razvrstitev po velikosti, ki pa se običajno ne da numerično izmeriti. Kot primer si poglejmo šolski uspeh, ki lahko zavzame 5 vrednosti. V kolikor želimo, da faktor hrani tudi informacijo o tem, da obstaja smiselna razvrstitev po velikosti, dodamo argument `ordered = TRUE`.
```{r}
uspeh <- factor(c("odlično", "dobro", "dobro", "prav dobro"),
levels = c("nezadostno", "zadostno", "dobro", "prav dobro", "odlično"),
ordered = TRUE)
uspeh
```
Opazimo, da imamo sedaj pri izpisu nivojev dodatno informacijo o razvrstitvi uspeha. V praksi nam to omogoča primerjavo, medtem ko tega pri faktorjih, ki nimajo razvrstitve po velikosti, ne moremo narediti.
```{r}
uspeh[2] > uspeh[1]
krvne_skupine_fac[2] > krvne_skupine_fac[1]
```
Poleg prednosti, ki smo jih že omenili, imajo faktorji posebno vlogo pri raznih statističnih modelih in modelih strojnega učenja. Nekatere metode eksplicitno zahtevajo faktorje. Prav tako razlikujejo med nominalnimi in ordinalnimi faktorji, kar se pozna na rezultatih. Ravno tako so faktorji pomembni v primeru, ko poznamo nabor možnih vrednosti a nekaterih pri meritvah še nismo opazili. Pri izrisu želimo take vrednosti izpisati z vrednostjo 0 ali NA.
Poglejmo si uporabo faktorjev na realni podatkovni množici. Ponovno bomo delali s podatki o zaposlitvah na področju podatkovnih ved. Preberimo podatke in ponovimo nekaj operacij, ki smo jih spoznali na prvem predavanju. Prav tako bomo izbrali samo podmnožico stolpcev za bolj jasen prikaz.
```{r}
library(tidyverse)
ds_jobs <- read_csv2("./data-raw/DS-jobs.csv") %>%
select(Country, Age, EmploymentStatus,
FormalEducation, CompensationAmount, ExchangeRate) %>%
filter(!is.na(ExchangeRate)) %>%
mutate(CompensationUSD = CompensationAmount * ExchangeRate) %>%
filter(CompensationUSD <= 2500000, CompensationUSD >= 10000)
ds_jobs
```
Imamo 3 spremenljivke, ki bi jih bilo smiselno shraniti kot faktorje -- `Country`, `EmploymentStatus` in `FormalEducation`. Pretvorimo jih v faktorje. Pri tem pustimo kar privzeto nastavitev, da se kot nivoji uporabijo vse vrednosti v stolpcih.
```{r}
library(tidyverse)
ds_jobs <- ds_jobs %>%
mutate(Country = factor(Country),
EmploymentStatus = factor(EmploymentStatus),
FormalEducation = factor(FormalEducation))
ds_jobs
levels(ds_jobs$Country)
levels(ds_jobs$EmploymentStatus)
levels(ds_jobs$FormalEducation)
```
Kaj se zgodi, če želimo v tibblu spremeniti vrednost faktorja v neko vrednost, ki je ni v tem faktorju? Recimo, da želimo v 2. vrstici spremeniti državo v Narnia:
```{r}
ds_jobs2 <- ds_jobs
ds_jobs2$Country[2] <- "Narnia"
ds_jobs2
```
Ker Narnia ni nivo v faktorju, se nadomesti z `NA`. Če želimo dodati nov nivo temu faktorju, uporabimo `fct_expand()`:
```{r}
ds_jobs2 <- ds_jobs %>%
mutate(Country = fct_expand(Country, "Narnia"))
ds_jobs2$Country[2] <- "Narnia"
ds_jobs2
```
### Sprememba razvrstitve faktorja
Kot smo omenili že pri krvnih skupinah, imajo velikokrat tudi faktorji, ki niso razvrščeni po velikosti, neko ustaljeno ureditev. Ureditev pa lahko tudi kasneje spremenimo. Ta operacija je običajno uporabna pri vizualizaciji. Poglejmo si, na primer, kako so plače povezane z izobrazbo. Za vizualizacijo rezultatov bomo uporabili razsevni diagram:
```{r, fig.height = 2.5, fig.width = 8}
ds_jobs_agg <- ds_jobs %>%
group_by(FormalEducation) %>%
summarise(MeanCompensationUSD = mean(CompensationUSD))
ds_jobs_agg
ggplot(ds_jobs_agg, aes(x = FormalEducation, y = MeanCompensationUSD)) +
geom_point() +
coord_flip() +
xlab("")
```
Ta graf je sicer zelo informativen, ampak bi s težavo hitro ugotovili, kako so nivoji faktorja razvrščeni glede na plačo. ggplot razvrsti vrednosti glede na to, kako so razvrščene v faktorju:
```{r}
levels(ds_jobs$FormalEducation)
```
Morda bi bilo bolje tak graf urediti glede na vrednosti spremenljivke `MeanCompensationUSD`, za kar moramo določiti novo razvrstitev te spremenljivke. Za to obstaja v paketu **forcats**, ki je del tidyverse, funkcija `fct_reorder()`.
```{r, fig.height = 2.5, fig.width = 8}
ggplot(ds_jobs_agg, aes(x = fct_reorder(FormalEducation, MeanCompensationUSD), y = MeanCompensationUSD)) +
geom_point() +
coord_flip() +
xlab("")
```
Razvrstitev lahko uredimo tudi ročno s funkcijo `fct_relevel()`, ki ohrani privzeto razvrstitev tako, da podane nivoje premakne na začetek, oziroma lahko podamo argument `after`.
```{r, fig.height = 2.5, fig.width = 8}
ggplot(ds_jobs_agg, aes(x = fct_relevel(FormalEducation, "I prefer not to answer", "I did not complete any formal education past high school", after = 0), y = MeanCompensationUSD)) +
geom_point() +
coord_flip() +
xlab("")
```
### Preimenivanje obstoječih nivojev
Nivoje faktorjev lahko preimenujemo s funkcijo `fct_recode()`.
```{r}
ds_jobs <- ds_jobs %>%
mutate(EmploymentStatus = fct_recode(EmploymentStatus,
"full-time" = "Employed full-time",
"part-time" = "Employed part-time",
"other" = "Independent contractor, freelancer, or self-employed"))
head(ds_jobs$EmploymentStatus)
```
Sorodno lahko zgornjo funkcijo zamenjamo s `fct_collapse()`, ki lahko združi več nivojev v enega.
### Razbitje numerične spremenljivke na intervale
Pogosto želimo numerično spremenljivko segmentirati na določene intervale. Na primer, pri določanju avtomobilskih zavarovalnih premij lahko zavarovance segmentiramo glede na starost. V R za to uporabimo funkcijo `cut()`. Razdelimo spremenljivko `Age` na intervale, kjer bodo osebe razdeljene do 25 let, nad 25 in to 35 let, nad 35 do 50 let, in nad 50 let.
```{r}
ds_jobs <- ds_jobs %>%
mutate(AgeInterval = cut(Age, breaks = c(0, 25, 35, 50, 100)))
ds_jobs_agg <- ds_jobs %>%
group_by(AgeInterval) %>%
summarise(CompensationByAge = mean(CompensationUSD))
ggplot(ds_jobs_agg, aes(x = AgeInterval, y = CompensationByAge)) + geom_point()
```
## Datumi in ure
Delo z datumi in urami na prvi pogled morda deluje enostavno. Vendar pa zaradi različnih fizikalnih zakonitosti ali človeških konstruktov lahko pride do težav. Na primer, vsako leto nima 365 dni. Prav tako v nekaterih časovnih conah 3. ura zjutraj ne sledi vedno 2. uri, saj pride do premika ure.
Za delo z datumi bomo uporabljali paket **lubridate**. Glavni komponenti v tem paketu sta **datum** (date) in **čas** (time) ter združena komponenta **datum in čas** (datetime). S tem paketom lahko datume ustvarimo na 2 načina:
1) Z nizom:
```{r}
library(lubridate)
ymd("2021-04-02")
ymd("2021/04/02")
ymd(20210402)
dmy("02.04.2021")
ymd_hms("2021-04-02 12:01:00") # Tipa datetime.
ymd(20210402, 20210403)
mdy("April 2nd, 2021") # Deluje za angleška imena mesecev.
```
2) S posameznimi komponentami:
```{r}
make_date(2021, 4, 2)
make_datetime(2021, 4, 2, 12, 1, 0)
```
Opazimo, da pri datumu in času spremenljivka hrani tudi informacijo o časovnem pasu. Privzeto lubridate dela s časovnim pasom UTC (Coordinated Universal Time), ki je naslednik GMT (Greenwich Mean Time). Prednost tega časovnega pasu je , da se ne prilagaja spremembi ure v pomladnih in jesenskih mesecih. Te spremembe lahko privedejo do napak pri računanju z datumi in časi, tako da je računanje v UTC bolj varno. Seveda pa lahko ročno nastavimo drugi časovni pas z argumentom `tz`. Paket lubridate uporablja IANA časovne pasove (https://www.iana.org/time-zones), ki so definirani s kombinacijo celine in države. Za Ljubljano bi časovni pas nastavili tako:
```{r}
ymd_hms("2021-04-02 12:01:00", tz = "Europe/Ljubljana")
```
Pomembno je , da vemo, v katerem časovnem pasu so bile opravljene meritve v naših podatkih, da lahko potem ustrezno pretvorimo spremenljivko v časovno. Seveda pa lahko tudi pretvarjamo časovne spremenljivke med časovnimi pasovi. Za to uporabimo funkcijo `with_tz()`. Vsakemu času v določenem časovnem pasu lahko priredimo nek čas v drugem časovnem pasu. V kolikor želimo bolj robustno računati z datumi in urami, lahko vedno datume pretvorimo v UTC čas, naredimo izračune in potem pretvorimo nazaj v lokalni časovni pas.
```{r}
my_datetime <- ymd_hms("2021-04-02 12:01:00", tz = "Europe/Ljubljana")
my_datetime
my_datetime_UTC <- with_tz(my_datetime, tz = "UTC")
my_datetime_UTC
```
V ozdaju pa spremenljivki hranita isti čas. To lahko preverimo z:
```{r}
my_datetime == my_datetime_UTC
```
V R je časovni pas namenjen samo izpisu datumov in časov. Vrednost spremenljivke ostane nespremenjena. To lahko preverimo tako, da odštejemo en datum od drugega, kar nam vrne razliko v času:
```{r}
my_datetime - my_datetime_UTC
```
V kolikor smo narobe prebrali datum v začetku (na primer, v podatkih je bil datum v UTC, prebrali pa smo v lokalnem času) zgornja pretvorba med časovnimi pasovi ni ustrezna, saj bomo s tem zajeli napačen čas. V tem primeru moramo uporabiti funkcijo `force_tz()`. Poizkusite kaj naredi ta funkcija, tako da z njo pretvorijo `my_datetime` v UTC in potem izračunajte razliko, podobno kot smo to naredili zgoraj.
Kadar delamo sekvence datumov in časov, te upoštevajo premik ure in prehodov v naslednje dni. Poglejmo si prehod na poletni čas v letu 2021:
```{r}
datetime_dst <- seq(ymd_hms("2021-03-28 00:00:00", tz = "Europe/Ljubljana"),
ymd_hms("2021-03-28 04:00:00", tz = "Europe/Ljubljana"),
by = "30 min")
datetime_dst
with_tz(datetime_dst, tz = "UTC")
```
Pozorni moramo biti tudi na kombiniranje datumov. V kolikor uporabimo funkcijo `c()`, vedno preverimo, v katerem časovnem pasu je rezultat.
### Računanje z datumi in časi
Vsaka časovna spremenljivka, ki vsebuje datum in čas, je sestavljena iz komponent. Te so leto, mesec, dan, ura, minuta in sekunda. Za dostop do posameznih komponent imamo na voljo več funkcij:
- `year()`
- `month()`
- `mday()` Dan v mesecu.
- `wday()` Dan v tednu. Privzeto se začne z nedeljo. To lahko spremenimo z argumentom `week_start`.
- `hour()`
- `minute()`
- `second()`
Poglejmo sedaj kaj vračajo te funkcije:
```{r}
x <- now()
x
year(x)
month(x)
mday(x)
wday(x)
wday(x, week_start = 1)
hour(x)
minute(x)
second(x)
```
S komponentami lahko tudi spreminjamo dele časovne spremenljivke:
```{r}
mday(x) <- 5
x
```
Pri računanju s časovnimi enotami v lubridate poznamo tri razrede:
- **trajanja** (ang. duration). Čas v sekundah. Funkcije `dseconds()`, `dminutes()`, `ddays()`, `dweeks()` in `dyears()`. Pri trajanjih se vedno uporabi pretvorba, da ima vsak dan 24 ur in vsako leto 365.25 dni. Slednje predstavlja povprečno šteilo dni v letu. Tako da bo funkcija `dyears(4)` vedno vrnila število sekund, ki ustreza 4x365.25 dnem, ki imajo vsak po 24 ur.
- **obdobja** (ang. period). Čas v človeških enotah kot je na primer teden. Funkcije `seconds()`, `minutes()`, `days()`, `weeks()`, `months()` in `years()`.
- **intervali** (ang. interval). Časovni interval med dvema točkama.
Pozoren bralec je opazil, da pri trajanjih nismo navedli funkcije za mesece. To pa zato, ker imajo meseci lahko 28, 29, 30 ali 31 dni. Vsekakor bi pri izbiri osnovne enote za trajanja prišlo do neke arbitrarne odločitve, koliko dni vzamemo privzeto. 30 ali 31? V vsakem primeru bo vsaj polovica mesecev imela napačno trajanje. Pri dnevih in letih si lažje privoščimo posplošitev.
```{r}
ddays(1)
days(1)
```
Poglejmo si preprost primer, kako dodati določena obdobja:
```{r}
my_datetime <- ymd_hms("2021/06/08 11:05:30", tz = "Europe/Ljubljana")
my_datetime + ddays(1)
my_datetime + days(1)
my_datetime + dminutes(120)
my_datetime + minutes(120)
my_datetime + months(2)
```
Trajanja in obdobja so si očitno zelo podobni, ampak imajo eno veliko razliko, kadar računamo z dnevi, tedni in leti. Prvič, kadar bomo uporabljali `dyears()` lahko hitro pride do težave, saj bomo prišteli 0.25 dneva. Poglejmo si to na primeru:
```{r}
my_datetime + years(1)
my_datetime + dyears(1)
```
Opazimo, da smo prišteli 6 dodatnih ur. Drugič, kaj se zgodi, kadar prištejemo teden ali dan v času, ko pride do premika ure. Premik ure se je po lokalnem času zgodil 28. 3. 2021 ob 2 zjutraj.
```{r}
my_datetime <- ymd_hms("2021/03/27 11:05:30", tz = "Europe/Ljubljana")
my_datetime + ddays(1)
my_datetime + days(1)
my_datetime + dweeks(1)
my_datetime + weeks(1)
```
Funkcija `years()` deluje, kot bi pričakovali tudi na prestopnem letu:
```{r}
my_datetime <- ymd_hms("2020/06/08 11:05:30", tz = "Europe/Ljubljana")
my_datetime + years(1)
```
S funkcijami trajanja in obdobji lahko tudi računamo, na primer:
```{r}
dyears(2) + ddays(4) + dseconds(20)
days(2) + minutes(20) + seconds(120)
5 * dminutes(20)
5 * minutes(20)
```
V bazi letalskih letov posodobimo podatke tako, da izračunamo niz, ki predstavlja odhod letala.
```{r}
library(nycflights13)
head(flights)
```
``` {r}
flights_datetime <- flights %>% select(year, month, day, hour, minute) %>%
mutate(DepartureTime = make_datetime(year, month, day, hour, minute))
flights_datetime
head(flights_datetime$DepartureTime)
```
Ali opazimo kakšno težavo? Datume smo prebrali v časovni coni UTC, podani pa so v lokalni časovni coni. Podatkov torej nismo pravilno pretvorili! Poizkusimo še enkrat:
``` {r}
flights_datetime <- flights %>% select(year, month, day, hour, minute) %>%
mutate(DepartureTime = make_datetime(year, month, day, hour, minute,
tz = "America/New_York"))
flights_datetime
head(flights_datetime$DepartureTime)
```
## Shranjevanje in branje podatkov
### Delo z binarnimi datotekami
V programskem jeziku R lahko spremenljivke shranjujemo in beremo (v trenutno sejo R) kot binarne objekte. Prednost tega shranjevanja pred shranjevanjem v csv ali excelove razpredelnice je, da se ohranijo tudi meta informacije, kot so recimo nivoji pri faktorjih. Shranjujemo na dva prevladujoča načina:
1) S kombinacijo funkcij `save()` in `load()`.
2) S kombinacijo funkcij `saveRDS()` in `readRDS()`.
Pomembna razlika med prvim in drugim pristopom je, da lahko s prvim shranimo več spremenljivk naenkrat, z drugim pa samo eno. Na prvi pogled bi torej pričakovali, da je prvi pristop boljši, oziroma bolj zaželen. Ampak ima eno pomembno slabost, zaradi katere predlagamo uporabo drugega pristopa.
Funkcija `save()` shrani spremenljivke v trenutni seji R v datoteko s končnico *rda* ali *RData*. To naredi tako, da shrani tako vrednost spremenljivke kot tudi ime spremenljivke. To pomeni, da ko bomo takšno datoteko prebrali v novo sejo R, bomo ustvarili spremenljivke z enakimi imeni, kot smo jih shranili. Pri tem pa lahko pride do težav. Recimo, da imamo v trenutni seji R že nek nabor spremenljivk nato pa želimo vanjo prenesti še neke druge spremenljivke, ki smo jih pred časom shranili s funkcijo `save()` v datoteko saved-data.rda. Kaj se bo zgodilo, če bo katera od spremenljivk v naši trenutni seji imela enako ime kot ena od spremenljivk shranjenih v saved-data.rda? R bo enostavno to spremenljivko prepisal s spremenljivko, ki se je nahajala v tej rda datoteki. Takšen postopek dela je lahko torej nevaren, saj lahko *nevede* izbrišemo obstoječe spremenljivke.
Predlagamo torej uporabo kombinacije funkcij `saveRDS()` in `readRDS()`. Funkcija `saveRDS()` shrani samo vrednost spremenljivke, ne pa tudi njenega imena, tako da ne pride do podobnih težav kot pri prvem pristopu. Končnica tako shranjenih datotek je *rds*. Poglejmo si uporabo teh funkcij:
```{r}
x <- c(3, 6, 3, 7)
x
saveRDS(x, "./my-saved-files/my-x.rds")
x2 <- readRDS("./my-saved-files/my-x.rds")
x2
```
Vedno ko preberemo podatke v sejo R s funkcijo `readRDS()`, ji moramo prirediti ime, saj je v rds datoteki shrnajena samo njena vrednost. S tem se tudi izognemo podobnim težavam kot pri funkcijah `save()` in `load()`.
Pomanjkljivost shranjevanja datotek rds je v tem, da lahko naenkrat shranimo samo 1 spremenljivko. Ampak to pomanjkljivost lahko zaobidemo tako, da več spremenljivk enostavno shranimov v seznam (`list()`). Poglejmo si sedaj na primer, kako bi shranili več spremenljivk.
```{r}
tmp_list <- list(
"x" = x,
"some_datetime" = my_datetime,
"ds_jobs" = ds_jobs
)
saveRDS(tmp_list, "./my-saved-files/my-list.rds")
read_list <- readRDS("./my-saved-files/my-list.rds")
names(read_list)
x2 <- read_list[["x"]]
x2
my_datetime2 <- read_list[["some_datetime"]]
my_datetime2
ds_jobs2 <- read_list[["ds_jobs"]]
ds_jobs2
```
### Branje in shranjevanje z ostalimi datotekami
#### Excel
Paket **openxlsx** omogoča delo z exelovimi razpredelnicami. Poglejmo si preproste a uporabne ukaze. Za branje podatkov s tem paketom uprabljamo `read.xlsx()`, za shranjevanje datotek pa `write.xlsx()`.
Preberimo podatke ocen, ki smo jih imeli v testni skripti, in jo pretvorimo v tibble:
```{r}
library(openxlsx)
ocene <- tibble(openxlsx::read.xlsx("./test-script/data-raw/student-performance.xlsx"))
ocene
```
Privzeto funkcija `read.xlsx()` odpre prvi list v datoteki in poišče glavo razpredelnice. Če želimo druge liste moramo podati ime lista ali številko lista s parametrom `sheet`:
```{r}
library(openxlsx)
ocene_portugalscina <- tibble(read.xlsx("./test-script/data-raw/student-performance.xlsx", sheet = 1))
ocene_matematika <- tibble(read.xlsx("./test-script/data-raw/student-performance.xlsx", sheet = "Math scores"))
ocene_portugalscina %>%
select(school, G1, G2, G3, averageScore)
ocene_matematika %>%
select(school, G1, G2, G3, averageScore)
```
Če ima datoteka veliko število listov, jih lahko shranimo v R-jev list:
```{r}
library(openxlsx)
path <- "./test-script/data-raw/student-performance.xlsx"
imenaListov <- getSheetNames(path)
ocene_s <- list()
for(i in 1:length(imenaListov)) {
ocene_s[[i]] <- tibble(read.xlsx(path))
}
ocene_s
```
V primeru, da je razpredelnica v excelovem listu premaknjena ali pa želimo prebrati le del podatkov, lahko uporabimo parametra `startRow` in `cols`. Preberimo samo ocene za matematiko:
```{r}
path <- "./test-script/data-raw/student-performance.xlsx"
ocene_math <- tibble(read.xlsx(path, sheet = "Math scores", startRow = 1,
cols = 9:12))
ocene_math
```
Da podatke shranimo, uporabimo `write.xlsx()`.
```{r}
path <- "./test-script/data-raw/samo-ocene-math.xlsx"
write.xlsx(ocene_math, path)
```
#### SPSS
SPSS je program za statistično analizo. Datoteke povezane z SPSS imajo običajno končnico *.sav*. Za branje iz in uvažanje v SPSS lahko uporabimo paket **haven**. V mapi *data_raw* imamo podatke o osebah *osebe.sav*. Za uvoz teh podatkov v R uporabimo funkcijo `read_sav`.
```{r}
library(haven)
podatki <- read_sav("./data-raw/osebe.sav")
podatki