【问题标题】:Join 2 data frames using data.table with conditions使用带有条件的 data.table 连接 2 个数据框
【发布时间】:2018-01-16 17:24:08
【问题描述】:

我有这两个数据框:

set.seed(42)
A <- data.table(station = sample(1:10, 1000, replace=TRUE), 
            hash = sample(letters[1:5], 1000, replace=TRUE),
            point = sample(1:24, 1000, replace=TRUE))

B <- data.table(station = sample(1:10, 100, replace=TRUE), 
            card = sample(letters[6:10], 100, replace=TRUE),
            point = sample(1:24, 100, replace=TRUE))

数据框 A 包含超过 1M 行。

我尝试为每个 card(来自 B)找到 hash(来自 A)。我在那里有一些条件:A中的stationspoints位于一个范围内(对于站+- 1,对于点+ 2)。

我使用card 对 B 进行分组,并在实现此类条件后为每个组函数执行绑定行并按频率获取最大值。

detect <- function(x){
  am0 <- data.frame(station = 0,
                    hash = 0, 
                    point = 0)
  for (i in 1:nrow(x)) {
        am1 <- A %>%
      filter(station %in% (B$station[i] - 1) : (B$station[i] + 1) &
               point > B$point[i] & point < B$point[i] + 2)
        am0 <- rbind(am0, am1)
  }
  t <- as.data.frame(table(am0$hash))
  t <- t %>%
    arrange(-Freq) %>%
    filter(row_number() == 1)
  return(t)
}

然后就是:

library(dplyr)
B %>% 
  group_by(card) %>%
  do(detect(.)) %>%
  ungroup

但我不知道如何使用索引[i] 实现每个组的功能,所以我实际上得到了错误的结果。

# A tibble: 5 x 3
   card   Var1  Freq
  <chr> <fctr> <int>
1     f      c    46
2     g      c    75
3     h      c    41
4     i      c    64
5     j      c    62

我是初学者,但我知道大型数据集的最佳解决方案 - 使用 data.table 库连接 2 个这样的数据集。你能帮我做决定吗?

【问题讨论】:

    标签: r dplyr data.table


    【解决方案1】:

    我想你想做的是:

    #### Prepare join limits
    B[, point_limit := as.integer(point + 2)]
    B[, station_lower := as.integer(station - 1)]
    B[, station_upper := as.integer(station + 1)]
    
    ## Join A on B, creates All combinations of points in A and B fulfilling the conditions
    joined_table <- B[A,
      , on = .( point_limit >= point, point <= point,
                station_lower <= station, station_upper >= station),
      nomatch = 0,
      allow.cartesian=TRUE]
    
    
    ## Count the occurrences of the combinations
    counted_table  <- joined_table[,.N, by=.(card,hash)][order(card, -N)]
    
    ## Select the top for each group. 
    counted_table[, head(.SD, 1 ),by = .(card)][order(card)]
    

    这将创建一个包含所有信息的完整表,然后对其进行计数。它完全依赖于 data.tables,因为它可以充分利用该软件包带来的速度提升。如果您不熟悉语法,data.table vignette 很好。 nomatch 条件确保我们正在进行内部连接。

    如果 A 只有 1M 行并且 B 保持相同大小,这可能会很好,具体取决于您的数据分布。然而,我们也可以使用包purrr 以与您的do 语句类似的方式拆分B。但是,我不确定这如何与 R:s garabage collection 交互。

    frame_list <- purrr::map(unique(B$card),
                ~ B[card == .x][A,
                                , on = .(point_limit >= point,
                                         point <= point,
                                         station_lower <= station,
                                         station_upper >= station),
                                nomatch = 0,
    
      allow.cartesian = TRUE][, .N, by = .(card, hash)])
    counted_table_mem <- rbindlist(frame_list )
    

    这里需要注意的是,我使用的是 rbindlist 而不是多个 rbind。重复调用 rbind 会很慢,因为每次都需要分配新的内存。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-12
      • 2015-04-29
      • 1970-01-01
      • 1970-01-01
      • 2018-05-22
      • 1970-01-01
      • 2014-03-02
      • 1970-01-01
      相关资源
      最近更新 更多