🍚

Visualizing the distribution of a dataset

서문

When dealing with a set of data, often the first thing you’ll want to do is get a sense for how the variables are distributed. This chapter of the tutorial will give a brief introduction to some of the tools in seaborn for examining univariate and bivariate distributions. You may also want to look at the categorical plots chapter for examples of functions that make it easy to compare the distribution of a variable across levels of other variables.
데이터 셋을 다룰 때 대개 가장 먼저 하고 싶은 건 변수들의 분포에 대해 감을 잡아가는 것이다. 이번 튜토리얼에서는 Seaborn에서 단변량(univariate) 혹은 이변량(bivariate) 분포들을 파악하는 몇몇 도구에 대해서 간단한 설명을 할 것이다. 변수와 타 변수들 간의 분포를 쉽게 비교하려면 범주형 그래프 부분의 튜토리얼을 함께 살펴봐야할 수도 있을 것이다.
import numpy as np import pandas as pd import seaborn as sns import matplotlib.pyplot as plt from scipy import stats sns.set(color_codes=True)
Python
복사

Plotting univariate distributions¶

단변량에 따른 분포 그리기
The most convenient way to take a quick look at a univariate distribution in seaborn is the distplot() function. By default, this will draw a histogram and fit a kernel density estimate (KDE).
Seaborn에서 단변량 (종속 변수가 하나인 것) 분포를 빠르게 나타내는 가장 간편한 방법은 distplot() 클래스를 쓰는 것이다. 기본적으로, 이 클래스는 데이터에 대한 히스토그램을 구현하고 커널 밀도 추정 (KDE) 을 적용한다.
x = np.random.normal(size=100) sns.distplot(x);
Python
복사

Histograms

히스토그램
Histograms are likely familiar, and a hist function already exists in matplotlib. A histogram represents the distribution of data by forming bins along the range of the data and then drawing bars to show the number of observations that fall in each bin.
히스토그램이야 모두에게 친숙할 가능성이 높고, hist 클래스는 matplotlib에 예전부터 존재해왔다. 히스토그램이란 구간(bin)을 형성해 그 구간에 속한 데이터의 범위를 막대로 나타냄으로써 각 구간에서 관찰되는 수를 표현하는 것이다.
To illustrate this, let’s remove the density curve and add a rug plot, which draws a small vertical tick at each observation. You can make the rug plot itself with the rugplot() function, but it is also available in distplot():
아래처럼 그리기 위해서, 밀도 곡선은 제거하고, 러그 그래프(rug plot)를 추가해 보자. 러그 그래프란 각 관찰값에 해당하는 세로형 막대 표기(tick)를 작게 그리는 걸 말한다. 러그 그래프는 rugplot()을 통해 직접 만들 수도 있지만, distplot() 을 통해서도 가능하다.
sns.distplot(x, kde=False, rug=True);
Python
복사
When drawing histograms, the main choice you have is the number of bins to use and where to place them. distplot() uses a simple rule to make a good guess for what the right number is by default, but trying more or fewer bins might reveal other features in the data:
히스토그램을 그릴 때 중점적으로 선택해야 할 것은 표기하고자 하는 구간의 수와 구간을 어디에 둘 지 여부이다. distplot()은 적당한 구간 수를 결정하는 간단한 규칙을 기본적으로 내장하고 있다. 그러나 더 많게 혹은 적게 설정해 봄으로써 다양한 데이터의 형태를 표현할 수 있다.
sns.distplot(x, bins=20, kde=False, rug=True);
Python
복사

Kernel density estimation¶

