5  Speciální datové typy

Kromě základních datových typů existují v R i další datové typy, které jsou implementované jako třídy objektů (o nich se více dozvíte v kapitole 8). Z nich nejdůležitější jsou faktory a třídy pro uchovávání datumů a času. Všechny tyto typy jsou implementovány jako rozšíření datových typů integer nebo double.

V této kapitole se naučíte

5.1 Faktory

Faktory slouží k uchovávání kategoriálních proměnných. Kategoriální proměnné jsou proměnné, kterou mohou nabývat jen určitých předem stanovených úrovní. Tyto úrovně mohou být buď uspořádané (pak se jedná o ordinální proměnné), nebo neuspořádané. Příkladem ordinální kategoriální proměnné je kvalita služeb zjišťovaná v dotazníku. Ta může nabývat např. hodnot “velmi špatná”, “špatná”, “průměrná”, “dobrá” a “výborná”. Jiné úrovně nejsou (v rámci kódování dotazníku) možné. Přitom platí, že hodnoty jsou uspořádané od nejhorší po nejlepší, takže vždy můžeme dvě úrovně porovnat a říci, která je lepší. Příkladem neordinální kategoriální proměnné je např. pohlaví, které může nabývat hodnot “žena” nebo “muž”. Na rozdíl od předchozího případu zde není jasné pořadí, ve kterém by měly být hodnoty uspořádány.

Kategoriální proměnné je možné kódovat např. jako celá čísla: např. muž bude 0 a žena 1, nejhorší kvalita služby bude 0, druhá nejhorší 1 atd. To však není dobrý nápad hned z několika důvodů: 1) je obtížné pamatovat si, co která hodnota znamená, 2) R nebude vědět, jak s takovými proměnnými zacházet a bude je považovat za kardinální veličiny (tj. bude např. kvalitu služby “průměrnou” kódovanou jako 2 považovat za dvakrát lepší než kvalitu “špatnou” kódovanou jako 1, přestože jediné, co víme, je, že “průměrná” kvalita je lepší než “špatná”, ale už ne o kolik nebo kolikrát) a 3) R nebude schopné hlídat, zda není zadána nesmyslná úroveň proměnné (např. kvalita služby 7). Faktory řeší všechny tyto problémy: jednotlivým hodnotám dávají “nálepky”, které ukazují na jejich význam, a zároveň říkají R, že se jedná o kategoriální proměnnou. Ve statistické analýze pak zachází s faktory správně; v ekonometrické analýze např. automaticky vytvoří pro jednotlivé úrovně faktoru potřebné umělé proměnné. R také zná platné úrovně faktorů a hlídá, zda je zadaná úroveň platná.

Tvorba faktorů ze zadaných hodnot

Faktory se tvoří pomocí funkce factor(). Ta vyžaduje nutně pouze vektor hodnot, které se mají na faktor převést:

factor(c("žena", "muž", "muž", "žena"))
[1] žena muž  muž  žena
Levels: muž žena

V tomto případě R odhaduje úrovně faktoru z dat, což není příliš bezpečné, protože v konkrétním datovém vzorku může některá platná úroveň faktoru chybět a některá zadaná úroveň nemusí být platná (např. kvůli chybě zapisovatele do dotazníků). Obecně je bezpečnější říct funkci factor() i to, jakých hodnot může faktor nabývat (pomocí parametru levels). To umožní zadat i hodnoty, které nyní zadaný vektor neobsahuje, ale obsahovat by je mohl, a také určit pořadí faktorů, které R jinak řadí podle abecedy. Pořadí úrovní je důležité zejména pro ordinální faktory. U neordinálních faktorů je první úroveň brána v některých statistických metodách jako referenční. Parametr labels navíc umožňuje úrovně faktorů překódovat:

factor(c("male", "female", "female", "male", "female"),  # hodnoty vektoru
       levels = c("female", "male", "asexual"),          # možné úrovně
       labels = c("žena", "muž", "asexuální"))            # co se bude vypisovat
[1] muž  žena žena muž  žena
Levels: žena muž asexuální

Všimněte si, že při vypsání faktor vypisuje nejen hodnoty vektoru, nýbrž i seznam hodnot, kterých mohou nabývat.

Pokud se pokusíte uložit do faktoru hodnotu, která neodpovídá zadaným úrovním, R danou hodnotu tiše nahradí hodnotou NA:

