혼합 차트 레이아웃
2x2 대시보드는 완성했지만, 지윤에게는 해결하지 못한 문제가 하나 남아 있었습니다.
"매출은 억 단위인데, 성장률은 퍼센트야. 같은 Y축에 넣으면 성장률이 너무 작아서 안 보이잖아."
막대 그래프 위에 선 그래프를 겹쳐서 이중 Y축으로 보여주는 차트. 경영 발표에서 자주 보이는 바로 그 형태였습니다. make_subplots()에는 이를 위한 specs 파라미터가 있습니다.
이중 Y축 설정
secondary_y: True를 specs에 지정하면 해당 서브플롯에 두 번째 Y축이 생깁니다.
# 새 파일: secondary_y_axis.pyfrom plotly.subplots import make_subplotsimport plotly.graph_objects as go# secondary_y 활성화fig = make_subplots( specs=[[{"secondary_y": True}]])# 첫 번째 Y축: 매출 (막대 그래프)fig.add_trace( go.Bar( x=["1Q", "2Q", "3Q", "4Q"], y=[120, 145, 132, 178], name="매출 (억원)", marker_color="#1B3A5C" ), secondary_y=False)# 두 번째 Y축: 성장률 (선 그래프)fig.add_trace( go.Scatter( x=["1Q", "2Q", "3Q", "4Q"], y=[None, 20.8, -8.9, 34.8], name="성장률 (%)", mode="lines+markers", line=dict(color="#E74C3C", width=2), marker=dict(size=8) ), secondary_y=True)# 축 레이블fig.update_yaxes(title_text="매출 (억원)", secondary_y=False)fig.update_yaxes(title_text="성장률 (%)", secondary_y=True)fig.update_layout( title_text="분기별 매출과 성장률", xaxis_title="분기")fig.show()
add_trace() 호출 시 secondary_y=False이면 왼쪽 Y축, secondary_y=True이면 오른쪽 Y축에 배치됩니다.
혼합 타입 설정
막대와 선뿐 아니라, 일반 XY 차트와 도메인 기반 차트(원형 등)를 같은 figure에 배치할 수도 있습니다. specs의 type 키로 구분합니다.
"xy": 일반 축 기반 차트 (기본값)"domain": 축이 없는 차트 (Pie, Funnel 등)"scene": 3D 차트"mapbox": 지도 차트
# 새 파일: mixed_type_layout.pyfrom plotly.subplots import make_subplotsimport plotly.graph_objects as go# 1행 2열: 왼쪽은 XY, 오른쪽은 도메인(파이)fig = make_subplots( rows=1, cols=2, specs=[[{"type": "xy"}, {"type": "domain"}]], subplot_titles=("분기별 매출", "카테고리 비중"))# 왼쪽: 막대 그래프fig.add_trace( go.Bar( x=["1Q", "2Q", "3Q", "4Q"], y=[120, 145, 132, 178], name="매출", marker_color="#1B3A5C" ), row=1, col=1)# 오른쪽: 원형 차트 (domain 타입)fig.add_trace( go.Pie( labels=["국내", "해외", "온라인"], values=[55, 30, 15], name="비중" ), row=1, col=2)fig.update_layout(title_text="매출 현황 대시보드")fig.show()
원형 차트를 XY 서브플롯에 넣으면 오류가 납니다. type: "domain"으로 명시해야 합니다.
행과 열의 크기 비율
기본적으로 모든 행과 열은 같은 크기입니다. column_widths와 row_heights로 비율을 바꿀 수 있습니다. 합계가 1이 되도록 작성합니다.
fig = make_subplots( rows=2, cols=2, column_widths=[0.6, 0.4], # 왼쪽 60%, 오른쪽 40% row_heights=[0.7, 0.3] # 위 70%, 아래 30%)
실전: 매출 막대 + 성장률 선 대시보드
지윤의 캡스톤 발표 자료 핵심 슬라이드를 만들어봅니다. 위쪽에는 매출과 성장률을 이중 Y축으로, 아래쪽에는 카테고리 비중 원형 차트를 배치합니다.
# 새 파일: capstone_dashboard.pyfrom plotly.subplots import make_subplotsimport plotly.graph_objects as goquarters = ["1Q", "2Q", "3Q", "4Q"]sales = [120, 145, 132, 178]growth = [None, 20.8, -8.9, 34.8]categories = ["국내 영업", "해외 수출", "온라인 판매"]shares = [55, 30, 15]# 위쪽: 이중 Y축 (2칸 차지), 아래쪽: 원형 차트fig = make_subplots( rows=2, cols=2, specs=[ [{"secondary_y": True, "colspan": 2}, None], [{"type": "domain"}, {"type": "xy"}] ], subplot_titles=( "분기별 매출 및 성장률", "", "판매 채널 비중", "지역별 실적" ), row_heights=[0.6, 0.4], vertical_spacing=0.2)# 위: 매출 막대fig.add_trace( go.Bar( x=quarters, y=sales, name="매출 (억원)", marker_color="#1B3A5C" ), row=1, col=1, secondary_y=False)# 위: 성장률 선fig.add_trace( go.Scatter( x=quarters, y=growth, name="성장률 (%)", mode="lines+markers", line=dict(color="#E74C3C", width=2), marker=dict(size=9) ), row=1, col=1, secondary_y=True)# 아래 왼쪽: 원형fig.add_trace( go.Pie( labels=categories, values=shares, hole=0.4, name="채널" ), row=2, col=1)# 아래 오른쪽: 막대fig.add_trace( go.Bar( x=["서울", "경기", "인천", "부산"], y=[85, 72, 68, 91], name="지역 실적", marker_color="#4A90D9" ), row=2, col=2)fig.update_yaxes(title_text="매출 (억원)", secondary_y=False, row=1, col=1)fig.update_yaxes(title_text="성장률 (%)", secondary_y=True, row=1, col=1)fig.update_layout( title_text="캡스톤 프로젝트 최종 발표 대시보드", height=700, showlegend=True)fig.show()
colspan=2는 해당 셀이 2열을 차지하도록 합니다. 옆 셀은 None으로 처리해서 비워둡니다. 덕분에 위쪽 차트는 화면 전체 너비를 씁니다.
지윤은 완성된 대시보드를 보며 선배에게 문자를 보냈습니다.
"선배, 이중 Y축이라는 게 있더라고요. 덕분에 매출이랑 성장률을 같이 보여줄 수 있게 됐어요."
답장은 빠르게 왔습니다.
"잘했어. 이제 색이랑 테마만 맞추면 돼."