커널 (확률)밀도 추정
The kernel density estimate may be less familiar, but it can be a useful tool for plotting the shape of a distribution. Like the histogram, the KDE plots encode the density of observations on one axis with height along the other axis:
확률 밀도 추정법은 그렇게 익숙하진 않아도 분포의 모양을 표현하는데에는 매우 유용한 도구이다. 히스토그램처럼, 확률밀도추정(KDE)그래프도 그 축의 변수에 해당되는 데이터의 밀도를 다른 축의 높이에 해당하도록 그래프를 그린다.
sns.distplot(x, hist=False, rug=True);
Python
복사
Drawing a KDE is more computationally involved than drawing a histogram. What happens is that each observation is first replaced with a normal (Gaussian) curve centered at that value:
KDE 곡선을 그리는 것은 히스토그램을 그리는 것보다는 계산적으로 복잡하다. 각 변수를 중심으로 정규분포 곡선(가우시안 곡선)을 대신 그리는 것이 실제 계산 과정에서의 첫 단계이다.
x = np.random.normal(0, 1, size=30) bandwidth = 1.06 * x.std() * x.size ** (-1 / 5.) support = np.linspace(-4, 4, 200) kernels = [] for x_i in x: kernel = stats.norm(x_i, bandwidth).pdf(support) kernels.append(kernel) plt.plot(support, kernel, color="r") sns.rugplot(x, color=".2", linewidth=3);
Python
복사
Next, these curves are summed to compute the value of the density at each point in the support grid. The resulting curve is then normalized so that the area under it is equal to 1:
그 다음 (수많은) 곡선에서 나온 y값들을 모두 더해 밀도를 구한다. 그리고 각 X축의 값 (보조 격자support grid가 X축의 값 마다 위치되어 있으므로) 에 해당되는 밀도를 그래프에 표기한다. 그 결과물로 나온 그래프는 정규화되어 있어서 적분하면 1이 나온다.
( 역주 : 좌표 평면의 여러 요소를 지칭하는 단어가 matplotlib를 기준으로 서술되어 있기 때문에 고유명사 표현이 많은 편이다. support grid는 위 그래프 이미지에서 파란 네모로 가리킨 흰색선이며, 앞으로도 "X축의 값"은 tick label 을 생각하면 쉽게 떠올릴 수 있을 것이다.)
from scipy.integrate import trapz density = np.sum(kernels, axis=0) density /= trapz(density, support) plt.plot(support, density);
Python
복사
We can see that if we use the kdeplot() function in seaborn, we get the same curve. This function is used by distplot(), but it provides a more direct interface with easier access to other options when you just want the density estimate:
Seaborn의 kdeplot()을 쓰면 결국 위와 같은 kde곡선이 나온다는 걸 확인할 수 있다. 이 기능은 distplot() 클래스를 통해서도 사용할 수 있지만(kde = True 매개변수 입력을 통해), 만약 당신이 단순히 밀도 추정만 하고 싶다면 kdeplot() 메소드를 통해 더 손쉽게 적용할 수 있다.
sns.kdeplot(x, shade=True);
Python
복사
The bandwidth (bw) parameter of the KDE controls how tightly the estimation is fit to the data, much like the bin size in a histogram. It corresponds to the width of the kernels we plotted above. The default behavior tries to guess a good value using a common reference rule, but it may be helpful to try larger or smaller values:
KDE 그래프의 대역폭(밴드위드bandwidth, bw) 매개변수는 얼마나 밀도 추정을 데이터에 엄격하게 맞추는지를 조절한다. 마치 히스토그램의 구간 설정(bin)과 매우 유사하다고 생각하면 된다. 아래의 그래프는 방금 위에서 우리가 그린 KDE 그래프로부터 커널의 대역폭만 조절한 것이다. Seaborn의 기본 작용은 대중적으로 알려진 방식을 통해 적당한 대역폭을 적용하는 것이지만, 더 크거나 작게 bw 값을 조정해보는 것도 도움이 될 것이다.
sns.kdeplot(x) sns.kdeplot(x, bw=.2, label="bw: 0.2") sns.kdeplot(x, bw=2, label="bw: 2") plt.legend();
Python
복사
As you can see above, the nature of the Gaussian KDE process means that estimation extends past the largest and smallest values in the dataset. It’s possible to control how far past the extreme values the curve is drawn with the cut parameter; however, this only influences how the curve is drawn and not how it is fit:
위 사례들을 통해 봤겠지만, 가우시안 KDE 는 그 특성상 가장 크거나 작은 데이터값들은 그냥 건너 뛴다. 어느정도나 멀리 떨어진 이상치까지 그 곡선으로 포함해 그릴 것인가는 cut 매개변수(파라미터)를 통해 조절이 가능하다. 그러나 이는 곡선을 그리는 방식에만 영향을 미칠뿐이지 kde 추정 절차에까지 영향을 주진 않는다.
sns.kdeplot(x, shade=True, cut=0) sns.rugplot(x);
Python
복사

Fitting parametric distributions

모수적 분포 적용
You can also use distplot() to fit a parametric distribution to a dataset and visually evaluate how closely it corresponds to the observed data:
distplot()을 이용해 데이터셋에 모수적 분포를 적용할 수 있다. 그리고 이를 이용해 실제 관찰된 데이터들과 얼마나 유사한지를 평가할 수 있다.
x = np.random.gamma(6, size=200) sns.distplot(x, kde=False, fit=stats.gamma);
Python
복사
코드 해석)
np.random.gamma : 랜덤한 감마 분포(연속확률분포)를 그린다.
kde=False, fit=stats.gamma : kde 분포 말고 감마 분포를 직접 그린다.
( 역주 : 아래에 직접 fit=stats.gamma를 한 그래프와 kde=True를 한 그래프를 그려보았다. 비교해보자.)