factor(c("male", "female", "female", "male", "beaver"),  # hodnoty vektoru
       levels = c("female", "male", "asexual"),          # možné úrovně
       labels = c("žena", "muž", "asexuální"))            # co se bude vypisovat
[1] muž  žena žena muž  <NA>
Levels: žena muž asexuální

To je velmi šikovné, protože to umožňuje hlídat, zda jsou všechny hodnoty zadané správně. Řekněme, že svá data stahujete z nějakého serveru, který s vámi nespolupracuje a může kdykoli bez varování změnit kódování některých kategoriálních hodnot. Jedna možnost, jak to zjistit, je převádět je z řetězců na faktory s pevně zadanými hodnotami úrovní. Pokud se mezi hodnotami faktoru objeví NA, znamená to, že server změnil kódování dané proměnné.

Implicitně jsou všechny faktory ne-ordinální. Pokud chcete R říci, že faktor je ordinální, přidáte parametr ordered = TRUE. Pak na faktory funguje porovnání větší a menší:

quality <- factor(c("poor", "satisfactory", "excelent"),
                  levels = c("poor", "satisfactory", "excelent"),
                  ordered = TRUE)
quality
[1] poor         satisfactory excelent    
Levels: poor < satisfactory < excelent
quality[1] < quality[3]  # quality[i] je i-tý prvek vektoru
[1] TRUE

Funkce is.ordered() vrací logickou hodnotu TRUE, pokud je faktor ordinální.

Pozor! Pokud opravdu dobře nevíte, co děláte, používejte raději ne-ordinální faktory. Ordinální faktory se ve formulích (tj. např. v ekonometrické analýze, viz kapitola 15) chovají jinak, než byste možná čekali, viz https://goo.gl/HY3uNf a https://goo.gl/F9Shll, oddíl 11.1.1.

Hodnoty úrovní můžete získat pomocí funkce levels(); jejich počet pomocí funkce nlevels(). Funkce levels() umožňuje i měnit hodnoty úrovní faktoru:

f <- factor(c("female", "male", "female"))
f
[1] female male   female
Levels: female male
levels(f) <- c("a", "b", "c")
f
[1] a b a
Levels: a b c

Tvorba faktoru ze spojité proměnné

Někdy je užitečné rozdělit spojitou škálu do diskrétních hodnot, takže spojitou proměnnou změníte na ordinální faktor. Např. můžeme chtít rozdělit studenty do tří kategorií podle jejich studijního průměru: na excelentní žáky (do průměru 1.2 včetně), běžné žáky (od průměru 1.2 do 2.5 včetně) a ostatní. K tomu slouží funkce cut():

grades <- c(1.05, 3.31, 2.57, 1.75, 2.15)  # studijní průměry
students <- cut(grades, breaks = c(0, 1.2, 2.5, Inf), right = TRUE)
students
[1] (0,1.2]   (2.5,Inf] (2.5,Inf] (1.2,2.5] (1.2,2.5]
Levels: (0,1.2] (1.2,2.5] (2.5,Inf]
levels(students) <- c("excelent", "normal", "rest")
students
[1] excelent rest     rest     normal   normal  
Levels: excelent normal rest

Změnu názvů úrovní je možné nastavit přímo ve funkci cut() parametrem labels. Detaily použití funkce cut() najdete v dokumentaci funkce.

Jako mnoho jiných funkcí ve standardní výbavě R má i funkce cut() mnoho (na první pohled) složitých parametrů. Balík ggplot2 nabízí tři specializovanějí funkce, které udělají stejnou práci jako cut(), ale jejich použití je jednodušší: cut_width() nařeže původní proměnnou na úrovně s danou šířkou, cut_interval() na daný počet úrovní se stejnou šířkou a cut_number() na daný počet úrovní se stejným počtem pozorování.

Převod faktorů na čísla

Faktory jsou užitečné, ale i zrádné. Technicky jsou implementované jako vektor celých čísel, který má navíc pojmenované úrovně:

unclass(factor(c("žena", "muž", "muž", "žena")))
[1] 2 1 1 2
attr(,"levels")
[1] "muž"  "žena"

Při automatické konverzi se tedy může stát, že se faktor převede na celá čísla – svých úrovní. Většinou to nevadí, existují však zrádné situace, kdy název úrovně faktoru je složen z číslic. Nejdříve si to ukážeme na poněkud legračním příkladu:

