获取直方图的数据
Posted
技术标签:
【中文标题】获取直方图的数据【英文标题】:Getting data for histogram plot 【发布时间】:2010-12-18 09:41:41 【问题描述】:有没有办法在 mysql 中指定 bin 大小?现在,我正在尝试以下 SQL 查询:
select total, count(total) from faults GROUP BY total;
正在生成的数据足够好,但行数太多。我需要的是一种将数据分组到预定义箱中的方法。我可以通过脚本语言做到这一点,但有没有办法直接在 SQL 中做到这一点?
例子:
+-------+--------------+
| total | count(total) |
+-------+--------------+
| 30 | 1 |
| 31 | 2 |
| 33 | 1 |
| 34 | 3 |
| 35 | 2 |
| 36 | 6 |
| 37 | 3 |
| 38 | 2 |
| 41 | 1 |
| 42 | 5 |
| 43 | 1 |
| 44 | 7 |
| 45 | 4 |
| 46 | 3 |
| 47 | 2 |
| 49 | 3 |
| 50 | 2 |
| 51 | 3 |
| 52 | 4 |
| 53 | 2 |
| 54 | 1 |
| 55 | 3 |
| 56 | 4 |
| 57 | 4 |
| 58 | 2 |
| 59 | 2 |
| 60 | 4 |
| 61 | 1 |
| 63 | 2 |
| 64 | 5 |
| 65 | 2 |
| 66 | 3 |
| 67 | 5 |
| 68 | 5 |
------------------------
我在寻找什么:
+------------+---------------+
| total | count(total) |
+------------+---------------+
| 30 - 40 | 23 |
| 40 - 50 | 15 |
| 50 - 60 | 51 |
| 60 - 70 | 45 |
------------------------------
我想这不能以直接的方式实现,但对任何相关存储过程的引用也可以。
【问题讨论】:
我不太确定你在问什么。示例输出可能会有所帮助。 对不起!刚刚用一个例子更新了我的帖子。 【参考方案1】:这是一篇关于创建直方图的超级快捷方式的帖子 在 MySQL 中用于数值。
还有多种其他方法可以创建更好的直方图 更灵活,使用 CASE 语句和其他类型的复杂逻辑。 这种方法一次又一次地赢得了我,因为它是如此简单 为每个用例进行修改,如此简短明了。这就是你的方式 这样做:
SELECT ROUND(numeric_value, -2) AS bucket, COUNT(*) AS COUNT, RPAD('', LN(COUNT(*)), '*') AS bar FROM my_table GROUP BY bucket;
只需将 numeric_value 更改为您的列,更改 舍入增量,就是这样。我已经把酒吧放进去 对数刻度,这样当你有 大值。
numeric_value 应该在 ROUNDing 操作中根据舍入增量进行偏移,以确保第一个存储桶包含与后续存储桶一样多的元素。
例如使用 ROUND(numeric_value,-1),范围 [0,4](5 个元素)中的 numeric_value 将被放置在第一个桶中,而 [5,14](10 个元素)在第二个桶中,[15,24] 在第三个桶中,除非numeric_value 通过 ROUND(numeric_value - 5, -1) 适当偏移。
这是对一些看起来很漂亮的随机数据进行此类查询的示例 甜的。足以快速评估数据。
+--------+----------+-----------------+ | bucket | count | bar | +--------+----------+-----------------+ | -500 | 1 | | | -400 | 2 | * | | -300 | 2 | * | | -200 | 9 | ** | | -100 | 52 | **** | | 0 | 5310766 | *************** | | 100 | 20779 | ********** | | 200 | 1865 | ******** | | 300 | 527 | ****** | | 400 | 170 | ***** | | 500 | 79 | **** | | 600 | 63 | **** | | 700 | 35 | **** | | 800 | 14 | *** | | 900 | 15 | *** | | 1000 | 6 | ** | | 1100 | 7 | ** | | 1200 | 8 | ** | | 1300 | 5 | ** | | 1400 | 2 | * | | 1500 | 4 | * | +--------+----------+-----------------+
一些注意事项:没有匹配的范围不会出现在计数中 - 计数列中不会有零。另外,我正在使用 ROUND函数在这里。您可以轻松地将其替换为 TRUNCATE 如果你觉得它对你更有意义。
我在这里找到它http://blog.shlomoid.com/2011/08/how-to-quickly-create-histogram-in.html
【讨论】:
从 MySQL 8.0.3 开始,您现在可以创建直方图统计信息,以便为优化器提供更多统计信息 - 请参阅 mysqlserverteam.com/histogram-statistics-in-mysql 你甚至不需要查询的“bar”部分;数字本身已经形成对数条形图/直方图。【参考方案2】:Mike DelGaudio 的回答是我这样做的方式,但略有不同:
select floor(mycol/10)*10 as bin_floor, count(*)
from mytable
group by 1
order by 1
优势?您可以根据需要使垃圾箱变大或变小。大小为 100 的垃圾箱? floor(mycol/100)*100
。大小为 5 的垃圾箱? floor(mycol/5)*5
.
伯纳多。
【讨论】:
正如 carillonator 所说,您的 group by 和 order by 更好应该是 bin_floor 或 1 - 如果您更正它,我会投票,这对我来说是最好的答案 很公平,@b-m。按照 carillonator 的建议进行了更改。 如果你想要一个更好的列名,你可以concat(floor(mycol/5)*5," to ",floor(mycol/5)*5+5)
这实际上比接受答案中的简单round(mycol, -2)
更好,因为它允许用户定义任何非十进制“范围”。我只使用round
而不是floor
,因为它正确地舍入了数字。
@luky 这意味着您按选择语句中的第一个字段(在本例中为 bin_floor)进行分组。据我所知,只有mysql实现了这种语法。【参考方案3】:
SELECT b.*,count(*) as total FROM bins b
left outer join table1 a on a.value between b.min_value and b.max_value
group by b.min_value
表 bins 包含定义 bins 的列 min_value 和 max_value。 请注意,运算符“join ... on x BETWEEN y and z”是包含在内的。
table1是数据表的名字
【讨论】:
为什么 SQL 的语法着色如此糟糕?我该如何改进呢?也许我应该把它贴在 meta 上;) 在这种情况下,需要一个模板表来定义最小值和最大值。仅使用 SQL 是不可能的。 SQL 大师!正是我想要的。我想在创建 bins 表时应该小心。否则一切正常。 :) 谢谢。我刚写完一个python脚本,但这正是我所需要的...... @Legend:实际上,在 SQL 方面,我是一个很牛的人。但这是一个很酷且有用的问题,所以我喜欢这个练习...... 重要的是要查看@David West 的回答(这里应该是评论)关于 COUNT(*) 在应该产生 0 时如何产生 1。这对你来说可能不是什么大问题,但它可能会扭曲统计数据,如果有人注意到它会让你看起来有点傻:)【参考方案4】:Ofri Raviv 的答案非常接近但不正确。 count(*)
将是 1
,即使直方图间隔中的结果为零。查询需要修改为使用条件sum
:
SELECT b.*, SUM(a.value IS NOT NULL) AS total FROM bins b
LEFT JOIN a ON a.value BETWEEN b.min_value AND b.max_value
GROUP BY b.min_value;
【讨论】:
【参考方案5】:select "30-34" as TotalRange,count(total) as Count from table_name
where total between 30 and 34
union (
select "35-39" as TotalRange,count(total) as Count from table_name
where total between 35 and 39)
union (
select "40-44" as TotalRange,count(total) as Count from table_name
where total between 40 and 44)
union (
select "45-49" as TotalRange,count(total) as Count from table_name
where total between 45 and 49)
etc ....
只要间隔不是太多,这是一个很好的解决方案。
【讨论】:
+1 这是这里唯一允许垃圾箱大小不同的解决方案 太棒了——不需要额外的表格 +1 这是 imo 最灵活的解决方案,似乎最适合希望从 SQL 中进行 bin 处理的用例。任何需要以编程方式派生 bin 范围的情况,在 SQL 之外执行此操作可能会更好。再次imo【参考方案6】:我做了一个程序,可以用来根据指定的数量或大小自动生成一个临时表用于 bin,以供以后与 Ofri Raviv 的解决方案一起使用。
CREATE PROCEDURE makebins(numbins INT, binsize FLOAT) # binsize may be NULL for auto-size
BEGIN
SELECT FLOOR(MIN(colval)) INTO @binmin FROM yourtable;
SELECT CEIL(MAX(colval)) INTO @binmax FROM yourtable;
IF binsize IS NULL
THEN SET binsize = CEIL((@binmax-@binmin)/numbins); # CEIL here may prevent the potential creation a very small extra bin due to rounding errors, but no good where floats are needed.
END IF;
SET @currlim = @binmin;
WHILE @currlim + binsize < @binmax DO
INSERT INTO bins VALUES (@currlim, @currlim+binsize);
SET @currlim = @currlim + binsize;
END WHILE;
INSERT INTO bins VALUES (@currlim, @maxbin);
END;
DROP TABLE IF EXISTS bins; # be careful if you have a bins table of your own.
CREATE TEMPORARY TABLE bins (
minval INT, maxval INT, # or FLOAT, if needed
KEY (minval), KEY (maxval) );# keys could perhaps help if using a lot of bins; normally negligible
CALL makebins(20, NULL); # Using 20 bins of automatic size here.
SELECT bins.*, count(*) AS total FROM bins
LEFT JOIN yourtable ON yourtable.value BETWEEN bins.minval AND bins.maxval
GROUP BY bins.minval
这将仅为填充的 bin 生成直方图计数。 David West 的更正应该是正确的,但由于某种原因,未填充的 bin 没有出现在我的结果中(尽管使用了 LEFT JOIN — 我不明白为什么)。
【讨论】:
【参考方案7】:应该可以。不那么优雅但仍然:
select count(mycol - (mycol mod 10)) as freq, mycol - (mycol mod 10) as label
from mytable
group by mycol - (mycol mod 10)
order by mycol - (mycol mod 10) ASC
通过Mike DelGaudio
【讨论】:
【参考方案8】:SELECT
CASE
WHEN total <= 30 THEN "0-30"
WHEN total <= 40 THEN "31-40"
WHEN total <= 50 THEN "41-50"
ELSE "50-"
END as Total,
count(*) as count
GROUP BY Total
ORDER BY Total;
【讨论】:
【参考方案9】:等宽分箱到给定的箱数中:
WITH bins AS(
SELECT min(col) AS min_value
, ((max(col)-min(col)) / 10.0) + 0.0000001 AS bin_width
FROM cars
)
SELECT tab.*,
floor((col-bins.min_value) / bins.bin_width ) AS bin
FROM tab, bins;
请注意,0.0000001 是为了确保值等于 max(col) 的记录不会单独使其成为自己的 bin。此外,当列中的所有值都相同时,附加常数可以确保查询不会因被零除而失败。
还要注意,bin 的数量(示例中为 10)应使用小数点写入以避免整数除法(未调整的 bin_width 可以是小数)。
【讨论】:
WITH something AS
非常有用,如果您必须计算进入垃圾箱的值。【参考方案10】:
除了很好的答案https://***.com/a/10363145/916682,您还可以使用phpmyadmin图表工具获得一个不错的结果:
【讨论】:
以上是关于获取直方图的数据的主要内容,如果未能解决你的问题,请参考以下文章