Seaborn vs Bokeh. Part 1. Seaborn tutorial

Seaborn 과 Bokeh 는 파이썬에서 이용할 수 있는 plotting 도구들이지만, 둘은 각자 지향하는 목적이 다르며 서로가 더 적합한 상황도 다릅니다. 데이터 분석 결과의 시각화 목적에서 두 패키지가 지원하는 기능을 비교해 봄으로써 각자가 할 수 있는 일과 할 수 없는 일을 알아봅니다. 또한 이 튜토리얼은 두 패키지의 사용법을 빠르게 익히려는 목적에 제작하였습니다. Part 1 은 seaborn 의 사용법이며, official tutorial 를 바탕으로, 알아두면 유용한 이야기들을 추가하고 중복되어 긴 이야기들을 제거하였습니다.

Plotting with numerical data

Python 으로 plot 을 그릴 때 가장 먼저 생각나는 도구는 matplotlib 입니다. 가장 오래된 패키지이며, 아마도 현재까지는 가장 널리 이용되고 있는 패키지라 생각됩니다. 하지만 matplotlib 은 그 문법이 복잡하고 arguments 이름들이 직관적이지 않아 그림을 그릴때마다 메뉴얼을 찾게 됩니다. 그리고 매번 그림을 그릴 때마다 몇 줄의 코드를 반복하여 작성하게 됩니다. Seaborn 은 이러한 과정을 미리 정리해둔, matplotlib 을 이용하는 high-level plotting package 입니다.

이 튜토리얼은 Seaborn=0.9.0 의 official tutorial 을 바탕으로, 추가적으로 알아두면 유용한 몇 가지 설명들을 더하였습니다. 기본적인 흐름과 예시는 official tutorials 을 참고하였습니다.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

Seaborn 은 Pandas 와 궁합이 좋습니다. Pandas.DataFrame 의 plot 함수는 기본값으로 matplotlib 을 이용합니다. 그리고 seaborn 은 DataFrame 을 입력받아 plot 을 그릴 수 있도록 구현되어 있습니다. Seaborn 에서 제공하는 tips 데이터를 이용하여 몇 가지 plots 을 그려봅니다.

tips = sns.load_dataset("tips")
tips.head(5)
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4

Scatter plots

두 변수 간의 관계를 살펴볼 수 있는 대표적인 plots 으로는 scatter plot 과 line plot 이 있습니다. 우선 scatter plot 을 그리는 연습을 통하여 seaborn 의 기본적인 문법을 익혀봅니다.

seaborn.scatterplot() 에 tips 데이터를 이용한다는 의미로 data=tips 를 입력합니다. 이 중 x 로 ‘total_bill’ 을, y 로 ‘tip’ 을 이용하겠다고 입력합니다. 그러면 그림이 그려집니다.

sns.scatterplot(x="total_bill", y="tip", data=tips)

흡연 유무에 따라 서로 다른 색을 칠할 수도 있습니다. 이는 hue 에 어떤 변수를 기준으로 다른 색을 칠할 것인지 입력하면 됩니다.

sns.scatterplot(x="total_bill", y="tip", hue="smoker", data=tips)

해당 변수값의 종류가 다양할 경우 각 종류별로 서로 다른 색이 칠해집니다.

sns.scatterplot(x="total_bill", y="tip", hue="day", data=tips)

hue 에 입력되는 값이 명목형이 아닌 실수형일 경우, 그라데이션 형식으로 색을 입력해줍니다.

sns.scatterplot(x="total_bill", y="tip", hue="total_bill", data=tips)

Marker style 도 변경이 가능합니다. style 에 변수 이름을 입력하면 해당 변수 별로 서로 다른 markers 를 이용합니다. 이후 seaborn 의 style 에 대하여 알아볼텐데, scatterplot() 에서의 style argument 는 marker style 을 의미합니다.

sns.scatterplot(x="total_bill", y="tip", hue="smoker", style="smoker", data=tips)

Marker 의 크기도 조절이 가능합니다.

sns.scatterplot(x="total_bill", y="tip", size="size", data=tips)

또한 marker 크기의 상한과 하한도 설정할 수 있습니다.

sns.scatterplot(x="total_bill", y="tip", size="size", sizes=(15, 200), data=tips)

alpha 는 투명도입니다 (0, 1] 사이의 값을 입력합니다.

sns.scatterplot(x="total_bill", y="tip", size="size",
    sizes=(15, 200), alpha=.3, data=tips)

relplot() vs scatterplot()

그리고 official tutorial 에서는 seaborn.relplot() 함수를 이용하여 이를 그릴 수 있다고 설명합니다. 그런데, 그 아래에 relplot()FacetGridscatterplot()lineplot() 의 혼합이라는 설명이 있습니다. 아직 우리가 한 번에 여러 장의 plots 을 그리는 일이 없었기 때문에 scatterplot()relplot() 의 차이가 잘 느껴지지는 않습니다. 하지만 scatterplot() 에서 제공하는 모든 기능은 relplot() 에서 모두 제공합니다. 다른 점은 relplot()scatterplot()lineplot() 을 모두 호출하는 함수입니다. 어떤 함수를 호출할 지 kind 에 정의해야 합니다. 즉 relplot(kind='scatter') 를 입력하면 이 함수가 scatterplot() 함수를 호출합니다. kind 의 기본값은 scatter 이므로, scatter plot 을 그릴 때에는 이 값을 입력하지 않아도 됩니다.

한 장의 scatter/line plot 을 그릴 때에도 relplot() 은 이용가능하기 때문에 이후로는 특별한 경우가 아니라면 relplot() 을 이용하도록 하겠습니다.

sns.relplot(x="total_bill", y="tip", hue="smoker", size="size",
    sizes=(15, 200), data=tips, kind='scatter')

그런데 seaborn.relplot() 함수를 실행시키면 그림이 그려진 것과 별개로 다음과 같은 글자가 출력됩니다. at 뒤의 글자는 함수를 실행할때마다 달라집니다. 이는 relplot() 함수가 return 하는 변수 설명으로, at 뒤는 메모리 주소입니다.

<seaborn.axisgrid.FacetGrid at 0x7f0dfda88278>

그리고 seaborn.scatterplot() 함수의 return 에는 FacetGrid 가 아닌 AxesSubplot 임도 확인할 수 있습니다. FacetGrid 는 1개 이상의 AxesSubplot 의 모음입니다. seaborn.scatterplot()seaborn.lineplot() 은 한 장의 matplotlib Figure 를 그리는 것이며, relplot() 은 이들의 묶음을 return 한다는 의미입니다. 이 의미는 뒤에서 좀 더 알아보도록 하겠습니다. 중요한 점은 두 함수가 각각 무엇인가를 return 한다는 것입니다.

<matplotlib.axes._subplots.AxesSubplot at 0x7f0dfd9c7358>