# vektor typů vašich letadel Boeing
f <- factor(c("747", "737", "777", "747"))
f
[1] 747 737 777 747
Levels: 737 747 777
# chcete dostat zpět typy letadel, ale ouha! dostanete čísla úrovní!
as.integer(f)
[1] 2 1 3 2
# je potřebná dvojí konverze!
as.integer(as.character(f))
[1] 747 737 777 747

Realističtější situace vznikne, když faktory odpovídají nařezané spojité proměnné. Zde si ji vytvoříme, ale typicky už je taková proměnná obsažena v datech:

# sto čísel náhodně vybraných z normovaného norm. rozdělení
x <- rnorm(100)
# faktor nařezaný od -10 po +10 po dvou, jméno binu je střed intervalu
h <- cut(x,
         breaks = seq(from = -10, to = 10, by = 2),
         labels = seq(from = -9, to = 9, by = 2))
h
  [1] 1  -1 -1 -1 -1 -1 -1 -1 1  1  1  1  -1 1  1  1  1  -1 -1 -1 1  -1 1  -1 -1
 [26] -1 -1 -1 1  1  -1 1  -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1  1  -1 1  -1 1  -1
 [51] 1  -1 -1 1  -1 1  1  1  1  -1 -1 1  -1 1  1  -1 -1 1  -1 1  -1 1  1  1  1 
 [76] -1 -1 -1 -1 -1 1  1  -1 1  -1 -1 -1 -1 1  -1 1  1  1  -1 1  -1 1  -1 -1 1 
Levels: -9 -7 -5 -3 -1 1 3 5 7 9

Řekněme, že chceme spočítat průměrnou hodnotu pozorování a zkusíme to udělat takto:

mean(as.numeric(h))
[1] 5.43

V našem případě bychom čekali výsledek někde kolem nuly, takže náš výsledek je očividně nesprávný. V případě skutečných dat s neznámým rozdělením si však nemusíte všimnout ničeho podezřelého. Chybný výsledek vznikl tak, že R prvně převedlo funkcí as.numeric() faktory na jejich úrovně označeného 1, 2 atd. a z nich následně spočítalo průměr. To, co jsme chtěli dostat, bylo ve skutečnosti toto:

mean(as.numeric(as.character(h)))
[1] -0.14
mean(as.numeric(levels(h)[h]))  # totéž jinými slovy
[1] -0.14

Úpravy faktorů

Většinou pracujeme s faktory tak, jak jsou. Někdy je však potřebujeme nějakým způsobem transformovat: vyřadit nepoužité úrovně, spojit různé faktory, přejmenovat jejich úrovně apod. Některé z těchto úprav jsou překvapivě netriviální. Naštěstí existuje balík forcats, který tyto úpravy zjednodušuje. Zde se podíváme jen na vybrané základní situace. Pro náročnější situace doporučujeme se podívat na dokumentaci balíku forcats.

Obvykle nejpotřebnější úpravou faktorů je vyřazení nepotřebných úrovní. Pokud totiž z faktoru vybereme jen některá pozorování, zůstanou ve faktoru zachovány všechny úrovně, i ty, kterým ve vektoru už neodpovídá žádné pozorování:

h[1:5]
[1] 1  -1 -1 -1 -1
Levels: -9 -7 -5 -3 -1 1 3 5 7 9

Pokud chceme nepoužité úrovně vypustit, musíme operátoru hranatá závorka dát parametr drop=TRUE:

h[1:5, drop = TRUE]
[1] 1  -1 -1 -1 -1
Levels: -1 1

Druhou častou úpravou je spojování dvou vektorů faktorů. Od verze 4.1 funguje spojování faktorů přesně tak, jak byste očekávali:

f1 <- factor(c("a", "b", "c"))
f2 <- factor(c("x", "y", "z"))
c(f1, f2)
[1] a b c x y z
Levels: a b c x y z

Poslední obvyklou transformací faktorů je změna pořadí jejich úrovní. K tomu existují v principu dva důvody: Zaprvé, někdy potřebujeme nastavit referenční úroveň faktoru. Např. v ekonometrii R samo vytvoří pro jednotlivé úrovně faktoru potřebné umělé proměnné. Přitom samozřejmě jednu úroveň (referenční úroveň nebo také kontrast) vynechá. R automaticky vynechává první úroveň. Pokud jsme nezadali jména úrovní explicitně, pak R vynechá tu úroveň, která je první v abecedě. To však často není to, co chceme. Změnu referenční úrovně provedeme snadno pomocí funkce relevel(). Jejím první parametrem je faktor, druhým je nová referenční úroveň (všimněte si pořadí úrovní na konci výpisu faktoru):

