Python 데이터 시각화 라이브러리 예제 모음

※ 한글 폰트 설정 안내

Matplotlib, Seaborn, WordCloud 등 일부 라이브러리는 한글을 표시하기 위해 시스템에 설치된 폰트를 지정해야 합니다. 아래 코드는 Windows의 'Malgun Gothic', macOS의 'AppleGothic'을 사용하므로, 자신의 운영체제에 맞게 주석을 해제하거나 폰트 경로를 수정하여 사용하세요. Linux의 경우 'NanumGothic' 등 한글 폰트를 설치한 후 해당 폰트 이름을 지정해야 합니다.


# 한글 폰트 설정 (Matplotlib, Seaborn)
import matplotlib.pyplot as plt

# Windows
plt.rcParams['font.family'] = 'Malgun Gothic'
# macOS
# plt.rcParams['font.family'] = 'AppleGothic' 
# Linux (Nanum 폰트 설치 후)
# plt.rcParams['font.family'] = 'NanumGothic' 

# 마이너스 부호 깨짐 방지
plt.rcParams['axes.unicode_minus'] = False
        

1. Matplotlib

파이썬의 가장 대표적인 시각화 라이브러리로, 다양한 종류의 정적 그래프를 생성할 수 있습니다. 다른 라이브러리들의 기반이 되기도 합니다.

1.1. 기본 꺾은선 그래프


import matplotlib.pyplot as plt

# 한글 폰트 설정 (상단 안내 참고)
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False

x = [2020, 2021, 2022, 2023, 2024]
y = [100, 120, 150, 130, 160]

plt.plot(x, y)
plt.title('연도별 매출')
plt.xlabel('연도')
plt.ylabel('매출 (억 원)')
plt.grid(True)
plt.show()
        

코드 설명

1.2. 기본 막대 그래프


import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False

products = ['A제품', 'B제품', 'C제품']
sales = [250, 310, 190]

plt.bar(products, sales, color='skyblue')
plt.title('제품별 판매량')
plt.ylabel('판매량 (개)')
plt.show()
        

코드 설명

1.3. 수평 막대 그래프


import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False

cities = ['서울', '부산', '인천', '대구']
population = [940, 330, 290, 230]

plt.barh(cities, population, color='lightcoral')
plt.title('주요 도시별 인구 (백만 명)')
plt.xlabel('인구 (백만 명)')
plt.show()
        

코드 설명

1.4. 산점도 (Scatter Plot)


import matplotlib.pyplot as plt
import numpy as np

plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False

np.random.seed(0)
study_time = np.random.rand(50) * 10
scores = study_time * 8 + np.random.randn(50) * 5 + 10
scores = np.clip(scores, 0, 100) # 점수를 0~100 사이로 제한

plt.scatter(study_time, scores)
plt.title('공부 시간과 시험 점수 관계')
plt.xlabel('공부 시간 (시간)')
plt.ylabel('시험 점수 (점)')
plt.show()
        

코드 설명

1.5. 원 그래프 (Pie Chart)


import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False

labels = ['삼성전자', 'LG에너지솔루션', 'SK하이닉스', '기타']
sizes = [30, 20, 15, 35]
colors = ['gold', 'yellowgreen', 'lightcoral', 'lightskyblue']
explode = (0.1, 0, 0, 0) # 첫 번째 조각을 튀어나오게 설정

plt.pie(sizes, explode=explode, labels=labels, colors=colors,
        autopct='%1.1f%%', shadow=True, startangle=140)
plt.axis('equal') # 원을 완전한 원 형태로 유지
plt.title('국내 주식 시장 점유율')
plt.show()
        

코드 설명

1.6. 히스토그램


import matplotlib.pyplot as plt
import numpy as np

plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False

# 평균 70, 표준편차 10인 정규분포를 따르는 학생 100명의 점수 데이터 생성
np.random.seed(1)
scores = np.random.normal(70, 10, 100)

plt.hist(scores, bins=10, edgecolor='black') # 10개의 구간으로 나눔
plt.title('학생들 시험 점수 분포')
plt.xlabel('점수')
plt.ylabel('학생 수')
plt.show()
        

코드 설명

1.7. 여러 개 그래프 그리기 (Subplots)


import matplotlib.pyplot as plt
import numpy as np

plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False

x = np.linspace(0, 2 * np.pi, 100)
y1 = np.sin(x)
y2 = np.cos(x)

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(6, 6)) # 2행 1열의 서브플롯 생성

fig.suptitle('삼각 함수 그래프')

ax1.plot(x, y1, color='blue')
ax1.set_ylabel('sin(x)')
ax1.grid(True)

ax2.plot(x, y2, color='red')
ax2.set_xlabel('x')
ax2.set_ylabel('cos(x)')
ax2.grid(True)

plt.tight_layout(rect=[0, 0.03, 1, 0.95]) # 제목과 겹치지 않게 레이아웃 조정
plt.show()
        

코드 설명

1.8. 박스 플롯 (Box Plot)


import matplotlib.pyplot as plt
import numpy as np

plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False

np.random.seed(10)
data1 = np.random.normal(100, 10, 200)
data2 = np.random.normal(90, 20, 200)
data3 = np.random.normal(80, 30, 200)
data = [data1, data2, data3]

plt.boxplot(data, labels=['A그룹', 'B그룹', 'C그룹'])
plt.title('그룹별 데이터 분포 (박스 플롯)')
plt.ylabel('값')
plt.show()
        

코드 설명

1.9. 꺾은선 그래프 스타일 변경


import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False

x = [1, 2, 3, 4, 5]
y = [2, 3, 5, 7, 11]

plt.plot(x, y, color='green', linestyle='--', marker='o', 
         linewidth=2, markersize=8, label='프라임 넘버')

plt.title('스타일이 적용된 꺾은선 그래프')
plt.xlabel('X축')
plt.ylabel('Y축')
plt.legend() # 범례 표시
plt.show()
        

코드 설명

1.10. 그래프에 텍스트/화살표 추가하기


import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False

x = [2020, 2021, 2022, 2023, 2024]
y = [100, 120, 150, 90, 160]

plt.plot(x, y, marker='o')
plt.title('연도별 실적')
plt.xlabel('연도')
plt.ylabel('실적')

# 텍스트 및 화살표 추가
plt.annotate('급락 지점', xy=(2023, 90), xytext=(2022, 110),
             arrowprops=dict(facecolor='black', shrink=0.05))

plt.text(2020.5, 150, '꾸준한 성장세', fontsize=12, color='blue')
plt.show()
        

코드 설명

2. Seaborn

Matplotlib을 기반으로 더 아름답고 통계적인 그래프를 쉽게 그릴 수 있게 해주는 라이브러리입니다.

2.1. 기본 막대 그래프 (Count Plot)


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

plt.rcParams['font.family'] = 'Malgun Gothic'

data = {'요일': ['월', '화', '수', '목', '금', '토', '일', '월', '화', '수', '수']}
df = pd.DataFrame(data)

sns.countplot(x='요일', data=df, palette='viridis', order=['월','화','수','목','금','토','일'])
plt.title('요일별 방문 횟수')
plt.show()
        

코드 설명

2.2. 히트맵 (Heatmap)


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

# 샘플 데이터 생성
np.random.seed(0)
data = np.random.rand(10, 12)
months = [f'{i}월' for i in range(1, 13)]
years = [f'20{i:02d}년' for i in range(15, 25)]
df = pd.DataFrame(data, index=years, columns=months)

plt.figure(figsize=(12, 8)) # 그래프 크기 조정
sns.heatmap(df, annot=True, fmt='.1f', cmap='YlGnBu')
plt.title('월별 데이터 히트맵')
plt.show()
        

코드 설명

2.3. 상관관계 히트맵


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

plt.rcParams['font.family'] = 'Malgun Gothic'

# 샘플 데이터프레임 생성
np.random.seed(1)
data = {
    '수학 점수': np.random.randint(50, 101, 100),
    '영어 점수': np.random.randint(40, 101, 100),
    '과학 점수': np.random.randint(60, 101, 100),
    '공부 시간': np.random.uniform(1, 10, 100)
}
df = pd.DataFrame(data)
df['과학 점수'] = (df['수학 점수'] * 0.5 + df['공부 시간'] * 2 + np.random.randn(100) * 5).clip(0, 100)

corr = df.corr() # 데이터프레임의 상관계수 계산

