데이터 과학

시간 데이터 기초

학습 목표

  • 시간 데이터에 대한 기초 지식을 살펴본다.
  • 시간 데이터를 관장하는 주요 R 팩키지를 살펴본다.
  • 시계열 데이터 저장소에서 파악하고 데이터를 직접 다운로드 받는다.

1. 날짜와 시간 1

날짜정보와 시간정보는 R에서 다른 클래스를 통해 구현된다. 날짜(Date)는 1970-01-01을 기준으로 R 내부적으로 정수형으로 저장되고, 시간(Time)도 R 내부적으로 1970-01-01을 기준으로 초단위로 저장된다.

  • 날짜(Date): Date 클래스
  • 시간(time): POSIXct, POSIXlt 클래스
    • POSIXct 클래스는 매우 큰 정수로 시간정보를 데이터프레임으로 저장할 때 유용하다.
    • POSIXlt 클래스는 리스트 자료형으로 요일, 년, 월, 일 등의 정보를 리스트 내부 원소로 저장되어 유용하다.

날짜 시간 추상화

ISO 8601 국제 표준을 근간으로 날짜 및 시간을 문자열로 표현하면 이를 R에서 인식할 수 있도록 저수준 시간/날짜 객체 변환 함수를 통해 시간/날짜 객체로 변환을 한다. 날짜와 시간 객체로 변환을 하고 나면 고수준 날짜/시간 팩키지를 통해 다양한 날짜/시간 관련 분석작업을 손쉽게 수행한다.

먼저 ISO 8601 기준에 맞춰 문자열(“1970-01-01”)을 as.Date 함수를 통해 날짜 객체로 변환한다. 내부적으로 보면 정수 0 으로 저장된다. 기준일 다음 “1970-01-03”일을 보면 숫자 2가 된다.

x <- as.Date("1970-01-01")
x
[1] "1970-01-01"
unclass(x)
[1] 0
unclass(as.Date("1970-01-03"))
[1] 2

1.1. 날짜 자료형 확인

Sys.time() 함수를 통해 현재 시스템 시간정보를 받아내면 POSIXct 클래스임이 확인되고, unclass() 정보를 통해 내부적으로 정수값으로 저장된 것이 확인된다.

x <- Sys.time()
x
[1] "2016-12-26 16:48:33 KST"
unclass(x)
[1] 1482738513

문자열을 받아 as.POSIXlt(), as.POSIXct() 함수를 사용해서 시간자료형으로 변환시킨다. POSIXlt 자료형으로 변환시킨 경우 리스트로 저장되어 있어 시, 분, 초, 요일등 하위 원소값으로 뽑아낼 수 있다.

x <- Sys.time()
x
[1] "2016-12-26 16:48:33 KST"
p <- as.POSIXlt(x)
names(unclass(p))
 [1] "sec"    "min"    "hour"   "mday"   "mon"    "year"   "wday"  
 [8] "yday"   "isdst"  "zone"   "gmtoff"
p$wday
[1] 1

1.2. 날짜, 시간 자료형 변환

결국 문자열을 받아 날짜, 시간 자료형으로 변환시키는데 strptime() 함수를 사용한다. 문자열을 입력으로 받아 문자열의 날짜, 시간 형식을 매칭하여 넘기게 되면 날짜, 시간 자료형으로 변환된다.

datestring <- c("2006-01-08 10:07:52", "2006-08-07 19:33:02")
x <- strptime(datestring, "%Y-%m-%d %H:%M:%S", tz = "EST5EDT")
x
[1] "2006-01-08 10:07:52 EST" "2006-08-07 19:33:02 EDT"
class(x)
[1] "POSIXlt" "POSIXt" 

1.3. 날짜, 시간 데이터 기본 연산

문자열 데이터를 날짜 시간 자료형으로 변환시키게 되면 컴퓨터 내부적으로 윤년, 윤초, 일광절약시간, 표준시간대를 자동으로 계산해 준다. “2012-03-01”, “2012-02-28” 두 날짜사이는 보통 하루 차이가 나지만 윤년이 있는 경우 이틀이 된다.

