Histogram grafiği için veri alma


82

MySQL'de bin boyutlarını belirlemenin bir yolu var mı? Şu anda aşağıdaki SQL sorgusunu deniyorum:

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

Oluşturulan veriler yeterince iyi ancak çok fazla satır var. İhtiyacım olan şey, verileri önceden tanımlanmış kutularda gruplandırmanın bir yolu. Bunu bir betik dilinden yapabilirim, ancak bunu doğrudan SQL'de yapmanın bir yolu var mı?

Misal:

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

Aradığım şey:

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

Sanırım bu doğrudan bir şekilde başarılamaz, ancak ilgili herhangi bir saklı yordama atıfta bulunmak da iyi olur.


tam olarak ne istediğinden emin değilim. örnek çıktı yardımcı olabilir.
Berek Bryan

Afedersiniz! Yazımı bir örnekle güncelledim.
Efsane

Yanıtlar:


162

Bu, sayısal değerler için MySQL'de bir histogram oluşturmanın süper hızlı ve kirli bir yolu hakkında bir gönderidir.

CASE ifadelerini ve diğer karmaşık mantık türlerini kullanarak daha iyi ve daha esnek histogramlar oluşturmanın birçok başka yolu vardır. Bu yöntem, her bir kullanım durumu için değiştirilmesi çok kolay ve çok kısa ve öz olduğundan beni tekrar tekrar kazanıyor. Bunu nasıl yapıyorsun:

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

Sadece sayısal_değer'i sütununuz ne ise değiştirin, yuvarlama artışını değiştirin, hepsi bu. Çubukları logaritmik ölçekte olacak şekilde yaptım, böylece büyük değerlere sahip olduğunuzda çok fazla büyümesinler.

Numeric_value, ilk bölümün aşağıdaki bölümler kadar çok sayıda öğe içermesini sağlamak için, Yuvarlama işleminde, yuvarlama artışına göre ofset yapılmalıdır.

örneğin, ROUND (sayısal_değer, -1), sayısal_değer [0,4] aralığındaki (5 öğe) birinci gruba, [5,14] (10 öğe) ikinci, [15,24] üçüncü gruba yerleştirilecek, sayısal_değer, YUVARLA (sayısal_değer - 5, -1) aracılığıyla uygun şekilde kaydırılmadıkça.

Bu, oldukça hoş görünen bazı rastgele veriler üzerinde böyle bir sorgu örneğidir. Verilerin hızlı bir şekilde değerlendirilmesi için yeterince iyi.

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

Bazı notlar: Eşleşmesi olmayan aralıklar sayılarda görünmez - sayı sütununda sıfır olmaz. Ayrıca burada YUVARLA işlevini kullanıyorum. Sizin için daha anlamlı olduğunu düşünüyorsanız, onu TRUNCATE ile kolayca değiştirebilirsiniz.

Burada buldum http://blog.shlomoid.com/2011/08/how-to-quickly-create-histogram-in.html


1
MySQL 8.0.3 itibariyle, artık optimize ediciye
Jaro

Sorgunun "çubuk" kısmına bile ihtiyacınız yok; sayıların kendisi zaten bir logaritmik çubuk grafik / histogram oluşturur.
Enharmonic

31

Mike DelGaudio'nun cevabı benim bunu yapma şeklimdir, ancak küçük bir değişiklikle:

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

Avantaj? Kutuları istediğiniz kadar büyük veya küçük yapabilirsiniz. 100 beden bidon mu? floor(mycol/100)*100. 5 beden bidon mu? floor(mycol/5)*5.

Bernardo.


carillonator'ın söylediği gibi grubunuz ve daha iyiye göre sıralamanız bin_floor veya 1 olmalıdır - Düzeltirseniz, benim için en iyi cevap budur
BM

Yeterince adil, @bm. Carillonator tarafından önerildiği gibi değiştirildi.
Bernardo Siu

ve daha güzel bir sütun adı istiyorsanız, yapabilirsinizconcat(floor(mycol/5)*5," to ",floor(mycol/5)*5+5)
alex9311

Bu, round(mycol, -2)kullanıcının ondalık olmayan herhangi bir "aralığı" tanımlamasına izin verdiği için, kabul edilen cevaptan daha basittir . Sayıları düzgün bir şekilde yuvarladığı için roundbunun yerine kullanırdım floor.
meridius