plt.figure(figsize=(8, 6))
sns.heatmap(corr, annot=True, cmap='coolwarm', fmt='.2f')
plt.title('과목 점수 및 공부 시간 상관관계')
plt.show()
        

코드 설명

2.4. 분포도 (Distribution Plot)


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

plt.rcParams['font.family'] = 'Malgun Gothic'
np.random.seed(42)
x = np.random.randn(100) * 15 + 75 # 평균 75, 표준편차 15인 데이터

sns.displot(x, kde=True, rug=True, bins=20)
plt.title('데이터 분포도 (displot)')
plt.xlabel('값')
plt.show()
        

코드 설명

2.5. 박스 플롯 (Box Plot)


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

plt.rcParams['font.family'] = 'Malgun Gothic'

# Seaborn에 내장된 'tips' 데이터셋 사용
tips = sns.load_dataset("tips")

plt.figure(figsize=(10, 6))
sns.boxplot(x="day", y="total_bill", hue="smoker", data=tips, palette="Set3")
plt.title('요일 및 흡연 여부별 식사 금액 (박스 플롯)')
plt.xlabel('요일')
plt.ylabel('식사 금액 ($)')
plt.show()
        

코드 설명

2.6. 바이올린 플롯 (Violin Plot)


import seaborn as sns
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'Malgun Gothic'
tips = sns.load_dataset("tips")

sns.violinplot(x="day", y="total_bill", data=tips, palette="muted")
plt.title('요일별 식사 금액 분포 (바이올린 플롯)')
plt.xlabel('요일')
plt.ylabel('식사 금액 ($)')
plt.show()
        

코드 설명

2.7. 관계도 (Relational Plot - scatter)


import seaborn as sns
import matplotlib.pyplot as plt

tips = sns.load_dataset("tips")

sns.relplot(x="total_bill", y="tip", hue="smoker", style="time", size="size",
            data=tips, sizes=(15, 200))
plt.title('식사 금액과 팁의 관계')
plt.xlabel('총 식사 금액 ($)')
plt.ylabel('팁 ($)')
plt.show()
        

코드 설명

2.8. 페어 플롯 (Pair Plot)


import seaborn as sns
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'Malgun Gothic'
iris = sns.load_dataset("iris") # 붓꽃 데이터셋

sns.pairplot(iris, hue="species", markers=["o", "s", "D"])
plt.suptitle('붓꽃 데이터의 페어 플롯', y=1.02)
plt.show()
        

코드 설명

2.9. 조인트 플롯 (Joint Plot)


import seaborn as sns
import matplotlib.pyplot as plt

tips = sns.load_dataset("tips")

# 산점도와 각 축의 히스토그램을 함께 표시
sns.jointplot(x="total_bill", y="tip", data=tips, kind="scatter") 
plt.suptitle('식사 금액과 팁의 조인트 플롯', y=1.02)
plt.show()

# 육각(hex) 타일로 밀도를 표현
sns.jointplot(x="total_bill", y="tip", data=tips, kind="hex")
plt.suptitle('식사 금액과 팁의 조인트 플롯 (Hex)', y=1.02)
plt.show()
        

코드 설명

2.10. 회귀선이 있는 산점도 (Regression Plot)


import seaborn as sns
import matplotlib.pyplot as plt

tips = sns.load_dataset("tips")

plt.figure(figsize=(8, 6))
sns.regplot(x="total_bill", y="tip", data=tips, ci=95) # ci: 신뢰구간 95%
plt.title('식사 금액과 팁의 관계 (회귀선 포함)')
plt.xlabel('총 식사 금액 ($)')
plt.ylabel('팁 ($)')
plt.show()
        

코드 설명

3. Pandas

데이터 분석을 위한 핵심 라이브러리지만, Matplotlib을 기반으로 한 간단한 시각화 기능도 내장하고 있어 빠르고 편리하게 그래프를 확인할 수 있습니다.

3.1. 기본 꺾은선 그래프


import pandas as pd
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'Malgun Gothic'

s = pd.Series([1, 3, 5, 6, 8, 9], index=pd.to_datetime(['2024-01-01', '2024-02-01', '2024-03-01', '2024-04-01', '2024-05-01', '2024-06-01']))
s.plot(title='월별 데이터', grid=True)
plt.show()
        

코드 설명

3.2. 데이터프레임 꺾은선 그래프


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

plt.rcParams['font.family'] = 'Malgun Gothic'

df = pd.DataFrame(np.random.randn(10, 4).cumsum(axis=0),
                  columns=['A', 'B', 'C', 'D'],
                  index=np.arange(0, 100, 10))
df.plot(figsize=(10,6), title='여러 데이터 계열의 꺾은선 그래프')
plt.ylabel('값')
plt.xlabel('시간')
plt.show()
        

코드 설명

3.3. 기본 막대 그래프


import pandas as pd
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'Malgun Gothic'

df = pd.DataFrame({'제품': ['A', 'B', 'C', 'D'],
                   '판매량': [34, 55, 29, 41]})
df.plot(kind='bar', x='제품', y='판매량', legend=False, rot=0)
plt.title('제품별 판매량')
plt.ylabel('판매량')
plt.show()
        

코드 설명

3.4. 누적 막대 그래프


import pandas as pd
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'Malgun Gothic'

df = pd.DataFrame({
    '1분기': [100, 150, 200],
    '2분기': [120, 180, 210],
    '3분기': [150, 200, 230],
    '4분기': [130, 190, 220]
}, index=['A팀', 'B팀', 'C팀'])

df.plot(kind='bar', stacked=True, figsize=(10, 7))
plt.title('팀별 분기 실적 (누적 막대)')
plt.ylabel('실적')
plt.xticks(rotation=0)
plt.show()
        

코드 설명

3.5. 수평 막대 그래프


import pandas as pd
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'Malgun Gothic'

df = pd.DataFrame({
    '도시': ['서울', '부산', '인천', '대구'],
    '면적': [605, 770, 1063, 883]
})
df.plot(kind='barh', x='도시', y='면적', color='tomato', legend=False)
plt.title('주요 도시 면적 (km^{2})')
plt.xlabel('면적 (km^{2})')
plt.show()
        

코드 설명

3.6. 히스토그램


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

plt.rcParams['font.family'] = 'Malgun Gothic'

data = pd.Series(np.random.normal(loc=170, scale=10, size=500))
data.plot(kind='hist', bins=30, title='키 분포 히스토그램')
plt.xlabel('키 (cm)')
plt.show()
        

코드 설명

3.7. 여러 열의 히스토그램


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

plt.rcParams['font.family'] = 'Malgun Gothic'

df = pd.DataFrame({
    'A반': np.random.normal(75, 10, 100),
    'B반': np.random.normal(80, 5, 100)
})

df.plot(kind='hist', bins=20, alpha=0.5, title='A반, B반 점수 분포')
plt.xlabel('점수')
plt.show()
        

코드 설명

3.8. 박스 플롯


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

plt.rcParams['font.family'] = 'Malgun Gothic'

df = pd.DataFrame(np.random.rand(10, 5), columns=['A', 'B', 'C', 'D', 'E'])
df.plot(kind='box', title='데이터 그룹별 박스 플롯')
plt.ylabel('값')
plt.show()
        

코드 설명

3.9. 면적 그래프 (Area Plot)


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

plt.rcParams['font.family'] = 'Malgun Gothic'

df = pd.DataFrame(np.random.rand(10, 4), columns=['A', 'B', 'C', 'D'])
df.plot(kind='area', stacked=False, title='누적되지 않은 면적 그래프')
plt.ylabel('값')
plt.show()
        

코드 설명

3.10. 산점도 (Scatter Plot)


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

plt.rcParams['font.family'] = 'Malgun Gothic'

df = pd.DataFrame(np.random.rand(50, 4), columns=['a', 'b', 'c', 'd'])
df['d'] = df['d'] * 150 # 마커 크기 조절을 위해 d값 확대

df.plot(kind='scatter', x='a', y='b', c='c', s='d', colormap='viridis',
        title='4개 변수를 표현하는 산점도')
plt.show()
        

코드 설명

4. Plotly

인터랙티브(Interactive)한 웹 기반 시각화를 만드는 데 특화된 라이브러리입니다. 마우스를 올리면 정보가 표시되고, 확대/축소/이동이 가능합니다.

