9.2 날짜/시간 생성하기

시각을 나타내는 날짜/시간의 세 가지 유형이 있다.

  • 날짜형 (date) : 날짜. 티블에서 <date> 로 출력한다.
  • 시간형 (time) : 하루 중 시간. 티블에서 <time> 으로 출력한다.
  • 날짜-시간형 (date-time) : 날짜 더하기 시간. 시점을 고유하게 (일반적으로 가장 가까운 초로) 식별한다. 티블에서 <dttm> 으로 출력한다. R의 다른 부분에서는 POSIXct 라고 부른다.

R에는 시간 저장을 위한 네이티브 클래스가 없기 때문에 이 장에서는 데이트형과 데이트-타임형에만 집중할 것이다. 네이티브 클래스가 필요하다면 hms 패키지를 이용해보라.

사용자 요구를 충족하는 데이터 유형 중, 가능한 한 가장 간단한 것을 사용해야 한다. 즉 데이트-타임형 대신 데이트형을 써도 된다면 그래야 한다는 말이다. 데이트-타임형은 시간대를 다루기 때문에 훨씬 더 복잡한데 이 장의 끝에서 살펴볼 것이다.

현재 날짜 또는 데이트-타임을 얻으려면 today() 또는 now() 를 사용하면 된다. (Sys.date())

today()
## [1] "2021-02-07"
now()
## [1] "2021-02-07 14:30:02 KST"

이 외에 날짜/시간을 생성하는 세 가지 방법이 있다.

  • 문자열로부터
  • 개별 데이트-타임형 구성요소로부터
  • 기존의 날짜/시간 객체로부터

이들의 작동 방식을 살펴보자.

9.2.1 문자열에서 생성

날짜/시간 데이터는 종종 문자열로 주어진다. 우리는 이미 date-times 에서 문자열을 데이트-타임형으로 파싱하는 법을 보았다. 다른 방법은 lubridate 에서 제공하는 도우미를 사용하는 것이다. 구성요소의 순서만 지정하면 자동으로 형식을 맞춘다. 도우미를 이용하는 법은 주어진 날짜에 나타난 연, 월, 일의 순서를 확인한 후, “y”, “m”, “d” 를 같은 순서로 배치하는 것이다. 이것이 바로 lubridate 함수의 이름이 되고 주어진 날짜를 파싱한다. 예를 들면

ymd("2017-01-31")
## [1] "2017-01-31"
mdy("January 31st, 2017")
## [1] "2017-03-01"
dmy("31-Jan-2017")
## [1] "2017-01-31"

이 함수들은 따옴표로 둘러싸이지 않은 숫자도 받아들인다. 이는 단일 날짜/시간 객체를 생성하는 방법들 중 가장 간결한 방법인데, 날짜/시간 데이터를 필터링할 때 사용할 수 있다. ymd() 는 간결하고 모호하지 않다.

ymd(20170131)
## [1] "2017-01-31"

ymd() 와 같은 함수들은 데이트형을 생성한다. 데이트-타임형을 생성하려면 파싱 함수 이름에 언더스코어와 “h,” “m,” “s” 중 하나 이상을 추가해야 한다.

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"

시간대를 제공하여 날짜로부터 데이트-타임형을 강제 생성할 수도 있다.

ymd(20170131, tz = "UTC")
## [1] "2017-01-31 UTC"

9.2.2 개별 구성요소로 생성

때로는 문자열이 아니라 데이트-타임형의 개별 구성요소들이 여러 열에 걸쳐 있는 경우가 있을 것이다. flights 데이터에 있는 것이 그렇다.

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
## # ... with 336,773 more rows

이러한 입력으로 날짜/시간을 생성하려면 데이트형은 make_date() 를, 데이트-타임형은 make_datetime() 를 쓰면 된다.

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
## 2  2013     1     1     5     29 2013-01-01 05:29:00
## 3  2013     1     1     5     40 2013-01-01 05:40:00
## # ... with 336,773 more rows

