Python Programming
  • Home
  • Intro
    • History & Background
    • Python Setup
  • Exercises
    • Chapter 5: Lists, Tuples, Sets
    • Chapter 6: Strings
    • Chapter 7: Dictionaries
    • Chapter 8: Control flow
    • Chapter 9: Functions
    • Chapter 14: Exceptions
    • Chapter 15: Classes
  • Exploring Data
    • NumPy & pandas
    • Inspecting data
    • Visualization
  • Library System
  • Netflix Movie Analysis
    • Notes
    • Project-Native
    • Project-pandas
  • References
    • QPB Part 1
    • QPB Part 2
    • QPB Part 3
    • QPB Part 4

On this page

  • First steps
  • Aesthetic mappings
    • Color
    • Pointsize
    • Marker
    • Alpha
    • Linestyle
    • 두 가지 이상의 속성
  • Setting properties
  • Faceting
  • Geometric objects
  • Statistical transformations
    • Fitted lines
  • Seaborn.objects 요약
  • Applications
    • Visualizing distributions
      • A categorical variable
      • A numerical variable
    • Visualizing relationships
      • A numerical and a categorical variable
      • Two categorical variables
      • Two numerical variables
      • Three or more variables
      • Time series
    • Overploting
    • New data

Plots

Author

Sungkyun Cho

Published

Jun, 2026

First steps

Load packages
# numerical calculation & data frames
import numpy as np
import pandas as pd

# visualization
import matplotlib.pyplot as plt
import seaborn as sns
import seaborn.objects as so

# statistics
import statsmodels.api as sm

# pandas options
pd.set_option('mode.copy_on_write', True)  # pandas 2.0
pd.options.display.float_format = '{:.2f}'.format  # pd.reset_option('display.float_format')
pd.options.display.max_rows = 7  # max number of rows to display

# NumPy options
np.set_printoptions(precision = 2, suppress=True)  # suppress scientific notation

# For high resolution display
import matplotlib_inline
matplotlib_inline.backend_inline.set_matplotlib_formats("retina")

Data: Fuel economy data from 1999 to 2008 for 38 popular models of cars

# import the dataset
mpg_data = sm.datasets.get_rdataset("mpg", "ggplot2")
mpg = mpg_data.data
# Description
print(mpg_data.__doc__)
mpg
    manufacturer   model  displ  year  cyl       trans drv  cty  hwy fl  \
0           audi      a4   1.80  1999    4    auto(l5)   f   18   29  p   
1           audi      a4   1.80  1999    4  manual(m5)   f   21   29  p   
2           audi      a4   2.00  2008    4  manual(m6)   f   20   31  p   
..           ...     ...    ...   ...  ...         ...  ..  ...  ... ..   
231   volkswagen  passat   2.80  1999    6    auto(l5)   f   16   26  p   
232   volkswagen  passat   2.80  1999    6  manual(m5)   f   18   26  p   
233   volkswagen  passat   3.60  2008    6    auto(s6)   f   17   26  p   

       class  
0    compact  
1    compact  
2    compact  
..       ...  
231  midsize  
232  midsize  
233  midsize  

[234 rows x 11 columns]

Q: 엔진의 크기(displ)와 연비(hwy)는 어떤 관계에 있는가?

# Scatter plot: 산포도
(
    so.Plot(mpg, x="displ", y="hwy") # empty plot을 생성하고, x, y축에 mapping할 mpg 데이터의 변수를 지정
    .add(so.Dot()) # layer를 추가하여, points들을 Dot이라는 mark object를 써서 표현
)

Layer-specific mappings

Global vs. local mapping

다음과 같이 첫번째 layer 안에서 x, y를 mapping하는 경우, 이후 새로 추가되는 layer에는 그 mapping이 적용되지 않음

(
    so.Plot(mpg)
    .add(so.Dot(), x="displ", y="hwy") # 이 layer에서만 mapping이 유효
)