Plotly 코드를 실행하면 웹 브라우저가 자동으로 열리면서 그래프가 표시되거나, 주피터 노트북/코랩 환경에서는 셀 내부에 그래프가 그려집니다. 일반 파이썬 스크립트에서는 자동으로 브라우저가 열립니다.

4.1. 기본 꺾은선 그래프


import plotly.express as px
import pandas as pd

df = pd.DataFrame(dict(
    연도=[2020, 2021, 2022, 2023, 2024],
    매출=[100, 130, 110, 160, 180]
))

fig = px.line(df, x="연도", y="매출", title="연도별 매출")
fig.show()
        

코드 설명

4.2. 기본 막대 그래프


import plotly.express as px
import pandas as pd

df = pd.DataFrame({
    "과일": ["사과", "바나나", "오렌지", "사과", "바나나", "사과"],
    "판매량": [10, 15, 7, 12, 10, 18],
    "도시": ["서울", "부산", "서울", "부산", "서울", "부산"]
})

fig = px.bar(df, x="과일", y="판매량", color="도시", barmode="group",
             title="도시별/과일별 판매량")
fig.show()
        

코드 설명

4.3. 산점도 (Scatter Plot)


import plotly.express as px

# Plotly에 내장된 iris 데이터셋 사용
df = px.data.iris()

fig = px.scatter(df, x="sepal_width", y="sepal_length", 
                 color="species", size='petal_length', 
                 hover_data=['petal_width'],
                 title="붓꽃 종류에 따른 꽃받침 너비와 길이")
fig.show()
        

코드 설명

4.4. 원 그래프 (Pie Chart)


import plotly.express as px

df = px.data.tips() # tips 데이터셋 사용
fig = px.pie(df, values='tip', names='day', title='요일별 팁 분포',
             hover_data=['total_bill'], labels={'day':'요일', 'tip':'팁'})
fig.update_traces(textposition='inside', textinfo='percent+label')
fig.show()
        

코드 설명

4.5. 박스 플롯


import plotly.express as px

df = px.data.tips()
fig = px.box(df, x="smoker", y="total_bill", color="sex",
             title="흡연 및 성별에 따른 식사 금액",
             labels={"smoker":"흡연여부", "total_bill":"총 금액", "sex":"성별"})
fig.show()
        

코드 설명

4.6. 히스토그램


import plotly.express as px
import numpy as np
import pandas as pd

np.random.seed(0)
df = pd.DataFrame({'점수': np.random.randint(0, 101, size=200)})

fig = px.histogram(df, x="점수", nbins=20, title="시험 점수 분포",
                   marginal="rug") # 위쪽에 rug plot 추가
fig.show()
        

코드 설명

4.7. 3D 산점도


import plotly.express as px

df = px.data.iris()
fig = px.scatter_3d(df, x='sepal_length', y='sepal_width', z='petal_width',
                    color='species', title="붓꽃 데이터 3D 산점도")
fig.show()
        

코드 설명

4.8. 지도 위에 산점도 표시 (Scatter on Map)


import plotly.express as px

# 미국 도시 인구 데이터
df = px.data.us_cities()

fig = px.scatter_geo(df, lat="lat", lon="lon", hover_name="city", 
                     size="population",
                     scope='usa', # 지도의 범위를 미국으로 한정
                     title='미국 주요 도시 인구')
fig.show()
        

코드 설명

4.9. 단계 구분도 (Choropleth Map)


import plotly.express as px
import pandas as pd

# Gapminder 데이터셋 사용
df = px.data.gapminder().query("year==2007")

fig = px.choropleth(df, locations="iso_alpha",
                    color="lifeExp", # 색상은 기대수명
                    hover_name="country", # 마우스 올리면 국가명 표시
                    color_continuous_scale=px.colors.sequential.Plasma,
                    title="2007년 국가별 기대 수명")
fig.show()
        

코드 설명

4.10. 타임라인 (Gantt Chart)


import plotly.express as px
import pandas as pd

df = pd.DataFrame([
    dict(Task="프로젝트 기획", Start='2024-01-01', Finish='2024-01-15', Resource="기획팀"),
    dict(Task="디자인 작업", Start='2024-01-10', Finish='2024-02-10', Resource="디자인팀"),
    dict(Task="개발 착수", Start='2024-02-01', Finish='2024-04-30', Resource="개발팀"),
    dict(Task="테스트 및 배포", Start='2024-05-01', Finish='2024-05-31', Resource="QA팀")
])

fig = px.timeline(df, x_start="Start", x_end="Finish", y="Task", color="Resource", title="프로젝트 간트 차트")
fig.update_yaxes(autorange="reversed") # Task를 위에서부터 순서대로
fig.show()
        

코드 설명

5. Bokeh

Plotly와 유사하게 웹 브라우저를 위한 인터랙티브 시각화를 만드는 라이브러리입니다. 대용량 데이터셋을 다루거나 스트리밍 데이터를 시각화하는 데 강점이 있습니다.

Bokeh 코드를 실행하면 output_file("filename.html")로 지정된 HTML 파일이 생성되고, show(p) 함수가 이 파일을 웹 브라우저에서 자동으로 열어줍니다.

5.1. 기본 꺾은선 그래프


from bokeh.plotting import figure, show, output_file

# 출력할 HTML 파일 이름 지정
output_file("bokeh_line.html")

p = figure(width=800, height=400, title="월별 방문자 수")

x = [1, 2, 3, 4, 5, 6]
y = [6, 7, 2, 4, 5, 9]

p.line(x, y, legend_label="방문자", line_width=2)
p.xaxis.axis_label = "월"
p.yaxis.axis_label = "방문자 (천 명)"

show(p)
        

코드 설명

5.2. 여러 선 그리기


from bokeh.plotting import figure, show, output_file

output_file("bokeh_multi_line.html")

p = figure(width=800, height=400, title="분기별 제품 판매량")

x = [1, 2, 3, 4]
y1 = [200, 250, 300, 320]
y2 = [150, 180, 220, 260]

p.line(x, y1, legend_label="A제품", line_color="blue", line_width=2)
p.line(x, y2, legend_label="B제품", line_color="red", line_dash="dashed", line_width=2)

p.legend.location = "top_left"
p.xaxis.axis_label = "분기"
p.yaxis.axis_label = "판매량"

show(p)
        

코드 설명

5.3. 산점도 (Scatter Plot)


from bokeh.plotting import figure, show, output_file
import numpy as np

output_file("bokeh_scatter.html")

N = 100
x = np.random.random(size=N) * 10
y = np.random.random(size=N) * 10
radii = np.random.random(size=N) * 1.5
colors = ["#%02x%02x%02x" % (int(r), int(g), 150) for r, g in zip(50+2*x, 30+2*y)]

p = figure(width=800, height=400, title="랜덤 데이터 산점도")
p.scatter(x, y, radius=radii, fill_color=colors, fill_alpha=0.6, line_color=None)

show(p)
        

코드 설명

5.4. 막대 그래프


from bokeh.plotting import figure, show, output_file

output_file("bokeh_bar.html")

fruits = ['사과', '배', '오렌지', '포도', '수박']
counts = [5, 3, 4, 2, 6]

p = figure(x_range=fruits, height=350, title="과일 판매 개수",
           toolbar_location=None, tools="")

p.vbar(x=fruits, top=counts, width=0.8)

p.xgrid.grid_line_color = None
p.y_range.start = 0

show(p)
        

코드 설명

5.5. 인터랙티브 툴팁 (HoverTool)


from bokeh.plotting import figure, show, output_file
from bokeh.models import ColumnDataSource, HoverTool

output_file("bokeh_hover.html")

source = ColumnDataSource(data=dict(
    x=[1, 2, 3, 4, 5],
    y=[2, 5, 8, 2, 7],
    desc=['A', 'B', 'C', 'D', 'E'],
))

# 툴팁에 표시될 내용 정의: (라벨, @변수명)
hover = HoverTool(tooltips=[
    ("인덱스", "$index"),
    ("(x,y)", "($x, $y)"),
    ("설명", "@desc"),
])

p = figure(width=800, height=400, tools=[hover], title="마우스를 올려 정보를 확인하세요")

p.circle('x', 'y', size=20, source=source)

show(p)
        

코드 설명

5.6. 패치(Patch)로 면적 채우기


from bokeh.plotting import figure, show, output_file

output_file("bokeh_patch.html")

