
논문용 그룹 막대 그래프 만드는 법 (matplotlib 전체 코드 + CSV로 바로 만들기)
그대로 복사해 실행할 수 있는 논문 품질 그룹 막대 그래프 matplotlib 레시피: 롱 포맷 CSV, 오차 막대, 색각 이상을 고려한 색상, 단 너비에 맞춘 크기, 폰트 임베딩, 그리고 가장 흔한 레이아웃 문제 해결법.
그룹 막대 그래프(묶은 세로 막대 그래프)는 여러 방법을 여러 데이터셋이나 설정에서 비교할 때 쓰는 표준적인 그림입니다. 데이터셋마다 한 그룹, 방법마다 막대 하나를 둡니다. 거의 모든 논문의 실험 섹션에 등장하지만, 사소한 실수가 생기기 쉬운 그림이기도 합니다. 라벨이 겹치거나, 오차 막대가 숫자를 가리거나, 범례가 막대를 덮거나, PDF가 학회의 폰트 검사를 통과하지 못하는 경우가 흔합니다.
이 글에서는 그대로 붙여 넣어 실행할 수 있는 matplotlib 스크립트를 제공하고, 논문에서 중요한 설정을 설명하며, 가장 자주 겪는 문제의 해결법을 정리합니다. 마지막에는 CSV와 한 문장 설명으로 시작하고 싶은 분을 위한 지름길도 소개합니다.
그룹 막대 그래프가 적합한 경우
다음과 같은 경우에 적합합니다.
- 적은 수의 방법(보통 2~5개)을 적은 수의 범주(데이터셋, 작업, 모델 크기)에서 비교할 때.
- 범주가 이산적일 때. 가로축이 학습 스텝이나 노이즈 강도처럼 연속적이라면 선 그래프가 더 명확합니다.
- 독자가 정확한 수치보다 그룹 안의 상대적인 차이에 더 관심이 있을 때. 정확한 수치가 가장 중요하다면 표가 더 나을 수 있고, 아래처럼 값 라벨을 추가할 수도 있습니다.
그룹당 막대가 5개를 넘거나 그룹이 6개를 넘으면, 그림을 나누거나, 가로 막대로 바꾸거나, 표를 쓰는 것을 고려하세요.
1단계: 데이터를 롱 포맷으로 정리하기
측정 하나당 한 행으로 둡니다. 이 "롱 포맷"은 실험 로그에서 내보내기 쉽고 변형하기도 쉽습니다.
method,dataset,accuracy,std
Baseline,CIFAR-10,91.2,0.3
Ours,CIFAR-10,94.7,0.2
Ours+Aug,CIFAR-10,95.3,0.2
Baseline,CIFAR-100,68.4,0.5
Ours,CIFAR-100,74.9,0.4
Ours+Aug,CIFAR-100,76.8,0.3
Baseline,Tiny-ImageNet,55.1,0.6
Ours,Tiny-ImageNet,61.8,0.5
Ours+Aug,Tiny-ImageNet,63.5,0.4위 숫자는 이 튜토리얼을 위한 예시 데이터이며 실제 벤치마크 결과가 아닙니다.
2단계: 전체 matplotlib 스크립트
matplotlib 3.11과 pandas에서 테스트했습니다. LaTeX용 벡터 PDF와 300 DPI PNG를 함께 출력합니다.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
df = pd.read_csv("results.csv")
datasets = ["CIFAR-10", "CIFAR-100", "Tiny-ImageNet"] # x-axis order
methods = ["Baseline", "Ours", "Ours+Aug"] # bar order in each group
mean = df.pivot(index="dataset", columns="method", values="accuracy").loc[datasets, methods]
std = df.pivot(index="dataset", columns="method", values="std").loc[datasets, methods]
plt.rcParams.update({
"font.family": "serif",
"font.size": 9,
"axes.spines.top": False,
"axes.spines.right": False,
"pdf.fonttype": 42, # embed TrueType fonts (many venues require this)
"ps.fonttype": 42,
})
colors = ["#0072B2", "#E69F00", "#009E73"] # Okabe-Ito, colorblind-safe
hatches = ["", "//", "xx"] # still readable in grayscale
x = np.arange(len(datasets))
n = len(methods)
width = 0.8 / n
fig, ax = plt.subplots(figsize=(3.5, 2.6)) # single-column width in inches
for i, method in enumerate(methods):
offset = (i - (n - 1) / 2) * width
ax.bar(
x + offset, mean[method], width,
yerr=std[method], capsize=2, error_kw={"elinewidth": 0.8},
label=method, color=colors[i], hatch=hatches[i],
edgecolor="black", linewidth=0.5,
)
# Put value labels above the error bar, not on top of it.
for xi, m, s in zip(x + offset, mean[method], std[method]):
ax.text(xi, m + s + 1.5, f"{m:.1f}", ha="center", va="bottom", fontsize=5.5)
ax.set_xticks(x, ["CIFAR-10", "CIFAR-100", "Tiny-\nImageNet"]) # wrap long names
ax.set_ylabel("Top-1 accuracy (%)")
ax.set_ylim(0, 105)
ax.yaxis.grid(True, linestyle=":", linewidth=0.5)
ax.set_axisbelow(True)
ax.legend(loc="upper center", bbox_to_anchor=(0.5, -0.24), ncol=n, frameon=False)
fig.tight_layout()
fig.savefig("grouped_bar.pdf", bbox_inches="tight") # vector, for LaTeX
fig.savefig("grouped_bar.png", dpi=300, bbox_inches="tight") # raster, 300 DPI300 DPI로 렌더링한 결과입니다.

