Chapter 12 日期時間處理
日期時間在醫學研究中, 是一個非常重要的變數,
然而對於日期時間的儲存, 列印與計算, 每一個軟體各不相同的處理方法.
原則上, 軟體對於日期時間的輸入通常是以文字型式輸入
日歷時間(**calendar date and time
),
例如,
"12/10/1979"
,
"12/10/1979 20:30:10"
,
"2/28/1947"
,
"2-28-1947"
,
022847
, 2Feb47
,
"Feburary 2 1947"
等,
{R} 讀入文字型式的日歷時間,
轉換成 {R} 的
**日期時間類別物件` (**date-time class object`). {R} 的
日期類別物件'' (**dates class object
).
{R} 有不同日期時間類別格式存在,
非別為
Date
,
以及 Date-Time
類別,
而 Time
包含
POSIXlt
與
POSIXct
等 2 時間類別.
Date
類別 僅考慮以天數計算,
Date-Time
類別
考慮以總秒數計算,
{R} 以 January 1, 1970 為 第 0 天.
使用日期函式
as.Date()
可以將文字型式的日歷日期 (calendar date)
轉換成 {R} 的
日期類別(**Dates class
)
物件.
使用日期函式
strptime()
可以將文字型式的日歷時間轉換成 {R} 的
日期-時間類別物件''; 使用日期函式 `as.POSIXlt()` 與 `as.POSIXct()` 可以將
日期-時間類別物件’’ 分別轉換成
POSIXlt, POSIXct類別格式. 使用日期函式
format()可以將 {R} 的 ``日期-時間類別物件'' 轉換成一般人可讀的文字型式的日期, 日, 星期, 月, 與時間.
POSIXct: 非常大的正整數, 通常儲存在
data.frame中使用,
POSIXlt: 是一個列表物件 (list), 分別儲存 星期中的第幾天, 一年中的第幾天, 月份, 月中的第幾天等等. {R} 的日期時間的內設有些複雜, 因此 {R} 有些專門日期時間套件, 例如
tidyverse套件系統提供
lubridate套件, 或更完整的財務金融
Rmetrics` 套件,
這些套件可以在處裡時間上變得較為方便.
12.1 lubridate 套件的日期時間
lubridate
套件內日期時間資料,
在tidyverse
套件系統的 tibble
物件呈現 3 種類型.
date
tibble
列印日期成<date>
.time
tibble
列印時間成<time>
.date-time
資料同時包含日期時間tibble
列印日期成<dttm>
. 這是 {R} 的POSIXct
物件.
使用 lubridate
套件的函式 today()
或 now()
取得今日的日期時間.
{R} base 函式 Sys.time()
取得今日的日期時間之絕對數值為 POSIXct
物件, 可依照時區進行轉喚.
而函式 Sys.Date()
則依照電腦使用時區傳回 Date
物件.
options(digits.secs = 6)
op <-Sys.time()
## [1] "2020-10-29 14:53:21.91146 CST"
Sys.Date()
## [1] "2020-10-29"
class(Sys.time())
## [1] "POSIXct" "POSIXt"
class(Sys.Date())
## [1] "Date"
## locale-specific version of date()
format(Sys.time(), "%a %b %d %X %Y")
## [1] "週四 十月 29 下午 02:53:21 2020"
Sys.Date()
## [1] "2020-10-29"
## lubriate package
library(tidyverse)
library(lubridate)
today()
## [1] "2020-10-29"
now()
## [1] "2020-10-29 14:53:21.985474 CST"
class(today())
## [1] "Date"
class(now())
## [1] "POSIXct" "POSIXt"
不同程式語言系統的時間日期參照個不相同,
{R} 是以 UTC 時區 (Universal Time, Coordinated)
January 1, 1970, 0 時, UTC 0 分 0 秒 為 `{0
’’ (origin),
UTC 以國際原子時為計算基準, 修正了一些時間微小的飄移,
而 UTC 與 GMT (格林威治標準時間) 相差不多.
有 3 種方法可以創件日期時間物件.
- 從文字或字串轉換創件日期時間物件.
- 從個別 date-time 資料, 創件日期時間物件.
- 從已經建立的 date-time 物件或其他資料物件, 進行運算而創件日期時間物件.
12.1.1 從文字或字串轉換創件日期時間物件
只要文字輸入時, 依照適當的日期時間順序即可創件日期時間物件. 也可加入時區.
ymd(..., quiet = FALSE, tz = NULL,
locale = Sys.getlocale("LC_TIME"), truncated = 0
)
其中引數 tz
為時區識別, locale
為地區識別.
## strings -> date-time
## date
library(lubridate)
ymd("2017-01-31")
## [1] "2017-01-31"
mdy("January 31st, 2017")
## [1] "2017-01-31"
dmy("31-Jan-2017")
## [1] "2017-01-31"
## date and time
ymd_hms("2017-01-31 20:11:59")
## [1] "2017-01-31 20:11:59 UTC"
mdy_hm("01/31/2017 08:01")
## [1] "2017-01-31 08:01:00 UTC"
## without quotation
ymd(20170131)
## [1] "2017-01-31"
## add time-zone
ymd(20170131, tz = "UTC")
## [1] "2017-01-31 UTC"
##
c("09-01-01", "09-01-02", "09-01-03")
x <-ymd(x)
## [1] "2009-01-01" "2009-01-02" "2009-01-03"
c("2009-01-01", "2009-01-02", "2009-01-03")
x <-ymd(x)
## [1] "2009-01-01" "2009-01-02" "2009-01-03"
ymd(090101, 90102)
## [1] "2009-01-01" "2009-01-02"
now() > ymd(20090101)
## [1] TRUE
dmy(010210)
## [1] "2010-02-01"
mdy(010210)
## [1] "2010-01-02"
yq('2014.2')
## [1] "2014-04-01"
##
## heterogeneous formats in a single vector:
c(20090101, "2009-01-02", "2009 01 03", "2009-1-4",
x <-"2009-1, 5", "Created on 2009 1 6", "200901 !!! 07")
ymd(x)
## [1] "2009-01-01" "2009-01-02" "2009-01-03" "2009-01-04" "2009-01-05" "2009-01-06"
## [7] "2009-01-07"
12.2 從date-time 資料個別成分, 創件日期時間物件.
許多資料在儲存時, 是將年, 月, 日, 時, 分, 秒 等分開以變數 (欄位) 儲存.
在資料分析時才創件成為日期時間物件,
例如 {R} 的 nycflights13
套件的 flights
資料,
此資料是有關紐約機場班機起降時間記錄.
可使用函式 make_datetime()
創件日期時間物件.
## nycflights13 package
library(nycflights13)
## store as separate variables
%>%
flights select(year, month, day, hour, minute)
## # A tibble: 336,776 x 5
## year month day hour minute
## <int> <int> <int> <dbl> <dbl>
## 1 2013 1 1 5 15
## 2 2013 1 1 5 29
## 3 2013 1 1 5 40
## 4 2013 1 1 5 45
## 5 2013 1 1 6 0
## 6 2013 1 1 5 58
## 7 2013 1 1 6 0
## 8 2013 1 1 6 0
## 9 2013 1 1 6 0
## 10 2013 1 1 6 0
## # ... with 336,766 more rows
## create date-time
%>%
flights select(year, month, day, hour, minute) %>%
mutate(departure = make_datetime(year, month, day, hour, minute))
## # A tibble: 336,776 x 6
## year month day hour minute departure
## <int> <int> <int> <dbl> <dbl> <dttm>
## 1 2013 1 1 5 15 2013-01-01 05:15:00.000000
## 2 2013 1 1 5 29 2013-01-01 05:29:00.000000
## 3 2013 1 1 5 40 2013-01-01 05:40:00.000000
## 4 2013 1 1 5 45 2013-01-01 05:45:00.000000
## 5 2013 1 1 6 0 2013-01-01 06:00:00.000000
## 6 2013 1 1 5 58 2013-01-01 05:58:00.000000
## 7 2013 1 1 6 0 2013-01-01 06:00:00.000000
## 8 2013 1 1 6 0 2013-01-01 06:00:00.000000
## 9 2013 1 1 6 0 2013-01-01 06:00:00.000000
## 10 2013 1 1 6 0 2013-01-01 06:00:00.000000
## # ... with 336,766 more rows
##
12.2.1 從已經建立的其他資料物件創件日期時間物件
若資料以其他的物件形式儲存在 {R} 系統內,
常使用函式 as_datetime()
與 as_date()
創件日期時間物件.
若是以數值輸入, 可分成以秒為單位或以日為單位輸入.
as_datetime(today())
## [1] "2020-10-29 UTC"
as_date(now())
## [1] "2020-10-29"
## import as numeric values
## import as seconds from 1970-01-01 0:00:00
as_datetime(60 * 60 * 10)
## [1] "1970-01-01 10:00:00 UTC"
## import days from 1970-01-01
as_date(365 * 10 + 2)
## [1] "1980-01-01"
12.3 時區轉換
時區的變動與轉換, 常互造成錯誤.
函式 with_tz()
可以進行正確時區轉換,
而函式 force_tz()
則強迫變動時區.
例如台北時區為 2011-07-01 09:00:00 Asia/Taipei
,
轉換成美國 Hawaii時區為 HST
.
時區可參考 (https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
## change time zone
ymd_hms("2020-10-10 09:00:00", tz = "Asia/Taipei")
meeting <-with_tz(meeting, "HST")
## [1] "2020-10-09 15:00:00 HST"
## force change
force_tz(meeting, "America/Chicago")
mistake <-
mistake## [1] "2020-10-10 09:00:00 CDT"
with_tz(mistake, "Asia/Taipei")
## [1] "2020-10-10 22:00:00 CST"
12.4 取出 date-time 資料的個別成分
一個完整 date-time 物件, 其資料包含年, 月, 日, 時, 分, 秒, 時區等訊息.
若要取出 date-time 資料的個別成分,
可藉由函式
year()
, month()
, mday()
(一個月內的天數 1–31),
yday()
(一年內的天數 1–366),
wday()
(一週內的天數 1–7),
hour()
, minute(),
second(). 其中
month()與
wday()可使用引數
label = TRUE回傳簡寫標註文字, 如 Jan, Mon. 而使用引數
bbr = FALSE` 回傳完整標註文字, 如 January, Monday.
這些函式也可用來更動 date-time 物件部分成分.
ymd_hms("2016-07-08 12:34:56")
datetime <-year(datetime)
## [1] 2016
month(datetime)
## [1] 7
mday(datetime)
## [1] 8
yday(datetime)
## [1] 190
wday(datetime)
## [1] 6
## labels
month(datetime, label = TRUE)
## [1] 七月
## 12 Levels: 一月 < 二月 < 三月 < 四月 < 五月 < 六月 < 七月 < 八月 < 九月 < ... < 十二月
wday(datetime, label = TRUE, abbr = FALSE)
## [1] 星期五
## Levels: 星期日 < 星期一 < 星期二 < 星期三 < 星期四 < 星期五 < 星期六
## change components
ymd_hms("2016-07-08 12:34:56"))
(datetime <-## [1] "2016-07-08 12:34:56 UTC"
year(datetime) <- 2020
datetime## [1] "2020-07-08 12:34:56 UTC"
month(datetime) <- 01
datetime## [1] "2020-01-08 12:34:56 UTC"
函式 floor_date()
, round_date()
, ceiling_date()
可將 date-time 物件簡化成最近的時間單位.
round_date(x, unit = "second", week_start = getOption("lubridate.week.start", 7))
其中引數 unit
可為
second
, minute
, hour
, day
, week
, month
, bimonth
,
quarter
, season
, halfyear
year
.
引數 week_start
為每週起始計算, 1 = Monday, 7 = Sunday.
ymd_hms("2009-08-03 12:01:59.23")
x <-floor_date(x, ".1s")
## [1] "2009-08-03 12:01:59.2 UTC"
floor_date(x, "second")
## [1] "2009-08-03 12:01:59 UTC"
floor_date(x, "minute")
## [1] "2009-08-03 12:01:00 UTC"
floor_date(x, "hour")
## [1] "2009-08-03 12:00:00 UTC"
floor_date(x, "day")
## [1] "2009-08-03 UTC"
floor_date(x, "week")
## [1] "2009-08-02 UTC"
floor_date(x, "month")
## [1] "2009-08-01 UTC"
floor_date(x, "bimonth")
## [1] "2009-07-01 UTC"
floor_date(x, "quarter")
## [1] "2009-07-01 UTC"
floor_date(x, "season")
## [1] "2009-06-01 UTC"
floor_date(x, "halfyear")
## [1] "2009-07-01 UTC"
floor_date(x, "year")
## [1] "2009-01-01 UTC"
可自行比較 round_date()
, ceiling_date()
的差異.
12.5 計算時間長度
資料分析常常需要輸入持續時間長度或是計算二個 date-time 物件之間的時間長度.
lubridate
套件有數種計算時間長度的工具.
duration
創建以秒計算的時間長度的物件.period
創建以人類文明的時間單位計算時間長度, 例如依照日歷的 years, months, days.interval
計算二個 date-time 物件之間的時間長度.
12.5.1 duration
創建以秒計算的時間長度的物件
duration()
系列函式用來輸入持續時間長度, 創建以秒計算的時間長度的物件
duration(num = NULL, units = "seconds", ...)
其中引數 num
為數值, units
為計算時間的單位.
duration
物件的時間計算是固定的, 例如,
Minutes = 60 seconds, hours = 3600 seconds, days = 86400 seconds, weeks = 604800.
這些 duration()
系列函式包含
dseconds(x = 1)
,
dminutes(x = 1)
,
dhours(x = 1)
,
ddays(x = 1)
,
dweeks(x = 1)
,
dmonths(x = 1)
,
dyears(x = 1)
,
dmilliseconds(x = 1)
,
dmicroseconds(x = 1)
,
dnanoseconds(x = 1)
,
dpicoseconds(x = 1)
,
is.duration(x)
等.
## ### Separate period and units vectors
duration(num = 90, units = "seconds")
## [1] "90s (~1.5 minutes)"
duration(num = 1.5, units = "minutes")
## [1] "90s (~1.5 minutes)"
## Units as arguments
duration(day = -1)
## [1] "-86400s (~-1 days)"
duration(second = 90)
## [1] "90s (~1.5 minutes)"
duration(second = 3, minute = 1.5, hour = 2, day = 6, week = 1)
## [1] "1130493s (~1.87 weeks)"
duration(hour = 1, minute = -60)
## [1] "0s"
## Elementary construction
dseconds(x = 1)
## [1] "1s"
dminutes(x = 3.5)
## [1] "210s (~3.5 minutes)"
12.5.2 period
創建以人類文明的時間單位計算時間長度
period()
系列函式用來輸入持續時間長度,
依照日歷的 years, months, days 計算.
例如, 2009-01-01 起計算 1 year = 365 days,
但 2012-01-01 起計算 1 year = 366 days.
period
物件的時間長度並不固定,
而是依據 date-time 物件所在的時際日歷而定.
若當年度有潤秒, 則 1 min > 60 seconds.
period
物件是人類文明使用的 clock time
.
seconds()
,
minutes()
,
hours()
,
days()
,
weeks()
,
months()
,
years()
,
milliseconds()
,
microseconds()
,
nanoseconds(),
picoseconds(),
period_to_seconds(),
seconds_to_period()` 等.
period(num = 1, units = "days")
## [1] "1d 0H 0M 0S"
period(c(3, 1, 2, 13, 1), c("second", "minute", "hour", "day", "week"))
## [1] "20d 2H 1M 3S"
## Units as arguments
period (second = 90, minute = 5)
## [1] "5M 90S"
period(second = 3, minute = 1, hour = 2, day = 13, week = 1)
## [1] "20d 2H 1M 3S"
## Elementary Construction
ymd("2009-08-03")
x <-
x## [1] "2009-08-03"
+ days(1) + hours(6) + minutes(30)
x ## [1] "2009-08-04 06:30:00 UTC"
+ days(100) - hours(8)
x ## [1] "2009-11-10 16:00:00 UTC"
## compare DST handling to durations
ymd_hms("2009-03-08 01:59:59", tz="America/Chicago")
boundary <-+ days(1) # period
boundary ## [1] "2009-03-09 01:59:59 CDT"
+ ddays(1) # duration
boundary ## [1] "2009-03-09 02:59:59 CDT"
is.duration(as.Date("2009-08-03")) # FALSE
## [1] FALSE
is.duration(duration(days = 12.4)) # TRUE
## [1] TRUE
is.period(as.Date("2009-08-03")) # FALSE
## [1] FALSE
is.period(period(months= 1, days = 15)) # TRUE
## [1] TRUE
12.5.3 interval
計算二個 date-time 物件之間的時間長度.
interval()
系列函式用來計算二個 date-time 物件之間的時間長度.
interval(start = NULL, end = NULL, tzone = tz(start))
若起始 date-time 物件早發生於結束 date-time 物件,
則 interval
物件計算為正值.
interval
物件計算依照日歷的 years, months, days 計算.
start %--% end
等同於 interval()
,
is.interval(x)
檢查是否為 interval
物件,
int_start(int)
,
int_start(int) <- value
,
int_end(int)
,
int_end(int) <- value
等可變更起始與結束時間點,
int_length(int)
以秒計算,
int_flip(int)
翻轉起始與結束時間點,
int_shift(int, by)
起始與結束時間點進行前後移動,
int_overlaps(int1, int2)
檢查二個 interval
物件是否重覆,
int_standardize(int)
確保所有 interval
物件為正值, 若無則翻轉,
int_aligns(int1, int2)
檢查二個 interval
物件是否有相同起始與結束時間點,
int_diff(times)
類似 {R} base POSIXt
與 Date
的 diff() 函式,
但回傳 interval
物件, 而不是 {R} base 的 difftime
物件.
interval(ymd(20090201), ymd(20090101))
## [1] 2009-02-01 UTC--2009-01-01 UTC
ymd_hms("2009-03-08 01:59:59")
date1 <- ymd_hms("2000-02-29 12:00:00")
date2 <-interval(date2, date1)
## [1] 2000-02-29 12:00:00 UTC--2009-03-08 01:59:59 UTC
interval(date1, date2)
## [1] 2009-03-08 01:59:59 UTC--2000-02-29 12:00:00 UTC
## Elementary Construction
is.interval(period(months = 1, days = 15)) # FALSE
## [1] FALSE
is.interval(interval(ymd(20090801), ymd(20090809))) # TRUE
## [1] TRUE
interval(ymd("2001-01-01"), ymd("2002-01-01"))
int <-int_start(int)
## [1] "2001-01-01 UTC"
int_start(int) <- ymd("2001-06-01")
int## [1] 2001-06-01 UTC--2002-01-01 UTC
interval(ymd("2001-01-01"), ymd("2002-01-01"))
int <-int_end(int)
## [1] "2002-01-01 UTC"
int_end(int) <- ymd("2002-06-01")
int## [1] 2001-01-01 UTC--2002-06-01 UTC
interval(ymd("2001-01-01"), ymd("2002-01-01"))
int <-int_length(int)
## [1] 31536000
interval(ymd("2001-01-01"), ymd("2002-01-01"))
int <-int_flip(int)
## [1] 2002-01-01 UTC--2001-01-01 UTC
interval(ymd("2001-01-01"), ymd("2002-01-01"))
int <-int_shift(int, duration(days = 11))
## [1] 2001-01-12 UTC--2002-01-12 UTC
int_shift(int, duration(hours = -1))
## [1] 2000-12-31 23:00:00 UTC--2001-12-31 23:00:00 UTC
interval(ymd("2001-01-01"), ymd("2002-01-01"))
int1 <- interval(ymd("2001-06-01"), ymd("2002-06-01"))
int2 <- interval(ymd("2003-01-01"), ymd("2004-01-01"))
int3 <-int_overlaps(int1, int2) # TRUE
## [1] TRUE
int_overlaps(int1, int3) # FALSE
## [1] FALSE
interval(ymd("2002-01-01"), ymd("2001-01-01"))
int <-int_standardize(int)
## [1] 2001-01-01 UTC--2002-01-01 UTC
interval(ymd("2001-01-01"), ymd("2002-01-01"))
int1 <- interval(ymd("2001-06-01"), ymd("2002-01-01"))
int2 <- interval(ymd("2003-01-01"), ymd("2004-01-01"))
int3 <-int_aligns(int1, int2) # TRUE
## [1] TRUE
int_aligns(int1, int3) # FALSE
## [1] FALSE
now() + days(1:3)
dates <-int_diff(dates)
## [1] 2020-10-30 14:53:24.003588 CST--2020-10-31 14:53:24.003588 CST
## [2] 2020-10-31 14:53:24.003588 CST--2020-11-01 14:53:24.003588 CST