〽️

Visualizing linear relationships

서문

Many datasets contain multiple quantitative variables, and the goal of an analysis is often to relate those variables to each other. We previously discussed functions that can accomplish this by showing the joint distribution of two variables. It can be very helpful, though, to use statistical models to estimate a simple relationship between two noisy sets of observations. The functions discussed in this chapter will do so through the common framework of linear regression.
많은 데이터셋들이 여러 개의 양적 변수(quantitative)들을 포함하고 있고, 그 분석의 목표는 각 변수들의 상관관계를 알아내는 것일 때가 많다. 앞선 튜토리얼에서는 jointplot() 와 같은 클래스를 통해 두 변수 간의 관계를 표현함으로써 상관관계를 밝히는 것에 대해 이야기했다. 통계적인 모델들을 이용해 노이즈 데이터가 들어간 두 변수 간의 상관관계를 간단히 추정하고 표현하는 것은 물론 굉장히 도움되는 일이다. 그러나 이번 챕터에서는 이러한 일을 대중적인 방법론인 선형 회귀를 통해 구현해 내는 것에 대해 이야기하고자 한다.
In the spirit of Tukey, the regression plots in seaborn are primarily intended to add a visual guide that helps to emphasize patterns in a dataset during exploratory data analyses. That is to say that seaborn is not itself a package for statistical analysis. To obtain quantitative measures related to the fit of regression models, you should use statsmodels. The goal of seaborn, however, is to make exploring a dataset through visualization quick and easy, as doing so is just as (if not more) important than exploring a dataset through tables of statistics.
통계학자 존 투키의 이론에 따라, Seaborn의 회귀 그래프들의 주요 기능도 디자인되었다. 이는 두드러지는 데이터의 패턴을 탐색하고 이에 대해 시각적인 가이드까지 더하는 걸 가장 중요시한다. 그 말인 즉슨 Seaborn 패키지가 그 자체로 통계학적 분석을 위한 것은 아니라는 이야기이다. 회귀 모델에 적용하여 정량적인 측정치를 얻고자 한다면 statsmodels 와 같은 패키지를 이용하시라. Seaborn의 목적은 데이터셋을 탐색하여 시각화를 쉽고 빠르게 실행하는 것이다. 통계치를 직접 산출하는 것과 비교해도 뒤지지 않게 이러한 시각화 기능 역시 중요하다.
import numpy as np import seaborn as sns import matplotlib.pyplot as plt
Python
sns.set(color_codes=True)
Python
tips = sns.load_dataset("tips")
Python

Functions to draw linear regression models¶