카테고리 변수인 경우

  • cyl (실린더 개수), hwy (고속도로 연비)의 관계를 scatterplot으로 살펴볼 수 있는가? (left)
  • class (차량 타입), drv (전륜 구동, 후륜 구동, 4륜 구동 타입)의 관계는 어떠한가? (right)

Aesthetic mappings

Q: 엔진의 크기와 연비와의 관계에서 보이는 트렌드 라인에서 심하게 벗어난 것이 있는가?


변수들을 x, y라는 position에 mapping하는 것에 추가하여 다음과 같은 속성(aesthetic)에 mapping할 수 있음

색(color), 크기(pointsize), 모양(marker), 선 종류(linestyle), 투명도(alpha)

Color

(
    so.Plot(mpg, x="displ", y="hwy", color="class")
    .add(so.Dot())
)

Pointsize

(
    so.Plot(mpg, x="displ", y="hwy", pointsize="class")
    .add(so.Dot())
)

Marker

(
    so.Plot(mpg, x="displ", y="hwy", marker="class")
    .add(so.Dot())
)

Alpha

(
    so.Plot(mpg, x="displ", y="hwy", alpha="class")
    .add(so.Dot())
)

Linestyle

healthexp = sns.load_dataset("healthexp")

p = so.Plot(healthexp, x="Spending_USD", y="Life_Expectancy", linestyle="Country")
p.add(so.Line())

두 가지 이상의 속성

ex. color & marker

(
    so.Plot(mpg, x="displ", y="hwy", color="class", marker="drv")
    .add(so.Dot())
)

(
    so.Plot(mpg, x="displ", y="hwy", color="class", pointsize="drv")
    .add(so.Dot())
    .scale(pointsize=(5, 15))  # pointsize의 range설정
)

Note

아래 그림에서처럼 연속 vs. 카테고리 변수 여부에 따라 다르게 작동

Setting properties

Setting properties vs. mapping properties (aesthetic)

변수에 속성을 할당하는 것이 아니라, graphical objects (Marks)의 속성을 지정

Marks (Dot(), Line(), Bar(), …) 마다 설정할 수 있는 속성이 다름
주로 쓰이는 속성들: color, pointsize, alpha

Tip

다양한 Mark properties에 대해서는 홈페이지 참고
Properties of Mark objects

Note

.Dot()의 경우
class seaborn.objects.Dot(artist_kws=, marker=<‘o’>, pointsize=<6>, stroke=<0.75>, color=<‘C0’>, alpha=<1>, fill=, edgecolor=, edgealpha=, edgewidth=<0.5>, edgestyle=<‘-’>)

.Dots()의 경우
class seaborn.objects.Dots(artist_kws=, marker=<rc:scatter.marker>, pointsize=<4>, stroke=<0.75>, color=<‘C0’>, alpha=<1>, fill=, fillcolor=, fillalpha=<0.2>)

API reference 참고

(
    so.Plot(mpg, x="displ", y="hwy")
    .add(so.Dot(color="deepskyblue")) # Mark object 안에 지정!
)

(
    so.Plot(mpg, x="displ", y="hwy")
    .add(so.Dot(color="deepskyblue", pointsize=12, edgecolor="white", edgewidth=1)) # Mark object 안에 지정!
)

(
    so.Plot(mpg, x="displ", y="hwy")
    .add(so.Dot(color="orange", pointsize=12, marker=">", alpha=.4))  # Mark object 안에 지정!
)

Faceting

카테고리 변수들이 지니는 카테고리들(레벨)별로 나누어 그리기

The penguins data: penguins in the Palmer Archipelago, Antarctica.


Artwork by @allison_horst

penguins = sns.load_dataset("penguins") # load a dataset: penguins
penguins.head()
  species     island  bill_length_mm  bill_depth_mm  flipper_length_mm  \
0  Adelie  Torgersen           39.10          18.70             181.00   
1  Adelie  Torgersen           39.50          17.40             186.00   
2  Adelie  Torgersen           40.30          18.00             195.00   
3  Adelie  Torgersen             NaN            NaN                NaN   
4  Adelie  Torgersen           36.70          19.30             193.00   

   body_mass_g     sex  