이 return 된 변수를 이용하여 그림을 수정할 수 있습니다. 이제부터 변수가 return 됨을 명시적으로 표현하기 위하여 seaborn.relplot() 이나 seaborn.scatterplot() 을 실행한 뒤, 그 값을 변수 g 로 받도록 하겠습니다.

Utils

Title

대표적인 수정 작업 중 하나는 그림의 제목을 추가하는 것입니다. 위의 그림에 제목을 추가해봅니다.

g = sns.relplot(x="total_bill", y="tip", hue="smoker", size="size",
    sizes=(15, 200), data=tips, kind='scatter')
g = g.set_titles('scatter plot example')

그런데 어떤 경우에는 (이유를 파악하지 못했습니다) 제목이 추가되지 않습니다. 이 때는 아래의 코드를 실행해보세요. Matplotlib 은 가장 최근의 그림 위에 plots 을 덧그립니다. 아래 코드는 이미 그려진 g 위에 제목을 추가하는 코드입니다.

g = sns.relplot(x="total_bill", y="tip", hue="smoker", size="size",
    sizes=(15, 200), data=tips, kind='scatter')
plt.title('scatter plot example')

Save

relplot() 함수를 실행할 때마다 새로운 그림을 그리기 때문에 이들을 변수로 만든 뒤, 각각 추가작업을 할 수 있습니다. 그 중 하나로 그림을 저장할 수 있습니다. 두 종류의 그림을 g0, g1 으로 만든 뒤, 각 그림을 savefig 함수를 이용하여 저장합니다. 저장된 그림을 살펴봅니다.

참고로 FacetGrid 는 savefig 기능을 제공하지만, AxesSubplot 은 이 기능을 제공하지 않습니다. 물론 matplotlib.pyplot.savefig() 함수나 get_figure().savefig() 함수를 이용하면 되지만, 코드가 조금 길어집니다. 이러한 측면에서도 scatterplot() 보다 relplot() 을 이용하는 것이 덜 수고스럽습니다.

g0 = sns.relplot(x="total_bill", y="tip", hue="smoker",
    size="size", data=tips, kind='scatter')
g1 = sns.relplot(x="total_bill", y="tip", size="size",
    sizes=(15, 200), data=tips)

g0.savefig('total_bill_tip_various_color_by_size.png')
g1.savefig('total_bill_tip_various_size_by_size.png')

Pandas.DataFrame.plot

Pandas 의 DataFrame 에는 손쉽게 plot 을 그리는 함수가 구현되어 있습니다. kind 에 plot 의 종류를, x, y, 그 외의 title 과 같은 attributes 를 keywords argument 형식으로 입력할 수 있습니다. 그런데 DataFrame 의 plot 함수의 return type 은 Figure 가 아닌, AxesSubplot 입니다. 앞서 언급한 것처럼 AxesSubplot 은 그림의 저장 기능을 직접 제공하지 않습니다. 대신 AxesSubplot.get_figure() 를 이용하여 Figure 를 만들면 savefig 를 이용할 수 있습니다.

g = tips.plot(x='total_bill', y='tip', kind='scatter', title='pandas plot example')
g = g.get_figure()
g.savefig('pandas_plot_example.png')

혹은 matplotlib.pyplot.savefig 를 이용하여 AxesSubplot 상태에서 바로 저장할 수도 있습니다.

또한 위에서 return 을 변수로 받지 않고도 그림을 저장하였는데, 이는 matplotlib 은 어떤 그림을 저장할 것인지 설정하지 않으면 가장 마지막에 그린 그림에 대하여 저장을 수행합니다. 그런데 이런 코드는 혼동이 될 수 있기 때문에 코드가 한 줄 더 길어지지만, 저는 return type 을 명시하는 위의 방식을 선호합니다.

ax = tips.plot(x='total_bill', y='tip', kind='scatter', title='pandas plot example')
plt.savefig('pandas_plot_example_2.png')

matplotlib.pyplot.close()

seaborn.relplot() 을 두 번 이용할 경우 각각의 그림이 그려졌습니다. 그런데 seaborn.scatterplot() 을 실행하면 두 그림이 겹쳐져 그려집니다. 이를 알아보기 위하여 random noise data 를 만들었습니다.

data = {
    'x': np.random.random_sample(100) * 50,
    'y': np.random.random_sample(100) * 10
}
random_noise_df = pd.DataFrame(data, columns=['x', 'y'])
random_noise_df.head(5)
x y
0 46.181098 1.073238
1 19.155420 6.603210
2 32.797057 3.273879
3 33.897212 3.974610
4 24.294968 5.602740

각각의 데이터를 seaborn.scatterplot() 에 넣으니 두 그림이 겹쳐져 그려집니다.

g0 = sns.scatterplot(x="total_bill", y="tip", hue='smoker',
    alpha=0.8, size="size", sizes=(15, 200), data=tips)
g1 = sns.scatterplot(x="x", y="y", alpha=0.2, color='g', data=random_noise_df)

실제로 g0, g1 의 메모리 주소가 같습니다.

g0, g1
(<matplotlib.axes._subplots.AxesSubplot at 0x7fc7ead8ff98>,
 <matplotlib.axes._subplots.AxesSubplot at 0x7fc7ead8ff98>)

이 경우, 두 그림을 다르게 그리기 위해서는 matplotlib.pyplot.close() 함수를 중간에 실행시켜야 합니다. Matplotlib 은 현재의 Figure 가 닫히지 않으면 계속 그 Figure 위에 그림을 덧그리는 형식입니다. 그래서 앞서 matplotlib.pyplot.title() 함수를 실행하여 제목을 더할 수도 있었습니다. 즉 그림이 계속 수정된다는 의미입니다.

g0 = sns.scatterplot(x="total_bill", y="tip", hue='smoker',
    alpha=0.8, size="size", sizes=(15, 200), data=tips)
plt.close()
g1 = sns.scatterplot(x="x", y="y", alpha=0.2, color='g', data=random_noise_df)

그래서 중간에 close() 를 실행한 경우에는 각각의 그림에 대하여 제목을 추가하여 Figure 로 만들 수 있습니다.

g0.set_title('total bill ~ tip scatter plot')
g0.get_figure()

g1.set_title('random noise')
g1.get_figure()

이 때는 메모리 주소가 다릅니다.

g0, g1
(<matplotlib.axes._subplots.AxesSubplot at 0x7fc7ead7a630>,
 <matplotlib.axes._subplots.AxesSubplot at 0x7fc7eacd00b8>)

그럼 언제 matplotlib.pyplot.close() 가 실행될까요? relplot() 이 다시 호출될 때 이전에 그리던 Figure 를 닫고, 새 Figure 를 그리기 시작합니다.

g0 = sns.relplot(x="total_bill", y="tip", hue='smoker',
    alpha=0.8, size="size", sizes=(15, 200), data=tips)