선형 회귀 모델을 그리는 클래스(API)들
Two main functions in seaborn are used to visualize a linear relationship as determined through regression. These functions, regplot() and lmplot() are closely related, and share much of their core functionality. It is important to understand the ways they differ, however, so that you can quickly choose the correct tool for particular job.
Seaborn에서 선형 관계를 회귀 이론을 통해 시각화하는 주된 방법에는 두 가지가 있다. regplot()lmplot() 둘은 서로 매우 관련되어 있으며 중심 기능들을 많이 공유하고 있다. 그러나 그 두 기능이 어떻게 다른지를 이해하여 특정 업무에 보다 적절한 모델을 빠르게 고르는 것이 중요하다.
In the simplest invocation, both functions draw a scatterplot of two variables, x and y, and then fit the regression model y ~ x and plot the resulting regression line and a 95% confidence interval for that regression:
별다른 옵션 없이 단순히 호출한다면 regplot()lmplot() 모두 x, y축을 기반으로 두 변수의 산점도를 그린다. 그리고 x에 대한 y의 회귀 모델을 적용해 회귀선을 그린 다음 95%의 신뢰구간까지 추가한다.
sns.regplot(x="total_bill", y="tip", data=tips);
Python
sns.lmplot(x="total_bill", y="tip", data=tips);
Python
You should note that the resulting plots are identical, except that the figure shapes are different. We will explain why this is shortly. For now, the other main difference to know about is that regplot() accepts the x and y variables in a variety of formats including simple numpy arrays, pandas Series objects, or as references to variables in a pandas DataFrame object passed to data. In contrast, lmplot() has data as a required parameter and the x and y variables must be specified as strings. This data format is called “long-form” or “tidy” data. Other than this input flexibility, regplot() possesses a subset of lmplot()’s features, so we will demonstrate them using the latter.
단순 호출 시 두 플롯은 동일하되, 형성된 figure의 모양만 (regplot()이 가로로 좀 더 길쭉하다) 조금 다르다는 걸 알아야 한다. 왜 그런지는 차차 설명하겠다. 지금 알아두어야 할 또 다른 이유는 다음과 같다.
regplot()이 독립 변수와 종속 변수에 쓸 (즉 x값과 y값으로) 변수 데이터의 형식을 굉장히 다양하게 수용하기 때문이다. 1차원 넘파이 array와 판다스의 series 데이터를 포함하거나 판다스의 dataframe 객체 자체를 데이터로 쓸 수 있다.
반면 lmplot()은 x와 y로 쓸 변수를 인자와 매개변수 입력을 통해 스트링 타입으로 직접 지정해 주어야만 한다. 이러한 테이터 형식은 "long-form (적은 칼럼 & 많은 로우 로 구성된 세로로 긴 데이터 구성) or tidy" 라고 불린다. 이와는 달리 데이터를 유연하게 쓸 수 있는 regplot()lmplot()의 특징을 포괄한다고 볼 수 있다. 튜토리얼에서는 조금 나중에 이를 더 설명하겠다.
It’s possible to fit a linear regression when one of the variables takes discrete values, however, the simple scatterplot produced by this kind of dataset is often not optimal:
변수 중 하나가 이산값(descrete :정수, 자연수와 같은, 뚝뚝 끊어지는 값)을 가져도 선형회귀 모델 적용은 가능하다. 그러나 이를 통해 만들어진 단순 산점도는 시각화에 적합하다고 볼 수 없다.
sns.lmplot(x="size", y="tip", data=tips);
Python
One option is to add some random noise (“jitter”) to the discrete values to make the distribution of those values more clear. Note that jitter is applied only to the scatterplot data and does not influence the regression line fit itself:
이때 랜덤한 노이즈("지터jitter 값")를 추가해 이산값에 모여있는 데이터의 분포를 보다 명확히 하는 옵션이 있다. 지터 값은 산점도의 모양 자체에만 적용되는 것이지, 원본 데이터를 건드려 회귀선 자체에 영향을 미치는 건 아니라는 걸 알아두자.
sns.lmplot(x="size", y="tip", data=tips, x_jitter=.05);
Python
A second option is to collapse over the observations in each discrete bin to plot an estimate of central tendency along with a confidence interval:
두 번째 방법은 각 이산값으로 매겨진 구간 속 데이터 분포 형태는 포기하고 주된 경향치 추정값 (예를 들면 평균)과 신뢰구간만을 표시하는 것이다.
sns.lmplot(x="size", y="tip", data=tips, x_estimator=np.mean);
Python

Fitting different kinds of models¶

