Julia 教程 / Julia 数据可视化(Plots.jl 与 Makie.jl)
Julia 数据可视化(Plots.jl 与 Makie.jl)
Julia 拥有两大可视化生态:Plots.jl(统一接口、快速绘图)和 Makie.jl(高性能、交互式、GPU 加速)。本文全面覆盖基础绘图、高级布局、3D 可视化和动画制作。
1. Plots.jl 基础
using Plots
# 折线图
x = range(0, 2π, length=100)
plot(x, sin.(x), label="sin(x)", xlabel="x", ylabel="y", title="三角函数")
plot!(x, cos.(x), label="cos(x)")
# 散点图
scatter(randn(100), randn(100), label="随机点", markersize=3)
# 柱状图
bar(["A", "B", "C", "D"], [23, 45, 12, 67], label="销量")
# 直方图
histogram(randn(10000), bins=50, label="正态分布", normalize=:pdf)
# 饼图(通过第三方实现)
using StatsPlots
pie(["Apple", "Banana", "Cherry"], [30, 45, 25], title="水果占比")
| 绘图函数 | 说明 | 常用参数 |
|---|---|---|
plot | 折线图 | label, linewidth, linestyle |
scatter | 散点图 | markersize, marker, markercolor |
bar | 柱状图 | bar_width, fillto |
histogram | 直方图 | bins, normalize |
heatmap | 热力图 | color, clims |
contour | 等高线 | levels, fill |
boxplot | 箱线图 | outliers |
violin | 小提琴图 | side |
2. 后端选择
# 查看当前后端
backend()
# 切换后端
gr() # GR — 默认,速度快,适合科学绘图
plotly() # Plotly — 交互式,适合 Web
pyplot() # PyPlot — Matplotlib 后端
pgfplotsx() # PGFPlotsX — LaTeX 风格,论文首选
unicodeplots() # UnicodePlots — 终端绘图
| 后端 | 速度 | 交互 | 导出 | 适用场景 |
|---|---|---|---|---|
| GR | ⚡⚡⚡ | 否 | PNG/PDF/SVG | 日常绘图、快速探索 |
| Plotly/PlotlyJS | ⚡⚡ | ✅ | HTML | Web 应用、交互探索 |
| PyPlot | ⚡⚡ | 有限 | 所有格式 | Matplotlib 用户 |
| PGFPlotsX | ⚡ | 否 | PDF/TEX | 学术论文 |
| CairoMakie | ⚡⚡ | 否 | PNG/PDF/SVG | 出版级静态图 |
| GLMakie | ⚡⚡⚡ | ✅ | PNG | 交互式 3D |
3. 子图布局
using Plots
# 方法1:layout 参数
p1 = plot(sin, 0, 2π, title="sin")
p2 = plot(cos, 0, 2π, title="cos")
p3 = plot(tan, 0, 2π, title="tan", ylims=(-5,5))
p4 = plot(x -> x^2, -2, 2, title="x²")
plot(p1, p2, p3, p4, layout=(2,2), size=(800,600))
# 方法2:@layout 宏
layout = @layout [a{0.3h}; b c]
plot(p1, p2, p3, layout=layout, size=(800,600))
# 方法3:嵌套布局
inner = plot(p2, p3, layout=(1,2))
plot(p1, inner, layout=(2,1), size=(800,600))
# 共享坐标轴
plot(p1, p2, layout=(2,1), link=:x) # 共享 x 轴
4. 图例、标签与注释
using Plots
x = range(0, 2π, length=100)
p = plot(x, sin.(x),
label = "sin(x)",
xlabel = "角度 (rad)",
ylabel = "振幅",
title = "正弦函数",
legend = :topright, # 图例位置 :topright/:topleft/:bottomright 等
foreground_color_legend = nothing, # 透明图例边框
background_color_legend = :white,
titlefontsize = 14,
guidefontsize = 12,
tickfontsize = 10,
legendfontsize = 9,
dpi = 150
)
# 添加注释
annotate!(p, π/2, 1.0, text("最大值", :red, 10))
hline!(p, [0.5], label="阈值", linestyle=:dash, color=:gray)
vline!(p, [π], label="x=π", linestyle=:dot)
# 添加箭头
quiver!(p, [1.0], [0.5], quiver=([1.0], [0.3]), color=:black)
5. 颜色主题
using Plots
# 内置主题
theme(:default) # 默认
theme(:dark) # 暗色
theme(:ggplot2) # ggplot2 风格
theme(:solarized) # Solarized
theme(:juno) # Juno 编辑器风格
# 自定义颜色
plot(rand(10, 3), color=[:red :green :blue])
plot(rand(10, 3), color=palette(:viridis, 3)) # 使用 ColorSchemes
# 热力图配色
heatmap(rand(10,10), color=:thermal)
heatmap(rand(10,10), color=cgrad([:blue, :white, :red]))
💡 提示: 使用 Plots.themes() 查看所有可用主题。palette(:tab10, n) 可以获取 ColorSchemes.jl 中的调色板。
⚠️ 注意: pyplot() 后端需要安装 PyCall.jl 和 Matplotlib。首次使用时会自动安装依赖,但可能需要较长时间。
6. Makie.jl 入门
using CairoMakie # 静态高质量导出
# using GLMakie # 交互式 OpenGL
# 基本绘图
fig = Figure(size=(800, 600))
ax = Axis(fig[1, 1],
xlabel = "x",
ylabel = "y",
title = "Makie 绘图"
)
lines!(ax, 0..2π, sin, label="sin(x)")
scatter!(ax, range(0, 2π, length=20), cos.(range(0, 2π, length=20)),
label="cos points", markersize=10)
axislegend(ax, position=:rt)
fig
# 保存
save("plot.png", fig, px_per_unit=2)
save("plot.pdf", fig)
Makie vs Plots.jl
| 特性 | Plots.jl | Makie.jl |
|---|---|---|
| 语法简洁性 | ⭐⭐⭐ | ⭐⭐ |
| 性能(大数据) | ⭐⭐ | ⭐⭐⭐ |
| 交互性 | 有限 | 优秀 |
| 3D 支持 | 基础 | 优秀 |
| 自定义能力 | 中等 | 极强 |
| GPU 加速 | 否 | ✅ |
| 社态生态 | 成熟 | 快速成长 |
7. 交互式绘图
using GLMakie # 必须使用 GLMakie
fig = Figure()
ax = Axis(fig[1, 1])
# 创建可观察变量
x_obs = Observable(range(0, 2π, length=100))
y_obs = Observable(sin.(x_obs[]))
lines!(ax, x_obs, y_obs)
# 更新数据时图形自动刷新
on(events(fig).keyboardbutton) do event
if event.action == Keyboard.press
x_obs[] = range(0, 4π, length=100)
y_obs[] = sin.(x_obs[]) .* rand(100)
end
end
# Slider 交互
fig2 = Figure()
ax2 = Axis(fig2[1, 1])
sl = Slider(fig2[2, 1], range=0.1:0.1:5.0, startvalue=1.0)
freq = sl.value
y_lifted = @lift(sin.($freq .* range(0, 2π, length=100)))
lines!(ax2, range(0, 2π, length=100), y_lifted)
fig2
8. 3D 绘图
using GLMakie
# 3D 曲线
fig = Figure()
ax = Axis3(fig[1, 1])
t = range(0, 10π, length=1000)
lines!(ax, sin.(t), cos.(t), t/10, color=t, colormap=:viridis)
fig
# 3D 曲面
x = range(-3, 3, length=100)
y = range(-3, 3, length=100)
z = [sin(x) * cos(y') for x in x, y in y] # 注意广播方式
fig2 = Figure()
ax2 = Axis3(fig2[1, 1], aspect=(1, 1, 0.5))
surface!(ax2, x, y, z, colormap=:plasma)
contour3d!(ax2, x, y, z, levels=10, linewidth=2)
fig2
# 散点 3D
fig3 = Figure()
ax3 = Axis3(fig3[1, 1])
scatter!(ax3, randn(500), randn(500), randn(500),
color=rand(500), markersize=10, colormap=:thermal)
fig3
9. 动画制作
using Plots
# 方法1:Plots.jl @animate 宏
anim = @animate for i in 1:100
x = range(0, 2π, length=200)
plot(x, sin.(x .+ i/10),
ylims=(-1.5, 1.5),
title="相位移动: $(round(i/10, digits=1))",
label="sin(x + $(round(i/10, digits=1)))"
)
end
gif(anim, "wave.gif", fps=30)
# 方法2:Makie.jl 录制
using CairoMakie
fig = Figure()
ax = Axis(fig[1, 1], limits=(0, 2π, -1.5, 1.5))
phase = Observable(0.0)
lines!(ax, range(0, 2π, length=200), @lift(sin.(range(0, 2π, length=200) .+ $phase)))
record(fig, "makie_wave.mp4", range(0, 10π, length=300); framerate=30) do t
phase[] = t
end
10. 导出图片
using Plots
p = plot(rand(10, 3), title="导出示例")
# PNG
savefig(p, "plot.png")
# PDF(矢量图,适合论文)
savefig(p, "plot.pdf")
# SVG(矢量图,适合 Web)
savefig(p, "plot.svg")
# 设置 DPI
Plots.gr(size=(800, 600), dpi=300)
savefig(p, "plot_hires.png")
# Makie 导出
using CairoMakie
fig = Figure(size=(800, 600))
save("makie_plot.png", fig, px_per_unit=3) # 高分辨率
save("makie_plot.pdf", fig)
11. 业务场景
11.1 金融图表
using Plots, Dates
# K 线图(需要 PlotlyJS 后端)
plotlyjs()
# 模拟股价数据
dates = Date(2024,1,1):Day(1):Date(2024,12,31)
close_price = cumsum(randn(length(dates)) .* 0.5) .+ 100
plot(dates, close_price,
xlabel="日期", ylabel="价格",
title="股票走势图",
label="收盘价",
fill=(0, 0.1, :blue), # 填充区域
linewidth=2
)
# 移动平均
ma20 = [mean(close_price[max(1,i-19):i]) for i in 1:length(close_price)]
plot!(dates, ma20, label="20日均线", linewidth=1.5, color=:red)
11.2 科学论文配图
using PGFPlotsX # LaTeX 风格
# 学术论文标准配图
pgfplotsx()
p = plot(randn(100), randn(100),
seriestype = :scatter,
xlabel = L"$\alpha$ (degrees)",
ylabel = L"$\beta$ (energy, eV)",
title = L"Scatter plot of $\alpha$ vs $\beta$",
legend = :topright,
marker = :circle,
markersize = 4,
markercolor = :steelblue,
size = (400, 300), # 适合双栏论文的尺寸
dpi = 300
)
savefig(p, "paper_figure.pdf")
扩展阅读
- Plots.jl 官方文档
- Makie.jl 官方文档
- StatsPlots.jl — 统计绘图扩展
- ColorSchemes.jl — 调色板
- PlotReferenceImages.jl — 示例图集
- Edward Tufte, The Visual Display of Quantitative Information