g1 = sns.scatterplot(x="x", y="y", alpha=0.2, color='g', data=random_noise_df)

그래서 seaborn.scatterplot() 을 실행한 뒤 seaborn.relplot() 을 실행하면 그림이 분리되어 그려집니다. 혼동될 수 있으니 새 그림이 그려질 때에는 습관적으로 close() 함수를 호출하는 것이 명시적입니다.

g0 = sns.scatterplot(x="total_bill", y="tip", hue='smoker',
    alpha=0.8, size="size", sizes=(15, 200), data=tips)
g1 = sns.relplot(x="x", y="y", alpha=0.2, color='g', data=random_noise_df)

Plotting with numerical data 2

Line plots

데이터가 순차적 형식일 경우 line plot 은 경향을 확인하는데 유용합니다. 우리는 임의의 시계열 데이터를 만들어 line plot 을 그려봅니다. cumsum() 함수는 지금까지의 모든 값을 누적한다는 의미입니다. 자연스러운 순차적 흐름을 지닌 데이터가 만들어질 겁니다.

data = {
    'time': np.arange(500),
    'value': np.random.randn(500).cumsum()
}
df = pd.DataFrame(data)
df.head(5)
time value
0 0 0.207275
1 1 -1.485134
2 2 -1.718115
3 3 -1.624952
4 4 -1.555609

seaborn.lineplot() 을 이용하여 xy 축에 어떤 변수를 이용할지 정의합니다.

g = sns.lineplot(x="time", y="value", data=df)

이는 relplot() 에서 kind 를 ‘line’ 으로 정의하는 것과 같습니다. 물론 return type 은 앞서 언급한것처럼 다릅니다.

g = sns.relplot(x="time", y="value", kind="line", data=df)

위 데이터는 x 를 중심으로 데이터가 정렬된 경우입니다. 그런데 때로는 데이터가 정렬되지 않은 경우도 있습니다. 이를 위하여 임의의 2 차원 데이터 500 개를 생성합니다.

data = np.random.randn(500, 2).cumsum(axis=0)
df = pd.DataFrame(data, columns=["x", "y"])
df.head(5)
x y
0 0.568999 -0.805323
1 -3.756781 -0.687732
2 -2.478935 0.032900
3 -1.896840 -0.350074
4 -2.633141 -0.623276

x 를 기준으로 정렬되지 않았기 때문에 마치 좌표 위를 이동하는 궤적과 같은 line plot 이 그려졌습니다.

g = sns.relplot(x="x", y="y", sort=False, kind="line", data=df)

이를 x 축 기준으로 정렬하여 그리려면 sort=True 로 설정하면 됩니다. 시계열 형식의 데이터의 경우, 안전한 plotting 을 위하여 sort 는 기본값이 True 로 정의되어 있습니다.

g = sns.relplot(x="x", y="y", sort=True, kind="line", data=df)

Aggregation and representing uncertainty

seaborn.lineplot() 의 장점 중 하나는 신뢰 구간 (confidence interval) 과 추정 회귀선 (estminated line) 을 손쉽게 그려준다는 점입니다. 이를 알아보기 위하여 fMRI 데이터를 이용합니다. 이 데이터는 각 사람 (subject) 의 활동 종류 (event) 에 따라 각 시점 (timepoint) 별로 fMRI 의 측정값 중 하나의 센서값을 정리한 시계열 데이터입니다.

fmri = sns.load_dataset("fmri")
fmri.head(5)
subject timepoint event region signal
0 s13 18 stim parietal -0.017552
1 s5 14 stim parietal -0.080883
2 s12 18 stim parietal -0.081033
3 s11 18 stim parietal -0.046134
4 s10 18 stim parietal -0.037970

lineplot() 의 기본값은 신뢰 구간과 추정 회귀선을 함께 그리는 것입니다. 아래 그림은 subject 와 event 의 구분 없이 timepoint 별로 반복적으로 관측된 값을 바탕으로 그려진, 신뢰 구간과 추정 회귀선 입니다.

g = sns.relplot(x="timepoint", y="signal", kind="line", data=fmri)

신뢰 구간을 제거하기 위해서는 ci 를 None 으로 설정합니다. ci 는 confidence interval 의 약자입니다. 하지만 추정된 회귀선은 그려집니다.

g = sns.relplot(x="timepoint", y="signal", kind="line", data=fmri, ci=None)

혹은 데이터의 표준 편차를 이용하여 confidence interval 을 그릴 수도 있습니다.

g = sns.relplot(x="timepoint", y="signal", kind="line", data=fmri, ci="sd")

혹은 bootstrap sampling (복원 반복 추출) 을 이용하여 50 % 의 값을 confidence interval 로 이용할 경우에는 ci=50 을 입력합니다. 이 때 boostrap sampling 의 개수도 n_boot 에서 설정할 수 있습니다.

g = sns.relplot(x="timepoint", y="signal", kind="line", data=fmri, ci=50, n_boot=5000)

추정 회귀선은 estimator 를 None 으로 설정하면 제거됩니다. 기본 추정 방법은 x 를 기준으로 moving windowing 을 하는 것입니다. 추정선이 없다보니 주파수처럼 signal 값이 요동치는 모습을 볼 수 있습니다.

g = sns.relplot(x="timepoint", y="signal", kind="line", data=fmri, estimator=None)

Add conditions to line plot

seaborn.lineplot()seaborn.scatterplot() 처럼 huestyle 을 설정할 수 있습니다.

g = sns.relplot(x="timepoint", y="signal", hue="event", kind="line", data=fmri)

g = sns.relplot(x="timepoint", y="signal", hue="event",
    style="event", kind="line", data=fmri)

혹은 huestyle 을 다른 기준으로 정의하거나, 선 중간에 x 의 밀도에 따라 marker 를 입력할 수도 있습니다.

g = sns.relplot(x="timepoint", y="signal", hue="region", style="event",
    markers=True, kind="line", data=fmri)

혹은 선의 색은 ‘region’ 에 따라 구분하지만, 각 선은 ‘subject’ 를 기준으로 중복으로 그릴 경우 units 에 ‘subject’ 를 입력합니다. 만약 units 을 설정하면 이때는 반드시 estimator=None 으로 설정해야 합니다. 여러 개의 ‘subject’ 가 존재하다보니 선이 지저분하게 겹칩니다.

g = sns.relplot(x="timepoint", y="signal", hue="region",
    units="subject", estimator=None, kind="line",
    data=fmri.query("event == 'stim'"))

Plotting with date data

시계열 형식의 데이터 중 하나는 x 축이 날짜 형식인 데이터입니다.

data = {
    'time': pd.date_range("2017-1-1", periods=500),
    'value': np.random.randn(500).cumsum()
}
df = pd.DataFrame(data)
df.head(5)
time value
0 2017-01-01 -0.641428
1 2017-01-02 0.324469
2 2017-01-03 0.732299
3 2017-01-04 -1.069557
4 2017-01-05 -2.109998

