HOWTO · Matplotlib

Matplotlib에서 데이터 목록에 대한 히스토그램을 그리는 방법

Matplotlib의 plt.hist()를 사용하여 Python 목록에서 히스토그램을 그린 다음, 빈, 밀도, 색상을 조정합니다.

이 페이지의 내용

Python 목록에서 히스토그램을 그리려면 목록을 Matplotlib의 plt.hist() 함수에 직접 전달합니다. 이 함수는 값을 빈(bin)이라고 하는 구간으로 그룹화하고, 각 구간에 속하는 값의 개수를 세어 구간마다 막대를 하나씩 그리므로, 그래프의 모양을 통해 값의 분포를 알 수 있습니다. 입력으로는 일반 Python 목록이면 충분합니다. NumPy 배열이나 pandas Series로 먼저 변환할 필요가 없습니다. 이 글에서는 0부터 9까지의 정수 21개로 이루어진 목록을 그린 뒤 빈, 막대 높이, 막대 색상을 조정하고, 마지막으로 잘못된 빈 개수와 빈 목록의 경우 어떤 일이 일어나는지 보여줍니다.

plt.hist()로 목록에서 히스토그램 그리기

추가 인수가 없으면 plt.hist()는 데이터 범위를 너비가 같은 빈 10개로 나눕니다. 이 예제는 함수가 반환하는 세 값도 함께 가져와 출력하므로, 목록이 어떻게 그룹화되었는지 정확히 확인할 수 있습니다.

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
counts, bin_edges, patches = plt.hist(data)
print("counts:", counts.tolist())
print("bin_edges:", bin_edges.tolist())
plt.xlabel("Value")
plt.ylabel("Count")
plt.title("Histogram of data")
plt.show()
counts: [1.0, 1.0, 2.0, 2.0, 5.0, 3.0, 3.0, 2.0, 1.0, 1.0]
bin_edges: [0.0, 0.9, 1.8, 2.7, 3.6, 4.5, 5.4, 6.3, 7.2, 8.1, 9.0]

plt.hist()가 기본 빈 10개로 그린 목록 값 21개의 히스토그램. 가장 높은 막대는 3.6부터 4.5까지의 값을 포함하며 개수는 5입니다.

plt.hist()는 세 개의 값을 반환합니다. counts는 각 빈에 들어 있는 관측 개수, bin_edges는 구간의 경계, patches는 나중에 개별 막대의 스타일을 바꾸고 싶을 때 사용하는 막대 아티스트를 담고 있습니다. counts의 항목 수는 빈의 개수와 같고, bin_edges는 각 구간의 양쪽 끝을 표시하므로 항상 항목이 하나 더 많습니다. 여기서는 데이터 범위가 0부터 9까지이므로 기본 빈 10개는 각각 너비가 0.9이며, 가장 높은 막대는 값 4가 다섯 번 등장하는 것을 포함하는 3.6부터 4.5까지의 구간입니다. 그래프만 필요하다면 할당을 생략하고 plt.hist(data)만 호출해도 됩니다.

빈의 개수 또는 빈 경계 설정하기

bins 매개변수는 데이터가 나뉘는 방식을 제어합니다. bins=20과 같은 정수를 전달하면 데이터 범위 전체에 그 개수만큼 너비가 같은 구간이 만들어집니다.

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
counts, bin_edges, patches = plt.hist(data, bins=20)
print("counts:", counts.tolist())
print("number of bins:", len(counts))
plt.xlabel("Value")
plt.ylabel("Count")
plt.title("Histogram with 20 bins")
plt.show()
counts: [1.0, 0.0, 1.0, 0.0, 2.0, 0.0, 2.0, 0.0, 5.0, 0.0, 0.0, 3.0, 0.0, 3.0, 0.0, 2.0, 0.0, 1.0, 0.0, 1.0]
number of bins: 20

빈 20개로 그린 같은 목록. 데이터가 0부터 9까지의 정수만 포함하므로 좁은 빈 중 다수가 비어 있습니다.

이 작은 목록에는 빈 20개가 너무 많습니다. 값이 정수이므로 빈 하나 걸러 하나씩 비게 되어 분포가 실제보다 더 들쭉날쭉해 보입니다. 일반적으로 빈이 너무 적으면 데이터의 구조가 가려질 수 있고, 좁은 빈이 너무 많으면 작은 데이터 집합이 거칠어 보일 수 있으므로, 목록의 크기와 퍼짐에 맞는 빈 개수를 선택하십시오. 정수 대신 bins=[0, 2, 4, 6, 8, 10]과 같은 경계 값 시퀀스를 전달해 구간의 경계를 직접 정할 수도 있습니다. 이 경우 구간의 너비는 서로 달라도 됩니다. 개수의 범위가 넓게 퍼져 있을 때는 선택적 인수인 log=True가 개수 축을 로그 척도로 전환해, 높은 막대 옆에서도 작은 막대가 보이도록 합니다. 이는 축의 척도만 바꾸며 입력 값이나 빈 나누기는 바꾸지 않습니다.