다른 종류의 모델들을 적용해보기
The simple linear regression model used above is very simple to fit, however, it is not appropriate for some kinds of datasets. The Anscombe’s quartet dataset shows a few examples where simple linear regression provides an identical estimate of a relationship where simple visual inspection clearly shows differences. For example, in the first case, the linear regression is a good model:
위에서 다뤄 본 선형회귀 모델은 적용하기에 매우 간단하지만, 이게 다른 종류의 데이터셋에는 적합하지 않을 수도 있다. 앤스콤의 4형제(Anscombe’s quartet) 데이터셋은 단순 선형회귀 모델을 적용했을 때 회귀선은 비슷한 추정치를 보여주나, 그 데이터를 들여다보면 서로 아주 다르게 생겼음을 알려주는 대표 사례이다.
anscombe = sns.load_dataset("anscombe")
Python
sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'I'"), ci=None, scatter_kws={"s": 80});
Python
The linear relationship in the second dataset is the same, but the plot clearly shows that this is not a good model:
위 그래프와 아래의 그래프 속 선형 회귀선은 똑같으나, 이게 좋은 모델이 아니라는 점은 그래프를 통해 파악할 수 있다.
sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'II'"), ci=None, scatter_kws={"s": 80});
Python
In the presence of these kind of higher-order relationships, lmplot() and regplot() can fit a polynomial regression model to explore simple kinds of nonlinear trends in the dataset:
만약 관계가 아래와 같은 고차함수 (2차, 3차 방정식의 함수가 나타내는 곡선) 와 같은 관계를 띈다면, lmplot()regplot()은 다항 회귀모델을 사용해 데이터셋 속 비선형 데이터들을 파악할 수 있다.
sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'II'"), order=2, ci=None, scatter_kws={"s": 80});
Python
A different problem is posed by “outlier” observations that deviate for some reason other than the main relationship under study:
이와 다른 문제는 모종의 이유로 주된 상관관계로부터 벗어난 "아웃라이어" 데이터가 놓인 경우이다.
sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'III'"), ci=None, scatter_kws={"s": 80});
Python
In the presence of outliers, it can be useful to fit a robust regression, which uses a different loss function to downweight relatively large residuals:
이러한 아웃라이어들이 있다면, 로버스트(robust)한 회귀 모델을 쓰는 것이 더 유용하다. 로버스트한 모델이란, 잔차값이 상대적으로 큰 데이터에 대해서는 가중치를 낮춰 비용 함수를 계산하는 회귀 모델이다
(🤔 역주 : 로버스트 하다는 건, 극단적인 값을 제거하거나 적게 반영한다는 의미이다. 평균과 같은 통계값은 극단적인 값에 크게 영향을 받지만 중위값은 영향을 적게 받기 때문에 중위값이 평균값보다 로버스트하다는 평가를 받는다.)
sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'III'"), robust=True, ci=None, scatter_kws={"s": 80});
Python
When the y variable is binary, simple linear regression also “works” but provides implausible predictions:
만약 y에 속한 변수가 yes or no와 같은 바이너리 변수였을 때, 단순 선형회귀는 "작동"할 순 있지만 신뢰할 수 없는 결과물을 제공한다.
tips["big_tip"] = (tips.tip / tips.total_bill) > .15 sns.lmplot(x="total_bill", y="big_tip", data=tips, y_jitter=.03);
Python
The solution in this case is to fit a logistic regression, such that the regression line shows the estimated probability of y = 1 for a given value of x:
위와 같은 상황에서의 해결책은 로지스틱 회귀 알고리즘을 쓰는 것이다. 로지스틱 회귀는 x값에 해당하는 y값이 1이 나올 확률 추정치를 나타낸다.
(🤔 역주 : 로지스틱 회귀 알고리즘과 그와 관련된 시그모이드 함수 개념은 추후 머신러닝 공부 등에 나오니 따로 학습하시길 권장합니다)
sns.lmplot(x="total_bill", y="big_tip", data=tips, logistic=True, y_jitter=.03);
Python
Note that the logistic regression estimate is considerably more computationally intensive (this is true of robust regression as well) than simple regression, and as the confidence interval around the regression line is computed using a bootstrap procedure, you may wish to turn this off for faster iteration (using ci=None).
로지스틱 회귀 추정이 단순 선형회귀에 비하면 계산과정에 있어서 좀 더 빡센 편이다. (이건 로버스트를 중시하는 회귀 알고리즘이라면 다 해당된다) 게다가 회귀선 주변의 신뢰구간을 계산할 땐 부트스트랩 기법을 사용하기에, 보다 빠른 반복작업을 위해선 이 기능을 끄길 권한다. (ci=None 매개변수 입력을 하면 된다)
An altogether different approach is to fit a nonparametric regression using a lowess smoother. This approach has the fewest assumptions, although it is computationally intensive and so currently confidence intervals are not computed at all:
평편화 기법(lowess = True)을 사용해 비모수적 회귀방법론을 적용하는 건 지금까지의 방식과 모두 다른 접근법이다. 이 방법은 계산과정이 매우 복잡하지만 가장 적은 폭의 회귀 예측을 한다. 그러므로 현재까지 신뢰구간을 계산 기능을 지원하진 않고 있다.
sns.lmplot(x="total_bill", y="tip", data=tips, lowess=True);
Python
The residplot() function can be a useful tool for checking whether the simple regression model is appropriate for a dataset. It fits and removes a simple linear regression and then plots the residual values for each observation. Ideally, these values should be randomly scattered around y = 0:
residplot() 클래스는 단순 회귀 모델이 해당 데이터셋에서 유용한지 아닌지에 대해 체크할 때 도움을 주는 도구이다. residplot()은 선형회귀를 적용하고 다시 제거한 뒤, 그 회귀를 통해 각 데이터로부터 얻은 잔차값을 그래프로 그리는 기능이다. 개념적으로는, 그 값은 y=0 절편 주변에 무작위로 흩어져 존재한다.
sns.residplot(x="x", y="y", data=anscombe.query("dataset == 'I'"), scatter_kws={"s": 80});
Python
If there is structure in the residuals, it suggests that simple linear regression is not appropriate:
만약 residplot()의 잔차 배치가 특정 구조를 띈다면, 이는 해당 데이터셋에는 단순 선형 회귀가 적합하지 않다는 뜻이다.
sns.residplot(x="x", y="y", data=anscombe.query("dataset == 'II'"), scatter_kws={"s": 80});
Python