이 역시 seaborn.lineplot() 을 이용하여 손쉽게 그릴 수 있습니다. 추가로 autofmt_xdate() 함수를 이용하면 x 축의 날짜가 서로 겹치지 않게 정리를 도와줍니다.

g = sns.relplot(x="time", y="value", kind="line", data=df)
g.fig.autofmt_xdate()

Multiple plots

앞서 seaborn.scatterplot()seaborn.relplot() 의 return type 이 각각 AxesSubplotFacetGrid 로 서로 다름을 살펴보았습니다. seaborn.relplot() 의 장점은 여러 장의 plots 을 손쉽게 그린다는 점입니다. 각 ‘subject’ 별로 line plot 을 그려봅니다. 이때는 col 을 ‘subject’ 로 설정한 뒤, col 의 최대 개수를 col_wrap 에 설정합니다. ‘subject’ 의 개수가 이보다 많으면 다음 row 에 이를 추가합니다. 몇 가지 유용한 attributes 도 함께 설정합니다. aspect 는 각 subplot 의 세로 대비 가로의 비율입니다. 세로:가로가 4:3 인 subplots 이 그려집니다. 그리고 세로의 크기는 height 로 설정할 수 있습니다.

g = sns.relplot(x="timepoint", y="signal", hue="event", style="event",
    col="subject", col_wrap=5, height=3, aspect=.75, linewidth=2.5,
    kind="line", data=fmri.query("region == 'frontal'"))

그런데 위의 그림에서 col 의 값이 정렬된 순서가 아닙니다. 순서를 정의하지 않으면 데이터에 등장한 순서대로 이 값이 그려집니다. 이때는 사용자가 col_order 에 원하는 값을 지정하여 입력할 수 있습니다. row 역시 row_order 를 제공하니, row 단위로 subplots 을 그릴 때는 이를 이용하면 됩니다.

col_order = [f's{i}' for i in range(14)]

g = sns.relplot(x="timepoint", y="signal", hue="event", style="event",
    col="subject", col_wrap=5, height=3, aspect=.75, linewidth=2.5,
    kind="line", data=fmri.query("region == 'frontal'"),
    col_order=col_order
)

이는 scatter plot 에도 적용할 수 있습니다. 예를 들어 column 은 변수 ‘time’ 에 따라 서로 다르게 scatter plot 을 그릴 경우, 다음처럼 col 에 ‘time’ 을 입력합니다. hue, size 와 같은 설정은 공통으로 적용됩니다.

g = sns.relplot(x="total_bill", y="tip", hue="smoker", data=tips, col="time")

row 를 성별 기준으로 정의하면 (2,2) 형식의 grid plot 이 그려집니다. 그런데 plot 마다 (sex, time) 이 모두 기술되니 title 이 너무 길어보입니다. 이후 살펴볼 FacetGrid 에서는 margin_title 을 이용하여 깔끔하게 col, row 의 기준을 표시하는 방법이 있습니다. 아마 0.9.0 이후의 버전에서는 언젠가 seaborn.relplot() 에도 그 기능이 제공되지 않을까 기대해봅니다.

g = sns.relplot(x="total_bill", y="tip", hue="smoker",
    data=tips, col="time", row="sex")

Plotting with categorical data

Categorical scatterplots

앞서 seaborn.scatterplot()seaborn.lineplot() 의 사용법, 그리고 이를 감싸는 seaborn.relplot() 함수와의 차이를 살펴보았습니다. 변수가 명목형일 경우에는 seaborn.relplot() 대신 seaborn.catplot() 을 이용할 수 있습니다. catplot()stripplot(), boxplot(), barplot() 등 다양한 함수들을 호출하는 상위 함수 입니다.

Strip plot

앞서 order, kind, 등의 argument 사용법에 대하여 살펴보았으니, 여기에서는 어떤 그림들을 그릴 수 있는지에 대해서만 간단히 살펴봅니다.

g = sns.catplot(x="smoker", y="tip", kind='strip',
    order=["No", "Yes"], jitter=False, data=tips)

seaborn.catplot()kind 에 입력되는 값은 함수 이름입니다. 이 역시 seaborn.stripplot() 을 이용할 수도 있습니다. jitter 는 데이터 포인트가 겹쳐 그리는 것을 방지하기 위하여 작은 permutation 을 함을 의미합니다.

g = sns.stripplot(x="tip", y="day", hue='smoker', alpha=0.5, data=tips)

Boxplots

Box plot 도 그릴 수 있습니다.

g = sns.catplot(x="day", y="total_bill", hue="smoker", kind="box", data=tips)

seaborn.boxplot() 이나 이 함수가 이용하는 matplotlib.pyplot.boxplot() 이 이용하는 arguments 를 입력할 수도 있습니다. dodge=False 로 입력하면 ‘smoker’ 유무 별로 각각 boxplot 이 겹쳐져 그려지는데, 이왕이면 각 box 를 투명하게 만들면 좋을듯 합니다. 그런데 아직 boxplot 의 투명도를 조절하는 argument 를 찾지 못했습니다.

찾다보면 seaborn 으로 여러 설정들을 할 수는 있지만, 이를 위해서는 matplotlib 함수들의 arguments 를 찾아야 하는 일들이 발생합니다. seaborn.catplot() 의 그림을 수정하기 위하여 seaborn.boxplot() 의 arguments 를 확인하고, 또 디테일한 설정을 하기 위해서 seaborn.boxplot() 이 이용하는 matplotlib.pyplot.boxplot() 의 arguments 를 확인해야 합니다. 복잡해지네요.

g = sns.catplot(x="day", y="total_bill", hue="smoker",
    kind="box", data=tips, dodge=False)

Boxen plot 은 데이터의 분포를 box 의 width 로 표현하는 plot 입니다. 이를 위하여 ‘diamonds’ dataset 을 이용합니다.

diamonds = sns.load_dataset("diamonds")
diamonds.head(5)
carat cut color clarity depth table price x y z
0 0.23 Ideal E SI2 61.5 55.0 326 3.95 3.98 2.43
1 0.21 Premium E SI1 59.8 61.0 326 3.89 3.84 2.31
2 0.23 Good E VS1 56.9 65.0 327 4.05 4.07 2.31
3 0.29 Premium I VS2 62.4 58.0 334 4.20 4.23 2.63
4 0.31 Good J SI2 63.3 58.0 335 4.34 4.35 2.75

이 데이터는 color 가 정렬되어 있지 않은 데이터입니다. 이를 정렬하여 ‘color’ 별 ‘price’ 에 대한 boxen plot 을 그려봅니다.

g = sns.catplot(x="color", y="price", kind="boxen", data=diamonds.sort_values("color"))

Violinplots

Violin plot 은 분포를 밀도 함수로 표현하는 그림입니다. 이 역시 hue 를 설정할 수 있습니다.