0      3750.00    Male  
1      3800.00  Female  
2      3250.00  Female  
3          NaN     NaN  
4      3450.00  Female  
(
    so.Plot(penguins, x="body_mass_g", y="flipper_length_mm")
    .add(so.Dot(alpha=.5))
    .facet("sex")  # 기본적으로 columns으로 나누어져 그림, wrap: column에 몇 개까지 그릴지
)

p = (
    so.Plot(penguins, x="body_mass_g", y="flipper_length_mm")
    .facet(col="species", row="sex")
    .add(so.Dot(alpha=.5))
)
p

# x, y축의 눈금을 일치할지 여부
p.share(x=False, y=True)

Important

Facet과 Color 중 어떤 방식으로 표현하는 것이 유리한가? 밸런스를 잘 선택!

left = (
    so.Plot(penguins, x="body_mass_g", y="flipper_length_mm")
    .facet(col="species")
    .add(so.Dot(alpha=.5))
)
right = (
    so.Plot(penguins, x="body_mass_g", y="flipper_length_mm", color="species")
    .add(so.Dot(alpha=.5))
)

bottom = (
    so.Plot(penguins, x="body_mass_g", y="flipper_length_mm")
    .facet(row="species")
    .add(so.Dot(alpha=.5))
)

display(left, right, bottom)

facetting; horizontal

colors

facetting; vertical
Figure 1: Facetting by row, column, or color

플롯 파일로 저장하기

p.save("data/filename.png") # p: a plot oject

Geometric objects

  • Dot marks: Dot, Dots
  • Line marks: Line, Lines, Path, Paths, Dash, Range
  • Bar marks: Bar, Bars
  • Fill marks: Area, Band
  • Text marks: Text

API reference: Mark objects

Statistical transformations

Agg, Est, Count, Hist, KDE, Perc, PolyFit

Important

위의 통계 함수들을 이용하여 변형된 데이터 값을 geometric objects에 mapping하여 다양한 플랏을 그릴 수 있음
원칙적으로는 직접 통계치을 계산한 후에 그 데이터로 플랏을 그릴 수 있으나, 신속한 탐색적 분석을 위해 사용

Note

현재 seaborn.objects에서 다음 두 가지 중요한 statistical transformations이 제공되지 않고 있음

  • (non-parametirc) fitted line을 보여주는 loess or GAM line
  • 분포의 간략한 summary인 boxplot

이 부분에 대해서는 아래 몇 가지 대안이 있음; 아래에서 설명

Fitted Lines 구하기: Machine Learning Algorithms

Data에 fitted line를 구하는 방식에는 여러 방법이 있음

  • Linear fit: 1차 함수형태로 fit
  • Smoothing fit
    • Polynominal fit: n차 다항함수형태로 fit
    • GAM: generalized additive model
      • Spline: piece-wise polynominal regression
    • Loess/lowess: locally estimated/weighted scatterplot smoothing

현재 seaborn.objects에서는 polynomial fit만 제공

Fitted lines

(
    so.Plot(mpg, x="displ", y="hwy")
    .add(so.Dot())
    .add(so.Line(), so.PolyFit(5))  # PolyFit(n): n차 다항식으로 fit
)

(
    so.Plot(mpg, x="displ", y="hwy")
    .add(so.Line(), so.PolyFit(5))  # PolyFit(n): n차 다항식으로 fit
)

(
    so.Plot(mpg, x="displ", y="hwy", color="drv")  # color mapping이 이후 모든 layer에 적용
    .add(so.Dot())
    .add(so.Line(), so.PolyFit(5))
)

(
    so.Plot(mpg, x="displ", y="hwy")
    .add(so.Dot(), color="drv")  # color mapping이 이 layer에만 적용
    .add(so.Line(), so.PolyFit(5))
)

(a) color가 모든 layers에 적용: global mapping
(b) color가 두번째 layer에만 적용: local mapping
Figure 2: Inherited mapping
(
    so.Plot(mpg, x="displ", y="hwy")
    .add(so.Dot(), color="drv")
    .add(so.Line(), so.PolyFit(5), group="drv") # color가 아닌 group으로 grouping
)
# 다항함수 fit의 특징 및 주의점

