Получение данных для построения гистограммы

StackOverflow https://stackoverflow.com/questions/1764881

  •  21-09-2019
  •  | 
  •  

Вопрос

Есть ли способ указать размеры ячеек в MySQL?Прямо сейчас я пытаюсь выполнить следующий 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 | 
------------------------------

Полагаю, этого невозможно добиться простым способом, но ссылка на любую связанную хранимую процедуру тоже подойдет.

Это было полезно?

Решение

Это пост о супер быстром способе создать гистограмму в MySQL для числовых значений.

Существует множество других способов создания гистограмм, которые являются лучше и более гибкими, используя операторы случаев и другие типы сложной логики.Этот метод выигрывает меня с течением времени снова, так как его так легко изменить для каждого варианта использования, и так коротко и кратко.Вот как вы это делаете:

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 должно быть смещено в операции ОКРУГЛЕНИЯ на основе приращения округления, чтобы гарантировать, что первый сегмент содержит столько же элементов, сколько последующие сегменты.

напримерс ROUND(numeric_value,-1), numeric_value в диапазоне [0,4] (5 элементов) будет помещен в первый сегмент, тогда как [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 | *               |
+--------+----------+-----------------+

Некоторые примечания:Диапазоны, которые не имеют совпадения, не появятся в подсчете - у вас не будет нуля в столбце подсчета.Кроме того, я использую круглую функцию здесь.Вы можете так же легко заменить его усечением, если вы чувствуете, что это имеет больше смысла для вас.

Я нашел это здесь http://blog.shlomoid.com/2011/08/how-to-quickly-create-histogram-in.html

Другие советы

Ответ Майка ДельГаудио такой же, как я, но с небольшим изменением:

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.

Бернардо.

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 содержит столбцы min_value и max_value, которые определяют ячейки.обратите внимание, что оператор «join...на x МЕЖДУ y и z" включительно.

table1 — имя таблицы данных

Ответ Офри Равива очень близок, но неверен.А 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;
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 ....

Если интервалов не слишком много, это довольно хорошее решение.

Я создал процедуру, которую можно использовать для автоматического создания временной таблицы для контейнеров в соответствии с указанным количеством или размером для последующего использования с решением Офри Равива.

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 — не понимаю почему).

Это должно сработать.Не так элегантно, но всё же:

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

с помощью Майк ДельГаудио

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 

Помимо отличного ответа https://stackoverflow.com/a/10363145/916682, вы можете использовать инструмент диаграмм phpmyadmin для получения хорошего результата:

enter image description here

enter image description here

Объединение одинаковой ширины в заданное количество ячеек:

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), не создавали собственную корзину сами по себе.Кроме того, аддитивная константа предназначена для того, чтобы гарантировать, что запрос не завершится сбоем при делении на ноль, когда все значения в столбце идентичны.

Также обратите внимание, что количество ячеек (10 в примере) должно быть записано с десятичной точкой, чтобы избежать целочисленного деления (нескорректированная ширина_бина может быть десятичной).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top