relevel(h, "1")
  [1] 1  -1 -1 -1 -1 -1 -1 -1 1  1  1  1  -1 1  1  1  1  -1 -1 -1 1  -1 1  -1 -1
 [26] -1 -1 -1 1  1  -1 1  -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1  1  -1 1  -1 1  -1
 [51] 1  -1 -1 1  -1 1  1  1  1  -1 -1 1  -1 1  1  -1 -1 1  -1 1  -1 1  1  1  1 
 [76] -1 -1 -1 -1 -1 1  1  -1 1  -1 -1 -1 -1 1  -1 1  1  1  -1 1  -1 1  -1 -1 1 
Levels: 1 -9 -7 -5 -3 -1 3 5 7 9

Zadruhé, někdy chceme seřadit úrovně faktorů podle nějaké charakteristiky dat – např. proto, že pořadí úrovní ovlivňuje pořadí, ve kterém jsou data vykreslena v grafu. Toto je překvapivě netriviální problém. Můžete použít bud základní funkci reorder(), která je velmi mocná, ale má poněkud komplikované ovládání, nebo přívětivější funkce z balíku forcats. Návod na jejich použití najdete v kapitole o balíku forcats v knize Wickham a Grolemund (2017) dostupné také na http://r4ds.had.co.nz/factors.html nebo v referenční příručce balíku, která je dostupná na http://forcats.tidyverse.org/.

Poznámka k faktorům

Historicky se faktory používaly velmi často, protože šetřily paměť počítače. Místo vektoru řetězců, kde se hodnoty často opakovaly, zbyl krátký vektor unikátních hodnot řetězců (úrovně levels) a paměťově úspornější vektor celých čísel, který říkal, která úroveň se právě používá. Dnes to však už není pravda: R skladuje řetězce v jednom velkém skladu, každý z nich pouze jednou a vektory řetězců jsou implementovány jako odkazy do tohoto skladu. Prakticky to znamená, že převedením řetězců na faktory se žádná paměť neušetří.

Nicméně, z těchto důvodů se funkce ze základních balíků R (jako např. data.frame() a read.csv()) v minulosti snažily řetězce převádět na faktory. Tomu šlo zabránit nastavením parametru stringsAsFactors na hodnotu FALSE. R od verze 4.0 však tyto konverze v těchto funkcích neprovádí. Pokud tedy chcete převést řetězec na faktor, musíte buď nastavit explicitně parametr stringsAsFactors na hodnotu TRUE, nebo převést proměnnou na faktor ručně pomocí funkce factor(), což je mnohem rozumnější, protože můžete hlídat platné úrovně a nastavit jejich pořadí.

5.2 Datum a čas

Datum a čas lze v R ukládat do vektorů několika různých tříd. Z nich jsou nejdůležitější jsou třídy Date pro reprezentaci celých dnů bez času a POSIXct pro reprezentaci dne i hodiny. Datové typy Date a POSIXct nepatří mezi základní datové typy v R, ale jedná se o třídy objektů odvozené od tříd integer a double. První ukládá datum tak, že celé číslo reprezentuje počet dnů od 1. ledna 1970, druhá reprezentuje čas jako počet sekund, které od tohoto dne uběhl (tzv. unix time). Při výpisu však obě funkce zobrazují datum uživatelsky přátelským způsobem.

R nabízí pro práci s datumy a časem několik užitečných funkcí, které však mohou být poněkud těžkopádné. Uživatelsky přívětivější funkce nabízí balík lubridate. Zde si ukážeme některé základní operace s datumy a časem jak pomocí základních funkcí, tak pomocí funkcí z balíku lubridate. Proto jej musíme nejprve načíst:

library(lubridate)

Zadávání datumů a času

Datum nejčastěji vytvoříme konverzí z řetězce. K tomu slouží funkce as.Date(), která předpokládá, že řetězec obsahuje datum uložené ve formátu obvyklém v anglofonním světě, kdy na prvním místě stojí rok, pak měsíc a nakonec den a jednotlivé položky jsou oddělené pomlčkou (tak R také datumy vypisuje):

as.Date("2016-11-25")  # 25. listopadu 2016
[1] "2016-11-25"