x <- as.Date("2012-03-01")
y <- as.Date("2012-02-28")
x-y
Time difference of 2 days

시간이 5시간 차이가 나지만, 표준시간대가 다른 경우 한 시간 차이만 난다.

x <- as.POSIXct("2012-10-25 01:00:00")
y <- as.POSIXct("2012-10-25 06:00:00", tz = "GMT")
y - x
Time difference of 14 hours

2. 날짜 시간 R 팩키지 2 3

날짜와 시간을 다루기 위한 팩키지가 다수 개발되었다. Data 클래스와 Time 클래스를 통해 날짜 뿐만 아니라 시간에 대한 데이터도 처리가 가능하다. 기본적으로 문자열 데이터를 저수준 날짜 및 시간 함수(as.Date, as.POSIXct, as.POSIXlt, strptime)를 통해 날짜 및 시간 데이터로 변환시킨 후에 고수준 날짜 및 시간 팩키지(zoo, xts, lubridate)로 작업을 진행하는 작업흐름을 갖는다.

3. 시간데이터 다루기

시간정보가 포함된 문자열 데이터 “2016-11-04 10:30:00” 정보를 받아 format="%Y-%m-%d %H:%M:%OS" 문자열 시간 형식을 strptime() 함수에 넘기면 문자열 데이터가 시간 데이터로 변환된다.

동영상 데이터를 처리할 경우 프레임 단위(초당 30프레임)로 동영상에 대한 데이터 정보가 저장된 경우, 이를 시간자료형으로 바꾸는 경우 seq 함수를 프레임 단위 by=인자로 넘기고, length.out =으로 데이터프레임 길이도 함께 넘긴다. 이를 키값으로 잡고 as.xts 함수에 넘기면 xts 객체로 변환된다. 이제 nseconds, nminutes 함수를 통해 동영상이 몇초인지, 몇분인지 쉽게 확인 가능하게 된다.

suppressPackageStartupMessages(library(xts))

strptime("2016-11-04 10:30:00", format="%Y-%m-%d %H:%M:%OS")
[1] "2016-11-04 10:30:00 KST"
dat <- read_csv("https://raw.githubusercontent.com/statkclee/identify_age_with_oxford_api/master/03_data/park_emo_01.csv")
Parsed with column specification:
cols(
  x = col_double(),
  y = col_double(),
  width = col_double(),
  height = col_double(),
  scores.neutral = col_double(),
  scores.happiness = col_double(),
  scores.surprise = col_double(),
  scores.sadness = col_double(),
  scores.anger = col_double(),
  scores.disgust = col_double(),
  scores.fear = col_double(),
  scores.contempt = col_double()
)
dat$times <- strptime("2016-11-04 10:30:00", format="%Y-%m-%d %H:%M:%OS", tz="Asia/Seoul") + seq(1, by = 1/30, length.out = dim(dat)[1])

dat_xts <- as.xts(dat, order.by = dat$times)
nseconds(dat_xts)
[1] 103
nminutes(dat_xts)
[1] 2

4. 시간데이터 파싱

데이터가 문자열로 저장되어 있고 년-월-일 오전/오후 시:분:초 형태를 갖는 시간자료를 R에서 처리하는 작업흐름은 다음과 같다.

  1. 데이터가 일관된 형태를 갖는 문자열인지 확인: 년-월-일 오전/오후 시:분:초
  2. 오전/오후 한글을 AM/PM으로 치환
  3. lubridate 팩키지 parse_date_time 함수를 사용해서 시간 자료형으로 변환: %Y-%m-%d %p %H:%M:%S
  4. 두 시간 사이 지속시간을 계산: as.period() 함수를 활용하여 시:분:초 자료형을 유지
  5. 지속시간을 익숙한 전체 시간, 분, 초 단위로 변환: as.numeric(processing_duration, unit=“hours”)

4.1. 시간 데이터