p = figure(width=800, height=400)

# 첫 번째 다각형
p.patch([1, 2, 3, 4], [2, 3, 5, 4], alpha=0.5, line_width=2, color="navy")

# 두 번째 다각형
p.patch([3, 4, 5, 6], [4, 7, 5, 3], alpha=0.5, line_width=2, color="firebrick")

show(p)
        

코드 설명

5.7. 대용량 데이터 시각화 (Datashader 연동)


# 이 예제는 datashader 라이브러리가 추가로 필요합니다. (pip install datashader)
# 여기서는 개념을 설명하는 코드를 보여줍니다. 실제 실행을 위해서는 설치가 필요합니다.
from bokeh.plotting import figure, show, output_file
from bokeh.models import ColumnDataSource
from datashader.bokeh_ext import InteractiveImage
import pandas as pd
import numpy as np

output_file("bokeh_datashader.html")

N = 1_000_000 # 100만개 데이터 포인트
df = pd.DataFrame(dict(x=np.random.randn(N), y=np.random.randn(N)))
source = ColumnDataSource(df)

p = figure(tools='pan,wheel_zoom,reset', x_range=(-4, 4), y_range=(-4, 4))
def image_callback(x_range, y_range, w, h):
    # 이 부분에서 datashader가 현재 보이는 영역의 데이터를 이미지로 렌더링
    from datashader import Canvas, transfer_functions as tf
    cvs = Canvas(plot_width=w, plot_height=h, x_range=x_range, y_range=y_range)
    agg = cvs.points(source, 'x', 'y')
    img = tf.shade(agg)
    return img

InteractiveImage(p, image_callback)

show(p)
        

코드 설명

5.8. 연결된 그래프 (Linked Plots)


from bokeh.plotting import figure, show, output_file
from bokeh.layouts import gridplot
import numpy as np

output_file("bokeh_linked.html")

N = 300
x = np.linspace(0, 4*np.pi, N)
y0 = np.sin(x)
y1 = np.cos(x)

# 툴과 선택 동작을 공유할 소스 객체 생성
source = dict(x=x, y0=y0, y1=y1)

# 첫 번째 플롯
s1 = figure(width=500, height=250, tools="pan,wheel_zoom,box_select,reset")
s1.circle('x', 'y0', source=source)

# 두 번째 플롯
# x_range와 y_range를 s1과 공유하여 확대/이동이 함께 동작
s2 = figure(width=500, height=250, x_range=s1.x_range, y_range=s1.y_range,
            tools="pan,wheel_zoom,box_select,reset")
s2.circle('x', 'y1', source=source, color="red")

layout = gridplot([[s1], [s2]])
show(layout)
        

코드 설명

5.9. 카테고리별 색상/마커 매핑


from bokeh.plotting import figure, show, output_file
from bokeh.transform import factor_cmap, factor_mark
from bokeh.sampledata.iris import flowers

output_file("bokeh_mappers.html")

SPECIES = ['setosa', 'versicolor', 'virginica']
MARKERS = ['hex', 'circle_x', 'triangle']

p = figure(width=800, height=400, title="붓꽃 데이터 (카테고리별 마커/색상)")

p.scatter("petal_length", "sepal_width", source=flowers, fill_alpha=0.4, size=12,
          legend_field="species",
          marker=factor_mark('species', MARKERS, SPECIES),
          color=factor_cmap('species', 'Category10_3', SPECIES))

p.xaxis.axis_label = "꽃잎 길이"
p.yaxis.axis_label = "꽃받침 너비"
show(p)
        

코드 설명

5.10. 수평 막대 그래프


from bokeh.plotting import figure, show, output_file
from bokeh.models import ColumnDataSource

output_file("bokeh_hbar.html")

data = {'나라': ['미국', '중국', '일본', '독일', '한국'],
        'GDP': [25.5, 18.0, 4.2, 4.0, 1.7]}
source = ColumnDataSource(data=data)

p = figure(y_range=data['나라'], height=400, width=700, title="주요 국가 GDP (조 달러)",
           tools="", toolbar_location=None)

p.hbar(y='나라', right='GDP', source=source, height=0.8)

p.x_range.start = 0
p.xaxis.axis_label = "GDP (조 달러)"
p.xgrid.grid_line_color = None

show(p)
        

코드 설명

6. Altair

선언적인(declarative) 문법을 사용하는 통계 시각화 라이브러리입니다. "어떻게" 그릴지가 아닌 "무엇을" 그릴지에 집중하여, 적은 코드로 다양한 그래프를 만들 수 있습니다. Vega-Lite라는 문법을 기반으로 합니다.

Altair는 주피터 노트북/코랩 환경에서 사용하기에 가장 적합합니다. 일반 파이썬 스크립트에서 실행하려면 chart.save('filename.html')을 사용해 HTML 파일로 저장해야 합니다.

6.1. 기본 산점도


import altair as alt
import pandas as pd

data = pd.DataFrame({'x': range(10),
                     'y': [2, 7, 4, 1, 9, 5, 3, 6, 8, 0]})

chart = alt.Chart(data).mark_point().encode(
    x='x',
    y='y'
).properties(
    title='기본 산점도'
)

# chart.show() # 주피터 환경이 아닐 경우 브라우저가 열릴 수 있음
chart.save('altair_scatter.html') # HTML 파일로 저장
        

코드 설명

6.2. 기본 막대 그래프


import altair as alt
import pandas as pd

data = pd.DataFrame({'category': ['A', 'B', 'C', 'D'],
                     'value': [28, 55, 43, 91]})

chart = alt.Chart(data).mark_bar().encode(
    x='category:N',  # :N은 Nominal(명목형) 데이터임을 명시
    y='value:Q'      # :Q는 Quantitative(양적) 데이터임을 명시
).properties(
    title='카테고리별 값'
)
chart.save('altair_bar.html')
        

코드 설명

6.3. 색상, 모양, 크기 인코딩


import altair as alt
from vega_datasets import data

# Altair/Vega에서 제공하는 예제 데이터셋 로드
source = data.cars()

chart = alt.Chart(source).mark_circle(size=60).encode(
    x='Horsepower:Q',
    y='Miles_per_Gallon:Q',
    color='Origin:N',
    shape='Cylinders:N'
).properties(
    title='자동차 마력과 연비의 관계'
)
chart.save('altair_encoding.html')
        

코드 설명

6.4. 꺾은선 그래프


import altair as alt
from vega_datasets import data

source = data.stocks()

chart = alt.Chart(source).mark_line().encode(
    x='date:T', # :T는 Temporal(시간) 데이터임을 명시
    y='price:Q',
    color='symbol:N'
).properties(
    title='주식 가격 변동'
)
chart.save('altair_line.html')
        

코드 설명

6.5. 히스토그램


import altair as alt
from vega_datasets import data

source = data.movies.url

chart = alt.Chart(source).mark_bar().encode(
    alt.X('IMDB_Rating:Q', bin=alt.Bin(maxbins=30), title='IMDB 평점'),
    alt.Y('count()', title='영화 수')
).properties(
    title='영화 IMDB 평점 분포'
)
chart.save('altair_hist.html')
        

코드 설명

6.6. 인터랙티브 툴팁


import altair as alt
from vega_datasets import data

source = data.cars()

chart = alt.Chart(source).mark_circle(size=60).encode(
    x='Horsepower:Q',
    y='Miles_per_Gallon:Q',
    color='Origin:N',
    tooltip=['Name', 'Origin', 'Horsepower', 'Miles_per_Gallon']
).properties(
    title='마우스를 올려 차량 정보를 확인하세요'
)
chart.save('altair_tooltip.html')
        

코드 설명

6.7. 인터랙티브 선택 (Selection)


import altair as alt
from vega_datasets import data

source = data.cars()

# 인터랙션을 위한 selection 객체 생성
interval = alt.selection_interval()

base = alt.Chart(source).mark_point().encode(
    y='Miles_per_Gallon',
    color=alt.condition(interval, 'Origin', alt.value('lightgray'))
).properties(
    selection=interval
)

chart = base.encode(x='Acceleration') | base.encode(x='Horsepower')

chart.save('altair_selection.html')
        

코드 설명

6.8. 결합된 그래프 (Concatenation)


import altair as alt
from vega_datasets import data

source = data.iris()

base = alt.Chart(source)