Pokud je datum zadáno v jiném formátu, musíte tento formát popsat v parametru format. Formát se zadává pomocí formátovacích řetězců, které jsou popsané v dokumentaci funkce strptime(). Základní parametry uvádí tabulka 5.1. Řekněme, že máme data zadaná ve formátu číslo dne, zkratka měsíce a rok, kde jednotlivé položky nejsou nijak oddělené. Pak bude mít řetězec formátu tvar “%d%b%Y”: “%d” znamená, že na prvním místě je den v měsíci, “%b” říká, že na druhém místě je zkrácený název měsíce a “%Y” že na posledním místě je rok zadaný čtyřmi číslicemi.

as.Date(
    c("1led1960", "2led1960", "31bře1960", "30čec1960"),
    format = "%d%b%Y"
)
[1] "1960-01-01" "1960-01-02" "1960-03-31" "1960-07-30"
Tabulka 5.1: Vybrané formátové značky pro zadávání datumu a času
kód význam
%d číslo dne v měsíci
%m číslo měsíce v roce
%b zkrácené jméno měsíce
%B plné jméno měsíce
%y rok zadaný dvěma číslicemi
%Y rok zadaný čtyřmi číslicemi
%H číslo hodiny (0–23)
%M číslo minuty (0–59)
%S číslo sekundy (0–61 pro přestupné vteřiny)

Pokud by jednotlivé položky datumu byly nějak oddělené, zadaly by se tyto oddělovače do formátu. Datum ve formátu “1. červen 1974” by mělo formátový řetězec “%d. %B %Y”:

as.Date(
    c("1. června 1974", "17. listopadu 1989"),
    format = "%d. %B %Y"
)
[1] "1974-06-01" "1989-11-17"

Jména měsíců a dnů v týdnu standardně se berou z lokalizace vašeho operačního systému. Pokud vám tato lokalizace nevyhovuje (potřebujete jména v jiném jazyce) nebo musíte svůj kód přenášet mezi více počítači, můžete lokalizaci manuálně přepnout (víc detailů najdete v dokumentaci k locales: ?locales):

# nastav locales, aby jména dnů byla anglicky
# (zde se tento řádek neprovede, aby byl zbytek textu česky)
Sys.setlocale("LC_TIME", "C")

Při načítání datumů je možné nastavit i časovou zónu pomocí parametru tz. Jinak se automaticky vezme časová zóna z vašeho operačního sytému (v mém případě nyní “CEST”, tj. středoevropský letní čas).

Pokud máte dobrodružnější povahu a jednotlivé části datumu jsou zadané pomocí čísel dnů, měsíců a let, můžete použít pohodlnější funkce z balíku lubridate. Těchto funkcí je celá řada a jmenují se na první pohled krypticky ymd(), ydm(), mdy(), myd(), dmy() a dym(). Jednotlivá písmena určují, v jakém pořadí jsou uvedeny jednotlivé složky datumu: y znamená rok, m měsíc a d den, takže funkce ymd() předpokládá, že v datumu je nejdříve uveden rok, pak měsíc a nakonec den. Všechny tyto funkce jsou chytré, takže si poradí s nejrůznějšími oddělovači. Pokud funkce na některém prvku vektoru selže, vydá varování. (Všimněte si, jak funkce naloží s rokem zadaným jen dvěma číslicemi.)

dmy(c("1.6.1974", "17. 11. 1989", "1-1-2001", "1.3.99", "1.3.39", "bžů"))
Warning: 1 failed to parse.
[1] "1974-06-01" "1989-11-17" "2001-01-01" "1999-03-01" "2039-03-01"
[6] NA          
ymd(20140531)  # funguje i celé číslo, pokud je jednoznačné
[1] "2014-05-31"

Datum je možné složit i z jednotlivých komponent pomocí funkce make_date():

make_date(year = 2011, month = 7, day = 5)
[1] "2011-07-05"

K zadání dne i hodiny slouží funkce as.POSIXct(), která opět implicitně předpokládá datum a čas v anglofonním formátu. Formát je opět možné změnit zadáním parametru format.

as.POSIXct("1974-06-01 7:30")
[1] "1974-06-01 07:30:00 CET"

Balík lubridate opět nabízí uživatelsky přívětivější (ale méně striktní, a tedy bezpečné) funkce ymd_hms(), ymd_hm(), ymd_h(), dmy_hms(), dmy_hm(), dmy_h(), mdy_hms(), mdy_hm(), mdy_h(), ydm_hms(), ydm_hm() a ydm_h(), kde písmena d, m a y mají stejný význam jako výše a h znamená hodiny, m minuty a s sekundy:

ymd_hm("1974-06-01 7:30")
[1] "1974-06-01 07:30:00 UTC"
dmy_hm("1. 6. 1974 7.30")
[1] "1974-06-01 07:30:00 UTC"

I datum a čas je možné složit z jednotlivých komponent pomocí funkce

make_datetime(year = 2017, month = 1:12, day = 1)
 [1] "2017-01-01 UTC" "2017-02-01 UTC" "2017-03-01 UTC" "2017-04-01 UTC"
 [5] "2017-05-01 UTC" "2017-06-01 UTC" "2017-07-01 UTC" "2017-08-01 UTC"
 [9] "2017-09-01 UTC" "2017-10-01 UTC" "2017-11-01 UTC" "2017-12-01 UTC"

Jednotlivé komponenty datumu a času

Často je potřeba z datumu nebo času získat příslušnou hodnotu dne, měsíce nebo hodiny. K je možné použít funkce weekdays(), months(), days() a quarters() ze základního balíku. Funkce weekdays() a months() vrací jména dnů a měsíců v locace počítače; mohou vrátit i zkrácenou verzi jména, pokud je parametr abbreviate nastaven na hodnotu TRUE.

d <- as.Date("2016-11-05")
weekdays(d)  # den v týdnu
[1] "Sobota"
months(d)    # měsíc v roce
[1] "listopadu"
quarters(d)  # čtvrtletí
[1] "Q4"

Balík lubridate opět definuje další funkce pro práci s jednotlivými komponentami datumu a času: year() vrací rok, month() vrací měsíc, mday() vrací číslo dne v měsíci, yday() číslo dne v roce, wday() číslo dne v týdnu, hour() hodinu, minute() minutu a second() sekundu. Funkce month() a wday() mají navíc parametry labels a abbr. Pokud je label nastaveno na hodnotu TRUE, pak funkce vrací místo čísla jméno. Pokud je navíc abbr nastaveno na TRUE, pak je jméno zkráceno. Tyto funkce však vracejí vždy anglická jména dnů a měsíců, navíc kódovaná jako faktor. Také prvním dnem týdne je neděle (s číslem 1). Funkce tz() vrací časovou zónu.

year(d)
[1] 2016
month(d)
[1] 11
month(d, label = TRUE)
[1] lis
12 Levels: led < úno < bře < dub < kvě < čen < čec < srp < zář < ... < pro
wday(d)
[1] 7
wday(d, label = TRUE)
[1] So
Levels: Ne < Po < Út < St < Čt < Pá < So
tz(d)
[1] "UTC"

Tyto funkce z balíku lubridate je možné použít i k úpravě datumu a času:

d <- ymd("2000-01-01")
day(d) <- 31
month(d) <- 12
d
[1] "2000-12-31"

Pokud je potřeba upravit více prvků data naráz, je možné použít funkci update():

update(d, year = 1989, month = 11, mday = 17)
[1] "1989-11-17"

Operace s datem a časem

Pro třídy Date a POSIXct fungují některé aritmetické a logické operace, jako je sčítání, odečítání a diference, násobení, dělení a porovnávání:

d <- as.Date(c("2016-01-10", "2016-03-11"))
d
[1] "2016-01-10" "2016-03-11"
# kolik dnů je mezi druhým a prvním dnem ve vektoru?
d[2] - d[1]              
Time difference of 61 days
# funguje i funkce pro diferenci
diff(d)                  
Time difference of 61 days
# převod na číslo
as.numeric(d[2] - d[1])  
[1] 61
# které dny ve vektoru jsou po 5. únoru 2016?
d > "2016-02-05"
[1] FALSE  TRUE

Většina aritmetických operací však pracuje na rozdílech mezi dvěma datumy, což je proměnná třídy difftime. Práce s touto třídou není úplně intuitivní, protože datumy a čas jsou plné různých nepravidelností (počet dnů se v jednotlivých měsících liší, přestupné roky mají více dnů a některé minuty mají 61 sekund). Proto se zde blíže podíváme jen na dvě operace: zjištění časové vzdálenosti dvou bodů a tvorbu vektoru datumů s konstantním rozestupem.

