删除与单个列中的值匹配的记录,该记录发生在5分钟窗口内,且同一列中的其他值不同

时间:2018-12-12 19:04:02

标签: r

我有一个看起来像这样的数据框:

require(data.table)
require(tidyverse)

df <- as.data.frame(matrix(c(123, "2018-01-05 09:09:02", "Mobile",
                         123, "2018-01-06 11:11:15", "Organic",
                         123, "2018-01-07 13:24:45", "Email",
                         123, "2018-01-07 13:24:55", "Organic",
                         321, "2018-01-05 15:15:29", "Organic",
                         989, "2018-01-08 08:09:21", "Feeds",
                         989, "2018-01-08 08:09:55", "Organic",
                         989, "2018-01-10 10:21:40", "Email"), nrow = 8,
                       ncol = 3, byrow = TRUE, dimnames = list(NULL, c("customer_id", "entry_time",
                                                                       "channel"))))

df$entry_time <- as.POSIXct(df$entry_time)


 df
 customer_id          entry_time channel
1         123 2018-01-05 09:09:02  Mobile
2         123 2018-01-06 11:11:15 Organic
3         123 2018-01-07 13:24:45   Email
4         123 2018-01-07 13:24:55 Organic
5         321 2018-01-05 15:15:29 Organic
6         989 2018-01-08 08:09:21   Feeds
7         989 2018-01-08 08:09:55 Organic
8         989 2018-01-10 10:21:40   Email

我想为每个客户删除在非有机记录的五分钟内出现的所有“有机”记录。

换句话说,我要删除所有记录,其中:1)channel =常规,2)entry_time <从前一条记录中删除5分钟,3)先前记录的channel!=常规。我需要能够为每个客户ID做到这一点。

我想要的输出如下:

df_desired <- as.data.frame(matrix(c(123, "2018-01-05 09:09:02", "Mobile",
                         123, "2018-01-06 11:11:15", "Organic",
                         123, "2018-01-07 13:24:45", "Email",
                         321, "2018-01-05 15:15:29", "Organic",
                         989, "2018-01-08 08:09:21", "Feeds",
                         989, "2018-01-10 10:21:40", "Email"), nrow = 6,
                       ncol = 3, byrow = TRUE, dimnames = list(NULL, c("customer_id", "entry_time",
                                                                       "channel"))))

df_desired$entry_time <- as.POSIXct(df_desired$entry_time)

df_desired
customer_id          entry_time channel
1         123 2018-01-05 09:09:02  Mobile
2         123 2018-01-06 11:11:15 Organic
3         123 2018-01-07 13:24:45   Email
4         321 2018-01-05 15:15:29 Organic
5         989 2018-01-08 08:09:21   Feeds
6         989 2018-01-10 10:21:40   Email

我可以使用以下嵌套循环来完成此操作(为使您暴露于这种怪诞的歉意)。

dat_splt <- split(df, df$customer_id)


for (h in 1:length(dat_splt)){
dat_splt[[h]]$prox_flag <- 0
if (nrow(dat_splt[[h]]) == 1)
{next}
else
{for (g in 2:nrow(dat_splt[[h]])){
if (dat_splt[[h]][g,]$channel != "Organic")
{next}
else if (dat_splt[[h]][g-1,]$channel != "Organic" &
         as.numeric((difftime(dat_splt[[h]][g,]$entry_time, dat_splt[[h]][g-1,]$entry_time, units = "mins")) < 5))
{dat_splt[[h]][g,]$prox_flag <- 1}
else
{next}
}}
}

dat <- rbindlist(dat_splt)

dat <- dat %>%
   filter(prox_flag != 1)

不用说,这种方法无法很好地扩展。有人可以帮我解开解决方案的戈尔丁结,变得更实用吗?

非常感谢。

1 个答案:

答案 0 :(得分:1)

R的优点在于,几乎所有操作都是矢量化的,因此您可以同时比较多个对象,并且不需要循环。

在这种情况下,您必须将所有值直接与之前的值进行比较,这可以通过将df[-1,]df[-nrow(df),]进行比较来完成,即第二行与第一行进行比较,第三行与第二,依此类推。
仅第一行是一个例外:它总是需要停留。

此外,我认为没有真正的需求按客户划分,还是可以将它们交错?如果不是,那么只需查看customer_id与上面的行是否不同即可。一次执行该操作的代码:

nr <- nrow(df)
df_desired <- rbind(
    df[1,],
    df[-1,][!(df$customer_id[-1]==df$customer_id[-nr] &
              df$channel[-1]=='Organic' &
              df$channel[-nr]!='Organic' &
              as.numeric(df$entry_time[-1]-df$entry_time[-nr],
                   units='mins')<5)
            ,])

最后一点:我不知道您从哪里得到数据,但是首先存储为矩阵然后使用as.data.frame通常不是最好的主意。用于将数据提供给矩阵的c意味着所有内容都被强制转换为同一类,这意味着所有数字都变为字符。虽然data.frame可以很好地处理不同的类。
在这种情况下,您只是使用“ id”作为标识符,但是如果您要使用数字列,则需要像使用POSIXct一样将它们转换回去。