强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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⚡⚡HTMLWeb 应用、交互探索
PyPlot⚡⚡有限所有格式Matplotlib 用户
PGFPlotsXPDF/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.jlMakie.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")

扩展阅读