Plotting bivariate distributions¶

이변량 분포 그리기
It can also be useful to visualize a bivariate distribution of two variables. The easiest way to do this in seaborn is to just use the jointplot() function, which creates a multi-panel figure that shows both the bivariate (or joint) relationship between two variables along with the univariate (or marginal) distribution of each on separate axes.
2개의 변수 간의 이변량 분포를 시각화하는 것 역시 도움이 된다. Seaborn에서 가장 쉽게 그 걸 사용하는 기능은 jointplot()이다. 여러 방면의 그래프를 만들어냄으로써 이변량 (joint 라고도 표현) 상관관계를 나타냄과 동시에 단변량 (marginal 라고도 표현) 분포 또한 각각의 축방향으로 나뉘어 표현한다.
mean, cov = [0, 1], [(1, .5), (.5, 1)] data = np.random.multivariate_normal(mean, cov, 200) df = pd.DataFrame(data, columns=["x", "y"])
Python
복사

Scatterplots¶

산점도
The most familiar way to visualize a bivariate distribution is a scatterplot, where each observation is shown with point at the x and y values. This is analogous to a rug plot on two dimensions. You can draw a scatterplot with scatterplot(), and it is also the default kind of plot shown by the jointplot() function:
이변량 분포를 시각화하는 가장 친숙한 방법은 산점도이다. x와 y값에 따라 점을 찍어 관찰값을 표현하는 것이다. 산점도는 2차원 상에서는 rugplot()과 유사하다고 볼 수 있다. 산점도는 scatterplot() 메소드를 통해 그릴 수 있으며, 이는 jointplot()의 기본적인 표현 방식(default kind)으로 정해져 있다.
sns.jointplot(x="x", y="y", data=df);
Python
복사
( 역주 : 위 그래프에서 산점도가 있는, 사이에 낀 그래프가 joint 부분에 해당하는 이변량 상관관계이다. x축 독립변수와 y축 종속변수 총 2개가 표현되었기 때문이다. 반면 양 사이드 즉 marginal 부분에 해당하는 단변량 상관관계는 단순히 각각 y축의 데이터 분포만, x축의 데이터 분포만을 따로 표시하고 있다.)

Hexbin plots

육각형 구간 그래프
A bivariate analogue of a histogram is known as a “hexbin” plot, because it shows the counts of observations that fall within hexagonal bins. This plot works best with relatively large datasets. It’s available through in matplotlib as matplotlib.axes.Axes.hexbin() and as a style in jointplot(). It looks best with a white background:
히스토그램과 유사한 이변량 분포 표현법은 "육각형 구간hexbin 그래프"로 알려져있다. 육각형으로 정의한 구간 내에 소속한 관찰값들의 갯수를 세어 표현하기 때문이다. (히스토그램 역시 구간 내에 소속한 데이터의 갯수로 막대그래프를 올린다) 이런 그래프는 상대적으로 큰 데이터셋에 가장 적합하다. matplotlib의 matplotlib.axes.Axes.hexbin() 라는 클래스를 통해 구현도 가능하지만, Seaborn의 jointplot()으로도 가능하다. 이 그래프는 흰색 배경에 가장 잘 어울린다.
x, y = np.random.multivariate_normal(mean, cov, 1000).T with sns.axes_style("white"): sns.jointplot(x=x, y=y, kind="hex", color="k");
Python
복사

Kernel density estimation

