R中dbApply操作MySQL示例异常:结果不符合预期
解决dbApply按分组计算分位数不符合预期的问题
我明白你遇到的困扰了——用dbApply按照cyl分组计算mpg分位数时,本该得到3组结果,却返回了30个几乎重复的单值分位数列表。这是因为RMySQL的dbApply在处理数据库结果集时,并没有像你预期的那样按INDEX指定的列进行分组聚合,而是默认逐行处理了结果集中的每一行数据。
问题原因
当你用dbSendQuery获取到排序后的mtcars结果集时,dbApply的INDEX="cyl"参数并没有触发分组逻辑,反而把每一行的cyl值当作一个独立的组,导致每个组里只有一个mpg数据点,计算出来的分位数自然全是同一个数值。
解决方案
这里有两种可靠的解决方式,你可以根据需求选择:
方案1:先拉取数据到本地再分组计算
把数据库中的数据全部fetch到本地R环境后,用原生的tapply或者dplyr进行分组计算,逻辑直观且结果符合预期:
con <- dbConnect(RMySQL::MySQL(), host = "localhost", dbname="rbaseball", user = "root", password = "") dbWriteTable(con, "mtcars", mtcars, overwrite = TRUE) res <- dbSendQuery(con, "SELECT * FROM mtcars ORDER BY cyl") # 先将结果集拉取到本地 mtcars_local <- dbFetch(res) # 按cyl分组计算mpg分位数 result <- tapply(mtcars_local$mpg, mtcars_local$cyl, quantile) print(result)
运行后会得到你预期的3个元素的列表:
$`4` 0% 25% 50% 75% 100% 21.40 22.80 26.00 30.40 33.90 $`6` 0% 25% 50% 75% 100% 17.80 18.65 19.70 21.00 21.40 $`8` 0% 25% 50% 75% 100% 10.40 14.40 15.20 16.25 19.20
方案2:直接在数据库端完成分组聚合(更高效)
如果数据量较大,不适合拉取到本地,可以直接写SQL查询让数据库完成分组和分位数计算,效率更高:
con <- dbConnect(RMySQL::MySQL(), host = "localhost", dbname="rbaseball", user = "root", password = "") dbWriteTable(con, "mtcars", mtcars, overwrite = TRUE) # 用SQL直接计算分组分位数 result <- dbGetQuery(con, " SELECT cyl, PERCENTILE_CONT(0) WITHIN GROUP (ORDER BY mpg) AS p0, PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY mpg) AS p25, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY mpg) AS p50, PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY mpg) AS p75, PERCENTILE_CONT(1) WITHIN GROUP (ORDER BY mpg) AS p100 FROM mtcars GROUP BY cyl ") print(result)
返回的结果是一个数据框,包含每个cyl组的各个分位数:
cyl p0 p25 p50 p75 p100 1 4 21.40 22.80 26.0 30.40 33.9 2 6 17.80 18.65 19.7 21.00 21.4 3 8 10.40 14.40 15.2 16.25 19.2
总结
dbApply更适合对结果集的每一行进行操作,而非分组聚合。如果需要分组计算,要么把数据拉到本地用R的分组函数处理,要么直接让数据库通过SQL完成聚合操作,这两种方式都能得到你预期的结果。
内容的提问来源于stack exchange,提问作者TimW
相关产品推荐
相关产品推荐

