Obtenir des données pour le tracé de l'histogramme

Existe - t-il un moyen de spécifier la taille des bin dans MySQL? En ce moment, j'essaie la requête SQL suivante:

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

Les données générées sont assez bonnes mais il y a juste trop de lignes. Ce dont j'ai besoin est un moyen de regrouper les données dans prédéfinis bacs. Je peux le faire à partir d'un langage de script, mais est-il un moyen de le faire directement en SQL?

Exemple:

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

Ce que je cherche:

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

Je suppose que cela ne peut pas être réalisé d'une manière simple mais un référence à toute procédure stockée serait très bien.

70
demandé sur Housy 2009-11-19 20:02:36

10 réponses

Ceci est un post sur un moyen super rapide et sale de créer un histogramme dans MySQL pour les valeurs numériques.

Il existe plusieurs autres façons de créer des histogrammes qui sont meilleurs et plus flexible, en utilisant des instructions de CAS et d'autres types de logique complexe. Cette méthode me gagne au fil du temps et du temps car c'est tellement facile à modifier pour chaque cas d'utilisation, et donc court et concis. C'est la façon dont vous faites-le:

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

Il suffit de changer numeric_value à quel que soit votre la colonne est le cas, modifiez l' incrément arrondi, et c'est tout. J'ai fait les barres pour être dedans l'échelle logarithmique, de sorte qu'ils ne poussent pas trop quand vous avez grandes valeurs.

Numeric_value doit être décalé dans l'opération D'arrondi, en fonction de l'incrément d'arrondi, afin de s'assurer que le premier compartiment contient autant d'éléments que les compartiments suivants.

Par exemple avec ROUND (numeric_value, -1), numeric_value dans la plage [0,4] (5 éléments) sera placé dans le premier compartiment, tandis que [5,14] (10 éléments) en deuxième, [15,24] en troisième, sauf si numeric_value est décalé de manière appropriée via ROUND (numeric_value-5, -1).

Ceci est un exemple d'une telle requête sur certaines données aléatoires qui semble jolie doux. Assez bon pour une évaluation rapide des données.

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

Quelques remarques: les Plages qui n'ont pas de correspondance n'apparaissent pas dans le décompte - vous n'aurez pas de zéro dans la colonne count. Aussi, je suis en utilisant le Fonction ROUND ici. Vous pouvez facilement le remplacer par TRONQUER si vous sentez que cela a plus de sens pour vous.

, je l'ai trouvé ici http://blog.shlomoid.com/2011/08/how-to-quickly-create-histogram-in.html

131
répondu Jaro 2015-12-03 16:53:27

La réponse de Mike DelGaudio est la façon dont je le fais, mais avec un léger changement:

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

L'avantage? Vous pouvez rendre les bacs aussi grands ou aussi petits que vous le souhaitez. Bacs de taille 100? floor(mycol/100)*100. Bacs de taille 5? floor(mycol/5)*5.

Bernardo.

22
répondu Bernardo Siu 2014-06-03 14:57:37
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

La table bins contient les colonnes min_value et max_value qui définissent les bins. notez que l'opérateur de jointure... sur x entre y et z " est inclus.

Table1 est le nom de la table de données

16
répondu Ofri Raviv 2009-11-19 17:38:30

La réponse D'Ofri Raviv est très proche mais incorrecte. Le {[1] } sera 1 même s'il n'y a aucun résultat dans un intervalle d'histogramme. La requête doit être modifiée pour utiliser un sum conditionnel:

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;
11
répondu David West 2011-03-01 22:14:11
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 ....

Tant Qu'il n'y a pas trop d'intervalles, c'est une assez bonne solution.

9
répondu sammy 2012-12-10 23:04:53

J'ai fait une procédure qui peut être utilisée pour générer automatiquement une table temporaire pour les bacs selon un nombre ou une taille spécifié, pour une utilisation ultérieure avec la solution D'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

Cela générera le nombre d'histogrammes uniquement pour les bacs qui sont remplis. David West devrait avoir raison dans sa correction, mais pour une raison quelconque, les bacs Non peuplés n'apparaissent pas dans le résultat pour moi (malgré l'utilisation D'une jointure gauche - Je ne comprends pas pourquoi).

3
répondu Dologan 2010-08-10 15:40:10

Ça devrait marcher. Pas si élégant mais quand même:

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

Par Mike DelGaudio

3
répondu Renaud 2011-02-17 14:36:46
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 
1
répondu GauravP 2017-03-10 06:12:51

En plus de la bonne réponse https://stackoverflow.com/a/10363145/916682 , Vous pouvez utiliser l'outil graphique phpmyadmin pour un bon résultat:

entrez la description de l'image ici

entrez la description de l'image ici

0
répondu zub0r 2017-05-23 12:18:01

Binning de largeur égale dans un nombre donné de bacs:

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;

Notez que le 0.0000001 est là pour s'assurer que les enregistrements avec la valeur égale à max(col) ne font pas son propre bac tout seul. En outre, la constante additive est là pour s'assurer que la requête n'échoue pas sur la division par zéro lorsque toutes les valeurs de la colonne sont identiques.

Notez également que le nombre de bins (10 dans l'exemple) doit être écrit avec une marque décimale pour éviter la division entière (le non ajusté bin_width peut être décimal).

0
répondu user824276 2016-11-18 14:26:55