获取直方图的数据

时间:2009-11-19 17:02:37

标签: mysql histogram binning

有没有办法在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 | 
------------------------------

我想这不可能以直接的方式实现,但是对任何相关存储过程的引用也可以。

10 个答案:

答案 0 :(得分:141)

  

这是一篇关于创建直方图的超快速方法的帖子   在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操作中基于舍入增量进行偏移,以确保第一个桶包含与后续桶一样多的元素。

     

e.g。使用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

找到了它

答案 1 :(得分:24)

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

伯纳。

答案 2 :(得分:16)

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

表格箱包含用于定义箱柜的列min_value和max_value。 请注意,运算符“在x BETWEEN y和z上加入...”是包含的。

table1是数据表的名称

答案 3 :(得分:11)

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;

答案 4 :(得分:9)

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 ....

只要没有太多间隔,这是一个非常好的解决方案。

答案 5 :(得分:3)

我制作了一个程序,可用于根据指定的数字或大小自动生成垃圾箱的临时表,以便以后与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

这将仅为填充的箱子生成直方图计数。大卫韦斯特应该纠正他的纠正,但由于某种原因,无人居住的箱子不会出现在我的结果中(尽管使用了LEFT JOIN - 我不明白为什么)。

答案 6 :(得分:3)

那应该有用。不是很优雅,但仍然:

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

答案 7 :(得分:1)

select case when total >= 30 and total <= 40 THEN "30-40"       
       else when total >= 40 and total <= 50 then "40-50" 
       else "50-60" END as Total , count(total) 
group by Total 

答案 8 :(得分:0)

除了很棒的答案https://stackoverflow.com/a/10363145/916682之外,您还可以使用phpmyadmin图表工具获得更好的结果:

enter image description here

enter image description here

答案 9 :(得分:0)

将等宽度合并到给定的箱数中:

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可以是十进制)。