Erste Daten für Histogramm
Frage
Gibt es eine Möglichkeit Behältergrößen in MySQL angeben? Gerade jetzt, ich versuche, die folgende SQL-Abfrage:
select total, count(total) from faults GROUP BY total;
Die Daten, die erzeugt wird, ist gut genug, aber es gibt einfach zu viele Zeilen. Was ich brauche, ist eine Möglichkeit, die Daten in vordefinierte Behälter zu gruppieren. Ich kann dies aus einer Skriptsprache, aber ist es eine Möglichkeit, sie direkt in SQL zu tun?
Beispiel:
+-------+--------------+
| 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 |
------------------------
Was ich suche:
+------------+---------------+
| total | count(total) |
+------------+---------------+
| 30 - 40 | 23 |
| 40 - 50 | 15 |
| 50 - 60 | 51 |
| 60 - 70 | 45 |
------------------------------
ich denke, das kann nicht in einer geradlinig Weise erreicht werden, sondern ein Hinweis auf eine im Zusammenhang gespeicherte Prozedur wäre auch in Ordnung sein.
Lösung
Dies ist ein Beitrag über eine super-quick-and-dirty Weg, um ein Histogramm zu erstellen in MySQL für numerische Werte.
Es gibt mehrere andere Möglichkeiten, Histogramme zu erstellen, die besser sind und mit CASE-Anweisungen und andere Arten von komplexer Logik flexibler. Diese Methode gewinnt mich im Laufe der Zeit immer wieder, da es nur so ist einfach für jeden Anwendungsfall zu ändern, und so kurz und prägnant. Dies ist, wie Sie es tut:
SELECT ROUND(numeric_value, -2) AS bucket, COUNT(*) AS COUNT, RPAD('', LN(COUNT(*)), '*') AS bar FROM my_table GROUP BY bucket;
Just Änderung numeric_value, was auch immer Ihre Spalte, ändern Sie die Rundungsschritt, und das ist es. Ich habe die Bars gemacht zu sein in logarithmische Skala, so dass sie wachsen nicht zu viel, wenn Sie große Werte.
numeric_value sollte in der Rundungsoperation ausgeglichen werden, basierend auf dem Rundungsschritt, um den ersten Eimer enthält so viele Elemente wie die folgenden Eimer zu gewährleisten.
z. mit ROUND (numeric_value, -1), numeric_value im Bereich [0,4] (5 Elemente) werden in ersten Eimer gelegt werden, während [5,14] (10 Elemente) in zweiten, [15,24] in dritten, es sei denn, numeric_value versetzt ist in geeigneter Weise über ROUND (numeric_value - 5, -1).
Dies ist ein Beispiel für eine solche Abfrage auf einige zufällige Daten, die hübsch aussieht Süss. Gut genug für eine schnelle Auswertung der Daten. +--------+----------+-----------------+ | 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 | * | +--------+----------+-----------------+
Einige Anmerkungen: Bereiche, die kein Spiel haben, werden nicht in die Zählung erscheinen - Sie werden nicht eine Null in der Zählspalte haben. Auch, ich bin mit dem ROUND funktioniert hier. Sie können genauso einfach ersetzen Sie es mit TRUNCATE wenn Sie das Gefühl es mehr Sinn macht für Sie.
Ich fand es hier http://blog.shlomoid.com /2011/08/how-to-quickly-create-histogram-in.html
Andere Tipps
Mike DelGaudio Antwort ist so, wie ich es tun, aber mit einer leichten Änderung:
select floor(mycol/10)*10 as bin_floor, count(*)
from mytable
group by 1
order by 1
Der Vorteil? Sie können die Behälter so groß oder so klein machen wie Sie wollen. Bins der Größe 100? floor(mycol/100)*100
. Behälter von der Größe 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
Die Tabelle Bins enthält Spalten min_value und max_value, die die Behälter definieren. Beachten Sie, dass der Operator „join ... auf x zwischen y und z“ ist inklusive.
tabelle1 ist der Name der Datentabelle
Ofri Raviv Antwort ist ganz in der Nähe, aber nicht korrekt. Die count(*)
wird 1
auch wenn es Null resultiert in einem Histogramm-Intervall. Die Abfrage Bedürfnisse modifiziert werden eine bedingte sum
zu verwenden:
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 ....
Solange es nicht zu viele Intervalle, das ist eine ziemlich gute Lösung.
habe ich ein Verfahren, das automatisch eine temporäre Tabelle für Behälter erzeugen nach einer bestimmten Anzahl oder Größe verwendet werden kann, für die spätere Verwendung mit Ofri Raviv-Lösung.
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
Dies wird die Histogrammzählwert nur für die Behälter erzeugen, die aufgefüllt werden. David West sollte Recht in seiner Korrektur sein, aber aus irgendeinem Grunde, unpopulated Bins erscheint nicht im Ergebnis für mich (trotz der Verwendung eines LEFT JOIN - Ich verstehe nicht, warum)
.sollte, die funktionieren. Nicht so elegant, aber immer noch:
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
Zusätzlich zu großer Antwort https://stackoverflow.com/a/10363145/916682 , können Sie phpMyAdmin Chart-Tool für ein schönes Ergebnis:
Equal Breite Binning in eine gegebene Anzahl der Bins:
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;
Beachten Sie, dass die 0.0000001 gibt es sicher zu stellen, dass die Datensätze mit dem Wert max (col) gleich nicht seine eigenen bin macht einfach von selbst. Auch die additive Konstante gibt es um sicherzustellen, dass die Abfrage nicht von Null auf Teilung nicht versagen, wenn alle Werte in der Spalte identisch sind.
Beachten Sie auch, dass die Anzahl der Behälter (10 im Beispiel) sollte mit einer Kommazeichen zu vermeiden Integer-Division (die unbereinigten bin_width kann dezimal) geschrieben werden.