Решение для фильтрации data.table на основе posixct datetime и двух переменных? - PullRequest
0 голосов
/ 04 декабря 2018

У меня есть данные, когда животные обнаруживаются на разных участках.Я хочу исключить строки (фильтр) из файла обнаружения (df) только для сайта A, если отдельное животное не было обнаружено на сайте B в течение периода времени (5 минут). Мне нужно повторить это для каждого отдельного животного и для нескольких сайтов .В моих реальных данных много животных и более миллиона наблюдений.Я ищу решение data.table, чтобы быть эффективным.

Двумя переменными будут индивидуумы (животные) и обнаруженный участок.

Пример:

obs.num<-1:21 # a simple observation number 
animal<-c(rep("RBT 1",10),rep("RBT 2",7) ,rep("RBT 3",2),"RBT 4","RBT 2") # 
a fake list of animal id's (my data has many)
now <- Sys.time()
ts <- seq(from = now, length.out = 16, by = "mins")
ts <- c(ts,seq(from=tail(ts,1), length.out = 3, by = "hour")) # create a 
fake series of time stamps 
ts <- c(ts,seq(from=tail(ts,1), length.out = 2, by = "hour"))
df<-data.frame(obs.num,animal,ts) # make data frame 
df$site<-c("A","B","A","B","A","B","A","B","A","B","A","B","A","B","A","B","A","B","A","B","B")# make a fake series of sites detection occurred at 
str(df)
df # my example data frame 

В этом примере я хотел бы удалить всю строку для наблюдения 19.

Я ищу решение data.table, аналогичноеэто решение

library(sqldf)

sqldf("with B as (select * from df where site == 'B')
  select distinct df.* from df 
  join B on df.animal = B.animal and 
        B.ts - df.ts between -5 * 60 and 5 * 60
  order by 1")

1 Ответ

0 голосов
/ 04 декабря 2018

Немного неуклюже, но вы можете сделать это с помощью неравных объединений в data.table:

library(data.table)
setDT(df)
nm = names(df)
# unfortunately non-equi-joins don't support on-the-fly
#   columns yet, so we have to first define them explicitly; see:
#   https://github.com/Rdatatable/data.table/issues/1639
df[ , ts_minus_5 := ts - 5*60]
df[ , ts_plus_5 := ts + 5*60]

# identify the observations _matching_ your criteria (i.e. those to keep)
found_at_b = unique(
  df[site == 'A'][df[site == 'B'], .(x.obs.num, x.animal),
                  on = .(animal == animal, ts >= ts_minus_5, ts <= ts_plus_5),
                  # allow.cartesian allows this join to return any
                  #   number of rows, necessary since any "B" row
                  #   might match multiple "A" rows;
                  # nomatch = 0L drops any "B" row without a 
                  #   match found in "A" rows
                  allow.cartesian = TRUE, nomatch = 0L]
)

# to filter, define a "drop" flag (could also call it "filter")
df[site == 'B', drop := FALSE]
df[found_at_b, on = c(obs.num = 'x.obs.num', animal = 'x.animal'),
   drop := FALSE]

# could define drop = TRUE for the other rows, but no need
df = df[(!drop)]

Есть и другие способы немного очистить код, проявляя большую осторожность в отношении потенциальныхсоздание копий, возможно split - сначала данные site, максимально возможное количество вызовов [] и т. д., но это поможет вам начать работу.

...