根据条件查找每行和最近的匹配项

Dah*_*bid 7 r sqldf data.table

考虑以下data.table事件:

library(data.table)
breaks <- data.table(id = 1:8,
                     Channel = c("NP1", "NP1", "NP2", "NP2", "NP3", "NP3", "AT4", "AT4"),
                     Time = c(1000, 1100, 975, 1075, 1010, 1080, 1000, 1050),
                     Day = c(1, 1, 1, 1, 1, 1, 1, 1),
                     ZA = c(15, 12, 4, 2, 1, 2, 23, 18),
                     stringsAsFactors = F)

breaks
   id Channel Time Day ZA
1:  1     NP1 1000   1 15
2:  2     NP1 1100   1 12
3:  3     NP2  975   1  4
4:  4     NP2 1075   1  2
5:  5     NP3 1010   1  1
6:  6     NP3 1080   1  2
7:  7     AT4 1000   1 23
8:  8     AT4 1050   1 18
Run Code Online (Sandbox Code Playgroud)

对于休息中的每个唯一事件,我想使用Time变量where 在所有其他通道中找到最近的事件Day == Day,然后对这些事件的ZA值求和.

这是我想要实现的结果:

   id Channel Time Day ZA Sum
1:  1     NP1 1000   1 15  28
2:  2     NP1 1100   1 12  22
3:  3     NP2  975   1  4  39
4:  4     NP2 1075   1  2  32
5:  5     NP3 1010   1  1  42
6:  6     NP3 1080   1  2  32
7:  7     AT4 1000   1 23  20
8:  8     AT4 1050   1 18  19
Run Code Online (Sandbox Code Playgroud)

所以对于第一行,通道是NP1.所有其他通道中的关闭事件Time = 1000是第3,5和7行.4+1+23 = 28

我使用data.table使用以下代码工作:

breaks[breaks[, c("Day", "Time", "Channel", "ZA")], on = "Day", allow.cartesian = TRUE][
  Channel != i.Channel][
    order(id)][
      , delta := abs(Time - i.Time)][
        , .SD[delta == min(delta)], by = .(Channel, Time, Day, i.Channel)][
          , unique(.SD, by = c("id", "i.Channel"))][
            , .(Sum = sum(i.ZA)), by = .(id, Channel, Time, Day, ZA)]
Run Code Online (Sandbox Code Playgroud)

但是,这会在第一步中创建一个包含64行的数据集,并且我希望使用超过一百万行的数据集来执行此操作.

任何人都可以帮我找到更有效的方法吗?

编辑:

我在140个行的完整数据集上尝试了G. Grothendieck(sqldf),eddi(data.table)和MarkusN(dplyr)的解决方案,其中包含39个不同的通道.数据集在内存中.

sqldf:      54 minutes
data.table: 11 hours
dplyr:      29 hours
Run Code Online (Sandbox Code Playgroud)

G. *_*eck 3

在内部选择中,将每一行分解为同一天和不同通道上的那些行,然后在所有连接的行中到特定的原始行,仅保留具有最小绝对时间差的连接行。在外部选择中,将 id 内其他通道的 ZA 相加,给出结果。

请注意,我们在这里假设默认的 SQLite 后端为 sqldf,并使用特定于该数据库的功能,即,如果min在选择中使用,则该选择中指定的其他值也将从最小化行填充。

默认情况下,它将使用内存数据库,如果适合的话最好,但如果您指定dbname = tempfile()为参数,sqldf它将使用文件作为内存数据库。还可以添加一个或多个索引,这可能会或可能不会加快速度。有关更多示例,请参阅 sqldf github 主页。

library(sqldf)

sqldf("select id, Channel, Time, Day, ZA, sum(bZA) Sum
 from (
   select a.*, b.ZA bZA, min(abs(a.Time - b.Time))
   from breaks a join breaks b on a.Day = b.Day and a.Channel != b.Channel
   group by a.id, b.Channel)
 group by id")
Run Code Online (Sandbox Code Playgroud)

给予:

  id Channel Time Day ZA Sum
1  1     NP1 1000   1 15  28
2  2     NP1 1100   1 12  22
3  3     NP2  975   1  4  39
4  4     NP2 1075   1  2  32
5  5     NP3 1010   1  1  42
6  6     NP3 1080   1  2  32
7  7     AT4 1000   1 23  20
8  8     AT4 1050   1 18  19
Run Code Online (Sandbox Code Playgroud)

这比针对这种规模问题的问题中的 data.table 代码稍快,但对于更大的问题,必须重新进行比较。

此外,由于不必具体化中间结果(取决于查询优化器)并且可以处理内存不足(如果需要),因此它可能能够处理更大的大小。

library(data.table)
library(dplyr)
library(sqldf)
library(rbenchmark)

benchmark(sqldf = 
sqldf("select id, Channel, Time, Day, ZA, sum(bZA) Sum
 from (
   select a.*, b.ZA bZA, min(abs(a.Time - b.Time))
   from breaks a join breaks b on a.Day = b.Day and a.Channel != b.Channel
   group by a.id, b.Channel)
 group by id"),

data.table = breaks[breaks[, c("Day", "Time", "Channel", "ZA")], on = "Day",
     allow.cartesian = TRUE][
  Channel != i.Channel][
    order(id)][
      , delta := abs(Time - i.Time)][
        , .SD[delta == min(delta)], by = .(Channel, Time, Day, i.Channel)][
          , unique(.SD, by = c("id", "i.Channel"))][
            , .(Sum = sum(i.ZA)), by = .(id, Channel, Time, Day, ZA)],

dplyr = { breaks %>% 
  inner_join(breaks, by=c("Day"), suffix=c("",".y")) %>%
  filter(Channel != Channel.y) %>%
  group_by(id, Channel, Time, Day, ZA, Channel.y) %>%
  arrange(abs(Time - Time.y)) %>%
  filter(row_number()==1) %>%
  group_by(id, Channel, Time, Day, ZA) %>%
  summarise(Sum=sum(ZA.y)) %>%                           
  ungroup() %>% 
  select(id:Sum) },

order = "elapsed")[1:4]
Run Code Online (Sandbox Code Playgroud)

给予:

        test replications elapsed relative
1      sqldf          100    3.38    1.000
2 data.table          100    4.05    1.198
3      dplyr          100    9.23    2.731
Run Code Online (Sandbox Code Playgroud)