scatter_plot = base.mark_point().encode(
    x='petalLength:Q',
    y='petalWidth:Q',
    color='species:N'
)

hist_plot = base.mark_bar().encode(
    x='count()',
    y='species:N',
    color='species:N'
)

# & 연산자로 두 차트를 수직으로 결합
chart = scatter_plot & hist_plot
chart.save('altair_concat.html')
        

코드 설명

6.9. 회귀선 추가하기


import altair as alt
from vega_datasets import data

source = data.iris()

scatter_plot = alt.Chart(source).mark_circle().encode(
    x='petalLength:Q',
    y='petalWidth:Q'
)

# transform_regression을 이용해 회귀선 추가
regression_line = scatter_plot.transform_regression(
    'petalLength', 'petalWidth'
).mark_line(color='red')

chart = (scatter_plot + regression_line).properties(
    title='붓꽃 꽃잎 길이와 너비의 관계 (회귀선 포함)'
)
chart.save('altair_regression.html')
        

코드 설명

6.10. 지역별 단계 구분도


import altair as alt
from vega_datasets import data

# 미국 주별 실업률 데이터
unemployment = data.unemployment.url 
# 미국 주 경계 데이터 (topojson)
states = alt.topo_feature(data.us_10m.url, 'states')

chart = alt.Chart(states).mark_geoshape().encode(
    color='rate:Q'
).transform_lookup(
    lookup='id',
    from_=alt.LookupData(unemployment, 'id', ['rate'])
).project(
    type='albersUsa'
).properties(
    width=700,
    height=400,
    title='미국 주별 실업률'
)
chart.save('altair_choropleth.html')
        

코드 설명

7. Folium

Leaflet.js 라이브러리를 기반으로, 파이썬에서 인터랙티브한 지도를 쉽게 만들 수 있도록 도와줍니다. 지도 위에 마커, 원, 다각형 등을 추가하고 데이터를 시각화하는 데 특화되어 있습니다.

Folium 코드를 실행하면 map.save('filename.html') 메소드로 지정된 HTML 파일이 생성됩니다. 이 파일을 웹 브라우저로 열어야 지도를 확인할 수 있습니다.

7.1. 기본 지도 생성


import folium

# 위도(latitude), 경도(longitude)를 지정하여 지도 생성
# 서울 시청 위치
m = folium.Map(location=[37.5665, 126.9780], zoom_start=13)

m.save('folium_basic_map.html')
        

코드 설명

7.2. 지도에 마커 추가하기


import folium

m = folium.Map(location=[37.56, 126.97], zoom_start=12)

# 기본 마커
folium.Marker(
    [37.5665, 126.9780],
    popup='서울 시청',
    tooltip='클릭해보세요!'
).add_to(m)

# 아이콘을 사용한 마커
folium.Marker(
    [37.5512, 126.9882],
    popup='남산서울타워',
    icon=folium.Icon(color='red', icon='info-sign')
).add_to(m)

m.save('folium_markers.html')
        

코드 설명

7.3. 지도 타일 변경하기


import folium

m = folium.Map(location=[37.5665, 126.9780], zoom_start=13)

# 기본 타일 (OpenStreetMap)
folium.TileLayer('OpenStreetMap').add_to(m)

# 흑백 스타일 타일
folium.TileLayer('Stamen Toner').add_to(m)

# 수채화 스타일 타일
folium.TileLayer('Stamen Watercolor').add_to(m)

# 위성사진 타일
folium.TileLayer(
    tiles='https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
    attr='Esri',
    name='Esri Satellite',
    overlay=False,
    control=True
).add_to(m)

# 레이어 컨트롤 추가 (우측 상단에서 타일 변경 가능)
folium.LayerControl().add_to(m)

m.save('folium_tiles.html')
        

코드 설명

7.4. 원(Circle) 마커 추가


import folium

m = folium.Map(location=[37.52, 126.98], zoom_start=12)

# Circle: 고정된 픽셀 크기의 원
folium.Circle(
    location=[37.5512, 126.9882],
    radius=100,
    color='crimson',
    fill=False,
    popup='Circle'
).add_to(m)

# CircleMarker: 확대/축소해도 크기가 변하지 않는 원
folium.CircleMarker(
    location=[37.5124, 127.0591],
    radius=50,
    color='#3186cc',
    fill=True,
    fill_color='#3186cc',
    popup='CircleMarker'
).add_to(m)

m.save('folium_circles.html')
        

코드 설명

7.5. 클릭 시 위도/경도 표시


import folium

m = folium.Map(location=[37.5665, 126.9780], zoom_start=11)

# 지도를 클릭하면 위도/경도를 보여주는 팝업 추가
m.add_child(folium.LatLngPopup())

# 지도를 클릭하면 해당 위치에 마커를 추가
m.add_child(folium.ClickForMarker(popup="새 마커"))

m.save('folium_click_event.html')
        

코드 설명

7.6. GeoJSON 데이터 표시


import folium
import requests # GeoJSON 데이터를 웹에서 가져오기 위해

m = folium.Map(location=[36.5, 127.5], zoom_start=7)

# 대한민국 시도 경계 GeoJSON 데이터 URL
url = 'https://raw.githubusercontent.com/southkorea/southkorea-maps/master/kostat/2018/json/skorea-provinces-2018-geo.json'
geo_data = requests.get(url).json()

# GeoJSON 데이터 지도에 추가
folium.GeoJson(
    geo_data,
    name='대한민국 시도'
).add_to(m)

folium.LayerControl().add_to(m)
m.save('folium_geojson.html')
        

코드 설명

7.7. 단계 구분도 (Choropleth)


import folium
import pandas as pd
import requests

# 1. 지도 객체 생성
m = folium.Map(location=[36, 127.5], zoom_start=7, tiles='CartoDB positron')

# 2. 시도 경계 GeoJSON 데이터
url = 'https://raw.githubusercontent.com/southkorea/southkorea-maps/master/kostat/2018/json/skorea-provinces-2018-geo.json'
geo_data = requests.get(url).json()

# 3. 시각화할 데이터 (예: 2023년 시도별 인구 데이터)
data = {
    'code': ['11', '26', '27', '28', '29', '30', '31', '36', '41', '42', '43', '44', '45', '46', '47', '48', '50'],
    'province': ['서울특별시', '부산광역시', '대구광역시', '인천광역시', '광주광역시', '대전광역시', '울산광역시', '세종특별자치시', '경기도', '강원도', '충청북도', '충청남도', '전라북도', '전라남도', '경상북도', '경상남도', '제주특별자치도'],
    'population': [9407540, 3300488, 2374960, 2997410, 1424619, 1444605, 1103661, 386525, 13630821, 1530052, 1593459, 2130188, 1754757, 1804423, 2554324, 3251769, 675883]
}
df = pd.DataFrame(data)
df['code'] = df['code'].astype(str) # GeoJSON의 key와 타입 맞추기

# 4. Choropleth 레이어 추가
folium.Choropleth(
    geo_data=geo_data,
    data=df,
    columns=['code', 'population'],
    key_on='feature.properties.code', # GeoJSON 파일의 키
    fill_color='YlGn',
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name='인구 수'
).add_to(m)

m.save('folium_choropleth.html')
        

코드 설명

7.8. 히트맵


import folium
from folium.plugins import HeatMap
import numpy as np

m = folium.Map(location=[37.55, 126.98], zoom_start=12)

# 100개의 랜덤 좌표 데이터 생성
n = 100
data = np.random.randn(n, 2) * 0.05 + np.array([37.55, 126.98])

# 히트맵 플러그인 사용
HeatMap(data).add_to(m)

m.save('folium_heatmap.html')
        

코드 설명

7.9. 마커 클러스터링


import folium
from folium.plugins import MarkerCluster
import numpy as np

m = folium.Map(location=[37.55, 126.98], zoom_start=11)

# 500개의 랜덤 좌표 생성
n = 500
locations = np.random.randn(n, 2) * np.array([0.2, 0.4]) + np.array([37.55, 126.98])

# 마커 클러스터 객체 생성
marker_cluster = MarkerCluster().add_to(m)

# 클러스터에 마커 추가
for lat, lon in locations:
    folium.Marker(location=[lat, lon], icon=None).add_to(marker_cluster)

m.save('folium_marker_cluster.html')
        

코드 설명

7.10. 미니맵(Minimap) 추가


import folium
from folium.plugins import MiniMap