g = sns.catplot(x="day", y="total_bill", kind="violin", hue="sex", data=tips)

그런데 hue 가 두 종류라면 굳이 두 개의 분포를 나눠 그릴 필요는 없어보입니다. 이때는 split=True 로 설정하면 두 종류의 분포를 서로 붙여서 보여줍니다.

g = sns.catplot(x="day", y="total_bill", hue="sex",
    kind="violin", split=True, inner="stick", data=tips)

Bar plots

Bar plot 은 명목형 데이터의 분포를 확인하는데 이용됩니다. 이를 위하여 타이타닉 생존자 데이터를 이용합니다.

titanic = sns.load_dataset("titanic")
titanic.head(5)
survived pclass sex age sibsp parch fare embarked class who adult_male deck embark_town alive alone
0 0 3 male 22.0 1 0 7.2500 S Third man True NaN Southampton no False
1 1 1 female 38.0 1 0 71.2833 C First woman False C Cherbourg yes False
2 1 3 female 26.0 0 0 7.9250 S Third woman False NaN Southampton yes True
3 1 1 female 35.0 1 0 53.1000 S First woman False C Southampton yes False
4 0 3 male 35.0 0 0 8.0500 S Third man True NaN Southampton no True

seaborn.barplot() 역시 seaborn.catplot() 을 이용하여 그릴 수 있습니다. 성별, 그리고 선실별 생존율을 그려봅니다.

g = sns.catplot(x="sex", y="survived", hue="class", kind="bar", data=titanic)

hue 의 종류가 여러 개이면 x 축의 종합적인 분포가 잘 보이지 않습니다. 누적 형식의 bar plot 을 그리기 위해서는 dodge=False 로 설정합니다.

g = sns.catplot(x="sex", y="survived", hue="class",
    kind="bar", data=titanic, dodge=False)

누적 형식으로 그림을 그리니 생존율이 명확히 보이지 않습니다. 생존자 수를 bar plot 으로 그려봅니다. 이를 위해서 seaborn.countplot() 를 이용합니다. 이번에는 x, y 축을 바꿔보았고, bar 의 모서리에 선을 칠하기 위하여 edgecolor 를 조절하였습니다. edgecolor 는 그 값이 분명 실수형식인데, 입력할 때에는 str 형식으로 입력해야 합니다. 이는 matplotlib 의 함수를 이용하기 때문인데, 다음 버전에서는 직관적이게 float 를 입력하도록 바꿔줬으면 좋겠네요.

g = sns.catplot(y="deck", hue="class", kind="count",
    data=titanic, dodge=False, edgecolor=".5")

Point plots

그 외에도 class 별 생존율을 선으로 연결하는 point plot 을 그릴 수 있고, 이 때 이용하는 linestylesmarkers 를 입력할 수도 있습니다. 이 때 linestylesmarkers 의 길이는 hue 의 종류의 개수와 같아야 합니다.

g = sns.catplot(x="class", y="survived", hue="sex",
    palette={"male": "g", "female": "m"},
    markers=["^", "o"], linestyles=["-", "--"],
    kind="point", data=titanic)

Visualizing distribution

이번에는 data distribution plot 을 그려봅니다.

Plotting univariate distributions

Seaborn 은 univariate distribution 과 bivariate distribution 을 그리는 plot 을 지원합니다. 이를 위하여 평균 0, 표준편차 1인 정규분포에서 임의의 100 개의 데이터 x 를 만듭니다. seaborn.distplot() 함수에 이를 입력하면 histogram 과 추정된 밀도 곡선이 함께 그려집니다.

x = np.random.normal(size=100)
g = sns.distplot(x)

그런데 seaborn.distplot() 의 return type 이 matplotlib 의 AxesSubplot 입니다.

g
<matplotlib.axes._subplots.AxesSubplot at 0x7fa032760e80>

seaborn.distplot() 함수를 호출할 때마다 새로운 그림을 그리는 것이 아니라, 이전 그림에 덧칠을 할 수 있다는 의미입니니다. 이번에는 동일한 정규분포에서 다른 샘플 y 를 만들고, xy 를 각각 seaborn.distplot() 에 입력합니다. 두 개의 그림이 겹쳐져 그려짐을 확인할 수 있습니다. 분포 그림들이 주로 다른 분포들과 겹쳐져 거려지는 경우가 많기 때문으로 생각됩니다.

y = np.random.normal(size=100)
g0 = sns.distplot(x)
g1 = sns.distplot(y)

Histograms

seaborn.distplot() 의 기본값은 hist=True, kde=True, rug=False 입니다. hist 는 historgram 을 그릴지 묻는 것이며, kde 는 kernel density estimation 을 수행할지 묻는 것입니다. 또한 rug 는 데이터 포인트를 그릴지 묻는 것입니다. 참고로 seaborn.kdeplot()seaborn.rugplot() 은 함수입니다. 즉 seaborn.distplot() 은 여러 종류의 data distribution plots 을 한 번에 그려주는 종합함수입니다. kde=False, rug=True 로 변경하면 여전히 histogram 은 그려지지만 밀도 함수는 제거되고, x 축에 데이터의 밀도를 표현하는 그림이 그려집니다.

g = sns.distplot(x, hist=True, kde=False, rug=True, bins=20)

Kernel density estimation

seaborn.kdeplot() 은 다양한 종류의 kernel 을 제공합니다. 기본값은 gaussian kernel 을 이용합니다. 이때 gaussian kernel 의 bandwidth 를 데이터 기반으로 측정하기도 하고, 혹은 bw 를 통하여 직접 설정할 수도 있습니다. 아래 그림은 bandwidth 가 넓어지면 smooth 한 distribution 이, bandwidth 가 좁아지면 날카로운 density estimation 이 이뤄짐을 볼 수 있습니다.

sns.kdeplot(x, label='bw: default')
sns.kdeplot(x, bw=.2, label="bw: 0.2")
sns.kdeplot(x, bw=2, label="bw: 2")
plt.legend()

Fitting parametric distributions

혹은 fit 에 특정 함수를 입력할 수도 있습니다.

x = np.random.gamma(6, size=200)
g = sns.distplot(x, kde=False, fit=stats.gamma)

또한 seaborn.distplot() 에서 kde=True 를 설정하는 것은 seaborn.kdeplot() 을 실행하는 것과 같기 때문에 이 때 필요한 설정은 kde_kws 에 입력할 수 있습니다.

g = sns.distplot(x, hist=True, kde=True, fit=stats.gamma,
    kde_kws={'bw':2.0, 'color':'c', 'label':'bw 2'})
g = sns.distplot(x, hist=False, kde=True, fit=stats.gamma,
    kde_kws={'bw':0.2, 'color':'r', 'label':'bw 0.2'})

Plotting bivariate distributions

2 차원의 정규분포로부터 임의의 데이터 200 개를 만들었습니다.

