Удалите записи, соответствующие значению в одном столбце, в 5-минутном окне другого значения в этом же столбце. - PullRequest
0 голосов
/ 12 декабря 2018

У меня есть фрейм данных, который выглядит следующим образом:

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

Что я хотел бы сделать, это удалить все «органические» записи, встречающиеся в пятиминутном окне неорганической записи, для каждойcustomer.

Другими словами, я хочу удалить все записи, где: 1) канал = органический и 2) запись_время <5 минут удалено из предыдущей записи и 3) канал предыдущей записи! = органический.Я должен быть в состоянии сделать это для каждого идентификатора клиента. </p>

Мой желаемый результат выглядит следующим образом:

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 голосов
/ 12 декабря 2018

Прелесть 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...