Linear fit vs. smoothing fit:
선형적인 트렌드에서 얼마나 벗어나는가?

(
    so.Plot(mpg, x="displ", y="hwy")
    .add(so.Dot(color=".6"))
    .add(so.Line(), so.PolyFit(5))
    .add(so.Line(), so.PolyFit(1))
)

Seaborn.objects 요약

(
    so.Plot(df, x=, y=, color=, ...)  # global mapping
    .add(so.Dot(color=, pointsize=,...))  # mark object + setting properties
    .add(so.Line(), x=, y=, color=, ...)  # local mapping
    .add(so.Line(), so.Polyfit(5))  # 통계적으로 변환한 값을 Line plot으로 표현
    .add(so.Bar(), so.Hist(stat="proportion"))  # 통계적으로 변환한 값을 Bar plot로 표현
    ...
    .facet(col=, row=, wrap=) # 카테고리의 levels에 따라 나누어 표현
)
  1. Aesthetic mapping

    • 위치(position): x축, y축
    • 색(color), 크기(pointsize), 모양(marker), 선 종류(linestyle), 투명도(alpha)
    • global vs. local mapping
  2. Geometric objects

    • Dot marks: Dot, Dots
    • Line marks: Line, Path, Dash, Range
    • Bar marks: Bar, Bars
    • Fill marks: Area, Band
    • Text marks: Text
  3. Setting properties

    • Marks (.Dot(), .Line(), .Bar(), …) 내부에 속성을 지정하고, marks마다 설정할 수 있는 속성이 다름.
    • 주로 쓰이는 속성들: color, pointsize, alpha
  4. Statistical transformations

    • 변수들을 통계적 변환 후 그 값을 이용
    • Agg, Est, Count, Hist, KDE, Perc, PolyFit
  5. Faceting: 카테고리 변수들의 levels에 따라 나누어 그림

Applications

Visualizing distributions

분포를 살펴보는데 변수가 연속인지 카테고리인지에 따라 다른 방식

A categorical variable

tips = sns.load_dataset("tips")
tips.info()
<class 'pandas.DataFrame'>
RangeIndex: 244 entries, 0 to 243
Data columns (total 7 columns):
 #   Column      Non-Null Count  Dtype   
---  ------      --------------  -----   
 0   total_bill  244 non-null    float64 
 1   tip         244 non-null    float64 
 2   sex         244 non-null    category
 3   smoker      244 non-null    category
 4   day         244 non-null    category
 5   time        244 non-null    category
 6   size        244 non-null    int64   
dtypes: category(4), float64(2), int64(1)
memory usage: 7.4 KB
(
    so.Plot(tips, x="day")
    .add(so.Bar(), so.Count())  # category type의 변수는 순서가 존재. 
                                # 그렇지 않은 경우 알바벳 순서로. 
)

Note

복잡한 통계치의 경우 직접 구한후 plot을 그리는 것이 용이

count_day = tips.value_counts("day", normalize=True).reset_index(name="pct")
#     day  pct
# 0   Sat 0.36
# 1   Sun 0.31
# 2  Thur 0.25
# 3   Fri 0.08
(
    so.Plot(count_day, x="day", y="pct")
    .add(so.Bar())
)
penguins = sns.load_dataset("penguins") # load a dataset: penguins

# Species에 inherent order가 없음; 알파벳 순으로 정렬
(
    so.Plot(penguins, x="species")
    .add(so.Bar(), so.Count())
)

(
    so.Plot(penguins, x="species")
    .add(so.Bar(), so.Hist("proportion"))  # Hist()의 default는 stat="count"
)

# grouping의 처리에 대해서는 뒤에... 에를 들어, color="sex"

Important

표시 순서를 변경하는 일은 의미있는 플랏을 만드는데 중요
나중에 좀 더 자세히 다룸