Pokud nás zajímá časová vzdálenost mezi dvěma daty, obyčejná diference vypíše rozdíl v automaticky zvolených jednotkách. Užitečnější proto může být funkce difftime(), která vrací vzdálenost mezi dvěma dny v jednotkách zadaných uživatelem pomocí parametru units, který může nabývat hodnot "auto", "secs", "mins", "hours", "days" nebo "weeks":

difftime(d[2], d[1], units = "weeks") 
Time difference of 8.714286 weeks

Balík lubridate definuje velké množství dalších funkcí datumovou aritmetiku a práci s časovými intervaly. Zde se jim však nebudeme věnovat. Zájemci se mohou podívat do Wickham a Grolemund (2017), kap. 13 dostupné na http://r4ds.had.co.nz/dates-and-times.html, na web balíku lubridate na http://lubridate.tidyverse.org/ nebo do původního článku o tomto balíku, Grolemund a Wickham (2011), který je dostupný ke stažení na stránkách časopisu i balíku lubridate.

Tvorba ekvidistantních časových vektorů

Často potřebujeme vytvořit vektor s datumy nebo časem s ekvidistantním odstupem. V některých speciálních případech to můžeme udělat pomocí funkcí pro práci s jednotlivými složkami datumu. Řekněme například, že potřebujeme vektor datumů pro začátek každého měsíce v roce. To uděláme snadno např. takto:

d <- ymd("2017-01-01")
month(d) <- 1:12
d
 [1] "2017-01-01" "2017-02-01" "2017-03-01" "2017-04-01" "2017-05-01"
 [6] "2017-06-01" "2017-07-01" "2017-08-01" "2017-09-01" "2017-10-01"
[11] "2017-11-01" "2017-12-01"

Vytvořit jiné vektory je však obtížnější. Například vektor posledních dnů v měsíci takto jednoduše vytvořit nejde, protože různé měsíce mají různý počet dnů. Náš algoritmus také nevytvořil data s ekvidistantním odstupem, a to právě proto, že různé měsíce jsou různě dlouhé.

K vytvoření vektorů se skutečně stejným rozestupem mezi jednotlivými daty je možné použít funkci seq(). Stejně jako v případě číselných vektorů je možné nastavit počátek a konec období a počet hodnot, nebo počátek období a odstup mezi položkami. Ten se nastaví pomocí parametru by. Tento parametr může mít hodnotu “day”, “week”, “month”, “quarter” nebo “year” případně násobenou celým číslem; ke jménu délky periody je také možné přidat koncové “s”. Jednodenní odstup je pak “day”, dvoudenní “2days” apod.

seq(from = ymd("2015-1-1"), to = ymd("2015-12-31"), length.out = 15)
 [1] "2015-01-01" "2015-01-27" "2015-02-22" "2015-03-20" "2015-04-15"
 [6] "2015-05-11" "2015-06-06" "2015-07-02" "2015-07-28" "2015-08-23"
[11] "2015-09-18" "2015-10-14" "2015-11-09" "2015-12-05" "2015-12-31"
seq(ymd("2015-1-1"), by = "2 days", length.out = 10)
 [1] "2015-01-01" "2015-01-03" "2015-01-05" "2015-01-07" "2015-01-09"
 [6] "2015-01-11" "2015-01-13" "2015-01-15" "2015-01-17" "2015-01-19"

Kromě toho je odstup možné nastavit i na jakoukoli hodnotu třídy difftime, kterou vrací funkce difftime(), nebo na kteroukoli jednotku, kterou funkce difftime() dokáže zpracovat. Před jednotku je možné přidat celé číslo jako násobek této jednotky:

odstup <- ymd("2017-06-05") - ymd("2017-06-01")  # odstup 4 dny
seq(ymd("2017-06-01"), by = odstup, length.out = 6)
[1] "2017-06-01" "2017-06-05" "2017-06-09" "2017-06-13" "2017-06-17"
[6] "2017-06-21"

Systémový čas

Aktuální (systémový) čas lze zjistit funkcí Sys.time(), která vrací objekt třídy POSIXct:

Sys.time()
[1] "2023-12-13 14:05:42 CET"

Balík lubridate navíc přidává funkce today(), která vrací dnešní datum jako objekt třídy Date, a now(), která vrací okamžité datum a čas jako objekt třídy POSIXct:

today()  # den, kdy byla tato kniha naposledy zkompilována
[1] "2023-12-13"
now()  # a nyní včetně času
[1] "2023-12-13 14:05:42 CET"