考虑以下DataFrame:
DF = structure(list(c_number = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L,
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L,
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L,
3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L,
3L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 5L, 5L, 5L, 5L,
5L, 5L, 5L, 5L, 5L), date = c("2001-01-06", "2001-01-07", "2001-01-08",
"2001-01-09", "2001-01-10", "2001-01-11", "2001-01-12", "2001-01-13",
"2001-01-14", "2001-01-15", "2001-01-16", "2001-01-17", "2001-01-18",
"2001-01-19", "2001-01-20", "2001-01-21", "2001-01-22", "2001-01-23",
"2001-01-24", "2001-01-25", "2001-01-26", "2001-01-11", "2001-01-12",
"2001-01-13", "2001-01-14", "2001-01-15", "2001-01-16", "2001-01-17",
"2001-01-18", "2001-01-19", "2001-01-20", "2001-01-21", "2001-01-22",
"2001-01-23", "2001-01-24", "2001-01-25", "2001-01-26", "2001-01-27",
"2001-01-28", "2001-01-12", "2001-01-13", "2001-01-14", "2001-01-15",
"2001-01-16", "2001-01-17", "2001-01-18", "2001-01-19", "2001-01-20",
"2001-01-21", "2001-01-22", "2001-01-23", "2001-01-24", "2001-01-25",
"2001-01-26", "2001-01-27", "2001-01-28", "2001-01-29", "2001-01-30",
"2001-01-21", "2001-01-22", "2001-01-23", "2001-01-24", "2001-01-25",
"2001-01-26", "2001-01-27", "2001-01-28", "2001-01-29", "2001-01-30",
"2001-01-31", "2001-01-24", "2001-01-25", "2001-01-26", "2001-01-27",
"2001-01-28", "2001-01-29", "2001-01-30", "2001-01-31", "2001-02-01"
), value = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)), .Names = c("c_number",
"date", "value"), row.names = c(NA, -78L), class = "data.frame")
我连续5天有5位客户的销售数据;对于客户1,我有连续21个日期的销售数据....对于客户#5,我有连续9个日期的销售数据......:
> table(DF[, 1])
1 2 3 4 5
21 18 19 11 9
对于每个客户,我想抽样连续15天的子DF(如果我有至少15个该客户的连续日期)或该客户的所有日期(如果我没有该客户的15个连续日期) 。
关键部分是,在案例1中(如果我至少有15个连续日期为该客户),那15个连续日应具有随机开始日期(例如,并非始终是客户的第一个或最后15个日期)避免在分析中引入偏见。
在简单的R中我会这样做:
library(dplyr)
slow_function <- function(i, DF, length_out = 15){
sub_DF = DF[DF$c_number == i, ]
if(nrow(sub_DF) <= length_out){
out_DF = sub_DF
} else {
random_start = sample.int(nrow(sub_DF) - length_out, 1)
out_DF = sub_DF[random_start:(random_start + length_out - 1), ]
}
}
a_out = lapply(1:nrow(a_1), slow_function, DF = DF, length_out = 15)
a_out = dplyr::bind_rows(a_out)
table(a_out[, 1])
1 2 3 4 5
15 15 15 11 9
但我的数据要大得多,而且上面的操作难以忍受。有没有一种快速的方法可以在data.table / dplyr中获得相同的结果?
num_customer = 10
m = 2 * num_customer
a_0 = seq(as.Date("2001-01-01"), as.Date("2001-12-31"), by = "day")
a_1 = matrix(sort(sample(as.character(a_0), m)), nc = 2)
a_2 = list()
for(i in 1:nrow(a_1)){
a_3 = seq(as.Date(a_1[i, 1]), as.Date(a_1[i, 2]), by = "day")
a_4 = data.frame(i, as.character(a_3), round(runif(length(a_3), 1)))
colnames(a_4) = c("c_number", "date", "value")
a_2[[i]] = a_4
}
DF = dplyr::bind_rows(a_2)
dim(DF)
table(DF[, 1])
dput(DF)
答案 0 :(得分:2)
在基数R中加速的方法可能是在子集化之前使用索引而不是整个data.frame。
output = DF[unlist(lapply(
split(1:NROW(DF), DF$c_number), #Split indices along rows of DF
function(x){
if(length(x) < 15){ #Grab all indices if there are less than 15
x
} else{
#Grab an index randomly such that there will be 14 more left after it
x[sample(0:(length(x) - 15), 1) + sequence(15)]
}
})),
]
sapply(split(output, output$c_number), NROW)
# 1 2 3 4 5
#15 15 15 11 9
答案 1 :(得分:2)
试试这个:
sample15consecutive <- function(DF) {
runs <- rle(DF$c_number)$lengths
start <- ifelse(runs > 15, sapply(pmax(runs-15, 1), sample.int, size=1), 1)
end <- ifelse(runs >= 15, 15, runs)
previous <- cumsum(c(0, head(runs, -1)))
DF[unlist(mapply(seq, previous + start, previous + start + end - 1), length),]
}
根据microbenchmark,它快4倍。必须对c_numbers和日期进行排序。
答案 2 :(得分:1)
使用 tidyverse 套餐(特别是 dplyr 和 tidyr )非常简单。
library(tidyverse)
df.sample <- arrange(DF, date) %>%
group_by(c_number) %>%
do(head(., 15))
输出(前30行/ 2名员工):
# A tibble: 65 x 3
c_number date value
<int> <chr> <dbl>
1 1 2001-01-06 1
2 1 2001-01-07 1
3 1 2001-01-08 1
4 1 2001-01-09 1
5 1 2001-01-10 1
6 1 2001-01-11 1
7 1 2001-01-12 1
8 1 2001-01-13 1
9 1 2001-01-14 1
10 1 2001-01-15 1
11 1 2001-01-16 1
12 1 2001-01-17 1
13 1 2001-01-18 1
14 1 2001-01-19 1
15 1 2001-01-20 1
16 2 2001-01-11 1
17 2 2001-01-12 1
18 2 2001-01-13 1
19 2 2001-01-14 1
20 2 2001-01-15 1
21 2 2001-01-16 1
22 2 2001-01-17 1
23 2 2001-01-18 1
24 2 2001-01-19 1
25 2 2001-01-20 1
26 2 2001-01-21 1
27 2 2001-01-22 1
28 2 2001-01-23 1
29 2 2001-01-24 1
30 2 2001-01-25 1
# ... with 35 more rows
df.sample <- arrange(DF, date) %>%
group_by(c_number) %>%
mutate(date = as.Date(date), start = sample(date, 1)) %>%
filter(date >= start & date <= (start + 14))
答案 3 :(得分:1)
samp
生成一个1(样本中)和0(样本外)的向量,我们按此为子集。我没有对其进行基准测试,但它没有将DF
分解为子数据帧,而是仅拆分c_number
向量,然后在原始DF
上执行单个子集。
samp <- function(x) {
n <- length(x)
replace(0*x, seq(sample(max(n - 15, 1), 1), length = min(n, 15)), 1)
}
s <- subset(DF, ave(c_number, c_number, FUN = samp) == 1)
答案 4 :(得分:1)
试试这个:
library(data.table)
setDT(DF)
DF[
,
{
if (.N <= 15) {
# 15 or fewer rows? Grab them all.
.SD
} else {
# Grab a random starting row not too close to the end
random_start <- sample(seq_len(.N - 14), size = 1)
.SD[random_start + 0:14]
}
},
by = c_number
]