# value_counts()는 크기대로 sorting!
reorder = penguins.value_counts("species").index.values
#> array(['Adelie', 'Gentoo', 'Chinstrap'], dtype=object)

(
    so.Plot(penguins, x="species")
    .add(so.Bar(), so.Count())
    .scale(x=so.Nominal(order=reorder))  # x축의 카테고리 순서를 변경
)

# 직접 개수를 구해 그리는 경우, 테이블의 순서대로 그려짐
(
    so.Plot(penguins.value_counts("species").reset_index(), 
            x="species", y="count")
    .add(so.Bar())
)

A numerical variable

(
    so.Plot(penguins, x="body_mass_g")
    .add(so.Bars(), so.Hist())  # Histogram; x값을 bins으로 나누어 count를 계산!
                                # .Bars()는 .Bar()에 비해 연속변수에 더 적합: 얇은 경계선으로 나란히 붙혀서 그려짐
)

(
    so.Plot(penguins, x="body_mass_g")
    .add(so.Bars(), so.Hist(binwidth=100))  # binwidth vs. bins
)
(
    so.Plot(penguins, x="body_mass_g")
    .add(so.Bars(), so.Hist(bins=10))  # binwidth vs. bins
)
(a) binwidth=100
(b) bins=10
Figure 3: binwidth vs. bins
(
    so.Plot(penguins, x="body_mass_g")
    .add(so.Bars(), so.Hist("proportion"))  # 비율을 계산; stat="count"가 default
)

# Density plot: 넓이가 1이 되도록
(
    so.Plot(penguins, x="body_mass_g")
    .add(so.Area(), so.KDE())  # Density plot
)

# Density plot: 넓이가 1이 되도록
(
    so.Plot(penguins, x="body_mass_g")
    .add(so.Line(color="orange"), so.KDE(bw_adjust=.2))  # Density bandwidth: binwidth에 대응
    .add(so.Bars(alpha=.3), so.Hist("density", binwidth=100))  # stat="density"
)

pandas의 method를 이용한 여러 히스토그램 그리기

pandas의 hist() method: 모든 연속 변수에 대해 histogram을 그림

penguins.hist(bins=20);  # 파라미터 figsize=(6, 5)

# 세미콜론(;) 대신 plt.show() 쓸수도 있음

seaborn 스타일로 histogram 그리기
sns.set_theme()
penguins.hist(bins=20);

sns.set_theme(): set aspects of the visual theme for all matplotlib and seaborn plots.
hist() 메서드가 matplotlib 대신 seaborn 스타일로 그려짐.

Visualizing relationships

A numerical and a categorical variable

  • Boxplot
  • Grouped distribution: histogram, frequency polygon, density plot

Boxplot

source: R for Data Science

각각에 상응하는 분포(KDE, kernel density plot)

  • 한쪽으로 쏠린 정도; 왜도(skewness)
  • 퍼져 있는 정도 정도; 표준편차
    • 정규분포로부터 벗어나는 정도: 첨도(kurtosis)

Seaborn 함수인 boxplot()을 이용하면,

sns.boxplot(penguins, x="species", y="body_mass_g", fill=False);  # fill: box의 색을 채울지 여부

Seaborn.object를 활용하면,

(
    so.Plot(penguins, x="species", y="body_mass_g")
    .add(so.Dot(pointsize=8), so.Agg("median"))  # Agg(): aggregation, default는 mean
    .add(so.Range(), so.Est(errorbar=("pi", 50)))   # Range(): 기본 min/max range, 
                                                    # Est(): estimator
)

(
    so.Plot(penguins, x="species", y="body_mass_g")
    .add(so.Dots(color=".5"), so.Jitter()) # so.Jitter(): 흐트려뜨려 그리기
    .add(so.Dot(pointsize=8), so.Agg("median"))
    .add(so.Range(), so.Est(errorbar=("pi", 50)))
)

Error bars에 대해서는 seaborn/statistical estimation and error bars

(
    so.Plot(penguins, x="species", y="body_mass_g", color="sex")
    .add(so.Dots(), so.Jitter(), so.Dodge())
    .add(so.Dot(pointsize=5), so.Agg("median"), so.Dodge())
    .add(so.Range(), so.Est(errorbar=("pi", 50)), so.Dodge())
)

