ヒストグラムプロットのデータを取得する


82

MySQLでビンサイズを指定する方法はありますか?現在、次のSQLクエリを試しています。

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

生成されているデータは十分ですが、行が多すぎます。必要なのは、データを事前定義されたビンにグループ化する方法です。スクリプト言語からこれを行うことはできますが、SQLで直接行う方法はありますか?

例:

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

私が探しているもの:

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

これは簡単な方法では達成できないと思いますが、関連するストアドプロシージャへの参照も問題ありません。


私はあなたが何を求めているのか正確にはわかりません。出力例が役立つ場合があります。
Berek Bryan

ごめんなさい!例を挙げて私の投稿を更新しました。
凡例

回答:


162

これは、MySQLで数値のヒストグラムを作成するための非常に手っ取り早い方法に関する投稿です。

CASEステートメントや他のタイプの複雑なロジックを使用して、より優れた柔軟性のあるヒストグラムを作成する方法は他にも複数あります。この方法は、ユースケースごとに変更するのがとても簡単で、短くて簡潔なので、何度も何度も私を魅了します。これはあなたがそれをする方法です:

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

numeric_valueを列に変更し、丸めの増分を変更するだけで、それだけです。大きな値を持っているときにバーが大きくなりすぎないように、バーを対数スケールにするようにしました。

最初のバケットに次のバケットと同じ数の要素が含まれるようにするには、丸めの増分に基づいて、ROUNDing操作でnumeric_valueをオフセットする必要があります。

たとえば、ROUND(numeric_value、-1)の場合、範囲[0,4](5要素)のnumeric_valueは最初のバケットに配置され、[5,14](10要素)は2番目に、[15,24]は3番目に配置されます。 National_valueがROUND(numeric_value-5、-1)を介して適切にオフセットされていない限り。

これは、かなり見栄えのするランダムデータに対するこのようなクエリの例です。データをすばやく評価するには十分です。

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

いくつかの注意:一致しない範囲はカウントに表示されません-カウント列にゼロはありません。また、ここではROUND関数を使用しています。自分にとって意味があると感じた場合は、TRUNCATEに簡単に置き換えることができます。

ここで見つけましたhttp://blog.shlomoid.com/2011/08/how-to-quickly-create-histogram-in.html


1
MySQL 8.0.3以降、オプティマイザにより多くの統計を提供するためにヒストグラム統計を作成できるようになりました-mysqlserverteam.com/histogram-statistics-in-mysql
Jaro

クエリの「バー」部分も必要ありません。数値自体はすでに対数棒グラフ/ヒストグラムを形成しています。
異名

31

Mike DelGaudioの答えは私のやり方ですが、少し変更があります。

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

利点?ビンは好きなだけ大きくしたり小さくしたりできます。サイズ100のビン?floor(mycol/100)*100。サイズ5のビン?floor(mycol/5)*5

ベルナルド。


carillonatorがあなたのgroupby&order byはbin_floorまたは1-である必要があると言ったように-あなたがそれを修正すると反対票を投じます、これは私にとって最良の答えです
BM

十分に公平です、@ bm。カリロネーターの提案に従って変更。
Bernardo Siu 2014年

より良い列名が必要な場合は、次のことができますconcat(floor(mycol/5)*5," to ",floor(mycol/5)*5+5)
alex9311 2016

これはround(mycol, -2)、ユーザーが10進数以外の「範囲」を定義できるため、受け入れられた回答から単純なものよりも実際に優れています。数値が適切に丸められるため、round代わりに使用floorします。
子午線

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

テーブルのビンには、ビンを定義する列min_valueとmax_valueが含まれています。演算子「join ... on x BETWEEN yandz」が含まれることに注意してください。

table1はデータテーブルの名前です


2
SQLの構文の色付けが非常に悪いのはなぜですか?どうすればこれを改善できますか?多分私はそれをメタに投稿する必要があります;)
Ofri Raviv

2
この場合、最小値と最大値を定義するためのテンプレートテーブルが必要です。SQLのみでは不可能です。
セザール

SQLの達人!まさに私が欲しかったもの。binsテーブルを作成するときは注意が必要だと思います。それ以外の場合は、すべてが完全に機能します。:) ありがとうございました。Pythonスクリプトを書き終えたところですが、これが必要なものです...
Legend

@Legend:実際、SQLに関しては私はかなりn00bです。しかし、これはクールで有用な質問だったので、私は演習が好きでした...
Ofri Raviv

1
COUNT(*)がゼロを生成する必要があるときに1を生成する方法について、@ David Westの回答(ここではコメントであるはずです)を確認することが重要です。それはあなたにとって大きな問題ではないかもしれませんが、誰かが気づいた場合、統計データを歪め、少しばかげているように見える可能性があります:)
Christopher Schultz

11

Ofri Ravivの答えは非常に近いですが、正しくありません。count(*)なり1、ヒストグラムの区間でゼロの結果がある場合でも。条件付きを使用するには、クエリを変更する必要があります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 ....

間隔が多すぎない限り、これはかなり良い解決策です。


1
+1これは、ビンのサイズを変えることができる唯一のソリューションです
Gabe Moothart 2012

素晴らしい-追加のテーブルは必要ありません
NiRR 2016

+1これは最も柔軟なソリューションimoであり、SQL内からビンに入れたいというユースケースに最も適しているようです。bin範囲をプログラムで導出する必要がある場合は、SQLの外部で導出する方がよいでしょう。再び芋
ライアン・マッコイ

4

後でOfriRavivのソリューションで使用するために、指定した数またはサイズに従ってビンの一時テーブルを自動的に生成するために使用できるプロシージャを作成しました。

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

これにより、入力されたビンのヒストグラムカウントのみが生成されます。David Westは彼の訂正に正しいはずですが、何らかの理由で、入力されていないビンが結果に表示されません(LEFT JOINを使用しているにもかかわらず—理由はわかりません)。


3

それはうまくいくはずです。それほどエレガントではありませんが、それでも:

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

マイク・デルガウディオ経由


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

与えられた数のビンに等しい幅のビニング:

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は、max(col)に等しい値を持つレコードが、それ自体だけで独自のビンにならないようにするためにあることに注意してください。また、追加定数は、列のすべての値が同一である場合にクエリがゼロ除算で失敗しないようにするためにあります。

また、ビンの数(例では10)は、整数の除算を避けるために小数点付きで記述する必要があることに注意してください(未調整のbin_widthは10進数にすることができます)。


WITH something ASビンに入る値を計算する必要がある場合、これは非常に便利です。
ルナー・バーグ

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.