Rolling Join
데이터테이블의 Rolling-join
데이터 테이블은 dplyr보다 훨씬 빠를 뿐 아니라, dplyr에서 지원하지 않는 기능도 가지고 있다. 그 중의 하나가 Rolling-join이다.
Rolling-join은 기준되는 컬럼이 동일하지 않아도 병합되기에 inequality join의 하나이다. 예를 들어 접속 데이터와 구매 데이터를 합치고 싶다고 해보자. 접속 시각과 구매 시각이 완전히 일치할 수는 없다. 구매시간 time_purchase 값이 있을 때, 이보다 이르고, 가장 근접한 접속 시간 time_connect를 연결하고 싶다면 Rolling-join을 쓸 수 있다.
예를 들어 철수는 3월 14일 10시, 3월 15일 11시, 3월 16 14시에 접속을 했고, 3월 15일 13시에 어떤 상품을 구매했다고 해보자.
| 접속 시간 | 접속 위치 |
|---|---|
| 03/14 10:00 | 222.103.41.37 |
| 03/15 11:00 | 224.112.52.14 |
| 03/16 14:00 | 221.103.41.37 |
| 구매 시간 | 구매 상품 |
|---|---|
| 03/15 13:00 | 스포츠차 |
이 두 테이블은 기존의 방법으로 병합할 수 없다. 왜냐하면 접속 시간과 구매시간이 정확하게 일치하는 열은 없기 때문이다. 반면 통상적으로 접속이 이루어진 후, 구매가 완료되기 때문에 접속과 구매 행위를 연결하기 위해서는 접속 시간은 구매시간보다 이른 시간이며 다른 열과 비교했을 때 가장 가까워야 한다. 이런 경우 Rolling-join을 사용할 수 있다.
형식은 다음과 같다.
DT1[DT2, on=' ', roll= ]
다음은 조금 다른 예이다.
library(data.table)
DTA = data.table(
date = as.Date(c('2018-12-31', '2019-1-4', '2019-1-9')),
duration = c(60, 30, 40))
DTB = data.table(
date = as.Date(c('2018-1-2', '2019-1-5', '2019-1-6')),
product = c('Cosmetics', 'Toys', 'Books'))
| date | duration |
|---|---|
| 2018-12-31 | 60 |
| 2019-01-04 | 30 |
| 2019-01-09 | 40 |
| _ |
|---|
| date | product |
|---|---|
| 2018-01-02 | Cosmetics |
| 2019-01-05 | Toys |
| 2019-01-06 | Books |
DTA에는 어떤 사람의 온라인 쇼핑몰 접속 기록을 보여준다. 2018년 12월 31일에 60분간 접속했고, 2019년 1월 4일에는 30분간 접속했다(표 10.1).DTB는 구매 기록을 보여준다. 2018년 1월 2일에는 Cosmetics를 구매했다.
roll = -Inf
여기서 2018년 1월 2일보다 빠르지만 가장 근접한 쇼핑몰 접속 기록을 연결시키고 싶다면? 또는 쇼핑몰 접속 시간과 가장 근접한 이후 구매 시각을 알고 싶다면?
DTB[DTA, on=date, roll=-Inf]은 DTA의 date를 기준으로 DTB를 연결할 때 만약 일치하는 date가 없다면 DTB의 date를 이전으로 변경시켜서 일치하는 date를 찾는다. DTB[ , roll=-Inf] 형식임을 주목하자. DTB의 date가 하루씩 앞당겨진다. 결과에 date는 DTA의 date이고, DTB의 date는 하루씩 앞당겨지면서 병합될 행을 찾는다.
따라서 DTB의 2018년 1월 2일은 DTA의 2018년 1월 2일, 1월 1일, 2017년 12월 31일 등과 연결을 시도 한다. 여기서 roll=-Inf은 동일한 date을 찾을 때까지 지속됨을 의미한다. 결과는 다음과 같다.
DTB[DTA, on='date', roll=-Inf]
## date product duration ## 1: 2018-12-31 Toys 60 ## 2: 2019-01-04 Toys 30 ## 3: 2019-01-09 <NA> 40
이를 도식적으로 표현하면 다음과 같다.
DTB의 date은 DTA의 date과 일치할 때 까지 하루씩 과거를 거슬러 올라간다. 그리고 DTA의 date과 일치될 때, DTB와 DTA의 해당 행이 병합된다. 이때 date은 DTA의 date이다.
만약 구매 시간까지 보존하고자 한다면 다음과 같이 date를 하나 더 복사해 두면 될 것이다.
DTB[, datePurchase:=date]
DTB[DTA, on='date', roll=-Inf]
## date product datePurchase duration ## 1: 2018-12-31 Toys 2019-01-05 60 ## 2: 2019-01-04 Toys 2019-01-05 30 ## 3: 2019-01-09 <NA> <NA> 40
roll = +3
만약 구매 시간 이후의 가장 근접한 접속 기록을 찾고 싶다면 roll=Inf을 쓴다.
만약 구매 시간 이후 3일 내의 접속 기록을 찾고 싶다면 roll=3을 쓴다.
DTB[DTA, on='date', roll=Inf]
## date product datePurchase duration ## 1: 2018-12-31 Cosmetics 2018-01-02 60 ## 2: 2019-01-04 Cosmetics 2018-01-02 30 ## 3: 2019-01-09 Books 2019-01-06 40
DTB[DTA, on='date', roll=3]
## date product datePurchase duration ## 1: 2018-12-31 <NA> <NA> 60 ## 2: 2019-01-04 <NA> <NA> 30 ## 3: 2019-01-09 Books 2019-01-06 40
roll=+3은 다음과 같이 도식화할 수 있다.
DTB의 date을 DTA의 date과 일치시키기 위해서 하루씩 늦춰진다. 최대 3일까지. 만약 이렇게 했음에도 DTA의 date에 대응하는 DTB를 찾지 못한다면 DTB의 열은 모두 NA가 된다.
정리
여기서는 테이터 테이블의 Rolling-join에 대해 알아보았다. Rolling-join은 주로 날짜를 기준으로 두 데이터테이블을 병합할 때 쓰인다. 특히 roll=의 의미와 기능을 도식적으로 표현하여 이해를 도왔다.
아쉬운 점
만약 접속 시간과 접속을 유지한 시간이 있다면, 각 행별로 roll=의 값을 다른 게 설정하면 좋을 것 같다. 하지만 아쉽게도 data.table에서 roll=은 하나의 값을 설정해야 한다.
Leave a comment