首先,我对R有点陌生,在管理一些时间序列数据时遇到了麻烦。我找到了一个可行的解决方案(下面的代码),但是在较大的数据集上速度非常慢(750k行上的1个变量需要35分钟)。
我想要实现的是,每次USAGE
值超过某个预定义值(usage_limit
)时,它就会开始对行进行计数,直到再次超过相同的值为止。重置计数器。对于每个客户端,当计数器更改为0时,它以NA开头,并且一直为NA,直到通过usage_limit
为止。如果计数器已经更改为0,则NA现在显示在USAGE
中,则它将正常计数。或者更简单地说,我正在尝试创建一个变量,以显示用户过去USAGE
超过usage_limit
的行数(在我的情况下为几个月)。
这是用于计算USAGE_35PCT_MTH
的伪数据以及预期的输出和循环。这是在R 3.5.1,lubridate 1.7.4和tidyverse 1.3.0上完成的
library(lubridate)
library(tidyverse)
dummy_tb <- tibble("USER_ID"=c("000001", "000001", "000001", "000001", "000001", "000001", "000001", "000001", "000001", "000001", "000001", "000001", "000001", "000001", "000001", "200000", "200000", "200000", "200000", "200000", "200000", "200000", "200000"),
"REFERENCE_DATE"=c("31.01.2016", "29.02.2016", "31.03.2016", "30.04.2016", "31.05.2016", "30.06.2016", "31.07.2016", "31.08.2016", "30.09.2016", "31.10.2016", "30.11.2016", "31.12.2016", "31.01.2017", "28.02.2017", "31.03.2017", "31.03.2014", "30.04.2014", "31.05.2014", "30.06.2014", "31.07.2014", "31.08.2014", "30.09.2014", "31.10.2014"),
"USAGE"=c(0.30, 0.35, 0.34, 0.38, 0.40, 0.70, 0.78, 0.95, 0.36, 0.22, 0.11, 0.01, 0.1, 0.1, 0.1, NA, 0.36, 0.2, NA, 0.2, 0.2, NA, 0.2),
"USAGE_35PCT_MTH"=c(NA, 0, 1, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, NA, 0, 1, 2, 3, 4, 5, 6))
dummy_tb$REFERENCE_DATE <- as_datetime(dummy_tb$REFERENCE_DATE, format="%d.%m.%Y")
dummy_tb$REFERENCE_DATE <- as_date(dummy_tb$REFERENCE_DATE)
dummy_tb <- dummy_tb %>%
arrange(USER_ID, REFERENCE_DATE) %>%
mutate("USAGE_35PCT_MTH"=NA)
counter <- NA
user_curr <- ""
user_prev <- ""
usage_limit <- 0.35
for (row in 1:nrow(dummy_tb)){
user_curr <- dummy_tb[row, "USER_ID"]
if (user_curr != user_prev ) {
counter <- NA
}
checking_value <- dummy_tb[row, "USAGE"]
if (!is.na(checking_value)){
if (checking_value >= usage_limit) {
counter <- 0
}
}
dummy_tb[row, "USAGE_35PCT_MTH"] <- counter
counter <- counter + 1
user_prev <- user_curr
}
所以我的问题是,有没有办法加快速度?我一直在努力寻找与Dplyr的合作方式,但还没有取得成功。
感谢帮助!
答案 0 :(得分:2)
这里是dplyr
的一种方式:
library(dplyr)
dummy_tb %>%
#Replace `NA` with 0
mutate(USAGE = replace(USAGE, is.na(USAGE), 0)) %>%
#Group by USER_ID
group_by(USER_ID) %>%
#Create a new group which resets everytime USAGE is greater than usage_limit
group_by(temp = cumsum(USAGE >= usage_limit), add = TRUE) %>%
#Create an index
mutate(out = row_number() - 1) %>%
group_by(USER_ID) %>%
#Replace with NA values before first usage_limit cross.
mutate(out = replace(out, row_number() < which.max(USAGE >= usage_limit), NA))
返回:
# USER_ID REFERENCE_DATE USAGE USAGE_35PCT_MTH temp out
#1 000001 31.01.2016 0.30 NA 0 NA
#2 000001 29.02.2016 0.35 0 1 0
#3 000001 31.03.2016 0.34 1 1 1
#4 000001 30.04.2016 0.38 0 2 0
#5 000001 31.05.2016 0.40 0 3 0
#6 000001 30.06.2016 0.70 0 4 0
#7 000001 31.07.2016 0.78 0 5 0
#8 000001 31.08.2016 0.95 0 6 0
#9 000001 30.09.2016 0.36 0 7 0
#10 000001 31.10.2016 0.22 1 7 1
#11 000001 30.11.2016 0.11 2 7 2
#12 000001 31.12.2016 0.01 3 7 3
#13 000001 31.01.2017 0.10 4 7 4
#14 000001 28.02.2017 0.10 5 7 5
#15 000001 31.03.2017 0.10 6 7 6
#16 200000 31.03.2014 0.00 NA 0 NA
#17 200000 30.04.2014 0.36 0 1 0
#18 200000 31.05.2014 0.20 1 1 1
#19 200000 30.06.2014 0.00 2 1 2
#20 200000 31.07.2014 0.20 3 1 3
#21 200000 31.08.2014 0.20 4 1 4
#22 200000 30.09.2014 0.00 5 1 5
#23 200000 31.10.2014 0.20 6 1 6
答案 1 :(得分:0)
我只想添加一个附录,我在第一个问题中没有指定。尽管Ronak Shah的解决方案为最初的问题做出了出色的工作,但我遇到一个问题,其中USER_ID
在整个NA
中具有所有data.frame
的值。在Ronak的答案中,通常是从0到用户拥有的行数。
在这种情况下,我想拥有NA
值。我只添加了几行即可满足此要求。
library(dplyr)
dummy_tb %>%
#Replace `NA` with 0
mutate(USAGE = replace(USAGE, is.na(USAGE), 0)) %>%
#Group by USER_ID
group_by(USER_ID) %>%
#Create a new group which resets everytime USAGE is greater than usage_limit
group_by(temp = cumsum(USAGE >= usage_limit), add = TRUE) %>%
#Create an index
mutate(out = row_number() - 1) %>%
group_by(USER_ID) %>%
#Replace with NA values before first usage_limit cross.
mutate(out = replace(out, row_number() < which.max(USAGE >= usage_limit), NA)) %>%
#Ungroup to reset grouping
ungroup() %>%
#group by USER_ID again
group_by(USER_ID) %>%
#check if all USAGE values are NA by USER_ID
mutate(out_temp = all(is.na(USAGE))) %>%
#replace where out_temp == TRUE
mutate(out, replace(out, out_temp, NA))
编辑:
类似地,如果USAGE
从未超过usage_limit
,就会出现问题。由于USAGE
从未超过usage_limit
,因此通常计算该月为NA。我像以前一样添加了另一个类似的检查,只是temp
的所有USER_ID
值都是0
,因为这意味着它从未更改过的值也从未超过usage_limit
。
最后添加了这些行
ungroup() %>%
group_by(USER_ID) %>%
mutate(out_temp = all(temp==0) %>%
mutate(out, replace(out, out_temp, NA)) %>%
ungroup()