히스토그램 플롯에 대한 데이터 얻기


82

MySQL에서 bin 크기를 지정하는 방법이 있습니까? 지금 다음 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

죄송합니다! 예를 들어 내 게시물을 업데이트했습니다.
Legend

답변:


159

이것은 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를 열이 무엇이든 변경하고 반올림 증분을 변경하면됩니다. 막대를 로그 스케일로 만들었으므로 큰 값을 가질 때 너무 커지지 않습니다.

첫 번째 버킷에 다음 버킷만큼 많은 요소가 포함되도록하려면 numeric_value가 반올림 증분을 기준으로 ROUNDing 작업에서 오프셋되어야합니다.

예를 들어 ROUND (numeric_value, -1)을 사용하면 [0,4] (5 개 요소) 범위의 numeric_value가 첫 번째 버킷에 배치되고 [5,14] (10 개 요소)는 두 번째에, [15,24]는 세 번째에 배치됩니다. numeric_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 | *               |
+--------+----------+-----------------+

참고 : 일치하지 않는 범위는 개수에 표시되지 않습니다. 개수 열에 0이 표시되지 않습니다. 또한 여기에서는 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

쿼리의 "바"부분도 필요하지 않습니다. 숫자 자체는 이미 로그 막대 차트 / 히스토그램을 형성합니다.
enharmonic

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가 귀하의 그룹 by & order by better는 bin_floor 또는 1이어야한다고 말했듯이-수정하면 Ill upvote, 이것이 저에게 가장 좋은 답변입니다
BM

충분히 공평합니다, @bm. carillonator가 제안한대로 변경되었습니다.
Bernardo Siu 2014 년

당신은 더 좋은 열 이름을 원한다면 당신은 할 수concat(floor(mycol/5)*5," to ",floor(mycol/5)*5+5)
alex9311

이것은 round(mycol, -2)사용자가 십진수가 아닌 "범위"를 정의 할 수 있기 때문에 허용 된 답변에서 단순한 것보다 낫습니다 . 숫자를 올바르게 반올림하기 때문에 round대신 사용 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

테이블 bin에는 bin을 정의하는 min_value 및 max_value 열이 포함됩니다. 연산자 "join ... on x BETWEEN y and z"가 포함되어 있습니다.

table1은 데이터 테이블의 이름입니다.


2
SQL의 구문 색상이 왜 그렇게 나쁜가요? 어떻게 개선 할 수 있습니까? 메타에 게시해야 할 수도 있습니다;)
Ofri Raviv

2
이 경우 최소 및 최대를 정의하는 템플릿 테이블이 필요합니다. SQL만으로는 불가능합니다.
Cesar

SQL 전문가! 정확히 내가 원했던 것. bins 테이블을 만들 때주의를 기울여야한다고 생각합니다. 그렇지 않으면 모든 것이 완벽하게 작동합니다. :) 감사합니다. 방금 파이썬 스크립트 작성을 마쳤지만 이것이 제가 필요한 것입니다 ...
Legend

@Legend : 실제로 SQL에 관해서는 꽤 n00b입니다. 그러나 이것은 내가 ... 운동을 좋아하므로 시원하고 유용한 질문이었다
Ofri Raviv

1
0을 생성해야 할 때 COUNT (*)가 1을 생성하는 방법에 대한 @David West의 답변 (여기에서는 주석이어야 함)을 보는 것이 중요합니다. 그것은 당신에게 큰 문제가되지 않을 수 있지만 통계 데이터를 왜곡하고 :) 당신이 누군가 통지하면 바보 조금 볼 수 있습니다
크리스토퍼 슐츠

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

큰 - 추가 테이블이 필요하지 않습니다
NiRR

+1 이것은 가장 유연한 솔루션 imo이며 SQL 내에서 비닝하려는 사용 사례에 가장 적합한 것 같습니다. bin 범위를 프로그래밍 방식으로 파생해야하는 경우 SQL 외부에서 수행하는 것이 더 좋습니다. 다시 imo
Ryan McCoy

4

나중에 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

이렇게하면 채워진 빈에 대해서만 히스토그램 개수가 생성됩니다. 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

Mike DelGaudio 를 통해


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)과 같은 값을 가진 레코드가 자체적으로 자신의 bin을 만들지 않도록하기 위해 존재합니다. 또한 열의 모든 값이 동일 할 때 0으로 나누면 쿼리가 실패하지 않도록 추가 상수가 있습니다.

또한 bin 개수 (예제에서는 10)를 소수점으로 작성하여 정수 분할을 방지해야합니다 (조정되지 않은 bin_width는 10 진수 일 수 있음).


WITH something AS당신이 쓰레기통에 들어가는 값을 계산해야하는 경우 매우 유용합니다.
Rúnar Berg

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.