Декартово произведение с использованием пакета data.table
Используя пакет data.table в R, я пытаюсь создать декартово произведение двух data.tables, используя метод слияния, как в базе R.
В базе выполняются следующие работы:
#assume this order data
orders<-data.frame(date=as.POSIXct(c('2012-08-28','2012-08-29','2012-09-01')),
first.name=as.character(c('John','George','Henry')),
last.name=as.character(c('Doe','Smith','Smith')),
qty=c(10,50,6))
#and these dates
dates<-data.frame(date=seq(from=as.POSIXct('2012-08-28'),
to=as.POSIXct('2012-09-07'),by='day'))
#get the unique customers
cust<-unique(orders[,c('first.name','last.name')])
#using merge from base R, get the cartesian product
merge(dates,cust,by=integer(0))
Однако тот же метод не работает с использованием data.table, и эта ошибка возникает:
"Ошибка в файле merge.data.table(date.dt, cust.dt, by = integer (0)): Требуется непустой вектор имен столбцов для by
.
#data.table approach
library(data.table)
orders.dt<-data.table(orders)
dates.dt<-data.table(dates)
cust.dt<-unique(orders.dt[,list(first.name,last.name)])
#try to use merge (data.table) in the same manner as base
merge(dates.dt,cust.dt,by=integer(0))
# Error in merge.data.table(dates.dt, cust.dt, by = integer(0)) :
# A non-empty vector of column names for `by` is required.
Я хочу, чтобы результат отражал все имена клиентов для всех дат, как и в базе, но делайте это с помощью метода data.table-centric. Возможно ли это?
Ответы
Ответ 1
Если вы создаете полные имена из первого и последнего в dataframes, вы можете использовать CJ
(cross-join). Вы не можете использовать все три вектора, так как там будет 99 элементов.
> nrow(CJ(dates$date, cust$first.name, cust$last.name ) )
[1] 99
Возвращает объект data.table:
> CJ(dates$date,paste(cust$first.name, cust$last.name) )
V1 V2
1: 2012-08-28 George Smith
2: 2012-08-28 Henry Smith
3: 2012-08-28 John Doe
4: 2012-08-29 George Smith
5: 2012-08-29 Henry Smith
6: 2012-08-29 John Doe
7: 2012-08-30 George Smith
8: 2012-08-30 Henry Smith
9: 2012-08-30 John Doe
10: 2012-08-31 John Doe
11: 2012-08-31 George Smith
12: 2012-08-31 Henry Smith
13: 2012-09-01 John Doe
14: 2012-09-01 George Smith
15: 2012-09-01 Henry Smith
16: 2012-09-02 George Smith
17: 2012-09-02 Henry Smith
18: 2012-09-02 John Doe
19: 2012-09-03 Henry Smith
20: 2012-09-03 John Doe
21: 2012-09-03 George Smith
22: 2012-09-04 Henry Smith
23: 2012-09-04 John Doe
24: 2012-09-04 George Smith
25: 2012-09-05 George Smith
26: 2012-09-05 Henry Smith
27: 2012-09-05 John Doe
28: 2012-09-06 George Smith
29: 2012-09-06 Henry Smith
30: 2012-09-06 John Doe
31: 2012-09-07 George Smith
32: 2012-09-07 Henry Smith
33: 2012-09-07 John Doe
V1 V2
Ответ 2
merge.data.table(x, y)
- это функция удобства, которая завершает вызов x[y]
, поэтому слияние должно основываться на столбцах, которые находятся в обоих data.table
s. (Это то, что сообщение об ошибке пытается вам сказать).
Одной рабочей задачей является добавление фиктивного столбца в оба файла data.tables, единственная цель которых заключается в том, чтобы сделать возможным слияние:
## Add a column "k", and append it to each data.table vector of keyed columns.
setkeyv(cust.dt[,k:=1], c(key(cust.dt), "k"))
setkeyv(dates.dt[,k:=1], c(key(dates.dt), "k"))
## Merge and then remove the dummy column
res <- merge(dates.dt, cust.dt, by="k")
head(res[,k:=NULL])
# date first.name last.name
# 1: 2012-08-28 George Smith
# 2: 2012-08-28 Henry Smith
# 3: 2012-08-28 John Doe
# 4: 2012-08-29 George Smith
# 5: 2012-08-29 Henry Smith
# 6: 2012-08-29 John Doe
## Maybe also clean up cust.dt and dates.dt
# cust.dt[,k:=NULL]
# dates.dt[,k=NULL]
Ответ 3
Решение от @JoshO'Brien использует merge
, но ниже - аналогичная альтернатива, которая не выполняется (AFAIK).
Если я правильно понимаю документацию в ?data.table::merge
, X[Y]
должен быть немного быстрее, чем data.table::merge(X,Y)
(начиная с версии 1.8.7). Это относится к FAQ 2.12 для решения этого вопроса, но часто задаваемые вопросы немного запутывают. Во-первых, правильная ссылка должна быть 1.12, а не 2.12. И они не указывают, ссылаются ли они на базовую версию слияния или на таблицу данных. Или на то и другое. Таким образом, это может просто оказаться более беспорядочным решением, которое эквивалентно, или оно может быть быстрее.
[ Изменить из Matthew] Спасибо: теперь улучшено в v1.8.7 (?merge.data.table
, FAQ 1.12 и добавлен новый FAQ 2.24)
DT_orders<-data.table(date=as.POSIXct(c('2012-08-28','2012-08-29','2012-08-29','2012-09-01')),
first.name=as.character(c('John','John','George','Henry')),
last.name=as.character(c('Doe','Doe','Smith','Smith')),
qty=c(10,2,50,6),
key="first.name,last.name")
# Note that I added a second record to the orders table for John Doe, to make sure it could handle duplicate first/last name combinations.
DT_dates<-data.table(date=seq(from=as.POSIXct('2012-08-28'),
to=as.POSIXct('2012-09-07'),by='day'),
key="date")
DT_custdates<-data.table(k=1,unique(DT_dates),key="k")[unique(DT_orders)[,list(k=1,first.name,last.name)]][,k:=NULL]