Ottenere dati per il grafico dell'istogramma
Domanda
C'è un modo per specificare le dimensioni dei contenitori in MySQL?In questo momento, sto provando la seguente query SQL:
select total, count(total) from faults GROUP BY total;
I dati generati sono abbastanza buoni ma ci sono troppe righe.Ciò di cui ho bisogno è un modo per raggruppare i dati in contenitori predefiniti.Posso farlo da un linguaggio di scripting, ma c'è un modo per farlo direttamente in SQL?
Esempio:
+-------+--------------+
| 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 |
------------------------
Quello che sto cercando:
+------------+---------------+
| total | count(total) |
+------------+---------------+
| 30 - 40 | 23 |
| 40 - 50 | 15 |
| 50 - 60 | 51 |
| 60 - 70 | 45 |
------------------------------
Immagino che ciò non possa essere ottenuto in modo semplice, ma andrebbe bene anche un riferimento a qualsiasi procedura memorizzata correlata.
Soluzione
Questo è un post di un modo super veloce-e-sporco per creare un istogramma in MySQL per i valori numerici.
Ci sono diversi altri modi per creare istogrammi che sono meglio e più flessibile, utilizzando istruzioni CASE e altri tipi di logica complessa. Questo metodo mi conquista di volta in volta dal momento che è proprio così facile modificare per ciascun caso d'uso, e così breve e concisa. Ecco come farlo:
SELECT ROUND(numeric_value, -2) AS bucket, COUNT(*) AS COUNT, RPAD('', LN(COUNT(*)), '*') AS bar FROM my_table GROUP BY bucket;
Basta cambiare numeric_value a tutto ciò che la colonna è, cambiare la arrotondamento incremento, e questo è tutto. Ho fatto le barre di essere in scala logaritmica, in modo che essi non crescono troppo quando si ha grandi valori.
numeric_value dovrebbe essere compensato nella operazione di arrotondamento, basato sull'incremento arrotondamento, al fine di garantire il primo segmento contiene tanti elementi come i seguenti secchi.
es. con ROUND (numeric_value, -1), numeric_value nell'intervallo [0,4] (5 elementi) saranno collocati nel primo segmento, mentre [5,14] (10 elementi) a seconda, [15,24] nel terzo, a meno numeric_value è compensato in modo appropriato tramite ROUND (numeric_value - 5, -1).
Questo è un esempio di tale interrogazione su alcuni dati casuali che sembra piuttosto dolce. Abbastanza buono per una rapida valutazione dei dati.
+--------+----------+-----------------+ | 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 | * | +--------+----------+-----------------+
Alcune note: Gamme che non hanno alcuna corrispondenza non comparirà nel conteggio - non si avrà uno zero nella colonna conteggio. Inoltre, sto usando il ROUND funzione di qui. Si può altrettanto facilmente sostituirlo con TRUNCATE se si sente che ha più senso per voi.
L'ho trovato qui http://blog.shlomoid.com /2011/08/how-to-quickly-create-histogram-in.html
Altri suggerimenti
La risposta di Mike DelGaudio è il modo in cui lo faccio, ma con un leggero cambiamento:
select floor(mycol/10)*10 as bin_floor, count(*)
from mytable
group by 1
order by 1
Il vantaggio?Puoi rendere i contenitori grandi o piccoli quanto desideri.Bidoni da 100? floor(mycol/100)*100
.Bidoni di dimensione 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
I bidoni tabella contiene colonne MIN_VALUE e max_value che definiscono i bidoni. notare che l'operatore "join ... su x TRA yez" è inclusiva.
Tabella 1 è il nome della tabella di dati
La risposta di Ofri Raviv è molto vicino ma non corretto. Il count(*)
sarà 1
anche se ci sono zero risultati in un intervallo di istogramma. La query ha bisogno di essere modificati per utilizzare un sum
condizionale:
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 ....
Fino a quando non ci sono troppi intervalli, questa è una soluzione abbastanza buona.
feci una procedura che può essere utilizzato per generare automaticamente una tabella temporanea per contenitori secondo un numero o dimensione specificata, per un uso successivo con la soluzione di 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
Questo genererà il conteggio istogramma solo per i contenitori che sono popolate. David West dovrebbe essere ragione nella sua correzione, ma per qualche ragione, bidoni disabitate non appaiono nel risultato per me. (Nonostante l'uso di un LEFT JOIN - non capisco perché)
Che dovrebbe funzionare. Non è così elegante, ma ancora:
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
In aggiunta a grande risposta https://stackoverflow.com/a/10363145/916682 , è possibile utilizzare phpMyAdmin strumento grafico per un bel risultato:
La parità di larghezza binning in un determinato numero di bidoni:
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;
Si noti che il 0,0000001 è lì per fare in modo che i record con il valore pari a max (Col) non fanno il proprio bin solo per sé. Inoltre, la costante additivo è lì per assicurarsi che la query non sicuro sulla divisione per zero quando tutti i valori nella colonna sono identici.
Si noti inoltre che il conteggio di contenitori (10 nell'esempio) deve essere scritto con un punto decimale per evitare la divisione intera (il bin_width senza correzione può essere decimale).