각 부분의 역할
오프셋 공식. 각 방법의 막대를 (i - (n - 1) / 2) * width만큼 이동시키면, 방법이 몇 개든 그룹이 눈금 중앙에 맞춰집니다. width = 0.8 / n이면 한 그룹의 막대가 눈금 간격의 80%를 차지하고 그룹 사이에 틈이 남습니다.
순서 명시. pivot(...).loc[datasets, methods]로 그룹과 막대의 순서를 고정합니다. 지정하지 않으면 pandas가 알파벳순으로 정렬해, 내 방법이 엉뚱한 위치에 놓이기 쉽습니다.
색상과 해칭. 세 색상은 Okabe–Ito 팔레트에서 골랐으며, 가장 흔한 색각 이상에서도 구분됩니다. 해칭(무늬) 덕분에 흑백으로 인쇄해도 막대를 구분할 수 있습니다.
픽셀이 아닌 인치로 크기 지정. figsize=(3.5, 2.6)은 대부분의 2단 템플릿에서 한 단의 너비(약 3.3~3.5인치)에 해당합니다. 최종 크기로 그리면 matplotlib의 9pt가 PDF에서도 9pt가 되어 본문 글자 크기와 비슷해집니다. 두 단 전체를 쓰는 그림이라면 약 7인치로 하세요.
폰트 임베딩. pdf.fonttype: 42를 쓰면 matplotlib가 Type 3 폰트 대신 TrueType 폰트를 임베딩합니다. 많은 학회의 제출 검사기가 Type 3 폰트를 경고하므로, 이 설정으로 문제를 피할 수 있습니다.
벡터 우선. LaTeX에서는 \includegraphics[width=\columnwidth]{grouped_bar.pdf}로 PDF를 넣습니다. 300 DPI PNG는 슬라이드, Word, 또는 래스터 이미지를 요구하는 저널용으로 남겨 두세요.
흔한 문제와 해결법
다음은 같은 스크립트의 레이아웃 수정 전 첫 번째 버전입니다.

여기서 가장 많이들 겪는 세 가지 문제가 보입니다.
- 가로축 라벨 겹침. 한 단 너비에서는 긴 범주 이름이 서로 부딪힙니다.
\n으로 줄을 바꾸거나, 약어로 줄이고 캡션에서 설명하거나,ax.set_xticks(x, labels, rotation=20, ha="right")로 회전시키세요. - 값 라벨이 오차 막대를 가림.
ax.bar_label은 막대 꼭대기에 글자를 두는데, 그곳이 바로 오차 막대가 시작되는 위치입니다. 최종 스크립트처럼mean + std에 약간의 여백을 더한 위치에 두거나, 값 라벨을 빼고 표로 수치를 제시하세요. - 숫자가 너무 빽빽함. 이웃한 라벨이 붙어 있으면 글자 크기를 줄이거나, 소수점 한 자리만 쓰거나, 그룹마다 최고 결과에만 라벨을 다세요.
그 밖에 확인할 점입니다.
- 범례가 막대를 덮음. 위처럼
bbox_to_anchor로 축 아래로 옮기거나,loc="lower center", bbox_to_anchor=(0.5, 1.0)으로 그림 위에 두세요. - 세로축이 0에서 시작하지 않음. 막대의 길이가 값을 나타내므로 축을 자르면 차이가 과장됩니다. 차이가 작지만 중요하다면 축을 자르지 말고 점 그래프나 표로 보여 주세요.
- 오차 막대 설명이 없음. 표준편차, 표준오차, 95% 신뢰구간 중 무엇인지, 몇 번의 실행에 기반했는지를 캡션에 적으세요.
- PDF에서 그림이 흐릿함. 대개 저해상도 PNG를 확대한 경우입니다. 벡터 PDF를 쓰거나, 최종 인쇄 크기로 300 DPI 이상의 PNG를 내보내세요.
제출 전 체크리스트
- 그룹과 막대의 순서가 의도한 대로다(내 방법을 쉽게 찾을 수 있다).
- 색상이 색각 이상을 고려했고, 흑백에서도 구분된다.
- 최종 인쇄 너비에서 그림의 글자 크기가 캡션과 비슷하다.
- 축 라벨에 단위가 있고, 세로축이 0에서 시작한다.
- 캡션에 오차 막대의 의미와 실행 횟수가 적혀 있다.
- 폰트가 임베딩되어 있다(PDF에 Type 3 폰트가 없다).
- 같은 방법은 논문의 모든 그림에서 같은 색을 쓴다.
seaborn을 쓰고 싶다면
데이터가 이미 롱 포맷이라면 seaborn으로 같은 그룹을 한 번의 호출로 그릴 수 있습니다.
import seaborn as sns
ax = sns.barplot(data=df, x="dataset", y="accuracy", hue="method",
palette=["#0072B2", "#E69F00", "#009E73"], errorbar=None)측정마다 한 행이고 반복 실행이 없다면 errorbar=None으로 설정합니다. CSV에 실행마다 한 행이 있다면 seaborn이 오차 막대를 직접 계산할 수 있습니다(예: errorbar="sd"). 크기, 폰트, 내보내기 설정은 위 스크립트의 것을 그대로 쓰는 것을 권장합니다.
지름길: PaperBanana로 CSV에서 그림까지
그리기 코드를 직접 작성하고 디버깅하고 싶지 않다면, PaperBanana Paper Visualizer의 데이터 플롯 모드를 쓸 수 있습니다.
- CSV 또는 JSON 데이터를 붙여 넣습니다.
- 원하는 그림을 한 문장으로 설명합니다. 예: "데이터셋별 정확도 그룹 막대 그래프, 방법마다 막대 하나, 막대 위에 값 표시, 범례는 가로축 아래."
- PaperBanana가 matplotlib 코드를 작성하고, 격리된 샌드박스에서 실행해 300 DPI로 렌더링한 뒤, 크리틱이 한 번 이상의 라운드에 걸쳐 결과를 검토합니다.
이미지는 다운로드할 수 있고 생성된 코드도 복사할 수 있습니다. 그래서 로컬에서 다시 실행해 벡터 PDF를 내보내거나 원하는 대로 수정할 수 있습니다. 다음은 이 글과 같은 데이터로 실제 생성한 결과입니다.