m = folium.Map(location=[48.8566, 2.3522], zoom_start=10) # 프랑스 파리

# 미니맵 플러그인 추가
minimap = MiniMap(toggle_display=True, position='bottomright')
m.add_child(minimap)

folium.Marker([48.8584, 2.2945], popup="에펠탑").add_to(m)

m.save('folium_minimap.html')
        

코드 설명

8. GeoPandas

Pandas를 확장하여 지리/공간 데이터를 다룰 수 있게 만든 라이브러리입니다. 도형(geometry) 정보를 포함하는 GeoDataFrame을 사용하여 공간 연산 및 지도 시각화를 수행합니다. Matplotlib을 기반으로 동작합니다.

GeoPandas는 shapely, fiona, pyproj 등 여러 의존성 라이브러리가 필요하여 설치가 다소 복잡할 수 있습니다. Anaconda 환경에서 설치하는 것을 권장합니다.

8.1. 내장 데이터셋으로 기본 지도 그리기



import geopandas
import matplotlib.pyplot as plt

# 한글 폰트 설정 (Windows의 '맑은 고딕' 기준)
plt.rcParams['font.family'] = 'Malgun Gothic'
# 마이너스 부호 깨짐 방지
plt.rcParams['axes.unicode_minus'] = False

# 데이터셋의 전체 URL을 직접 지정합니다.
url = "https://naturalearth.s3.amazonaws.com/110m_cultural/ne_110m_admin_0_countries.zip"
world = geopandas.read_file(url)

# 국가 이름이 저장된 열이 'name'이 아니라 'ADMIN'이므로 수정합니다.
world = world[(world.ADMIN != "Antarctica")]

# 지도 그리기
world.plot(figsize=(12, 8))
plt.title('세계 지도')
plt.xlabel('경도')
plt.ylabel('위도')
plt.show()


        

코드 설명

8.2. 단계 구분도 (Choropleth Map)


import geopandas
import matplotlib.pyplot as plt

# 한글 폰트 설정 (Windows의 '맑은 고딕' 기준)
plt.rcParams['font.family'] = 'Malgun Gothic'
# 마이너스 부호 깨짐 방지
plt.rcParams['axes.unicode_minus'] = False

# 1. [수정] 데이터셋의 전체 URL을 직접 지정하여 불러옵니다.
url = "https://naturalearth.s3.amazonaws.com/110m_cultural/ne_110m_admin_0_countries.zip"
world = geopandas.read_file(url)

# (팁) 어떤 열이 있는지 확인하려면 아래 코드의 주석을 해제하고 실행해보세요.
# print(world.columns)

# 2. [수정] 인구를 기준으로 단계 구분도 그리기
# 'column' 이름이 소문자 'pop_est'가 아닌 대문자 'POP_EST'이므로 수정합니다.
world.plot(column='POP_EST', 
           legend=True,
           legend_kwds={'label': "추정 인구", 'orientation': "horizontal"},
           figsize=(15, 10),
           missing_kwds={"color": "lightgrey", "label": "정보 없음"}) # 데이터가 없는 국가는 회색으로 표시

plt.title('세계 인구 분포')
plt.show()

        

코드 설명

8.3. 특정 지역만 필터링하여 그리기


import geopandas
import matplotlib.pyplot as plt

# 한글 폰트 설정
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False

# 1. [수정] 데이터셋의 전체 URL을 직접 지정하여 불러옵니다.
url = "https://naturalearth.s3.amazonaws.com/110m_cultural/ne_110m_admin_0_countries.zip"
world = geopandas.read_file(url)

# (팁) 데이터의 열 이름을 확인하려면 아래 코드의 주석을 해제하고 실행해보세요.
# print(world.columns)

# 2. [수정] 아시아 대륙만 필터링
# 대륙 정보가 담긴 열 이름이 'continent'가 아닌 'CONTINENT'이므로 수정합니다.
asia = world[world['CONTINENT'] == 'Asia']

# 아시아 지도 그리기
asia.plot(figsize=(10, 10))
plt.title('아시아 대륙 지도')
plt.show()
        

코드 설명

8.4. 여러 레이어 겹쳐 그리기


import geopandas
import matplotlib.pyplot as plt

# 세계 지도와 도시 데이터 로드
world = geopandas.read_file(geopandas.datasets.get_path('naturalearth_lowres'))
cities = geopandas.read_file(geopandas.datasets.get_path('naturalearth_cities'))

# 남미 대륙만 필터링
south_america = world[world['continent'] == 'South America']

# Matplotlib의 axes 객체를 이용하여 여러 레이어 겹치기
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
south_america.plot(ax=ax, color='lightgray', edgecolor='black')
cities[cities.geometry.within(south_america.unary_union)].plot(ax=ax, marker='*', color='red', markersize=50)

plt.title('남미 대륙과 주요 도시')
plt.show()
        

코드 설명

8.5. 지도 위에 좌표 표시하기


import geopandas
from shapely.geometry import Point
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'Malgun Gothic'

# 서울시 지도 데이터 (예시, 실제 파일 경로 필요)
# 여기서는 간단한 예시를 위해 세계 지도를 사용
world = geopandas.read_file(geopandas.datasets.get_path('naturalearth_lowres'))
korea = world[world.name == 'South Korea']

# 서울, 부산의 좌표
points_data = {
    'city': ['서울', '부산'],
    'geometry': [Point(126.9780, 37.5665), Point(129.0756, 35.1796)]
}
gdf_points = geopandas.GeoDataFrame(points_data, crs="EPSG:4326")

fig, ax = plt.subplots(figsize=(10, 10))
korea.plot(ax=ax, color='lightblue')
gdf_points.plot(ax=ax, color='red', markersize=100)
plt.title('지도 위에 특정 좌표 표시')
plt.show()
        

코드 설명

8.6. 공간 결합 (Spatial Join)


import geopandas
import matplotlib.pyplot as plt

world = geopandas.read_file(geopandas.datasets.get_path('naturalearth_lowres'))
cities = geopandas.read_file(geopandas.datasets.get_path('naturalearth_cities'))

# 도시(points)를 국가(polygons)에 공간적으로 결합
# 각 도시가 어느 국가에 속하는지 찾기
cities_with_country = geopandas.sjoin(cities, world, how="inner", op='within')

# 인구가 1000만 이상인 도시가 속한 국가만 하이라이트
megacities_countries = cities_with_country[cities_with_country.pop_max > 10000000]
highlight_countries = world[world['name'].isin(megacities_countries['name_right'])]

fig, ax = plt.subplots(1, 1, figsize=(15, 10))
world.plot(ax=ax, color='#EEEEEE')
highlight_countries.plot(ax=ax, color='skyblue', edgecolor='black')
plt.title('인구 1000만 이상 도시가 있는 국가')
plt.show()
        

코드 설명

8.7. 버퍼(Buffer) 생성하기


import geopandas
from shapely.geometry import Point
import matplotlib.pyplot as plt

cities = geopandas.read_file(geopandas.datasets.get_path('naturalearth_cities'))
paris = cities[cities.name == 'Paris'].copy()

# 파리 좌표를 기준으로 버퍼 생성 (주의: 위도/경도(CRS:4326)에서의 버퍼는 부정확할 수 있음)
# 정확한 계산을 위해 투영 좌표계로 변환 후 버퍼 생성
paris_projected = paris.to_crs(epsg=3395) # Mercator 투영
paris_buffer = paris_projected.buffer(10000) # 10km 버퍼 (단위: 미터)
paris_buffer_wgs84 = paris_buffer.to_crs(epsg=4326) # 다시 WGS84로 변환

fig, ax = plt.subplots(1, 1, figsize=(10, 10))
ax.set_title('파리 중심부 10km 반경')
paris.plot(ax=ax, marker='*', color='red', markersize=100, zorder=2)
geopandas.GeoSeries(paris_buffer_wgs84).plot(ax=ax, color='blue', alpha=0.3, zorder=1)
plt.show()
        

코드 설명

8.8. 지도 투영법 변경


import geopandas
import matplotlib.pyplot as plt

world = geopandas.read_file(geopandas.datasets.get_path('naturalearth_lowres'))

# 남극 대륙 제외
world = world[(world.name != "Antarctica")]

# 기본 (WGS84, Plate Carree)
world.plot()
plt.title('기본 투영 (Plate Carrée)')
plt.show()