# seabron boxplot()에서는 hue로 color mapping
sns.boxplot(penguins, x="species", y="body_mass_g", hue="sex", fill=False);

# facetting을 하려면 catplot()을 이용
sns.catplot(
    data=penguins, x="species", y="bill_length_mm", hue="sex", col="island",
    kind="box", fill=False, height=3.5, aspect=.7,
);

# Build a boxplot!
def boxplot(df, x, y, color=None, alpha=0.1):
    return (
        so.Plot(df, x=x, y=y, color=color)
        .add(so.Dots(alpha=alpha, color=".6"), so.Jitter(), so.Dodge())
        .add(so.Range(), so.Est(errorbar=("pi", 50)), so.Dodge())
        .add(so.Dots(pointsize=8, marker="<"), so.Agg("median"), so.Dodge())
        .scale(color="Dark2")
        .theme({**sns.axes_style("whitegrid")})
    )

(
    boxplot(penguins, x="species", y="flipper_length_mm", color="sex")
    .facet("island")
    .layout(size=(8, 4))
)

# Build a rangeplot!
def rangeplot(df, x, y, color=None):
    return (
        so.Plot(df, x=x, y=y, color=color)
        .add(so.Range(), so.Est(errorbar=("pi", 50)), so.Dodge())
        .add(so.Dots(pointsize=8, marker="<"), so.Agg("median"), so.Dodge())
        .scale(color="Dark2")
        .theme({**sns.axes_style("whitegrid")})
    )

(
    rangeplot(penguins, x="species", y="flipper_length_mm", color="sex")
    .facet("island")
    .layout(size=(8, 4))
)

Histogram

# 별로 유용하지 못함
(
    so.Plot(penguins, x="body_mass_g", color="species")
    .add(so.Bars(), so.Hist(common_bins=False))  # bins을 공유하지 않도록
)
# Hist(): 다양한 parameter가 있음.

Frequency polygon

(
    so.Plot(penguins, x="body_mass_g", color="species")
    .add(so.Line(marker="."), so.Hist(binwidth=200))  # Line에 maker "."을 표시
)

# 비율로 표시 & 각 카테고리/레벨 별로 비율 계산
(
    so.Plot(penguins, x="body_mass_g", color="species")
    .add(
        so.Line(marker="."), 
        so.Hist(binwidth=200, stat="proportion", common_norm=False)
    )  
)

Density plot

(
    so.Plot(penguins, x="body_mass_g", color="species")
    .add(so.Area(), so.KDE(common_norm=False))  # Density plot, species별로 넓이가 1이 되도록
)

Two categorical variables

p = so.Plot(penguins, x="island", color="species")
p.add(so.Bar(), so.Count()) # Bar() mark + Count() transformation

p.add(so.Bar(), so.Count(), so.Dodge())   # 나란히 표시
p.add(so.Bar(), so.Count(), so.Stack())  # stacking
(a) dodge
(b) stack
Figure 4: dodge vs. stack

Count 대신 proportion을 표시하는 경우: Hist()를 사용
Count()는 Hist(stat="count")와 동일함.

# 각 비율값의 합이 1이 되도록, 즉 모든 카테고리에 걸쳐 normalize
p.add(
    so.Bar(width=.5), 
    so.Hist("proportion"),  # proportion; stat="count"로 하면 앞서 so.Count()와 동일
    so.Stack()  # stacking
)

# x축 기준으로 normalize
p.add(
    so.Bar(width=.5), 
    so.Hist("proportion", common_norm=["x"]),  # proportion; 
    so.Stack()  # stacking
)
# warning이 뜰 수 있음!

# x축, facet의 column 기준으로 normalize
p.add(
    so.Bar(width=.8), 
    so.Hist("proportion", common_norm=["x", "col"]),  # proportion
    so.Stack(),  # stacking
).facet(col="sex")  # faceting

common_norm: True vs. False 비교

