Pergunta

Existe uma maneira de especificar tamanhos de bin no MySQL? No momento, estou tentando a seguinte consulta SQL:

select total, count(total) from faults GROUP BY total;

Os dados que estão sendo gerados são bons o suficiente, mas há muitas linhas. O que eu preciso é uma maneira de agrupar os dados em caixas predefinidas. Eu posso fazer isso de uma linguagem de script, mas existe uma maneira de fazê -lo diretamente no SQL?

Exemplo:

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

O que estou procurando:

+------------+---------------+
| total      | count(total)  |
+------------+---------------+
|    30 - 40 |            23 | 
|    40 - 50 |            15 | 
|    50 - 60 |            51 | 
|    60 - 70 |            45 | 
------------------------------

Eu acho que isso não pode ser alcançado de maneira direta, mas uma referência a qualquer procedimento armazenado relacionado também ficaria bem.

Foi útil?

Solução

Esta é uma postagem sobre uma maneira super rápida e suja de criar um histograma no MySQL para valores numéricos.

Existem várias outras maneiras de criar histogramas que são melhores e mais flexíveis, usando instruções de caso e outros tipos de lógica complexa. Esse método me ganha uma e outra vez, já que é tão fácil de modificar para cada caso de uso e tão curto e conciso. É assim que se faz:

SELECT ROUND(numeric_value, -2)    AS bucket,
       COUNT(*)                    AS COUNT,
       RPAD('', LN(COUNT(*)), '*') AS bar
FROM   my_table
GROUP  BY bucket;

Basta alterar numeric_value para qualquer que seja a sua coluna, altere o incremento de arredondamento e é isso. Eu fiz as barras para estar em escala logarítmica, para que elas não cresçam muito quando você tem grandes valores.

numeric_value deve ser compensado na operação de arredondamento, com base no incremento de arredondamento, a fim de garantir que o primeiro balde contenha tantos elementos quanto os seguintes baldes.

por exemplo, com redondo (numeric_value, -1), numeric_value no intervalo [0,4] (5 elementos) serão colocados no primeiro balde, enquanto [5,14] (10 elementos) em segundo, [15,24] em terceiro, A menos que o numeric_value seja compensado adequadamente via rodada (numeric_value - 5, -1).

Este é um exemplo dessa consulta em alguns dados aleatórios que parecem muito doces. Bom o suficiente para uma avaliação rápida dos dados.

+--------+----------+-----------------+
| 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 | *               |
+--------+----------+-----------------+

Algumas notas: os intervalos que não têm correspondência não aparecerão na contagem - você não terá zero na coluna da contagem. Além disso, estou usando a função redonda aqui. Você pode substituí -lo com a mesma facilidade por truncate se sentir que faz mais sentido para você.

Eu encontrei aqui http://blog.shlomoid.com/2011/08/how-to-quickly-create-histogram-in.html

Outras dicas

A resposta de Mike Delgaudio é a maneira como faço, mas com uma ligeira mudança:

select floor(mycol/10)*10 as bin_floor, count(*)
from mytable
group by 1
order by 1

A vantagem? Você pode fazer as caixas tão grandes ou pequenas quanto quiser. Caixas de tamanho 100? floor(mycol/100)*100. Caixas de tamanho 5? floor(mycol/5)*5.

Bernardo.

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

As caixas de tabela contém colunas min_value e max_value que definem as caixas. Observe que o operador "ingressar ... em x entre y e z" é inclusivo.

Tabela1 é o nome da tabela de dados

A resposta de Ofri Raviv é muito próxima, mas incorreta. o count(*) vai ser 1 Mesmo se houver zero resultados em um intervalo de histograma. A consulta precisa ser modificada para usar um condicional 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 ....

Enquanto não houver muitos intervalos, essa é uma solução muito boa.

Fiz um procedimento que pode ser usado para gerar automaticamente uma tabela temporária para caixas de acordo com um número ou tamanho especificado, para uso posterior com a solução de 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

Isso gerará a contagem de histograma apenas para as caixas que são preenchidas. David West deveria estar certo em sua correção, mas, por algum motivo, os caixotes despovoados não aparecem no resultado para mim (apesar do uso de uma junção de esquerda - eu não entendo o porquê).

Isso deve funcionar. Não tão elegante, mas ainda assim:

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

através da Mike Delgaudio

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 

Além da ótima resposta https://stackoverflow.com/a/10363145/916682, você pode usar a ferramenta de gráfico de phpmyadmin para um bom resultado:

enter image description here

enter image description here

Igual largura de largura em uma determinada contagem de caixas:

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;

Observe que o 0.0000001 está lá para garantir que os registros com o valor igual ao max (col) não façam seu próprio compartimento apenas por si só. Além disso, a constante aditiva está lá para garantir que a consulta não falhe na divisão por zero quando todos os valores da coluna são idênticos.

Observe também que a contagem de caixas (10 no exemplo) deve ser escrita com uma marca decimal para evitar a divisão inteira (o bin_width não ajustado pode ser decimal).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top