# 메르카토르 투영법으로 변경
world.to_crs("EPSG:3395").plot(figsize=(10,8))
plt.title('메르카토르 투영 (Mercator Projection)')
plt.show()
        

코드 설명

8.9. Dissolve: 경계 허물기


import geopandas
import matplotlib.pyplot as plt

world = geopandas.read_file(geopandas.datasets.get_path('naturalearth_lowres'))

# 'continent' 열을 기준으로 국가 경계를 허물어 대륙 경계 만들기
continents = world.dissolve(by='continent')

continents.plot(figsize=(15, 10), edgecolor='black', cmap='Pastel1')
plt.title('대륙별 경계 (Dissolve)')
plt.show()
        

코드 설명

8.10. 파일로 저장하기


import geopandas
import matplotlib.pyplot as plt

world = geopandas.read_file(geopandas.datasets.get_path('naturalearth_lowres'))
africa = world[world['continent'] == 'Africa']

# GeoDataFrame을 Shapefile 또는 GeoJSON으로 저장
# Shapefile로 저장
# africa.to_file("africa.shp", driver='ESRI Shapefile', encoding='utf-8')

# GeoJSON으로 저장
africa.to_file("africa.geojson", driver='GeoJSON')

print("파일 저장이 완료되었습니다. (africa.geojson)")
# 저장된 파일을 다시 불러와서 그려보기
africa_loaded = geopandas.read_file("africa.geojson")
africa_loaded.plot()
plt.title("저장 후 다시 불러온 아프리카 지도")
plt.show()
        

코드 설명

9. WordCloud

텍스트 데이터에서 단어의 빈도를 분석하여, 빈도가 높을수록 단어를 크게 표시하는 '워드 클라우드' 이미지를 생성하는 라이브러리입니다.

한글 워드 클라우드를 만들려면 시스템에 설치된 한글 폰트의 경로를 font_path에 정확히 지정해야 합니다.

9.1. 기본 워드 클라우드


from wordcloud import WordCloud
import matplotlib.pyplot as plt

text = "apple banana orange apple grape banana apple"

# WordCloud 객체 생성
wordcloud = WordCloud(width=800, height=400, background_color='white').generate(text)

# 이미지 표시
plt.figure(figsize=(10, 5))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off') # 축 숨기기
plt.show()
        

코드 설명

9.2. 한글 워드 클라우드


from wordcloud import WordCloud
import matplotlib.pyplot as plt

text = "데이터 분석 시각화 파이썬 데이터 파이썬 시각화 분석 워드클라우드 예제"

# 폰트 경로 지정 (Windows 예시, macOS/Linux는 경로 수정 필요)
font_path = 'C:/Windows/Fonts/malgun.ttf' 

wordcloud = WordCloud(
    font_path=font_path,
    width=800,
    height=400,
    background_color='white'
).generate(text)

plt.figure(figsize=(10, 5))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()
        

코드 설명

9.3. 빈도수 기반 워드 클라우드


from wordcloud import WordCloud
import matplotlib.pyplot as plt
from collections import Counter

text_list = ['사과', '바나나', '사과', '포도', '오렌지', '바나나', '사과']

# 단어 빈도수 계산
counts = Counter(text_list)

font_path = 'C:/Windows/Fonts/malgun.ttf'

# generate 대신 generate_from_frequencies 사용
wordcloud = WordCloud(
    font_path=font_path,
    width=800,
    height=400,
    background_color='white'
).generate_from_frequencies(counts)

plt.figure(figsize=(10, 5))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()
        

코드 설명

9.4. 불용어(Stopwords) 처리


from wordcloud import WordCloud, STOPWORDS
import matplotlib.pyplot as plt

text = "A computer is a machine that can be programmed to carry out sequences of arithmetic or logical operations automatically. Modern computers can perform generic sets of operations known as programs."

# 기본 제공되는 불용어 집합
print(f"기본 불용어 개수: {len(STOPWORDS)}")

# 사용자 정의 불용어 추가
stopwords = set(STOPWORDS)
stopwords.add("can")
stopwords.add("known")

wordcloud = WordCloud(
    stopwords=stopwords,
    background_color='white'
).generate(text)

plt.figure(figsize=(10, 5))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()
        

코드 설명

9.5. 모양(Mask) 적용하기


from wordcloud import WordCloud
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image

text = "Python is an interpreted, high-level and general-purpose programming language. Python's design philosophy emphasizes code readability with its notable use of significant indentation."

# 모양으로 사용할 이미지 불러오기 (흰 배경에 검은색 모양)
# 인터넷에서 'python logo silhouette' 검색 후 저장하여 사용
try:
    icon = Image.open("python_logo.png").convert("RGB")
    mask = np.array(icon)
except FileNotFoundError:
    print("python_logo.png 파일을 찾을 수 없습니다. 예제 진행을 위해 기본 모양으로 대체합니다.")
    x, y = np.ogrid[:300, :300]
    mask = (x - 150) ** 2 + (y - 150) ** 2 > 130 ** 2
    mask = 255 * mask.astype(int)


wordcloud = WordCloud(
    background_color='white',
    mask=mask # 마스크 이미지 적용
).generate(text)

plt.figure(figsize=(8, 8))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()
        

코드 설명

9.6. 색상 함수(Color Func) 적용


from wordcloud import WordCloud
import matplotlib.pyplot as plt
import random

text = "color function example word cloud python visualization random color"

# 랜덤 색상을 반환하는 함수 정의
def random_color_func(word, font_size, position, orientation, random_state=None, **kwargs):
    # HSL 색상 공간: 색상(0-360), 채도(0-100), 밝기(0-100)
    return f"hsl(0, 0%, {random.randint(20, 80)}%)" # 검정~회색 톤

wordcloud = WordCloud(
    width=800, height=400,
    color_func=random_color_func, # 색상 함수 지정
    background_color='white'
).generate(text)

plt.figure(figsize=(10, 5))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()
        

코드 설명

9.7. 최대 단어 수 및 폰트 크기 제한


from wordcloud import WordCloud
import matplotlib.pyplot as plt

text = "많은 텍스트 데이터가 있을 때 일부 단어만 표시하고 싶을 수 있습니다. 최대 단어 수를 제한하거나 폰트 크기 범위를 조절하여 가독성을 높일 수 있습니다."

font_path = 'C:/Windows/Fonts/malgun.ttf'

wordcloud = WordCloud(
    font_path=font_path,
    background_color='white',
    max_words=10,         # 표시할 최대 단어 수
    max_font_size=100,      # 가장 빈도가 높은 단어의 최대 폰트 크기
    min_font_size=10        # 가장 빈도가 낮은 단어의 최소 폰트 크기
).generate(text)

plt.figure(figsize=(10, 5))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()
        

코드 설명

9.8. 특정 색상 팔레트(Colormap) 사용


from wordcloud import WordCloud
import matplotlib.pyplot as plt

text = "WordCloud can be colored by a matplotlib colormap. There are many available colormaps like viridis, plasma, inferno, magma, and cividis."

wordcloud = WordCloud(
    width=800, height=400,
    colormap='viridis', # Matplotlib의 colormap 이름 지정
    background_color='white'
).generate(text)

plt.figure(figsize=(10, 5))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()
        

코드 설명

9.9. 가로/세로 단어 비율 조절


from wordcloud import WordCloud
import matplotlib.pyplot as plt

text = "You can control the ratio of horizontal to vertical words. A value of 1 means only horizontal words. A value of 0 means only vertical words."
font_path = 'C:/Windows/Fonts/malgun.ttf'
wordcloud = WordCloud(
    font_path=font_path,
    width=800, height=400,
    prefer_horizontal=0.5, # 수평 단어 비율 (0.0 ~ 1.0)
    background_color='black',
    colormap='Pastel1'
).generate(text + " 세로 단어 비율 조절 예제입니다.")

plt.figure(figsize=(10, 5))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.show()
        

코드 설명

9.10. 이미지 색상으로 워드 클라우드 색칠하기


from wordcloud import WordCloud, ImageColorGenerator
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image

text = "This is a word cloud example that takes colors from an image. The color of each word is determined by the color of the underlying pixel in the source image."

