修订后的新答案(2 个月后开始)
library(tidyverse)
library(lubridate)
df <- data.frame(
date = as.Date(c("2020-01-01", "2020-01-01", "2020-02-01", "2020-02-02", "2020-03-01", "2020-03-02", "2020-01-05", "2020-01-08", "2020-02-18", "2020-02-18", "2020-03-01", "2020-03-02", "2020-01-01", "2020-01-01", "2020-02-01", "2020-02-02", "2020-03-01", "2020-03-02")),
id = c("A", "A", "A", "A", "A", "A", "B", "B", "B", "B", "B", "B", "C", "C", "C", "C", "C", "C"),
amount = c(1, 5, 5, 5, 6, 2, 10, 4, 8, 10, 6, 5, 5, 1, 6, 2, 5, 5)
)
# function to calculate if condition is met for a given months range
calc_id <- function(.dat, m1, m2 = NULL) {
extr_date <- m1
if(is.null(m2)) {
m2 <- extr_date
} else {
m2 <- extr_date %m-% months(m2)
}
dat_end <- extr_date %m+% months(1)
dat_start <- m2
temp1 <- .dat %>%
filter(date < dat_end,
date >= dat_start)
if (nrow(temp1) == 0) return(NA)
temp2 <- temp1 %>%
summarise(
amount_sum = sum(amount),
date_distinct = n_distinct(date)
) %>%
filter(amount_sum >= 10 & date_distinct >= 2)
if (nrow(temp2) > 0) {
return(1)
} else {
return(0)
}
}
# function which decides which months range to choose
comb_calc <- function(.dat, m, mdiff) {
lag_date <- m %m-% months(1)
lag_date2 <- m %m-% months(2)
# added condition to return NA if one of the two preceeding month is NA
if (is.na(calc_id(.dat, lag_date2)) || is.na(calc_id(.dat, lag_date))) {
return(NA)
} else if (calc_id(.dat, lag_date) == 0) {
calc_id(.dat, m1 = m, m2 = mdiff)
} else {
calc_id(.dat, m1 = m)
}
}
# rearrange data
df %>%
nest_by(id) %>%
crossing(Date = floor_date(df$date, "month")) %>%
rowwise(id) %>%
# call comb_calc and choose number of months (here 2)
mutate(res = comb_calc(data, Date, 2)) %>%
select(-data) %>%
pivot_wider(names_from = Date,
values_from = res) %>%
rename_with(~ str_sub(., 1, 7), matches("^\\d{4}-\\d{2}"))
#> # A tibble: 3 x 4
#> id `2020-01` `2020-02` `2020-03`
#> <chr> <dbl> <dbl> <dbl>
#> 1 A NA NA 0
#> 2 B NA NA 1
#> 3 C NA NA 1
由reprex package (v0.3.0) 于 2020 年 6 月 29 日创建
新答案(适用于自定义月份)
考虑到不仅要考虑两个月,而且要考虑任何可能的月份,我改变了方法。它使用了两个自定义函数。
library(tidyverse)
library(lubridate)
df <- data.frame(
date = as.Date(c("2020-01-01", "2020-01-01", "2020-02-01", "2020-02-02", "2020-03-01", "2020-03-02", "2020-01-05", "2020-01-08", "2020-02-18", "2020-02-18", "2020-03-01", "2020-03-02", "2020-01-01", "2020-01-01", "2020-02-01", "2020-02-02", "2020-03-01", "2020-03-02")),
id = c("A", "A", "A", "A", "A", "A", "B", "B", "B", "B", "B", "B", "C", "C", "C", "C", "C", "C"),
amount = c(1, 5, 5, 5, 6, 2, 10, 4, 8, 10, 6, 5, 5, 1, 6, 2, 5, 5)
)
# function to calculate if condition is met for a given months range
calc_id <- function(.dat, m1, m2 = NULL) {
extr_date <- m1
if(is.null(m2)) {
m2 <- extr_date
} else {
m2 <- extr_date %m-% months(m2)
}
dat_end <- extr_date %m+% months(1)
dat_start <- m2
temp1 <- .dat %>%
filter(date < dat_end,
date >= dat_start)
if (nrow(temp1) == 0) return(NA)
temp2 <- temp1 %>%
summarise(
amount_sum = sum(amount),
date_distinct = n_distinct(date)
) %>%
filter(amount_sum >= 10 & date_distinct >= 2)
if (nrow(temp2) > 0) {
return(1)
} else {
return(0)
}
}
# function which decides which months range to choose
comb_calc <- function(.dat, m, mdiff) {
lag_date <- m %m-% months(1)
if (!is.na(calc_id(.dat, lag_date)) && calc_id(.dat, lag_date) == 0) {
calc_id(.dat, m1 = m, m2 = mdiff)
} else {
calc_id(.dat, m1 = m)
}
}
# rearrange data
df %>%
nest_by(id) %>%
crossing(Date = floor_date(df$date, "month")) %>%
rowwise(id) %>%
# call comb_calc and choose number of months (here 2)
mutate(res = comb_calc(data, Date, 2)) %>%
select(-data) %>%
pivot_wider(names_from = Date,
values_from = res,
values_fill = 0) %>%
rename_with(~ str_sub(., 1, 7), matches("^\\d{4}-\\d{2}"))
#> # A tibble: 3 x 4
#> id `2020-01` `2020-02` `2020-03`
#> <chr> <dbl> <dbl> <dbl>
#> 1 A 0 1 0
#> 2 B 1 0 1
#> 3 C 0 1 1
由reprex package (v0.3.0) 于 2020 年 6 月 29 日创建
旧答案(适用于两个月的窗口)
library(tidyverse)
df <- data.frame(
date = as.Date(c("2020-01-01", "2020-01-01", "2020-02-01", "2020-02-02", "2020-03-01", "2020-03-02", "2020-01-05", "2020-01-08", "2020-02-18", "2020-02-18", "2020-03-01", "2020-03-02", "2020-01-01", "2020-01-01", "2020-02-01", "2020-02-02", "2020-03-01", "2020-03-02")),
id = c("A", "A", "A", "A", "A", "A", "B", "B", "B", "B", "B", "B", "C", "C", "C", "C", "C", "C"),
amount = c(1, 5, 5, 5, 6, 2, 10, 4, 8, 10, 6, 5, 5, 1, 6, 2, 5, 5)
)
calc_id <- function(.dat) {
.dat %>%
group_by(id) %>%
summarise(
amount_sum = sum(amount),
date_distinct = n_distinct(date)
) %>%
ungroup() %>%
filter(amount_sum >= 10 & date_distinct >= 2) %>%
pull(id)
}
df %>%
mutate(month = paste(lubridate::year(date), lubridate::month(date), sep = "-")) %>%
nest_by(month) %>%
ungroup() %>%
mutate(data2 = lag(data)) %>%
rowwise(month) %>%
mutate(data2 = list(bind_rows(data, data2)),
res = list(calc_id(data)),
id = list(calc_id(data2))) %>%
ungroup() %>%
mutate(res2 = lag(res, default = list(""))) %>%
unnest(res) %>%
unnest(res2) %>%
unnest(id) %>%
filter(! id == res2) %>%
select(month, id) %>%
distinct() %>%
mutate(val = 1) %>%
pivot_wider(names_from = month,
values_from = val,
values_fill = 0) %>%
arrange(id)
#> `summarise()` ungrouping output (override with `.groups` argument)
#> `summarise()` ungrouping output (override with `.groups` argument)
#> `summarise()` ungrouping output (override with `.groups` argument)
#> `summarise()` ungrouping output (override with `.groups` argument)
#> `summarise()` ungrouping output (override with `.groups` argument)
#> `summarise()` ungrouping output (override with `.groups` argument)
#> # A tibble: 3 x 4
#> id `2020-1` `2020-2` `2020-3`
#> <chr> <dbl> <dbl> <dbl>
#> 1 A 0 1 0
#> 2 B 1 0 1
#> 3 C 0 1 1
由reprex package (v0.3.0) 于 2020 年 6 月 27 日创建