R 滚动日期范围内唯一值的计数
Count of unique values in a rolling date range for R
这个问题已经有了 answer for SQL,我能够使用 sqldf
在 R 中实现该解决方案。但是,我一直无法找到使用 data.table
来实现它的方法。
问题是计算滚动日期范围内一列的不同值,例如(并直接从链接的问题中引用)如果数据是这样的:
Date | email
-------+----------------
1/1/12 | test@test.com
1/1/12 | test1@test.com
1/1/12 | test2@test.com
1/2/12 | test1@test.com
1/2/12 | test2@test.com
1/3/12 | test@test.com
1/4/12 | test@test.com
1/5/12 | test@test.com
1/5/12 | test@test.com
1/6/12 | test@test.com
1/6/12 | test@test.com
1/6/12 | test1@test.com
如果我们使用 3 天的日期周期,结果集将看起来像这样
date | count(distinct email)
-------+------
1/1/12 | 3
1/2/12 | 3
1/3/12 | 3
1/4/12 | 3
1/5/12 | 2
1/6/12 | 2
下面是使用 data.table
在 R 中创建相同数据的代码:
date <- as.Date(c('2012-01-01','2012-01-01','2012-01-01',
'2012-01-02','2012-01-02','2012-01-03',
'2012-01-04','2012-01-05','2012-01-05',
'2012-01-06','2012-01-06','2012-01-06'))
email <- c('test@test.com', 'test1@test.com','test2@test.com',
'test1@test.com', 'test2@test.com','test@test.com',
'test@test.com','test@test.com','test@test.com',
'test@test.com','test@test.com','test1@test.com')
dt <- data.table(date, email)
如有任何帮助,我们将不胜感激。谢谢!
编辑 1:
这是一个玩具问题,我想将其应用于更大的数据集,因此笛卡尔积的使用存在问题。相反,我想要等同于 SQL 中的 相关子查询 的东西,例如我最初链接的问题的解决方案是:
SELECT day
,(SELECT count(DISTINCT email)
FROM tbl
WHERE day BETWEEN t.day - 2 AND t.day -- period of 3 days
) AS dist_emails
FROM tbl t
WHERE day BETWEEN '2012-01-01' AND '2012-01-06'
GROUP BY 1
ORDER BY 1;
编辑 2:
这是根据@jangorecki 的要求,基于@MichaelChirico 的解决方案的一些时间安排:
# The data
> dim(temp)
[1] 2627785 4
> head(temp)
date category1 category2 itemId
1: 2013-11-08 0 2 1713
2: 2013-11-08 0 2 90485
3: 2013-11-08 0 2 74249
4: 2013-11-08 0 2 2592
5: 2013-11-08 0 2 2592
6: 2013-11-08 0 2 765
> uniqueN(temp$itemId)
[1] 13510
> uniqueN(temp$date)
[1] 127
# Timing for data.table
> system.time(dtTime <- temp[,
+ .(count = temp[.(seq.Date(.BY$date - 6L, .BY$date, "day"),
+ .BY$category1, .BY$category2 ), uniqueN(itemId), nomatch = 0L]),
+ by = c("date","category1","category2")])
user system elapsed
6.913 0.130 6.940
>
# Time for sqldf
> system.time(sqlDfTime <-
+ sqldf(c("create index ldx on temp(date, category1, category2)",
+ "SELECT date, category1, category2,
+ (SELECT count(DISTINCT itemId)
+ FROM temp
+ WHERE category1 = t.category1 AND category2 = t.category2 AND
+ date BETWEEN t.date - 6 AND t.date
+ ) AS numItems
+ FROM temp t
+ GROUP BY date, category1, category2
+ ORDER BY 1;"))
user system elapsed
87.225 0.098 87.295
输出是等效的,但使用 data.table 而不是 sqldf 导致 12.5 倍的加速。相当可观!
这里有一些有用的东西,利用了 data.table
的新的非等值连接功能。
dt[dt[ , .(date3=date, date2 = date - 2, email)],
on = .(date >= date2, date<=date3),
allow.cartesian = TRUE
][ , .(count = uniqueN(email)),
by = .(date = date + 2)]
# date V1
# 1: 2011-12-30 3
# 2: 2011-12-31 3
# 3: 2012-01-01 3
# 4: 2012-01-02 3
# 5: 2012-01-03 1
# 6: 2012-01-04 2
老实说,我对它的工作原理有点不满,但我的想法是在 date
上将 dt
连接到自身,匹配任何 date
从 2 天前到今天。我不确定为什么我们必须在之后设置 date = date + 2
来清理。
这是使用键的方法:
setkey(dt, date)
dt[ , .(count = dt[.(seq.Date(.BY$date - 2L, .BY$date, "day")),
uniqueN(email), nomatch = 0L]), by = date]
利用最近在 data.table, v1.9.7
的 current development version 中实现的 non-equi
连接功能,可以按如下方式完成:
dt[.(date3=unique(dt$date2)), .(count=uniqueN(email)), on=.(date>=date3, date2<=date3), by=.EACHI]
# date date2 count
# 1: 2011-12-30 2011-12-30 3
# 2: 2011-12-31 2011-12-31 3
# 3: 2012-01-01 2012-01-01 3
# 4: 2012-01-02 2012-01-02 3
# 5: 2012-01-03 2012-01-03 1
# 6: 2012-01-04 2012-01-04 2
这个问题已经有了 answer for SQL,我能够使用 sqldf
在 R 中实现该解决方案。但是,我一直无法找到使用 data.table
来实现它的方法。
问题是计算滚动日期范围内一列的不同值,例如(并直接从链接的问题中引用)如果数据是这样的:
Date | email
-------+----------------
1/1/12 | test@test.com
1/1/12 | test1@test.com
1/1/12 | test2@test.com
1/2/12 | test1@test.com
1/2/12 | test2@test.com
1/3/12 | test@test.com
1/4/12 | test@test.com
1/5/12 | test@test.com
1/5/12 | test@test.com
1/6/12 | test@test.com
1/6/12 | test@test.com
1/6/12 | test1@test.com
如果我们使用 3 天的日期周期,结果集将看起来像这样
date | count(distinct email)
-------+------
1/1/12 | 3
1/2/12 | 3
1/3/12 | 3
1/4/12 | 3
1/5/12 | 2
1/6/12 | 2
下面是使用 data.table
在 R 中创建相同数据的代码:
date <- as.Date(c('2012-01-01','2012-01-01','2012-01-01',
'2012-01-02','2012-01-02','2012-01-03',
'2012-01-04','2012-01-05','2012-01-05',
'2012-01-06','2012-01-06','2012-01-06'))
email <- c('test@test.com', 'test1@test.com','test2@test.com',
'test1@test.com', 'test2@test.com','test@test.com',
'test@test.com','test@test.com','test@test.com',
'test@test.com','test@test.com','test1@test.com')
dt <- data.table(date, email)
如有任何帮助,我们将不胜感激。谢谢!
编辑 1:
这是一个玩具问题,我想将其应用于更大的数据集,因此笛卡尔积的使用存在问题。相反,我想要等同于 SQL 中的 相关子查询 的东西,例如我最初链接的问题的解决方案是:
SELECT day
,(SELECT count(DISTINCT email)
FROM tbl
WHERE day BETWEEN t.day - 2 AND t.day -- period of 3 days
) AS dist_emails
FROM tbl t
WHERE day BETWEEN '2012-01-01' AND '2012-01-06'
GROUP BY 1
ORDER BY 1;
编辑 2: 这是根据@jangorecki 的要求,基于@MichaelChirico 的解决方案的一些时间安排:
# The data
> dim(temp)
[1] 2627785 4
> head(temp)
date category1 category2 itemId
1: 2013-11-08 0 2 1713
2: 2013-11-08 0 2 90485
3: 2013-11-08 0 2 74249
4: 2013-11-08 0 2 2592
5: 2013-11-08 0 2 2592
6: 2013-11-08 0 2 765
> uniqueN(temp$itemId)
[1] 13510
> uniqueN(temp$date)
[1] 127
# Timing for data.table
> system.time(dtTime <- temp[,
+ .(count = temp[.(seq.Date(.BY$date - 6L, .BY$date, "day"),
+ .BY$category1, .BY$category2 ), uniqueN(itemId), nomatch = 0L]),
+ by = c("date","category1","category2")])
user system elapsed
6.913 0.130 6.940
>
# Time for sqldf
> system.time(sqlDfTime <-
+ sqldf(c("create index ldx on temp(date, category1, category2)",
+ "SELECT date, category1, category2,
+ (SELECT count(DISTINCT itemId)
+ FROM temp
+ WHERE category1 = t.category1 AND category2 = t.category2 AND
+ date BETWEEN t.date - 6 AND t.date
+ ) AS numItems
+ FROM temp t
+ GROUP BY date, category1, category2
+ ORDER BY 1;"))
user system elapsed
87.225 0.098 87.295
输出是等效的,但使用 data.table 而不是 sqldf 导致 12.5 倍的加速。相当可观!
这里有一些有用的东西,利用了 data.table
的新的非等值连接功能。
dt[dt[ , .(date3=date, date2 = date - 2, email)],
on = .(date >= date2, date<=date3),
allow.cartesian = TRUE
][ , .(count = uniqueN(email)),
by = .(date = date + 2)]
# date V1
# 1: 2011-12-30 3
# 2: 2011-12-31 3
# 3: 2012-01-01 3
# 4: 2012-01-02 3
# 5: 2012-01-03 1
# 6: 2012-01-04 2
老实说,我对它的工作原理有点不满,但我的想法是在 date
上将 dt
连接到自身,匹配任何 date
从 2 天前到今天。我不确定为什么我们必须在之后设置 date = date + 2
来清理。
这是使用键的方法:
setkey(dt, date)
dt[ , .(count = dt[.(seq.Date(.BY$date - 2L, .BY$date, "day")),
uniqueN(email), nomatch = 0L]), by = date]
利用最近在 data.table, v1.9.7
的 current development version 中实现的 non-equi
连接功能,可以按如下方式完成:
dt[.(date3=unique(dt$date2)), .(count=uniqueN(email)), on=.(date>=date3, date2<=date3), by=.EACHI]
# date date2 count
# 1: 2011-12-30 2011-12-30 3
# 2: 2011-12-31 2011-12-31 3
# 3: 2012-01-01 2012-01-01 3
# 4: 2012-01-02 2012-01-02 3
# 5: 2012-01-03 2012-01-03 1
# 6: 2012-01-04 2012-01-04 2