निम्नलिखित डेटाफ़्रेम पर विचार करें:

    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 ग्राहकों के लिए बिक्री डेटा है; ग्राहक 1 के लिए, मेरे पास लगातार 21 तिथियों पर बिक्री डेटा है .... ग्राहक # 5 के लिए, मेरे पास लगातार 9 तिथियों पर बिक्री डेटा है...:

> table(DF[, 1])

 1  2  3  4  5 
21 18 19 11  9

प्रत्येक ग्राहक के लिए मैं लगातार 15 दिनों के उप डीएफ का नमूना लेना चाहता हूं (यदि मेरे पास उस ग्राहक के लिए कम से कम 15 लगातार तिथियां हैं) या उस ग्राहक के लिए सभी तिथियां (यदि मेरे पास उस ग्राहक के लिए लगातार 15 तिथियां नहीं हैं)।

मुख्य बात यह है कि मामले में 1 (यदि मेरे पास उस ग्राहक के लिए कम से कम 15 लगातार तिथियां हैं) तो उन 15 लगातार दिनों में एक यादृच्छिक प्रारंभ तिथि होनी चाहिए (उदाहरण के लिए ग्राहक के लिए हमेशा पहली या अंतिम 15 तिथियां नहीं होनी चाहिए) विश्लेषण में पूर्वाग्रह

सादे आर में मैं करूँगा:

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)

संपादित करें2:

100k ग्राहक DF पर, क्रिस्टोफ़ वॉक का समाधान सबसे तेज़ है। इसके बाद जी. ग्रोथेंडिक (लगभग 4 गुना अधिक समय) है, इसके बाद नाथन वर्थ (जी। ग्रोथेंडिक की तुलना में 2 धीमी गति का एक अन्य कारक) है। अन्य समाधान काफ़ी धीमे हैं। फिर भी, सभी प्रस्ताव मेरे अस्थायी 'slow_function' से तेज़ हैं, इसलिए सभी को धन्यवाद!

1
user189035 1 सितंबर 2017, 22:36

5 जवाब

सबसे बढ़िया उत्तर

ये कोशिश करें:

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),]
}

यह माइक्रोबेंचमार्क के अनुसार लगभग 4 गुना तेज है। c_numbers और तिथियों को क्रमबद्ध करना होगा।

2
Christoph Wolk 1 सितंबर 2017, 23:13

आधार आर में तेजी लाने का एक तरीका यह हो सकता है कि सब्मिटिंग से पहले पूरे डेटा के बजाय इंडेक्स के साथ काम करें।

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 
2
d.b 1 सितंबर 2017, 23:18

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)
1
G. Grothendieck 1 सितंबर 2017, 22:55

ये कोशिश करें:

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
]
1
Nathan Werth 1 सितंबर 2017, 23:00

यह 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

संपादित करें: निम्नलिखित प्रत्येक कर्मचारी के लिए एक यादृच्छिक प्रारंभ तिथि का चयन करता है और फिर यादृच्छिक रूप से चुने गए बिंदु के बाद लगातार 15 दिनों तक का चयन करता है:

df.sample <- arrange(DF, date) %>% 
  group_by(c_number) %>% 
  mutate(date = as.Date(date), start = sample(date, 1)) %>% 
  filter(date >= start & date <= (start + 14))
1
jdobres 1 सितंबर 2017, 23:02