date_txt <- structure(list
                      (last_access_time = c("2016-11-02 오전 9:09:45", 
                                           "2016-11-05 오전 12:25:31", "2016-11-04 오전 2:24:46", "2016-11-30 오전 3:17:15", 
                                           "2016-11-10 오전 10:25:01", "2016-11-27 오전 1:40:30", "2016-11-29 오후 5:59:42", 
                                           "2016-11-26 오전 10:48:21", "2016-11-07 오전 8:45:43", "2016-11-27 오후 2:33:26"),
                        register_time = c("2016-11-01 오전 8:55:00", "2016-11-04 오후 3:04:48", 
                                         "2016-11-03 오후 11:36:59", "2016-11-30 오전 1:50:17", "2016-11-10 오전 10:14:51", 
                                         "2016-11-26 오후 1:50:32", "2016-11-29 오후 5:45:26", "2016-11-26 오전 10:37:41", 
                                         "2016-11-06 오후 7:18:34", "2016-11-27 오후 12:08:02")), 
                      .Names = c("last_access_time", "register_time"), 
                      row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame"))

date_txt
# A tibble: 10 × 2
           last_access_time            register_time
                      <chr>                    <chr>
1   2016-11-02 오전 9:09:45  2016-11-01 오전 8:55:00
2  2016-11-05 오전 12:25:31  2016-11-04 오후 3:04:48
3   2016-11-04 오전 2:24:46 2016-11-03 오후 11:36:59
4   2016-11-30 오전 3:17:15  2016-11-30 오전 1:50:17
5  2016-11-10 오전 10:25:01 2016-11-10 오전 10:14:51
6   2016-11-27 오전 1:40:30  2016-11-26 오후 1:50:32
7   2016-11-29 오후 5:59:42  2016-11-29 오후 5:45:26
8  2016-11-26 오전 10:48:21 2016-11-26 오전 10:37:41
9   2016-11-07 오전 8:45:43  2016-11-06 오후 7:18:34
10  2016-11-27 오후 2:33:26 2016-11-27 오후 12:08:02

4.2. 파싱된 시간 데이터

date_txt <- date_txt %>% 
  mutate(last_access_time_old = last_access_time,
         register_time_old = register_time) %>% 
  mutate(last_access_time = ifelse(str_detect(last_access_time, "오전"), 
                                   str_replace(last_access_time, "오전", "AM"), 
                                   str_replace(last_access_time, "오후", "PM")),
         register_time = ifelse(str_detect(register_time, "오전"), 
                                str_replace(register_time, "오전", "AM"), 
                                str_replace(register_time, "오후", "PM"))) %>% 
  mutate(last_access_time = parse_date_time(last_access_time, c("%Y-%m-%d %p %H:%M:%S")),
         register_time = parse_date_time(register_time, c("%Y-%m-%d %p %H:%M:%S")),
         processing_duration = as.period(last_access_time - register_time),
         processing_hours = as.numeric(processing_duration, unit="hours"))
date_txt %>% dplyr::select(register_time, processing_duration, processing_hours) 
# A tibble: 10 × 3
         register_time processing_duration processing_hours
                <dttm>        <S4: Period>            <dbl>
1  2016-11-01 08:55:00       1d 0H 14M 45S       24.2458333
2  2016-11-04 15:04:48          9H 20M 43S        9.3452778
3  2016-11-03 23:36:59          2H 47M 47S        2.7963889
4  2016-11-30 01:50:17          1H 26M 58S        1.4494444
5  2016-11-10 10:14:51             10M 10S        0.1694444
6  2016-11-26 13:50:32         11H 49M 58S       11.8327778
7  2016-11-29 17:45:26             14M 16S        0.2377778
8  2016-11-26 10:37:41             10M 40S        0.1777778
9  2016-11-06 19:18:34          13H 27M 9S       13.4525000
10 2016-11-27 12:08:02          2H 25M 24S        2.4233333

5. 시계열 데이터 5 6

시계열에 관련된 다양한 데이터는 R 팩키지를 통해 얻을 수 있고, 많은 경우 시계열관련 책에 데이터가 포함되어 있고, 저자가 팩키지로 만들어 배포하는 형태가 많다.