‘flights’ 네 개의 시간 열 각각에 대해 같은 작업을 하자. 시간이 약간 이상한 형식으로 표시되었으므로, 나머지 연산으로 시와 분 구성요소를 추출한다. 이제 데이트-타임형 변수를 생성했으니 이 장의 나머지 부분에서는 이 변수들을 탐색해 볼 것이다.

make_datetime_100 <- function(year, month, day, time) {
  make_datetime(year, month, day, time %/% 100, time %% 100)
}

flights_dt <- flights %>% 
  filter(!is.na(dep_time), !is.na(arr_time)) %>% 
  mutate(
    dep_time = make_datetime_100(year, month, day, dep_time),
    arr_time = make_datetime_100(year, month, day, arr_time),
    sched_dep_time = make_datetime_100(year, month, day, sched_dep_time),
    sched_arr_time = make_datetime_100(year, month, day, sched_arr_time)
  ) %>% 
  select(origin, dest, ends_with("delay"), ends_with("time"))

flights_dt
## # A tibble: 328,063 x 9
##   origin dest  dep_delay arr_delay dep_time            sched_dep_time     
##   <chr>  <chr>     <dbl>     <dbl> <dttm>              <dttm>             
## 1 EWR    IAH           2        11 2013-01-01 05:17:00 2013-01-01 05:15:00
## 2 LGA    IAH           4        20 2013-01-01 05:33:00 2013-01-01 05:29:00
## 3 JFK    MIA           2        33 2013-01-01 05:42:00 2013-01-01 05:40:00
## # ... with 328,060 more rows, and 3 more variables: arr_time <dttm>,
## #   sched_arr_time <dttm>, air_time <dbl>

이 데이터로 한 해에 걸친 출발 시간의 분포를 시각화할 수 있다.

flights_dt %>% 
  ggplot(aes(dep_time)) + 
  geom_freqpoly(binwidth = 86400) # 86400 seconds = 1 day

하루 내에서의 분포로 보려면

flights_dt %>% 
  filter(dep_time < ymd(20130102)) %>% 
  ggplot(aes(dep_time)) + 
  geom_freqpoly(binwidth = 600) # 600 s = 10 minutes

수치형 맥락에서 데이트-타임형을 사용할 경우(히스토그램에서와 같이) 1은 1초를 의미하고, 따라서 86400 빈너비(binwidth)는 하루를 의미한다는 것을 주목하라. 데이트형에서는 1은 1일을 의미한다.

9.2.3 기타 유형에서 생성

데이트-타임형과 데이트형 사이를 상호 전환하고 싶을 때도 있을 것이다. as_datetime()as_date() 가 바로 이를 수행한다.

as_datetime(today())
## [1] "2021-02-07 UTC"
as_date(now())
## [1] "2021-02-07"

때로 날짜/시간을 ‘유닉스 신기원(Unix Epoch)’인 1970-01-01에서 수치형 오프셋으로 가지고 있을 수 있다. 오프셋이 초 단위인 경우엔 as_datetime() , 일 단위인 경우엔 as_date() 을 사용한다.

as_datetime(60 * 60 * 10)
## [1] "1970-01-01 10:00:00 UTC"
as_date(365 * 10 + 2)
## [1] "1980-01-01"

9.2.4 연습문제

  1. 유효하지 않은 날짜를 포함한 문자열을 파싱하면 어떻게 되는가?

    ymd(c("2010-10-10", "bananas"))
    ## Warning: 1 failed to parse.
    ## [1] "2010-10-10" NA
  2. today()tzone 인수의 역할은 무엇인가? 이 인수는 왜 중요한가?

  3. 적절한 lubridate 함수를 이용하여, 다음 날짜를 각각 파싱하라.

    d1 <- "January 1, 2010"
    d2 <- "2015-Mar-07"
    d3 <- "06-Jun-2017"
    d4 <- c("August 19 (2015)", "July 1 (2015)")
    d5 <- "12/30/14" # Dec 30, 2014