Tutorials¶
End-to-end workflows for common use cases. Each tutorial takes you from raw data to a finished, export-ready figure.
Create a publication-ready two-panel figure for an academic paper using the
scientific preset, GridSpec layout, and panel labels.
Build a quarterly revenue chart with Korean labels for an internal report
using the report-kr preset and bar+line combo.
Set up a Jupyter workflow with the dark preset, retina display, and
save_and_show for interactive exploration.
Paper Figure (scientific preset)¶
A typical journal submission requires a multi-panel figure at specific dimensions with consistent typography. Here’s a complete workflow:
import matplotlib.pyplot as plt
import dartwork_mpl as dm
import numpy as np
# 1. Apply style — scientific preset for journal figures
dm.style.use("scientific")
# 2. Create figure at single-column width (3.5 inches ~ 9 cm)
fig = plt.figure(figsize=(dm.cm2in(17), dm.cm2in(7)), dpi=300)
gs = fig.add_gridspec(1, 2, wspace=0.35)
ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1])
# 3. Panel (a): Time series with error band
t = np.linspace(0, 5, 200)
signal = np.exp(-0.3 * t) * np.sin(2 * np.pi * t)
noise = 0.1 * np.random.randn(200)
ax1.plot(t, signal, color="oc.blue6", lw=dm.lw(1))
ax1.fill_between(t, signal - 0.15, signal + 0.15,
color="oc.blue2", alpha=0.4)
ax1.set_xlabel("Time [s]", fontsize=dm.fs(0))
ax1.set_ylabel("Amplitude [V]", fontsize=dm.fs(0))
# 4. Panel (b): Frequency spectrum
freq = np.fft.rfftfreq(200, d=0.025)
spectrum = np.abs(np.fft.rfft(signal + noise))
ax2.semilogy(freq[:50], spectrum[:50], color="oc.red5", lw=dm.lw(1))
ax2.set_xlabel("Frequency [Hz]", fontsize=dm.fs(0))
ax2.set_ylabel("Magnitude", fontsize=dm.fs(0))
# 5. Add panel labels and optimize layout
dm.label_axes([ax1, ax2])
dm.simple_layout(fig, gs=gs)
# 6. Export for submission
dm.save_formats(fig, "fig_signal_analysis",
formats=("pdf", "svg", "png"), dpi=300, validate=True)
Key points:
dm.cm2in(17)converts cm to inches forfigsize– journals often specify dimensions in cmdm.fs(0)uses the preset’s base font size;dm.lw(1)uses relative line widthdm.label_axes()adds (a), (b) labels aligned to y-axis labelsvalidate=Truecatches clipped labels before you submit
Business Report (report-kr preset)¶
A quarterly revenue chart with Korean labels for an internal report:
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import dartwork_mpl as dm
import numpy as np
# 1. Korean report style
dm.style.use("report-kr")
# 2. Create figure
fig, ax = plt.subplots(figsize=(dm.cm2in(15), dm.cm2in(9)), dpi=200)
# 3. Quarterly data
quarters = ["1Q24", "2Q24", "3Q24", "4Q24", "1Q25"]
revenue = [1234, 1456, 1389, 1567, 1650]
margin = [12.5, 14.2, 13.1, 15.8, 16.3]
# 4. Bar chart for revenue
bars = ax.bar(quarters, revenue, color="oc.blue4", width=0.6, zorder=2)
ax.set_ylabel("매출액 (억원)", fontsize=dm.fs(0))
ax.yaxis.set_major_formatter(mticker.StrMethodFormatter("{x:,.0f}"))
# 5. Line chart for margin on secondary axis
ax2 = ax.twinx()
ax2.plot(quarters, margin, color="oc.red5", marker="o",
markersize=4, lw=dm.lw(1.5), zorder=3)
ax2.set_ylabel("영업이익률 (%)", fontsize=dm.fs(0))
ax2.yaxis.set_major_formatter(mticker.PercentFormatter(decimals=1))
ax2.spines["right"].set_visible(True)
# 6. Add value labels on bars
for bar, val in zip(bars, revenue):
ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 20,
f"{val:,}", ha="center", va="bottom", fontsize=dm.fs(-1.5))
# 7. Layout and save
dm.auto_layout(fig)
dm.save_formats(fig, "quarterly_revenue",
formats=("png", "pdf"), dpi=200)
Key points:
report-krpreset loads Paperlogy/Pretendard for Korean textax2.spines["right"].set_visible(True)– show right spine for dual-axis chartsPercentFormatter(decimals=1)– clean percentage displayauto_layouthandles the dual y-axis label overflow automatically
Dark Mode Notebook¶
Set up a Jupyter workflow optimized for dark themes:
# Cell 1: Setup
%config InlineBackend.figure_format = 'retina'
import matplotlib.pyplot as plt
import dartwork_mpl as dm
import numpy as np
dm.style.use("dark")
# Cell 2: Create and preview
fig, ax = plt.subplots(figsize=(dm.cm2in(14), dm.cm2in(8)), dpi=150)
x = np.linspace(0, 4 * np.pi, 300)
for i, (amp, phase) in enumerate([(1.0, 0), (0.7, 0.5), (0.4, 1.0)]):
y = amp * np.sin(x + phase)
ax.plot(x, y, color=f"oc.blue{3 + i*2}", lw=dm.lw(1),
label=f"A={amp}, phase={phase:.1f}")
ax.set_xlabel("Phase [rad]")
ax.set_ylabel("Amplitude")
ax.legend(fontsize=dm.fs(-1))
dm.simple_layout(fig)
dm.save_and_show(fig, size=720) # Preview at 720px width
# Cell 3: Export light version for paper
dm.style.use("scientific") # Switch to light style
fig2, ax2 = plt.subplots(figsize=(dm.cm2in(9), dm.cm2in(6)), dpi=300)
# Replot with same data but paper-appropriate styling
for i, (amp, phase) in enumerate([(1.0, 0), (0.7, 0.5), (0.4, 1.0)]):
y = amp * np.sin(x + phase)
ax2.plot(x, y, color=f"oc.blue{3 + i*2}", lw=dm.lw(1),
label=f"A={amp}, phase={phase:.1f}")
ax2.set_xlabel("Phase [rad]")
ax2.set_ylabel("Amplitude")
ax2.legend(fontsize=dm.fs(-1))
dm.simple_layout(fig2)
dm.save_formats(fig2, "waves", formats=("svg", "pdf"), dpi=300)
Key points:
retinaformat makes inline plots crisp on HiDPI displayssave_and_show(fig, size=720)previews at a specific pixel widthSwitch styles mid-notebook to export different versions from the same data
Dark preset uses
#1e1e1ebackground with subtle gray spines
See also¶
Quick Start – minimal 5-minute introduction
Styles and Presets – full preset comparison
Colors and Colormaps – palette reference
Layout and Typography – margin optimization details