mean, cov = [0, 1], [(1, .5), (.5, 1)]
data = np.random.multivariate_normal(mean, cov, 200)
df = pd.DataFrame(data, columns=["x", "y"])

df.head(3)
x y
0 -0.681701 1.977984
1 -0.108547 1.047261
2 -0.767767 -0.329327

이 데이터의 joint distribution plot 을 그리기 위하여 seaborn.jointplot() 을 이용할 수 있습니다. 종류는 scatter plot, kernel density estimation, regression, residual, hexbin plot 을 제공합니다. 그 중 세 종류에 대해서 알아봅니다. kind 의 기본값은 scatter plot 입니다. 데이터를 입력하고 x, y 의 변수 이름을 입력할 수 있습니다.

g = sns.jointplot(x="x", y="y", kind='scatter', data=df)

혹은 x 와 y 를 각각 입력할수도 있습니다. 각각 좌표의 sequence 를 준비합니다.

x, y = data.T
print(x[:5])
print(y[:5])
[-0.68170061 -0.10854677 -0.76776747  0.67274982 -0.82073625]
[ 1.97798404  1.04726107 -0.32932665  0.51447462  1.39611539]

이번에는 kernel density estimation plot 을 그려봅니다.

g = sns.jointplot(x=x, y=y, kind="kde")

Hexbin plot 은 지역을 육각형으로 나눈 뒤, 각 부분의 밀도를 색으로 표현합니다.

g = sns.jointplot(x=x, y=y, kind="hex", color="k")

모서리 부분의 style 이 지저분합니다. 이 그림에 대해서만 style 을 임시로 바꾸려면 파이썬 문법의 with 을 이용할 수 있습니다.

with sns.axes_style("white"):
    g = sns.jointplot(x=x, y=y, kind="hex", color="k")

이번에는 각 변수의 분포를 seaborn.rugplot() 으로 대체해봅니다. seaborn.kdeplot() 의 그림은 정방형이 아니기 때문에 미리 그림의 크기를 matplotlib.pyplot.subplots() 을 이용하여 정의합니다. subplots() 함수를 이용하면 grid plot 을 그릴 수 있는데, 이는 matplotlib 의 사용법을 추가로 찾아보시기 바랍니다. 지금은 grid plot 을 만들지 않았기 때문에 아래처럼 하나의 plot 에 여러 종류의 plots 을 덧그렸습니다.

f, ax = plt.subplots(figsize=(6, 6))
g = sns.kdeplot(x, y, ax=ax)
g = sns.rugplot(x, color="g", ax=ax)
g = sns.rugplot(y, vertical=True, ax=ax)
g = g.set_title('density estimation')

혹은 등고선이 아닌 색으로 밀도를 표현할 수도 있습니다. 이를 위해 colormap 을 따로 설정하고 shade=True 를 설정합니다. 등고선이 아니라 색으로 표현한다는 의미입니다. cmap 은 256 단계의 밀도에 대하여 RGBA 형식으로 표현된 color vector 입니다. 그 형식은 numpy.ndarray 입니다.

f, ax = plt.subplots(figsize=(6, 6))
cmap = sns.cubehelix_palette(as_cmap=True, dark=0, light=1, reverse=True)
g = sns.kdeplot(x, y, cmap=cmap, n_levels=60, shade=True)

print(type(cmap))
print(cmap.colors.shape)
<class 'matplotlib.colors.ListedColormap'>
(256, 4)

혹은 colormap 을 반대로 정의하면 밀도가 높은 부분을 진하게 표현할 수도 있습니다.

f, ax = plt.subplots(figsize=(6, 6))
cmap = sns.cubehelix_palette(as_cmap=True, dark=0, light=1, reverse=False)
g = sns.kdeplot(x, y, cmap=cmap, n_levels=60, shade=True)

이번에는 kernel density estimation plot 위에 흰 색의 + marker 의 scatter plot 을 추가하였습니다.

g = sns.jointplot(x="x", y="y", data=df, kind="kde", color="m", shade=True)
g = g.plot_joint(plt.scatter, c="w", s=30, linewidth=1, marker="+")
g = g.set_axis_labels("$X$", "$Y$")

Visualizing linear relationships

seaborn.lineplot() 은 x 의 변화에 따른 y 값의 변화를 선으로 연결합니다. 이때 이용하는 estimator 의 기본 방식은 kernel density estimation 입니다. 다른 plotting 패키지와 비교하여 seaborn 의 장점 중 하나는 linear regression line 과 confidence interval 을 손쉽게 그려준다는 점입니다.

Linear regression models

regplot() 은 하나의 그림을 그리는 함수이며, lmplot()row, col 을 설정할 수 있는 multi plot 기능을 제공합니다. 그러므로 regplot() 의 return type 은 AxesSubplot 입니다.

g = sns.regplot(x="total_bill", y="tip", data=tips)

반대로 seaborn.lmplot() 은 FacetGrid 를 return 합니다. 즉 lmplot() 이 상위 함수 입니다.

g = sns.lmplot(x="total_bill", y="tip", data=tips)

그러므로 seaborn.lmplot()col, row, aspect, hue, markers 등등의 multi plot 을 그리는데 필요한 attributes 를 모두 이용할 수 있습니다. 또한 ci=None 으로 설정하면 confidence interval 도 그리지 않습니다.

g = sns.lmplot(x="total_bill", y="tip", col="time", aspect=0.75,
    hue="smoker", markers=["o", "x"], ci=None, data=tips)

Fitting different kinds of models

Linear regression 이기 때문에 다항 선형 회귀식도 지원 합니다. anscombe dataset 은 각각 1, 2 차식으로부터 생성된 데이터가 포함되어 있습니다.

anscombe = sns.load_dataset("anscombe")
anscombe.head(5)

2차식으로부터 만들어진 데이터는 1차 선형 회귀 모델로 추정되기 어렵습니다.

g = sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'II'"))

order=2 로 변경하면 2차 다항 선형 회귀 방정식을 학습합니다. 그런데 2차 이상에서는 confidence interval 이 그려지지 않네요 (seaborn==0.9.0).

g = sns.lmplot(x="x", y="y", order=2, data=anscombe.query("dataset == 'II'"))

그 외에도 noise 를 제거하며 선형 회귀 모델을 학습하는 기능도 제공하지만, 이러한 과정은 seaborn 을 이용하는 것보다 외부에서 모델을 학습한 뒤 이를 plotting 하는 것이 더 적절합니다. 편리한 기능은 다항 선형 회귀식을 이용하는 것 까지라 생각합니다.

g = sns.lmplot(x="x", y="y", robust=True, data=anscombe.query("dataset == 'III'"))

Regression with other plottings

그 외에도 seaborn.jointplot()seaborn.pairplot() 역시 두 변수 간의 관계를 표현하는 plot 이기 때문에 kind='reg' 로 설정하면 회귀식이 함께 표현됩니다.