try:
    # 색상을 가져올 원본 이미지
    coloring = np.array(Image.open("parrot.png"))
    
    wordcloud = WordCloud(background_color="white", mask=coloring).generate(text)

    # 이미지 색상 생성기
    image_colors = ImageColorGenerator(coloring)
    
    plt.figure(figsize=(10,10))
    # recolor 메소드를 사용하여 색상 적용
    plt.imshow(wordcloud.recolor(color_func=image_colors), interpolation="bilinear")
    plt.axis("off")
    plt.show()

except FileNotFoundError:
    print("parrot.png 파일을 찾을 수 없습니다. 이 예제는 실행할 수 없습니다.")
        

코드 설명

10. NetworkX

네트워크(그래프)를 생성, 조작, 연구하기 위한 파이썬 라이브러리입니다. 노드(node)와 엣지(edge)로 구성된 복잡한 관계망을 시각화하고 분석하는 데 사용됩니다.

NetworkX의 시각화는 Matplotlib을 기반으로 동작합니다. 따라서 한글을 표시하려면 Matplotlib의 폰트 설정이 필요합니다.

10.1. 기본 네트워크 생성 및 시각화


import networkx as nx
import matplotlib.pyplot as plt

# 그래프 객체 생성
G = nx.Graph()

# 노드 추가
G.add_node("A")
G.add_nodes_from(["B", "C", "D"])

# 엣지(연결선) 추가
G.add_edge("A", "B")
G.add_edges_from([("A", "C"), ("B", "C"), ("B", "D")])

# 네트워크 그리기
nx.draw(G, with_labels=True, node_color='skyblue', node_size=1500, font_size=20, font_weight='bold')
plt.show()
        

코드 설명

10.2. 방향성 그래프 (DiGraph)


import networkx as nx
import matplotlib.pyplot as plt

plt.rcParams['font.family'] = 'Malgun Gothic'

# 방향성 그래프 객체 생성
G = nx.DiGraph()

G.add_edges_from([('A', 'B'), ('A', 'C'), ('C', 'A'), ('B', 'D'), ('D', 'C')])

# 화살표 스타일을 포함하여 그리기
nx.draw(G, with_labels=True, node_color='lightcoral', node_size=1500,
        font_size=15, arrowsize=20)
plt.title('방향성 그래프 (DiGraph)')
plt.show()
        

코드 설명

10.3. 가중치 그래프 (Weighted Graph)


import networkx as nx
import matplotlib.pyplot as plt

G = nx.Graph()

# 엣지에 'weight' 속성 추가
G.add_edge('A', 'B', weight=6)
G.add_edge('A', 'C', weight=2)
G.add_edge('C', 'D', weight=1)
G.add_edge('C', 'E', weight=5)
G.add_edge('E', 'D', weight=8)
G.add_edge('B', 'E', weight=2)

# 가중치 값을 엣지 라벨로 표시
pos = nx.spring_layout(G) # 노드 위치 고정
edge_labels = nx.get_edge_attributes(G, 'weight')
nx.draw(G, pos, with_labels=True, node_color='gold', node_size=1200)
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)

plt.title('가중치 그래프 (Weighted Graph)')
plt.show()
        

코드 설명

10.4. 다양한 레이아웃 알고리즘


import networkx as nx
import matplotlib.pyplot as plt

G = nx.karate_club_graph() # 가라테 클럽 데이터셋 로드

fig, axes = plt.subplots(2, 2, figsize=(12, 12))

# 1. Spring Layout
pos_spring = nx.spring_layout(G)
nx.draw(G, pos_spring, with_labels=True, ax=axes[0, 0])
axes[0, 0].set_title('Spring Layout')

# 2. Circular Layout
pos_circular = nx.circular_layout(G)
nx.draw(G, pos_circular, with_labels=True, ax=axes[0, 1])
axes[0, 1].set_title('Circular Layout')

# 3. Random Layout
pos_random = nx.random_layout(G)
nx.draw(G, pos_random, with_labels=True, ax=axes[1, 0])
axes[1, 0].set_title('Random Layout')

# 4. Spectral Layout
pos_spectral = nx.spectral_layout(G)
nx.draw(G, pos_spectral, with_labels=True, ax=axes[1, 1])
axes[1, 1].set_title('Spectral Layout')

plt.show()
        

코드 설명

10.5. 노드 크기/색상 커스터마이징


import networkx as nx
import matplotlib.pyplot as plt

G = nx.karate_club_graph()

# 각 노드의 'degree' (연결된 엣지 수) 계산
degrees = [G.degree(n) * 100 for n in G.nodes()]

# 각 노드가 속한 'club' 정보에 따라 색상 결정
colors = ['skyblue' if G.nodes[n]['club'] == 'Mr. Hi' else 'lightcoral' for n in G.nodes()]

pos = nx.spring_layout(G, seed=42)
nx.draw(G, pos, with_labels=True, node_size=degrees, node_color=colors)
plt.title('가라테 클럽 (Degree와 Club으로 시각화)')
plt.show()
        

코드 설명

10.6. 완전 그래프 (Complete Graph)


import networkx as nx
import matplotlib.pyplot as plt

# 7개의 노드를 가진 완전 그래프 생성
G = nx.complete_graph(7)

nx.draw_circular(G, with_labels=True, node_color='plum', node_size=1000)
plt.title('완전 그래프 (K7)')
plt.show()
        

코드 설명

10.7. 최단 경로 찾기 및 시각화


import networkx as nx
import matplotlib.pyplot as plt

G = nx.karate_club_graph()
pos = nx.spring_layout(G, seed=42)

# 노드 0에서 33까지의 최단 경로 찾기
path = nx.shortest_path(G, source=0, target=33)
path_edges = list(zip(path, path[1:]))

# 기본 그래프 그리기
nx.draw(G, pos, with_labels=True, node_color='lightgray')

# 경로에 해당하는 노드와 엣지 하이라이트
nx.draw_networkx_nodes(G, pos, nodelist=path, node_color='red')
nx.draw_networkx_edges(G, pos, edgelist=path_edges, edge_color='red', width=2)

plt.title('최단 경로 시각화')
plt.show()
        

코드 설명

10.8. 소셜 네트워크 분석: 중심성(Centrality)


import networkx as nx
import matplotlib.pyplot as plt
import pandas as pd

G = nx.karate_club_graph()

# Degree Centrality 계산
degree_centrality = nx.degree_centrality(G)

# 노드 사이즈를 중심성 값에 비례하도록 설정
node_sizes = [v * 5000 for v in degree_centrality.values()]

pos = nx.spring_layout(G, seed=42)
nx.draw(G, pos, with_labels=True, node_size=node_sizes, node_color=list(degree_centrality.values()),
        cmap=plt.cm.viridis)

plt.title('가라테 클럽: 연결 중심성(Degree Centrality)')
plt.show()
        

코드 설명

10.9. Pandas DataFrame에서 그래프 생성


import networkx as nx
import matplotlib.pyplot as plt
import pandas as pd

# 샘플 데이터프레임 (From, To, Weight)
df = pd.DataFrame({
    'from': ['A', 'A', 'B', 'C', 'D'],
    'to': ['B', 'C', 'D', 'E', 'A'],
    'weight': [1, 2, 3, 4, 5]
})

# 데이터프레임으로부터 그래프 생성
G = nx.from_pandas_edgelist(df, 'from', 'to', edge_attr='weight', create_using=nx.DiGraph())

pos = nx.spring_layout(G, seed=1)
nx.draw(G, pos, with_labels=True, node_color='skyblue', node_size=1000)
edge_labels = nx.get_edge_attributes(G, 'weight')
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)

plt.title('Pandas DataFrame에서 생성한 그래프')
plt.show()
        

코드 설명

10.10. 커뮤니티 탐지 및 시각화


# 이 예제는 'python-louvain' 라이브러리가 추가로 필요합니다. (pip install python-louvain)
import networkx as nx
import matplotlib.pyplot as plt
import community as community_louvain

G = nx.karate_club_graph()

# Louvain 알고리즘으로 커뮤니티(군집) 탐지
partition = community_louvain.best_partition(G)

# 커뮤니티 ID에 따라 노드 색상 지정
cmap = plt.cm.get_cmap('viridis', max(partition.values()) + 1)
colors = [cmap(partition[node]) for node in G.nodes()]

pos = nx.spring_layout(G, seed=42)
nx.draw(G, pos, with_labels=True, node_color=colors, cmap=plt.cm.viridis)

plt.title('가라테 클럽 커뮤니티 탐지 (Louvain)')
plt.show()
        

코드 설명