使用dbplyr时避免算术溢出错误

问题描述

这个问题类似于this one,但概括起来,所以发布的答案不太合适。数据是这样的,一行标识一个单位时间对:

large_sql_df
id   t   var1   var2
1    1    10     0
1    2    20     1
2    1    11     0

我想按 var2 和时间 t 进行汇总:

localdf <- large_sql_df %>%
     group_by(var,t) %>%
     summarise(count = n(),var1_mean = mean(var1))

这给出了错误:“将表达式转换为数据类型 int 的算术溢出错误。”我认为这是因为 count 变成了一个非常大的数字。有没有办法阻止这种情况发生,而不必在 sql 中执行整个查询

解决方法

算术溢出限制是 2,147,483,647(见 here)所以这可能不是由计数引起的。它更有可能是由 mean(var1) 引起的,因为要计算平均值,计算机首先必须计算 sum(var1)。如果您的 var1 列通常包含大于 40 的值,则可能会出现溢出错误(40 * 3.5 亿 > 2,647)。

here 中的解决方案也与您的问题相关:将 var1 显式转换为更合适的格式。类似于以下内容:

localdf <- large_sql_df %>%
     mutate(var1 = CAST(var1 AS FLOAT)) %>% 
     group_by(var,t) %>%
     summarise(count = n(),var1_mean = mean(var1))

根据您的 dbplyr 版本,您可能需要类似 mutate(var1 = sql("CAST(var1 AS FLOAT)")) 的内容。

有关不同数据类型的讨论,请参阅 this 问题。虽然 float 是一种不精确的数据类型,但它可以处理高达 10E38 的数字。如果您对如此大的总数取平均值,则引入的印象不太可能显着。

,

这只是阐述了@simon-s-a 答案的一些元素:

library(dplyr,warn.conflicts = FALSE)

df <- read.table(header = TRUE,text = "
id   t   var1   var2
1    1    10     0
1    2    20     1
2    1    11     0")

large_sql_df <- dbplyr::tbl_memdb(df)

localdf <- 
  large_sql_df %>%
  mutate(var1 = as.double(var1)) %>% 
  group_by(var2,t) %>%
  summarise(count = n(),var1_mean = mean(var1,na.rm = TRUE),.groups = "drop")

localdf
#> # Source:   lazy query [?? x 4]
#> # Database: sqlite 3.35.5 [:memory:]
#>    var2     t count var1_mean
#>   <int> <int> <int>     <dbl>
#> 1     0     1     2      10.5
#> 2     1     2     1      20

localdf %>% show_query()
#> <SQL>
#> SELECT `var2`,`t`,COUNT(*) AS `count`,AVG(`var1`) AS `var1_mean`
#> FROM (SELECT `id`,CAST(`var1` AS REAL) AS `var1`,`var2`
#> FROM `df`)
#> GROUP BY `var2`,`t`

reprex package (v2.0.0) 于 2021 年 7 月 7 日创建