我有一个看起来像这样的数据框:
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)
不用说,这种方法无法很好地扩展。有人可以帮我解开解决方案的戈尔丁结,变得更实用吗?
非常感谢。
答案 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一样将它们转换回去。