16
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

Tablo bölmeleri, bölmeleri tanımlayan min_value ve max_value sütunlarını içerir. "join ... on x on x BETWEEN y and z" nin kapsayıcı olduğuna dikkat edin.

tablo1, veri tablosunun adıdır


2
SQL için sözdizimi renklendirmesi neden bu kadar kötü? Bunu nasıl geliştirebilirim? Belki de
metaya

2
Bu durumda, minimum ve maksimum değerleri tanımlamak için bir şablon tablosu gereklidir. Sadece SQL ile mümkün değildir.
Cesar

SQL Guru! Tam olarak istediğim gibi. Sanırım bin tablosu oluşturulurken dikkatli olunmalıdır. Aksi takdirde her şey mükemmel çalışır. :) Teşekkür ederim. Bir python komut dosyası yazmayı yeni bitirdim ama tam da ihtiyacım olan şey buydu ...
Efsane

@Legend: Aslında SQL söz konusu olduğunda epey bir n00b oluyorum. ama bu güzel ve faydalı bir soruydu, bu yüzden alıştırmayı beğendim ...
Ofri Raviv

1
@David West'in COUNT (*) sıfır üretmesi gerektiğinde nasıl 1 ürettiğine dair cevabını (burada bir yorum olmalıydı) görmek önemlidir. Bu sizin için büyük bir sorun olmayabilir, ancak istatistiksel verileri çarpıtabilir ve biri fark ederse sizi biraz aptal gösterebilir :)
Christopher Schultz

11

Ofri Raviv'in cevabı çok yakın ama yanlış. count(*)Olacak 1histogram aralığında sıfır sonuç olsa bile. Koşullu kullanmak için sorgunun değiştirilmesi gerekiyor 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;

10
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 ....

Çok fazla aralık olmadığı sürece, bu oldukça iyi bir çözümdür.


1
+1 Bu, kutuların farklı boyutta olmasına izin veren tek çözümdür
Gabe Moothart

harika - ek masalara gerek yok
NiRR

+1 Bu, en esnek çözüm imo'dur ve en iyi şekilde SQL içinden bin yapmak isteme kullanım durumuna uymaktadır. bin aralıklarının programatik olarak türetilmesi gereken her durumda, bunu SQL dışında yapmak muhtemelen daha iyidir. tekrar imo
Ryan McCoy

4

Daha sonra Ofri Raviv'in çözümüyle kullanılmak üzere, belirli bir sayıya veya boyuta göre kutular için otomatik olarak geçici bir tablo oluşturmak için kullanılabilecek bir prosedür yaptım.

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

Bu, yalnızca doldurulan bölmeler için histogram sayısını oluşturacaktır. David West düzeltmesinde haklı olmalı, ancak bazı nedenlerden dolayı, boş kutular benim için sonuçta görünmüyor (LEFT JOIN kullanımına rağmen - nedenini anlamıyorum).


3

Bu çalışmalı. Çok zarif değil ama yine de:

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

Mike DelGaudio aracılığıyla


3
SELECT
    CASE
        WHEN total <= 30 THEN "0-30"
        WHEN total <= 40 THEN "31-40"       
        WHEN total <= 50 THEN "41-50"
        ELSE "50-"
    END as Total,
    count(*) as count
GROUP BY Total 
ORDER BY Total;

2

Belirli bir bölme sayısına eşit genişlikte gruplama:

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;

0.0000001 değerinin max (col) değerine eşit olan kayıtların kendi başına kendi bölmesini yapmamasını sağlamak için orada olduğunu unutmayın. Ayrıca, toplama sabiti, sütundaki tüm değerler aynı olduğunda sorgunun sıfıra bölmede başarısız olmadığından emin olmak için oradadır.

Ayrıca, tamsayı bölmesini önlemek için bölme sayısının (örnekte 10) bir ondalık işaret ile yazılması gerektiğini unutmayın (ayarlanmayan bin_width ondalık olabilir).


WITH something ASEğer bidonları içine gider değerini hesaplamak için varsa çok yararlıdır.
Rúnar Berg

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.