Conditioning on other variables

제 3의 변수라는 조건을 다루기
The plots above show many ways to explore the relationship between a pair of variables. Often, however, a more interesting question is “how does the relationship between these two variables change as a function of a third variable?” This is where the difference between regplot() and lmplot() appears. While regplot() always shows a single relationship, lmplot() combines regplot() with FacetGrid to provide an easy interface to show a linear regression on “faceted” plots that allow you to explore interactions with up to three additional categorical variables.
지금까지 다룬 그래프들을 통해 짝 지어진 두 변수 간의 상관관계를 나타내는 많은 유형들을 알아보았다. 그러나 대개는 "두 변수 사이에 새로운 제3의 변수가 추가됨에 따라 관계는 어떻게 바뀌나" 같은 질문이 더 관심을 갖는 주제이긴 하다. 이 주제가 바로 regplot()lmplot()이 차이를 보이는 지점이다. regplot()은 언제나 단순 상관관계만을 보여주는 반면, lmplot()FacetGridregplot()을 혼합해 선형회귀를 보다 쉽게 보여준다. 단일한 그래프가 아니라 파셋facet 개념을 통해 여러개의 그래프를 보여주는 이 lmplot()의 인터페이스를 통해 당신은 제3의 범주형 변수가 추가됨에 따라 데이터가 어떤 상호작용을 보이는지 관찰할 수 있다.
The best way to separate out a relationship is to plot both levels on the same axes and to use color to distinguish them:
상관관계를 분리해서 표현하는 가장 좋은 방법은 색깔 구분(hue)이다. 동일한 axes 위에서 도표 두 개를 색만 구분해서 그리는 것이다.
sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips);
Python
In addition to color, it’s possible to use different scatterplot markers to make plots the reproduce to black and white better. You also have full control over the colors used:
색깔을 통한 구분에 더불어, 산점도의 점 모양(markers)을 달리하면 흑백 출력을 대비할 수 있다. 또한 색상의 선택 역시 완전히 조절이 가능하다.
sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips, markers=["o", "x"], palette="Set1");
Python
To add another variable, you can draw multiple “facets” which each level of the variable appearing in the rows or columns of the grid:
제3의 변수를 추가하려면, 복수의 파셋facets 으로 그래프를 여럿 그리면 된다. 각 파셋들은 변수 데이터의 어떤 단계를 맡아서 표현할지와, 칼럼 방향 혹은 로우 방향 중 어느 방향으로 배치될지 등을 결정해주면 된다.
(🤔 역주 : 아래 예제에서는 독립 변수 x로는 total_bill을, 종속 변수 y로는 tip을, 이때 제3의 변수로 time까지 설정하였다. 단, time에는 Lunch와 Dinner 각 2개의 단계가 있었기에 facets을 두 개로 설정해 나눠 그렸다. 이때 나눠 그린 방향을 칼럼 방향으로 설정했으며, col="time" 라고 매개변수를 설정했음을 알 수 있다. 만약 로우 방향으로 그리고 싶었다면 row="time"으로 했을 것이다)
sns.lmplot(x="total_bill", y="tip", hue="smoker", col="time", data=tips);
Python
sns.lmplot(x="total_bill", y="tip", hue="smoker", col="time", row="sex", data=tips);
Python

Controlling the size and shape of the plot