g = sns.jointplot(x="total_bill", y="tip", data=tips, kind="reg")

g = sns.pairplot(tips, x_vars=["total_bill", "size"],
    y_vars=["tip"], hue="smoker", height=5, kind="reg")

Logistic regression

‘tips’ dataset 을 이용하여 로지스틱 회귀분석을 수행하기 위하여 데이터셋에 총 지출액 대비 15 % 이상의 팁을 준 경우를 ‘big_tip’ 이라 명합니다. ‘total_bill’ 을 이용하여 big tip 인지 확인하는 로지스틱 회귀 모델을 학습하려면 logistic=True 로만 설정하면 됩니다.

tips["big_tip"] = (tips.tip / tips.total_bill) > .15
g = sns.lmplot(x="total_bill", y="big_tip", data=tips, logistic=True, y_jitter=.03)

Utils for mupti-plots

앞서 seaborn.relplot()rowcol 에 변수를 입력함으로써 여러 개의 plots 을 한 번에 그리는 방법에 대하여 알아보았습니다. 이번에는 이 그림을 직접 그리는 방법에 대하여 알아봅니다. Seaborn 은 FacetGridPairGrid 라는 클래스를 제공합니다.

FacetGrid and map()

seaborn.FacetGrid 클래스는 첫번째 position argument 로 데이터셋을 입력받습니다. 그 뒤, col 을 데이터셋의 ‘time’ 을 기준으로 나눌 것이라 명명합니다. FacetGrid instance 를 만들면 아래처럼 빈 grid plot 이 그려집니다.

g = sns.FacetGrid(tips, col="time")

map 함수를 이용하여 각 subplot 을 그릴 함수를 첫번째로 입력하고, 뒤이어 그 함수들이 이용하는 변수 이름을 순서대로 입력합니다. col 을 ‘time’ 으로 나누었으니, 시간대 별로 ‘tip’ 의 histogram 이 그려집니다.

# sns.FacetGrid(data, row=None, col=None, ...)
g = sns.FacetGrid(tips, col="time")
g = g.map(plt.hist, "tip")

FacetGrid 는 col 설정이 가능하니 당연히 row 설정도 가능합니다. map 에는 첫번째 함수, 그 이후 position argument 로 그 함수가 이용하는 데이터셋 내의 변수명, 그 뒤로 plot 함수가 이용하는 argument 를 keyword argument 로 입력합니다. 함수로는 seaborn 의 함수도 이용이 가능합니다.

g = sns.FacetGrid(tips, row="smoker", col="time")
g = g.map(sns.regplot, "size", "total_bill", color=".3", x_jitter=.1)

그런데 각 subplot 의 조건이 너무 길게 표현됩니다. 이를 한 번만 표기하기 위해 margin_titles=True 로 설정합니다. 또한 추정 회귀선은 표현하지 않기 위해 seaborn.regplot()fit_reg=False 로 설정합니다. 이처럼 subplot 을 그리는데 이용되는 arguments 를 입력할 수 있습니다.

g = sns.FacetGrid(tips, row="smoker", col="time", margin_titles=True)
g = g.map(sns.regplot, "size", "total_bill", color=".3", fit_reg=False, x_jitter=.1)

앞서서 seaborn.relplot() 을 이용하여 그렸던 fMRI 데이터의 subject 별 line plot 도 FacetGrid 를 이용하여 그릴 수 있습니다. 이 때 column 의 최대 개수나 column order 를 정의하는 부분은 FacetGrid 를 만들 때 모두 설정해야 합니다. hue 역시 이 때 미리 정의할 수 있습니다. 즉 앞서 seaborn.relplot() 을 그릴 때 kind=line 이고 col 에 변수가 입력되면 relplot() 함수 내에서 FacetGrid 를 만든 뒤, 각 subplots 을 그리는 것입니다.

fmri = sns.load_dataset("fmri").query("region == 'frontal'")
col_order = [f's{i}' for i in range(14)]

g = sns.FacetGrid(fmri, col='subject', col_wrap=5, col_order=col_order,
    aspect=.75, height=3, hue='event')
g = g.map(sns.lineplot, 'timepoint', 'signal')
g = g.add_legend()

Using custom functions

또한 사용자가 임의로 작성하는 함수를 FacetGrid 에 적용할 수도 있습니다. 아래는 quantile plot 을 그리는 함수를 만든 것입니다. quantile_plot() 함수는 x 변수를 입력받아 그 값을 정규분포로 fitting 한 뒤 이를 scatter plot 으로 표현합니다. quantile_plot() 함수는 하나의 변수만을 이용하니 map 함수에 ‘total_bill’ 변수 이름만을 입력합니다.

from scipy import stats

def quantile_plot(x, **kwargs):
    qntls, xr = stats.probplot(x, fit=False)
    plt.scatter(xr, qntls, **kwargs)

g = sns.FacetGrid(tips, col="sex", height=4)
g = g.map(quantile_plot, "total_bill");

두 개의 변수를 이용하는 함수라면 map 함수에 두 개의 변수 이름을 입력하면 됩니다. 각 변수의 값들이 각각 qqplot()xy 로 입력됩니다.

def qqplot(x, y, **kwargs):
    _, xr = stats.probplot(x, fit=False)
    _, yr = stats.probplot(y, fit=False)
    plt.scatter(xr, yr, **kwargs)

g = sns.FacetGrid(tips, col="smoker", height=4)
g = g.map(qqplot, "total_bill", "tip");

Pairwise relationships in a dataset

데이터셋의 탐색을 위하여 연속형 변수 별 상관관계를 확인할 scatter plot 과 각 변수 별 histogram 을 그릴 수도 있습니다. 이를 위하여 iris 데이터를 이용합니다.

iris = sns.load_dataset("iris")
iris.head(5)
sepal_length sepal_width petal_length petal_width species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa
3 4.6 3.1 1.5 0.2 setosa
4 5.0 3.6 1.4 0.2 setosa

seaborn.pairplot() 함수를 이용하면 대각선에는 각 변수의 histogram 이, 그 외에는 두 연속형 변수 간의 상관관계가 scatter plot 으로 표현됩니다.

g = sns.pairplot(iris)

명목형 변수인 ‘species’ 별로 색을 다르게 칠하기 위해서 seaborn.pairplot() 함수의 hue 에 변수 이름을 입력할 수 있습니다. 여러 종류의 species 에 대하여 histogram 을 그리기 어려우니 대각선의 subplots 에 밀도 추정 line plot 을 그렸습니다.

g = sns.pairplot(iris, hue="species")

만약 반드시 histogram 을 그리겠다면 seaborn.pairplot()diag_kind 에 ‘hist’ 를 입력합니다. seaborn==0.9.0 에서 지원되는 값은 ‘hist’ 와 ‘kde’ 뿐입니다. 그리고 diagonal subplots 을 그릴 때 이용하는 arguments 는 diag_kws 에 입력할 수 있습니다.

