Overview of seaborn plotting functions(seaborn 플롯팅 기능 개요)
Similar functions for similar tasks(유사한 작업을 위한 유사한 기능)
먼저 distribution 모듈에 있는 histplot과 kdeplot()을 그려보자!
필요 라이브러리 로드!
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
기본 테마 적용
# Apply the default theme
sns.set_theme()
데이터 셋은 펭귄으로 하자!
penguins = sns.load_dataset("penguins")
histplot(히스토그램)을 그려줄건데...
x는 날개?? 길이
hue는 종
multiple='stack'을 통해 겹치게 그려보자!
sns.histplot(data=penguins, x="flipper_length_mm", hue="species", multiple="stack");
날개 길이별 종별(hue를 통해) 분포를 볼 수 있다
이번엔 같은 것을 kdeplot(커닐 밀도 추정)으로 그려보자!
sns.kdeplot(data=penguins, x="flipper_length_mm", hue="species", multiple="stack");
방금 그린 두개의 plot은 축 수준 함수이다!
범레가 그래프 안에 있는 것도 확인할 수 있다!
Figure-level vs. axes-level functions(그림 수준 vs 축 수준 함수)
위에서 그린 histplot과 kdeplot은 축 수준 함수인데...
단일 matploylib.pyplot.Axes객체(함수의 반환값이다!)에 데이터를 표시한다!
그래서 matplotlib과 섞어쓰기 좋다!
반대로 그림 수준 함수는 FacetGrid(그림을 관리하는 씨본의 객체)를 통해 matplotlib과 접촉한다!
하나의 그림 수준 함수는 다양한 축 수준 함수에 대해 단일 인터페이스를 제공한다!
그림으로 보면 아래와 같다!
출처 : https://seaborn.pydata.org/tutorial/function_overview.html#figure-level-vs-axes-level-functions
replot, displot, catplot은 그림 수준 함수이고...
그 아래에 있는 것들은 축 수준 함수이다!
위에 있는 큰 상자가 아래에 있는 작은 상자들을 포함한다고 생각하면 편하다!
그러면 하나의 그림수준 함수가 어떻게 단일 인터페이스로 다양한 축 수준 함수를 표현할까?
displot으로 위에서 그려본 histplot, kdeplot을 똑같이 그려보자!
먼저 histplot!
sns.displot(data=penguins, x="flipper_length_mm", hue="species", multiple="stack");
displot의 kind 기본값이 histplot이기 때문에..
kind를 지정해주지 않아도 앞에서 그린 그림과 똑같은 그림이 그려졌다!
그 다음 kdeplot!
kind='kde'로 지정해 주면...
sns.displot(data=penguins, x="flipper_length_mm", hue="species", multiple="stack", kind="kde");
kdeplot이 그려진다!
이렇게 displot이라는 하나의 인터페이스로 다양한 축 수준 함수를 제공한다!
일단 차이점은 하나는 범례가 그래프 바깥에 있다는 것이다!
그림 수준 함수의 한가지 유용한 특징은 서브플롯을 col을 통해 쉽게 생성할 수 있다는 것이다!
이 특징을 그래프를 그려보면서 확인하자!
displot에서 col='species'로 지정하면...
sns.displot(data=penguins, x="flipper_length_mm", hue="species", col="species");
이렇게 종별로 서브플롯이 그려진다!
이렇게 그림 수준 함수는 편리한 점도 있지만...
단점도 있다!
함수종류별 파라미터가 함수의 시그니처나 독스트링에 나타나지 않는다는 것이다!
displot에서 histplot이나 kdeplot의 파라미터가 잘 나타나지 않는다는 의미인 것 같다!
실제로 histplot의 시그니처에서는...
이렇게 다양한 파라미터가 있는 것을 볼 수 있는데...
displot의 시그니처를 보면...
파라미터가 요정도..라서..
그림 수준 함수를 그렸을 때에는 종류 별 디테일한 설정을 하기가 어려울 수 있겠다 싶었다!
Axes-level functions make self-contained plots(축 수준 함수는 독립적 플롯을 만든다)
축 수준 함수는 matplotlib함수의 드롭인 대체로 쓰여진다!
(드롭인 대체 : 어떤 부품이나 프로그램을 대체 했을 때 설정 등을 바꿀 필요가 없고 오히려 성능이 올라가는 대체)
말하자면 matplotlib과 다르게 축 이름, 범례를 자동으로 생성해주지만 축을 넘어가서는 아무것도 바꾸지 않는다.
(범례가 안에 생성되는 이유인 듯하다!)
그래서 축 수준 함수는 임의로 복잡한 matplotlib그림들로 구성될 수 있다!
그림 두개를 같이 그리는데...
날개길이와 부리길이의 종별(hue로 지정) 산점도와
그 옆에 종별 빈도수를 나타내는 히스토그램을 그려보자!
먼저 아래의 코드로 plot이 들어갈 공간을 생성해준다!
f, axs = plt.subplots(1, 2, figsize=(8, 4), gridspec_kw=dict(width_ratios=[4, 3]))
사이즈는 (8,4)고 넓이 비율은 4:3으로 해줬다!
왼쪽이 axs[0]위치이고 오른쪽이 axs[1]위치이다!
그러면 코드를 추가해서 왼쪽에 산점도, 오른쪽에 히스토그램을 그려보자!
산점도는 별게 없고...
히스토그램에서 shrink의 기본값이 1(막대간의 공간이 없는 상태)인데...
.8로 해줘서 막대사이의 공간을 마련해줬고
alpha=.8로 색을 아주약간 투명하게 해줬다!
아마 이런 디테일한 부분을 그림수준 함수의 설명에서는 찾을 수 없다는 것을 강조하려고 한듯하다!
f, axs = plt.subplots(1, 2, figsize=(8, 4), gridspec_kw=dict(width_ratios=[4, 3]))
sns.scatterplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", hue="species", ax=axs[0])
sns.histplot(data=penguins, x="species", hue="species", shrink=.8, alpha=.8, legend=False, ax=axs[1])
그림이 조금 겹쳐서...
f.tight_layout()을 통해 그림 레이아웃을 조절해 줬다
f, axs = plt.subplots(1, 2, figsize=(8, 4), gridspec_kw=dict(width_ratios=[4, 3]))
sns.scatterplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", hue="species", ax=axs[0])
sns.histplot(data=penguins, x="species", hue="species", shrink=.8, alpha=.8, legend=False, ax=axs[1])
f.tight_layout()
Figure-level functions own their figure(그림 수준 함수는 자신의 그림을 소유한다)
그림 수준 함수는 설계상 자신의 그림을 소유하므로 기존 축에 새로운 플롯을 그림을 그리는 개념이 없다!
그래서 범례를 외부에 배치하는 기능을 구현할 수 있는 것이다.
그럼에도 그림수준 함수도 matplotlib의 축에 엑세스 하고 그림 수준 함수가 제공 하는 것 이상을 수행할 수 있다!
한번 그림수준 함수를 matplotlib 축에 엑세스하고 플롯에 다른 요소를 추가해보자!
데이터는...
2023.02.04 - [재미로 하는 코딩] - 시각화 뽀개기1
에서 썼던... tips데이터이다!
tips데이터를 로드하고...
x에 총계산금액
y에 팁으로 relplot을 g에 할당해 준다...
그리고 ax.axline()을 통해 그림 위에 대각선을 그려줬다!
정확히 말하면...
(10, 2)를 지나는 기울기가 0.2인 파란색 dashes=(5, 2)인 선이다!
tips = sns.load_dataset("tips")
g = sns.relplot(data=tips, x="total_bill", y="tip")
g.ax.axline(xy1=(10, 2), slope=.2, color="b", dashes=(5, 2));
이렇게 그림 수준 함수도 기존 축에 어찌저찌 플롯을 그려볼 순 있다!
Customizing plots from a figure-level function(그림 수준 함수에서 플롯 커스터마이징하기)
그림 수준 함수는 앞에서 설명했던 FacetGrid인스턴스를 반환하는데...
FacetGrids는 sub플롯 구조에 대해 똑똑하게 플롯의 속성을 커스터마이징하는 방법을 가지고있다!
예시로 단 한 줄로 x축, y축 이름을 변경할 수 있다!
g = sns.relplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", col="sex")
g.set_axis_labels("Flipper length (mm)", "Bill length (mm)");
펭귄 데이터로 col='sex'로 날개 길이와 부리 길이의 산점도를 그렸다!
set_axis_labels()로 원래 변수 이름이던 x축, y축 이름을 보기 좋게 변경해줬다!
이 방법은 matplot API가 아니어서 그림 수준 함수를 사용할 때만 사용할 수 있다!
Specifying figure sizes(그림 크기 지정)
matplotlib플롯의 크기를 늘리거나 줄이려면...
figsize파라미터를 사용(플롯을 설정하는 동안 전역 rcParams에서..._)하거나...
그림을 그린 후에는...
플롯 코드 위에 plt.figure(figsize=())을 적어주면 그림 개체에서 메서드를 호출하여 사이즈를 조정한다!(파이썬에서)
seaborn의 축 수준 함수를 사용할 때 동일한 규칙이 적용되는데...
주로 plt.figure(figsize=())를 통해 사이즈를 조정하는 것 같다!
그러나 seaborn의 그림수준 함수를 사용할 때는 함수 자체에 그림 크기를 제어하는 파라미터가 있다.
height와 aspect라는 파라미터인데...
matplotlib에서 width(height * aspect)와 height 파라미터와는 약간 다르다!
중요한 것은 height와 aspect 파라미터는 전체 그림보다는 각 서브플롯의 사이즈와 대응한다는 것이다!
그림을 그리면서 차이점을 알아보자!
일단 하나의 matplotlib에서 하나의 서브플롯이 있는 기본 출력은 아래와 같다!
f, ax = plt.subplots()
하나의 서브플롯 축이 만들어졌다!
이번엔 2개의 컬럼이 있고 y촉을 공유하는(sharey=True) 축을 만들어보자!
f, ax = plt.subplots(1, 2, sharey=True)
두 그림의 전체 크기는 같은데...
2개의 컬럼이 있는 그림은 x축이 압축되어 공간에 들어갔다!
이번에는 FacetGfid를 이용해 그림 수준 함수의 빈 플롯을 생성해줄건데...
replot, displot, catplot이 그려질때 뒤에서 작동되는 것이다!
g = sns.FacetGrid(penguins)
정사각형 그림임을 알 수 있다!
이번엔 col='sex'(성별이 두개!)를 이용해서 2개의 서브플롯을 그려보면...
g = sns.FacetGrid(penguins, col="sex")
그림이 더 넓어지고 앞에서 그린 것(정사각형 그림)과 같은 크기의 서브플롯 2개를 가지게 된다!
이번엔 사이즈를 조절해볼 건데...
height와 aspect파라미터를 통해 조절할 수 있다!
aspect=1이 가로세로 1:1 기준인 것 같다!
즉 aspect=0.75라서 각 서브플롯의 가로대 세로 비율이 3:4인 듯 하다?
즉 전체 그림 크기를 생각하지 않고 변수를 할당할 수 있지만...
matplotlib과 약간 다른 방식으로(height와 aspect로 작동!)한 다는 것을 기억해야한다!
Relative merits of figure-level functions(그림 수준 함수의 상대적 장점)
위에서 말한 내용을 정리하면...
장점 | 단점 |
데이터 변수로 쉬운 패싯 | 함수의 시그니처에 많은 변수들이 표시되지 않음 |
기본적으로 범례가 플롯 바깥 | 더 큰 matplotlib 그림의 일부가 되기 어려움 |
쉬운 그림 수준 커스터마이징 | matplotlib과 다른 API |
matplotlib과 다른 그림 크기조절 파라미터 | matplotlib과 다른 그림 크기조절 파라미터 |
정도이다...
그림 수준 함수는 추가복잡성 때문에 약간 더 혼란스러울 수 있지만...
고유한 추가 기능을 제공한다!
그림 수준 함수가 좋은 선택지가 아닌 상황은...
여러 다른 플롯 종류를 구성하는 복잡한 독립형 그림을 그릴 때이다!
이럴 때는 matplotlib의 축 수준 함수를 사용해 개별 구성요소를 채워넣는 것이 좋다!
그니까... 자기가 만들고 싶은 그림이....
지리는 그림(시본 그림 수준 함수를 넘어서는)을 만들때는 matplotlib을 사용하는 것이 좋다는 말인 것 같다!
Combining multiple views on the data(데이터에 관한 다양한 측면 결합)
jointplot과 pairplot은 앞에서 다룬 분류체계에 정확히 들어맞는 것은 아니다.
이 두개의 플롯은 데이터셋의 다양한 측면을 한 그림에 표현하기 위해 다른 모듈을 사용한다!
두 플롯 모두 그림 수준 함수이고 기본값으로 여러 서브플롯을 생성한다.
그러나 그림을 관리하기 위한 객체로
jointplot은 JoinGrid, pairplot은 PairGrid를 사용한다!
먼저 jointplot을 그려보자!
sns.jointplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", hue="species");
산점도에 더해...
옆에 모서리 부근에 x변수 y변수 각각의 쌓여있지 않은 kdeplot을 같이 보여준다.
이번엔 pairplot을 그려보자!
sns.pairplot(data=penguins, hue="species");
pairplot도 jointplot과 비슷한데...
두 변수관의 관계 하나에 집중하기보다는 변수간 모든 쌍의 조합을 동시에 보여준다!
pairplot과 jointplot은 축 수준 함수인 scatterplot과 kdeplot을 사용해서 작동하고...
친숙한 파라미터로 다르게 표현하기 쉽다!
앞에서 그린 jointplot을 좀 다르게 그려보면...(kind='hist'로 지정)
히스토그램 형식으로 표현할 수 있다!
참조사이트:
https://seaborn.pydata.org/tutorial/function_overview.html#
아따 이해하고 해석하기 어려워밍...
댓글