그래프의 크기와 모양 조절하기
Before we noted that the default plots made by regplot() and lmplot() look the same but on axes that have a different size and shape. This is because regplot() is an “axes-level” function draws onto a specific axes. This means that you can make multi-panel figures yourself and control exactly where the regression plot goes. If no axes object is explicitly provided, it simply uses the “currently active” axes, which is why the default plot has the same size and shape as most other matplotlib functions. To control the size, you need to create a figure object yourself.
앞서 regplot()lmplot()을 통해 각각 기본 옵션 그래프를 그리면 외관상 차이는 없고, 다만 그래프의 크기나 비율 면에서 다르다고 설명한 바 있다. 이는 regplot()이 Axes-level 에 속한 클래스이고 특정한 axes 위에 그래프를 그리는 특징이 있기 때문이다.
그 말인 즉슨, 한 figure에 여러 부분(axes)으로 나누어 회귀 그래프를 정확히 어떤 axes에 위치시킬지 여부까지 조절할 수 있다는 뜻이다. 만약 axes 지정을 하지 않은 객체는 현재 활성화된 면에서만 그래프가 그려지도록 명시되어 있다. 이게 바로 Seaborn의 기본형 그래프들이 matplotlib의 다른 기능을 통해 만들어진 그래프와 같은 크기와 모양을 갖는 이유이다. 사이즈를 조절하려면 figure 객체를 따로 형성해주어야 한다.
f, ax = plt.subplots(figsize=(5, 6)) sns.regplot(x="total_bill", y="tip", data=tips, ax=ax);
Python
In contrast, the size and shape of the lmplot() figure is controlled through the FacetGrid interface using the height and aspect parameters, which apply to each facet in the plot, not to the overall figure itself:
이와는 다르게 lmplot() 클래스의 모양과 크기는 FacetGrid 조작법을 통해 조절되고 있다. heightaspect 인자를 사용해 조절하며, 이때 조절 여부를 지정한 파셋facet의 그래프에만 영향을 미치며, 전체 figure에 걸쳐 영향이 미치진 않는다.
sns.lmplot(x="total_bill", y="tip", col="day", data=tips, col_wrap=2, height=3);
Python
sns.lmplot(x="total_bill", y="tip", col="day", data=tips, aspect=.5);
Python

Plotting a regression in other contexts

다른 맥락들과 함께 회귀 그래프 그리기
A few other seaborn functions use regplot() in the context of a larger, more complex plot. The first is the jointplot() function that we introduced in the distributions tutorial. In addition to the plot styles previously discussed, jointplot() can use regplot() to show the linear regression fit on the joint axes by passing kind="reg":
Seaborn의 몇몇 클래스들은 더 폭넓고 복잡한 그래프 도표를 그리기 위해 regplot()을 함께 사용한다. 분포 파트 튜토리얼에서 소개한 jointplot()이 첫번째 예시이다. 그때도 이야기했던 그래프의 스타일에 대해서 추가 설명을 하자면, jointplot()kind="reg" 매개변수를 통해 joint 부분(axes)에 선형 회귀선을 적용해 시각화 할 수 있다.
sns.jointplot(x="total_bill", y="tip", data=tips, kind="reg");
Python
Using the pairplot() function with kind="reg" combines regplot() and PairGrid to show the linear relationship between variables in a dataset. Take care to note how this is different from lmplot(). In the figure below, the two axes don’t show the same relationship conditioned on two levels of a third variable; rather, PairGrid() is used to show multiple relationships between different pairings of the variables in a dataset:
pairplot() 기능을 사용하면 kind="reg" 매개변수 입력을 통해 regplot()PairGrid 기능을 합쳐 데이터 셋의 여러 변수들 간의 선형 관계를 표현할 수 있다. 이때 이것이 lmplot()과 다른 부분을 유심히 알아둬야 한다. 아래 예시에서는, 두 개의 그래프들이 나타내는 상황이 제3의 변수가 갖고 있는 레벨에 따라 나타난 관계가 아니라는 점이다.
(🤔 역주 : 아래 예시는 total_bill에 따른 tip이란 종속 변수의 상관관계 하나, size에 따른 tip의 상관관계 하나. 각각 서로 다른 상관관계를 표현하고 있다. 만약 날씨라는 공통된 제3의 변수가 있고 그 날씨가 덥다 / 춥다 등으로 각기 다른 레벨에 대한 상관관계를 구하는 사례와 아래 예시의 상황은 다르다는 이야기이다)
되려 PairGrid()는 짝지어진 서로 다른 변수들 간의 여러 관계들을 보여주는 기능을 갖는다.
sns.pairplot(tips, x_vars=["total_bill", "size"], y_vars=["tip"], height=5, aspect=.8, kind="reg");
Python
Like lmplot(), but unlike jointplot(), conditioning on an additional categorical variable is built into pairplot() using the hue parameter:
pairplot()은 제3의 범주형 변수가 추가되는 걸 hue를 통해 의미구분을 한다. 이는 joinplot()이랑은 다르면서 lmplot()과는 유사하다고 볼 수 있다.
sns.pairplot(tips, x_vars=["total_bill", "size"], y_vars=["tip"], hue="smoker", height=5, aspect=.8, kind="reg");
Python