g = sns.pairplot(iris, hue="species", diag_kind="hist", height=2.5,
    diag_kws={'alpha':0.5})

이 역시 수작업으로 그릴 수 있습니다. 단, seaborn.PairGrid 는 변수 간 상관관계를 보이기 위한 그림이기 때문에 정방형의 grid plot 이 그려집니다. 그리고 대각선과 그 외에 각각 어떤 plot 을 그릴지 map_diag()map_offdiag() 로 정의할 수 있습니다.

g = sns.PairGrid(iris)
g = g.map_diag(sns.kdeplot)
g = g.map_offdiag(sns.kdeplot, n_levels=6)

map_diag()map_offdiag() 모두 각각의 plot 을 그리는데 필요한 arguments 를 keyword argument 형식으로 입력받습니다.

g = sns.PairGrid(iris, hue="species")
g = g.map_diag(plt.hist, alpha=0.5)
g = g.map_offdiag(plt.scatter)
g = g.add_legend()

혹은 대각선 위의 그림과 아래의 그림을 다르게 정의할 수도 있습니다. lw 는 line width 입니다.

g = sns.PairGrid(iris, hue='species')
g = g.map_upper(plt.scatter)
g = g.map_lower(sns.kdeplot)
g = g.map_diag(sns.kdeplot, lw=3, legend=True)

만약 데이터셋의 변수가 10 개라면 10 x 10 크기의 grid plot 이 그려집니다. 확인할 변수가 있다면 그 변수 이름들만을 seaborn.PairGrid 의 argument vars 에 입력합니다.

g = sns.PairGrid(iris, vars=["sepal_length", "sepal_width"], hue="species")
g = g.map(plt.scatter)

Style

Seaborn 의 그림들의 background color, grid line color, color map 등을 한 번에 설정할 수 있습니다. Seaborn 의 style 은 미리 정의되어 있는 위의 값들입니다. 기본적으로 다섯가지의 styles 을 제공합니다.

styles = ['darkgrid', 'whitegrid', 'dark', 'white', 'ticks']

Style 은 figure 단위로 적용되기 때문에 FacetGrid 의 각 subplot 에 서로 다른 style 을 적용할 수는 없습니다. 만약 반드시 그래야 한다면 각 grid 의 subplot 마다 설정을 다르게 적용하여 직접 그림을 그려야 합니다. tips 데이터를 이용하여 다섯가지 스타일에 대하여 scatter plot 에서의 변화를 살펴봅니다.

# styles = ['darkgrid', 'whitegrid', 'dark', 'white', 'ticks']
sns.set(style="darkgrid")
g = sns.relplot(x="total_bill", y="tip", hue="smoker", data=tips)

# styles = ['darkgrid', 'whitegrid', 'dark', 'white', 'ticks']
sns.set(style="whitegrid")
g = sns.relplot(x="total_bill", y="tip", hue="smoker", data=tips)

# styles = ['darkgrid', 'whitegrid', 'dark', 'white', 'ticks']
sns.set(style="dark")
g = sns.relplot(x="total_bill", y="tip", hue="smoker", data=tips)

# styles = ['darkgrid', 'whitegrid', 'dark', 'white', 'ticks']
sns.set(style="white")
g = sns.relplot(x="total_bill", y="tip", hue="smoker", data=tips)

# styles = ['darkgrid', 'whitegrid', 'dark', 'white', 'ticks']
sns.set(style="ticks")
g = sns.relplot(x="total_bill", y="tip", hue="smoker", data=tips)

Overriding seaborn style to matplotlib

Seaborn 은 matplotlib 을 이용하는 패키지이기 때문에, style 설정이 matplotlib 에도 영향을 줍니다. 아래의 예시는 matplotlib 을 이용하여 주기와 진폭이 서로 다른 sin 함수들의 플랏입니다.

sns.set(style="ticks")

def sinplot(flip=1):
    g = plt.figure(figsize=(6,4))
    x = np.linspace(0, 14, 100)
    for i in range(1, 7):
        plt.plot(x, np.sin(x + i * .5) * (7 - i) * flip)
    return g

g = sinplot()

위 그림의 style 을 Seaborn 의 default 인 darkgrid 로 변경해봅니다.

sns.set()
g = sinplot()

혹은 font size 나 line width 와 같은 attributes 를 변경할 수도 있습니다. 가능한 attributes 는 matplotlib 의 문서를 참고합니다.

sns.set_context(font_scale=1.5, rc={"lines.linewidth": 5.0})
g = sinplot()

이러한 style 설정의 영향은 matplotlib 을 이용하는 Pandas 에도 미칩니다. DataFrame 의 plot 함수는 기본값으로 matplotlib 을 이용하기 때문에 seaborn 의 style 을 변경하면 설정이 반영됩니다.

sns.set(style="ticks")
g = tips.plot(x='total_bill', y='tip', kind='scatter')

sns.set(style="darkgrid")
g = tips.plot(x='total_bill', y='tip', kind='scatter')

Customized palette

Palette 는 style 이 이용하는 color codes 입니다. 이들은 RGB 의 값을 [0, 1] 사이로 표현한 tuple 의 list 로 표현됩니다. 현재 이용하는 palette 는 seaborn.color_palette() 함수를 통하여 확인할 수 있습니다.

sns.set(style="ticks")
sns.color_palette()
[(0.2980392156862745, 0.4470588235294118, 0.6901960784313725),
 (0.8666666666666667, 0.5176470588235295, 0.3215686274509804),
  ...
 (0.39215686274509803, 0.7098039215686275, 0.803921568627451)]

Palette 를 사용자의 선호대로 변경할 수 있습니다. 그런데 일반적으로 RGB 값을 위의 예시처럼 float vector 로 알고 있기 보다는 아래처럼, # 뒤에 세 개의 16진수로 RGB 를 표현하는 HTML color code 로 알고 있는 경우들이 많습니다. seaborn.color_palette() 함수는 HTML color code 를 float vector 로 변환해 줍니다. 이를 seaborn.set_palette() 에 입력하면 palette 가 변경됩니다.

from pprint import pprint

# from Bokeh Accent[5] colors
color_codes = ['#7fc97f', '#beaed4', '#fdc086', '#ffff99', '#386cb0']
colors = sns.color_palette(color_codes)
pprint(colors)

sns.set_palette(colors)
g = sns.relplot(x="total_bill", y="tip", hue="smoker", data=tips)
[(0.4980392156862745, 0.788235294117647, 0.4980392156862745),
 (0.7450980392156863, 0.6823529411764706, 0.8313725490196079),
 (0.9921568627450981, 0.7529411764705882, 0.5254901960784314),
 (1.0, 1.0, 0.6),
 (0.2196078431372549, 0.4235294117647059, 0.6901960784313725)]