데이터 플롯 모드는 이미지 모델을 쓰지 않으므로, 그림의 숫자는 데이터에서 그대로 나옵니다. 크리틱 라운드당 10크레딧이며, 쓰지 않은 라운드는 자동으로 환불됩니다. 자세한 내용은 요금 페이지를 참고하세요.
자주 묻는 질문
그룹 막대 그래프와 표 중 무엇을 써야 하나요? "우리 방법이 모든 데이터셋에서 앞선다", "어려운 데이터셋일수록 격차가 커진다"처럼 패턴을 전달하고 싶다면 그래프를, 독자에게 정확한 수치가 필요하다면 표를 쓰세요. 본문에는 그래프를, 부록에는 전체 표를 싣는 논문도 많습니다.
그룹당 막대는 몇 개까지 괜찮나요? 한 단 너비에서는 그룹당 5개를 넘으면 읽기 어려워집니다. 그림을 두 개로 나누거나 덜 중요한 베이스라인을 부록으로 옮기세요.
표준편차와 표준오차 중 무엇을 쓰나요? 둘 다 괜찮지만 무엇을 썼는지 밝혀야 합니다. 표준편차는 실행 간 변동을, 표준오차와 신뢰구간은 평균을 얼마나 정확히 추정했는지를 보여 줍니다. 실행 횟수는 항상 적으세요.
막대를 가로로 그릴 수 있나요?
네. ax.bar를 ax.barh로 바꾸고, x축과 y축의 역할을 바꾼 뒤 ax.set_yticks를 사용하세요. 범주 이름이 길 때는 가로 막대가 읽기 편합니다.
논문 그림에 대한 일반적인 안내는 연구 논문을 위한 출판 품질 그림 만드는 법과 TikZ, matplotlib, AI 도구 비교를 참고하세요.
더 보기

학술 논문 일러스트 AI 생성 팁: 박사 연구자의 실전 노하우
재료과학 박사 연구자가 공유하는 AI 도구를 활용한 연구 논문 그림 제작 실전 팁. 도구 선택, 프롬프트 엔지니어링 공식, 실제 활용 사례까지 총정리합니다.


연구 논문을 위한 출판 수준의 그림을 만드는 방법
학술 논문을 위한 전문적인 그림 제작에 대한 종합 가이드입니다. DPI 요구사항, 폰트 크기, 색상 접근성, 도구 선택, 그리고 AI가 연구자의 그림 제작 워크플로우를 어떻게 변화시키고 있는지 알아보십시오.


PaperBanana 서비스 재개: GPT Image 2.6, Nano Banana 2.1, Plot 모드
2026년 11월 10일 PaperBanana가 돌아왔습니다. 두 이미지 모델, 2K 출력, 격리된 샌드박스의 300 DPI 차트, 라운드 과금 및 미사용분 자동 환불.

최신 소식 받기
PaperBanana 커뮤니티에 참여하세요
새로운 기능, 연구 팁, 조기 접근 기회에 대한 알림을 받으세요