# 각 비율값의 합이 1이 되도록, 즉 모든 카테고리에 걸쳐 normalize
p.add(
    so.Bar(width=.5), so.Hist("proportion", common_norm=True),  # default
    so.Stack()
)

# 각 species별로 normalize 
p.add(
    so.Bar(width=.5), so.Hist("proportion", common_norm=False),
    so.Stack()
)

Two numerical variables

Scatterplot

(
    so.Plot(penguins, x="flipper_length_mm", y="body_mass_g")
    .add(so.Dot())  # overplotting에는 so.Dots()가 유리 
)

Three or more variables

(
    so.Plot(penguins, x="flipper_length_mm", y="body_mass_g",
            color="species", marker="island")
    .add(so.Dot())
    .layout(size=(6, 4))  # plot size 조정
)

Facet의 활용

(
    so.Plot(penguins, x="flipper_length_mm", y="body_mass_g",
            color="species")
    .add(so.Dot(alpha=.5))
    .facet("island")
    .layout(size=(8, 4))
)

Time series

healthexp = sns.load_dataset("healthexp")

(
    so.Plot(healthexp, x="Year", y="Spending_USD", color="Country")
    .add(so.Lines())
)

# 전체와 각 그룹의 상태를 동시에 파악
(
    so.Plot(healthexp, x="Year", y="Spending_USD", color="Country")
    .add(so.Area(), so.Stack()) # add stacking
)

(
    so.Plot(healthexp, x="Year", y="Life_Expectancy")
    .add(so.Line(alpha=.3), group="Country", col=None)
    .add(so.Line(linewidth=3))
    .facet("Country", wrap=3)  # wrap!!!
)

fmri = sns.load_dataset("fmri")

p = so.Plot(fmri, x="timepoint", y="signal", color="region", linestyle="event")
p.add(so.Line(), so.Agg())  # Agg()의 default 함수는 mean

p.add(so.Line(marker="o", edgecolor="w"), so.Agg(), linestyle=None)  # linestyle을 overwrite!

Overploting

대표적으로 다음과 같은 방식으로 해결할 수 있음.

  • alpha property: 투명도를 조절
  • so.Jitter() mark: 흐트려뜨려 그리기
  • so.Dots() mark: 불투명, 테두리 선명한 점들
  • .facet() facet: 다른 면에 그리기

특별히 overplotting에 특화된 독립적인 plots도 있음. 예를 들어,

Beeswarm plot: 겹치지 않게 그리기

sns.catplot(
    data=penguins, kind="swarm",
    x="species", y="body_mass_g", hue="sex", col="island",
    height=4.5, aspect=.6
);

2d histogram/hexbin plot: x, y모두 binning하여 상대적 개수를 컬러로 표시

sns.jointplot(penguins, x="bill_length_mm", y="bill_depth_mm", kind="hex", gridsize=30, height=5);  # gridsize: bin 개수

Matplotlib을 이용

plt.figure(figsize=(6, 4), dpi=100)
plt.hexbin(x=penguins["bill_depth_mm"], y=penguins["body_mass_g"], gridsize=30, cmap="Blues")

plt.colorbar()
plt.xlabel("Bill Depth (mm)")
plt.ylabel("Body Mass (g)")
plt.show()

New data

새로운 데이터 값을 이용하고자 할 때, 변수이름 대신 직접 데이터(series, array, list, …) 입력

mpg_suv = mpg.query('`class` == "suv"')

(
    so.Plot(mpg, x="displ", y="hwy")
    .add(so.Dot(), color="class")
    .add(so.Line(), so.PolyFit(5), 
         x=mpg_suv["displ"], y=mpg_suv["hwy"])
)

Tip

원칙적으로 다음과 같이 matplotlib의 스타일로 데이터를 직접 입력해도 됨.

(
    so.Plot()
    .add(so.Dot(), x=mpg["displ"], y=mpg["hwy"], color=mpg["class"])
)

Data Visualization

This work © 2025 by Sungkyun Cho is licensed under CC BY-NC-SA 4.0