données pour obtenir histogramme
Question
Y at-il un moyen de spécifier la taille bin dans MySQL? En ce moment, je suis en train de la requête SQL suivante:
select total, count(total) from faults GROUP BY total;
Les données qui est généré est assez bon, mais il y a trop de lignes. Ce que je besoin est un moyen de regrouper les données dans des bacs prédéfinis. Je peux le faire à partir d'un langage de script, mais est-il un moyen de le faire directement dans 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é de manière avant droit, mais une référence à une procédure stockée connexe serait bien aussi.
La solution
Ceci est un post sur un moyen super rapide et sale pour créer un histogramme MySQL pour les valeurs numériques.
Il y a plusieurs autres façons de créer des histogrammes qui sont mieux et plus souple, en utilisant des déclarations de cas et d'autres types de logique complexe. Cette méthode me gagne au fil du temps et encore, car il est tellement facile à modifier pour chaque cas d'utilisation, et si courte et concise. Voici comment vous faire:
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 à ce que votre colonne, changer la incrément d'arrondi, et c'est tout. J'ai fait les barres pour être en échelle logarithmique, de sorte qu'ils ne poussent pas trop quand vous avez de grandes valeurs.
numeric_value devrait être compensé dans l'opération Arrondi, en fonction de l'incrément d'arrondi, afin d'assurer le premier seau contient autant d'éléments que les seaux suivants.
par exemple. avec ROUND (numeric_value, -1), numeric_value dans la gamme [0,4] (5 éléments) sera placé dans le premier godet, tandis que [5,14] (10) des éléments en second, [15,24] à la troisième, à moins que numeric_value est décalée de façon appropriée par l'intermédiaire d'ROUND (numeric_value - 5, -1).
Ceci est un exemple d'une telle requête sur certaines données aléatoires qui semble assez sucré. 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 notes: plages qui ont aucune correspondance ne figurera pas dans le compte - vous n'aurez pas un zéro dans la colonne de comptage. , J'utilise aussi la ROND fonction ici. Vous pouvez aussi remplacer facilement avec TRUNCATE si vous sentez qu'il est plus logique de vous.
Je l'ai trouvé ici http://blog.shlomoid.com /2011/08/how-to-quickly-create-histogram-in.html
Autres conseils
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 faire les poubelles aussi grand ou aussi petit que vous voulez. Bins de taille 100? floor(mycol/100)*100
. Bins de taille 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
Les bacs de table contient des colonnes MIN_VALUE et max_value qui définissent les bacs. Notez que l'opérateur « se joindre à ... sur x ENTRE y et z » est inclus.
table1 est le nom de la table de données
La réponse de Ofri Raviv est très proche mais incorrect. Le count(*)
sera 1
même s'il y a des résultats nuls dans un intervalle d'histogramme. La requête doit être modifié 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;
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 solution assez bonne.
I fait une procédure qui peut être utilisée pour générer automatiquement une table temporaire de bacs en fonction d'un certain nombre ou de la taille, pour une utilisation ultérieure avec une solution 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
Cela va générer le compte de l'histogramme uniquement pour les bacs qui sont peuplées. David Ouest devrait avoir raison dans sa correction, mais pour une raison quelconque, les bacs inhabitées ne figurent pas dans le résultat pour moi. (Malgré l'utilisation d'un LEFT JOIN - Je ne comprends pas pourquoi)
Cela devrait fonctionner. Pas si élégant mais encore:
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
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
En plus de grande réponse https://stackoverflow.com/a/10363145/916682 , vous pouvez utiliser phpmyadmin outil graphique pour un bon résultat:
binning largeur égale à un nombre donné de cases:
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 vous assurer que les enregistrements avec la valeur égale au maximum (col) ne font pas de son propre balconnet juste par lui-même. En outre, la constante additif est là pour vous assurer que la requête ne manque pas de division par zéro lorsque toutes les valeurs de la colonne sont identiques.
Notez également que le nombre de bacs (10 dans l'exemple) doit être écrit avec une marque décimale pour éviter la division entière (le bin_width non ajusté peut être décimal).