개수 대신 확률 밀도 표시하기

기본적으로 막대 높이는 개수입니다. 대신 확률 밀도가 필요하면 density=True로 설정합니다. 그러면 Matplotlib는 막대의 총 면적이 1이 되도록 높이를 조정합니다.

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
counts, bin_edges, patches = plt.hist(data, bins=10, density=True)
print("densities:", [round(d, 4) for d in counts.tolist()])
area = sum(d * (bin_edges[i + 1] - bin_edges[i]) for i, d in enumerate(counts))
print("total area:", round(area, 6))
plt.xlabel("Value")
plt.ylabel("Probability density")
plt.title("Probability histogram of data")
plt.show()
densities: [0.0529, 0.0529, 0.1058, 0.1058, 0.2646, 0.1587, 0.1587, 0.1058, 0.0529, 0.0529]
total area: 1.0

density=True로 그린 목록의 확률 밀도 히스토그램. 막대 높이는 밀도이며 막대의 총 면적은 1입니다.

density=True인 경우, 반환되는 튜플의 첫 번째 값에는 개수 대신 밀도가 들어가며, 이 예제는 각 밀도에 빈 너비를 곱하고 면적을 합산해 정확히 1이 되는 것으로 정규화를 검증합니다. 개별 막대의 높이는 밀도이지 그 구간의 확률이 아니라는 점에 유의하십시오. 구간의 확률은 막대의 면적, 즉 높이에 너비를 곱한 값입니다. 이 차이는 빈의 너비가 서로 다를 때 중요합니다. 넓은 막대가 더 높지만 좁은 막대보다 더 큰 확률을 나타낼 수 있기 때문입니다. 예제처럼 전환할 때는 세로 축 레이블도 함께 업데이트해, 그래프가 막대 높이의 의미를 설명하도록 하십시오.

막대 색상 변경하기

color 매개변수는 막대의 채우기 색상을 설정합니다.

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
plt.hist(data, bins=10, color="red")
plt.xlabel("Value")
plt.ylabel("Count")
plt.title("Red histogram of data")
plt.show()

빈 10개와 color 매개변수를 빨간색으로 설정해 그린, 빨간 막대의 목록 히스토그램.

"red"와 같은 이름, "#1f77b4"와 같은 16진수 문자열, 한 글자 코드 등 Matplotlib의 모든 색상 지정 방식이 동작합니다. 표시 옵션은 한 번의 호출에서 자유롭게 조합할 수 있습니다. 예를 들어 plt.hist(data, bins=20, density=True, color="red")는 밀도 높이를 가진 빨간 막대 20개를 그립니다. 어떤 옵션을 조합하든 제목과 축 레이블은 그에 맞게 유지하십시오. 밀도 그래프에 개수 레이블이 남아 있어서는 안 되고, 빈이 20개인 그래프가 기본 빈 10개를 보여준다고 주장해서도 안 됩니다.

잘못된 빈 개수와 빈 목록 처리하기

놀라기 전에 알아두면 좋은 입력 실수가 두 가지 있습니다. 첫 번째는 양수가 아닌 빈 개수입니다.

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
plt.hist(data, bins=0)
Traceback (most recent call last):
  ...
ValueError: `bins` must be positive, when an integer

bins=0(그리고 음의 정수는 모두)은 ValueError를 발생시키며 아무것도 그려지지 않습니다. 빈 개수가 사용자 입력이나 계산에서 오는 경우, 그리기 전에 양의 정수인지 검증하거나 대신 경계 시퀀스를 전달하십시오. 두 번째 경우인 빈 목록은 매우 다르게 동작합니다. 전혀 오류가 아닙니다.

import matplotlib.pyplot as plt

counts, bin_edges, patches = plt.hist([])
print("counts:", counts.tolist())
print("bin_edges:", [round(e, 2) for e in bin_edges.tolist()])
counts: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
bin_edges: [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]

데이터가 없으면 plt.hist()는 0부터 1까지의 기본 범위로 대체되고, 0인 개수 10개를 반환하며, 예외를 발생시키지 않고 빈 축을 그립니다. 이 때문에 빈 목록은 조용한 실패 방식이 됩니다. 데이터가 파일, 쿼리, 필터에서 오는 경우, 누락된 데이터 집합이 유효한 결과처럼 보이는 빈 그래프로 바뀌지 않도록 그리기 전에 목록이 비어 있지 않은지 확인하십시오.