Engee 文档
Notebook

使用Makie创建复杂的图形布局

此示例演示Makie库的工具,通过使用嵌套GridLayout、链接轴、自定义图例和色阶,以及高级元素定位和空间管理技术将各种类型的图形(散点图、等值线图、3D模型和时

1. 软件包安装和数据准备
In [ ]:
Pkg.add("Makie")
Pkg.add("CairoMakie")

为第一个图表准备数据:

In [ ]:
seconds = 0:0.1:2
measurements = [8.2, 8.4, 6.3, 9.5, 9.1, 10.5, 8.6, 8.2, 10.5, 8.5, 7.2,
                8.8, 9.7, 10.8, 12.5, 11.6, 12.1, 12.1, 15.1, 14.7, 13.1]

using CairoMakie
2. 创建基本图表

让我们从实验数据和指数模型的简单可视化开始。
主要组件:

  • Figure() 为图形创建容器
  • Axis() 定义带有标题的坐标系
  • scatter!() 增加测量点
  • lines!() 绘制模型线
  • axislegend() 创建一个传奇

显示带有实验数据和指数模型的图形:

In [ ]:
f = Figure()
ax = Axis(f[1, 1],
    title = "Experimental data and exponential fit",
    xlabel = "Time (seconds)",
    ylabel = "Value",
)
CairoMakie.scatter!(
    ax,
    seconds,
    measurements,
    color = :tomato,
    label = "Measurements"
)
lines!(
    ax,
    seconds,
    exp.(seconds) .+ 7,
    color = :tomato,
    linestyle = :dash,
    label = "f(x) = exp(x) + 7",
)
# 在右下角添加图例
axislegend(position = :rb)

# 形状显示
f
Out[0]:
No description has been provided for this image
3. 形成复杂的布局

创建一个复杂的图形对象(形状),其中包含多个用于绘图的区域:

  1. 我们使用 GridLayout 用于组织嵌套网格
  2. 我们定义了4个主要区域(A,B,C,D)
  3. 调整背景和形状大小
  4. 我们连接轴以同步刻度
In [ ]:
using CairoMakie
using Makie.FileIO  # 用于处理文件

# 激活后端并创建形状
CairoMakie.activate!()
f = Figure(
    backgroundcolor = RGBf(0.98, 0.98, 0.98),
    size = (1000, 700)
)

# 创建嵌套网格
ga = f[1, 1] = GridLayout()  # A区
gb = f[2, 1] = GridLayout()  # B区
gcd = f[1:2, 2] = GridLayout()  # C及D容器
gc = gcd[1, 1] = GridLayout()  # C区(3d大脑)
gd = gcd[2, 1] = GridLayout()  # D区(脑电信号)

# 为区域A创建相关轴
axtop = Axis(ga[1, 1])        # 上层分布
axmain = Axis(ga[2, 1],       # 主要时间表
    xlabel = "before", 
    ylabel = "after")
axright = Axis(ga[2, 2])      # 正确的分配

# 用于同步的链接轴
linkyaxes!(axmain, axright)  # 整体Y尺度
linkxaxes!(axmain, axtop)    # 整体X尺度

f
Out[0]:
No description has been provided for this image
4. 区域A:散射和分布图

A区示范:

-通过颜色编码对数据进行分组
-具有共同刻度的相关轴
-主图边缘的分布
-自动图例放置
-微调元素之间的边距

In [ ]:
# 生成三组的测试数据
labels = ["treatment", "placebo", "control"]
data = randn(3, 100, 2) .+ [1, 3, 5]  # 3组×100点×2尺寸

# 每个组的可视化
for (label, col) in zip(labels, eachslice(data, dims = 1))
    CairoMakie.scatter!(axmain, col, label = label)
    CairoMakie.density!(axtop, col[:, 1])  # 自上而下的分布
    CairoMakie.density!(axright, col[:, 2], direction = :y)  # 右边的分布
end

# 设置轴边界
CairoMakie.ylims!(axtop, low = 0)
CairoMakie.xlims!(axright, low = 0)

# 在轴上设置标签
axmain.xticks = 0:3:9
axtop.xticks = 0:3:9

# 添加图例并隐藏不必要的元素
leg = Legend(ga[1, 2], axmain)
hidedecorations!(axtop, grid = false)
hidedecorations!(axright, grid = false)
leg.tellheight = true  # 固定图例的高度

# 添加区域标题
Label(ga[1, 1:2, Top()], "Stimulus ratings", valign = :bottom,
    font = :bold,
    padding = (0, 0, 5, 0))

# 设置元素之间的距离
colgap!(ga, 10)
rowgap!(ga, 10)

f
Out[0]:
No description has been provided for this image
5. B区:等值线图

B区展示:

-用于2d数据可视化的等值线图
-图层次结构:contourf+contour
-自定义色标与bin标签
-通过 Mixed 对齐模式
-精确控制图形之间的距离

In [ ]:
# 准备等值线图的数据
xs = LinRange(0.5, 6, 50)
ys = LinRange(0.5, 6, 50)
data1 = [sin(x^1.5) * cos(y^0.5) for x in xs, y in ys] .+ 0.1 .* randn.()
data2 = [sin(x^0.8) * cos(y^1.5) for x in xs, y in ys] .+ 0.1 .* randn.()

# 建筑物等高线图
ax1, hm = CairoMakie.contourf(gb[1, 1], xs, ys, data1, levels = 6)
ax1.title = "Histological analysis"
CairoMakie.contour!(ax1, xs, ys, data1, levels = 5, color = :black)
hidexdecorations!(ax1)  # 在X轴上隐藏地标

ax2, hm2 = CairoMakie.contourf(gb[2, 1], xs, ys, data2, levels = 6)
CairoMakie.contour!(ax2, xs, ys, data2, levels = 5, color = :black)

# 设置色标
cb = CairoMakie.Colorbar(gb[1:2, 2], hm, label = "cell group")
low, high = CairoMakie.extrema(data1)
edges = range(low, high, length = 7)
centers = (edges[1:6] .+ edges[2:7]) .* 0.5
cb.ticks = (centers, string.(1:6))  # 自定义标签

# 优化对齐方式
cb.alignmode = Mixed(right = 0)

# 设定距离
colgap!(gb, 10)
rowgap!(gb, 10)

f
Out[0]:
No description has been provided for this image
6. 区域C:大脑的3d可视化

C区示范:

-3d模型的下载和可视化(STL格式)
-使用 Axis3 对于3D图形
-基于Y坐标的颜色编码
-倒色图更好的感知
-将3D可视化集成到整体布局中

In [ ]:
# 下载和可视化3d大脑模型
brain = load(assetpath("brain.stl"))

ax3d = Axis3(gc[1, 1], title = "Brain activation")
m = mesh!(
    ax3d,
    brain,
    color = [tri[1][2] for tri in brain for i in 1:3],
    colormap = Reverse(:magma),  # 倒色图
)
Colorbar(gc[1, 2], m, label = "BOLD level")  # 色标

f
Out[0]:
No description has been provided for this image
7. 区域D:脑电信号

D区展示:

-用于分组图形的轴阵列
-人工脑电信号的产生
-按点数动态缩放轴
-旋转标签以节省空间
-自动对齐列大小

In [ ]:
# 为脑电信号创建轴网格
axs = [Axis(gd[row, col]) for row in 1:3, col in 1:2]
hidedecorations!.(axs, grid = false, label = false)  # 简化设计

# 脑电信号的生成和可视化
for row in 1:3, col in 1:2
    xrange = col == 1 ? (0:0.1:6pi) : (0:0.1:10pi)  # 不同的范围
    eeg = [sum(sin(pi * rand() + k * x) / k for k in 1:10)
           for x in xrange] .+ 0.1 .* randn.()
    lines!(axs[row, col], eeg, color = (:black, 0.5))  # 半透明线条
end

# 轴签名和标题
axs[3, 1].xlabel = "Day 1"
axs[3, 2].xlabel = "Day 2"
Label(gd[1, :, Top()], "EEG traces", valign = :bottom,
    font = :bold,
    padding = (0, 0, 5, 0))

# 添加旋转的地点标记
for (i, label) in enumerate(["sleep", "awake", "test"])
    Box(gd[i, 3], color = :gray90)  # Placemark背景
    Label(gd[i, 3], label, rotation = pi/2, tellheight = false)  # 垂直文本
end

# 按点数自动缩放
n_day_1 = length(0:0.1:6pi)
n_day_2 = length(0:0.1:10pi)
colsize!(gd, 1, Auto(n_day_1))
colsize!(gd, 2, Auto(n_day_2))

# 设定距离
rowgap!(gd, 10)
colgap!(gd, 10)
colgap!(gd, 2, 0)  # 从带有标签的列中删除缩进

f
Out[0]:
No description has been provided for this image
8. 最终设置和标签

其他设置:

-添加A/B/C/D标签
-微调列比例
-行高平衡
-优化元素的整体排列

In [ ]:
# 添加区域标签
for (label, layout) in zip(["A", "B", "C", "D"], [ga, gb, gc, gd])
    Label(layout[1, 1, TopLeft()], label,
        fontsize = 26,
        font = :bold,
        padding = (0, 5, 5, 0),
        halign = :right)
end

# 调整比例
colsize!(f.layout, 1, Auto(0.5))  # 减小第一列的宽度
rowsize!(gcd, 1, Auto(1.5))       # 增加3d可视化的高度

# 显示最终结果
f
Out[0]:
No description has been provided for this image