커널 밀도 추정
It is also possible to use the kernel density estimation procedure described above to visualize a bivariate distribution. In seaborn, this kind of plot is shown with a contour plot and is available as a style in jointplot():
위 사례들로 본 이변량 분포(joint 부분)를 KDE를 사용해 시각화하는 것 역시 가능한 일이다. Seaborn에서 이런 종류의 그래프는 jointplot()에서 등고선(contour) 스타일로 구현 가능하다.
sns.jointplot(x="x", y="y", data=df, kind="kde");
Python
복사
You can also draw a two-dimensional kernel density plot with the kdeplot() function. This allows you to draw this kind of plot onto a specific (and possibly already existing) matplotlib axes, whereas the jointplot() function manages its own figure:
물론 kdeplot() 메소드를 통해서 2차원 kde 그래프를 그릴 수 있다. 이 방식은 특정 matplotlib의 axes 개념 위에 (혹은 axes 위에 겹쳐서) 그래프를 그리도록 해준다. 반면 jointplot()은 그래프가 하나의 이미지figure를 가질 수 있도록 관리한다.
f, ax = plt.subplots(figsize=(6, 6)) sns.kdeplot(df.x, df.y, ax=ax) sns.rugplot(df.x, color="g", ax=ax) sns.rugplot(df.y, vertical=True, ax=ax);
Python
복사
( 역주 : matplotlib의 figure, axes 개념에 익숙하지 않으면 다소 낯설게 느껴질 수 있는 부분이다. matplotlib는 figure라는 큰 도화지 개념과 그 도화지 안의 부분 부분을 할당하는 axes 개념을 구분해 사용한다. 따라서 객체를 만들 때 figure를 하나 만들고 axes를 하나 혹은 여럿으로 할당해 놓은 뒤 plot들을 배치하곤 한다.
위 예시에서도 f, ax 를 통해 figure 하나, axes 하나를 만들었으며, kdeplot과 rugplot들을 각각 ax=ax 메소드를 통해 할당해주고 있다.
만약 ax를 여럿 만들었다면 kdeplot은 ax=ax1, rugplot은 ax=ax2 등을 통해 다르게 배치해 주곤 한다.
반면 jointplot은 axes가 아닌 그 3개의 그래프들 (joint과 marginal들)이 하나의 figure를 이룬다는 걸 튜토리얼은 설명하고 있다)
If you wish to show the bivariate density more continuously, you can simply increase the number of contour levels:
만약 이변량 밀도를 보다 연속성있게 그리고 싶다면, 등고선contour의 단계 수를 증가시키기만 하면 된다.
f, ax = plt.subplots(figsize=(6, 6)) cmap = sns.cubehelix_palette(as_cmap=True, dark=0, light=1, reverse=True) sns.kdeplot(df.x, df.y, cmap=cmap, n_levels=60, shade=True);
Python
복사
( 역주 : n_levels = 매개변수 지정을 통해서 차수를 높였다)
The jointplot() function uses a JointGrid to manage the figure. For more flexibility, you may want to draw your figure by using JointGrid directly. jointplot() returns the JointGrid object after plotting, which you can use to add more layers or to tweak other aspects of the visualization:
jointplot()JointGrid 라는 Seaborn의 기반 클래스(API)을 사용하고 있다. 보다 유연하게 그래프를 그리고 싶다면 직접 JointGrid 클래스을 사용해 그리면 된다. jointplot()은 기능상, JointGrid 객체가 형성된 걸 그림을 옮긴 것이다. 따라서 JointGrid에서는 보다 많은 층의 그래프 요소 추가와 시각화의 단계 조정(tweak) 등이 가능하다.
g = sns.jointplot(x="x", y="y", data=df, kind="kde", color="m") g.plot_joint(plt.scatter, c="w", s=30, linewidth=1, marker="+") g.ax_joint.collections[0].set_alpha(0) g.set_axis_labels("$X$", "$Y$");
Python
복사

Visualizing pairwise relationships in a dataset

쌍쌍으로 이어진 관계를 시각화하기
To plot multiple pairwise bivariate distributions in a dataset, you can use the pairplot() function. This creates a matrix of axes and shows the relationship for each pair of columns in a DataFrame. By default, it also draws the univariate distribution of each variable on the diagonal Axes:
여러개의 쌍으로 이루어진 이변량 분포의 데이터셋에서는 pairplot()을 이용할 수 있다. 행렬화된 축들을 만들고 데이터프레임 상에서 쌍을 이루는 각 칼럼들의 관계를 보여준다. 동일한 변수가 교차하는 행렬의 대각선 축 방향에서는 기본적으로 단변량 분포(변수가 1개이기 때문에 상관관계가 아닌 데이터 분포 자체를 묘사하는 수준)를 그리는 게 기본 옵션이다.
iris = sns.load_dataset("iris") sns.pairplot(iris);
Python
복사
Specifying the hue parameter automatically changes the histograms to KDE plots to facilitate comparisons between multiple distributions.
이때 표현을 더 구체화하기 위해 hue를 통한 의미 구분을 추가하면, 대각선 축에 위치한 히스토그램을 KDE 그래프로 자동변환한다. 의미 구분 때문에 여러 개가 된 분포들 간의 비교가 가능하게 만들기 위해서이다.
sns.pairplot(iris, hue="species");
Python
복사
Much like the relationship between jointplot() and JointGrid, the pairplot() function is built on top of a PairGrid object, which can be used directly for more flexibility:
앞서 본 jointplot()JointGrid 클래스 간의 관계와 매우 유사하게, pariplot()PairGrid 클래스 기반으로 만들어졌다. 이로 인해 PairGrid를 직접 쓰면 그래프를 더 유연한 옵션으로 그릴 수 있다.
g = sns.PairGrid(iris) g.map_diag(sns.kdeplot) g.map_offdiag(sns.kdeplot, n_levels=6);
Python
복사