5.5 颜色和颜色图(Colormap)

在展示结果时,其中重要的一步是为图选择一组合适的颜色或 colorbar。 Makie.jl 支持使用 Colors.jl ,因此你可以使用 named colors 而不是传递 RGBRGBA 值。 另外,也可以使用 ColorSchemes.jlPerceptualColourMaps.jl 中的颜色图。 值得了解的是,可以使用 Reverse(:colormap_name) 反转颜色图 ,也可以通过 color=(:red,0.5) and colormap=(:viridis, 0.5) 获得透明的颜色或颜色图。

下文介绍不同的用例。 接下来使用新的颜色和颜色栏(Colorbar)调色盘来创建自定义主题。

默认情况下, Makie.jl 已经预定义一组颜色,可以循环使用该组颜色。 之前的图因此并未设置任何特定颜色。 覆盖这些默认颜色的方法是,在绘图函数中调用 color 关键字并使用 SymbolString 指定新的颜色。 该操作如下所示:

function set_colors_and_cycle()
    # Epicycloid lines
    x(r, k, θ) = r * (k .+ 1.0) .* cos.(θ) .- r * cos.((k .+ 1.0) .* θ)
    y(r, k, θ) = r * (k .+ 1.0) .* sin.(θ) .- r * sin.((k .+ 1.0) .* θ)
    θ = LinRange(0, 6.2π, 1000)
    axis = (; xlabel=L"x(\theta)", ylabel=L"y(\theta)",
        title="Epicycloid", aspect=DataAspect())
    figure = (; resolution=(600, 400), font="CMU Serif")
    fig, ax, _ = lines(x(1, 1, θ), y(1, 1, θ); color="firebrick1", # string
        label=L"1.0", axis=axis, figure=figure)
    lines!(ax, x(4, 2, θ), y(4, 2, θ); color=:royalblue1, #symbol
        label=L"2.0")
    for k = 2.5:0.5:5.5
        lines!(ax, x(2k, k, θ), y(2k, k, θ); label=latexstring("$(k)")) #cycle
    end
    Legend(fig[1, 2], ax, latexstring("k, r = 2k"), merge=true)
    fig
end
set_colors_and_cycle()
Figure 21: Set colors and cycle.

这里通过color 关键字指定了上例前两条曲线的颜色。 其余使用默认的颜色集。 稍后将学习如何使用自定义颜色循环。

关于颜色图,我们已经非常熟悉用于热力图和散点图的 colormap。下面展示的是,颜色图也可以像颜色那样通过 SymbolString 进行指定。 此外,也可以是 RGB 颜色的向量。 下面是第一个例子,通过 SymbolString 和分类值的 cgrad 来指定颜色图。 输入 ?cgrad 查看更多信息。

figure = (; resolution=(600, 400), font="CMU Serif")
axis = (; xlabel=L"x", ylabel=L"y", aspect=DataAspect())
fig, ax, pltobj = heatmap(rand(20, 20); colorrange=(0, 1),
    colormap=Reverse(:viridis), axis=axis, figure=figure)
Colorbar(fig[1, 2], pltobj, label = "Reverse colormap Sequential")
fig
Figure 22: Reverse colormap sequential and colorrange.

当设置 colorrange 后,超出此范围的颜色值会被相应地设置为颜色图的第一种和最后一种颜色。 但是,有时最好自行指定两端的颜色。这可以通过 highcliplowclip 实现:

using ColorSchemes
figure = (; resolution=(600, 400), font="CMU Serif")
axis = (; xlabel=L"x", ylabel=L"y", aspect=DataAspect())
fig, ax, pltobj=heatmap(randn(20, 20); colorrange=(-2, 2),
    colormap="diverging_rainbow_bgymr_45_85_c67_n256",
    highclip=:black, lowclip=:white, axis=axis, figure=figure)
Colorbar(fig[1, 2], pltobj, label = "Diverging colormap")
fig
Figure 23: Diverging Colormap with low and high clip.

另外 RGB 向量也是合法的选项。 在下面的例子中, 你可以传递一个自定义颜色图 perse 或使用 cgrad 来创建分类值的 Colorbar

using Colors, ColorSchemes
figure = (; resolution=(600, 400), font="CMU Serif")
axis = (; xlabel=L"x", ylabel=L"y", aspect=DataAspect())
cmap = ColorScheme(range(colorant"red", colorant"green", length=3))
mygrays = ColorScheme([RGB{Float64}(i, i, i) for i in [0.0, 0.5, 1.0]])
fig, ax, pltobj = heatmap(rand(-1:1, 20, 20);
    colormap=cgrad(mygrays, 3, categorical=true, rev=true), # cgrad and Symbol, mygrays,
    axis=axis, figure=figure)
cbar = Colorbar(fig[1, 2], pltobj, label="Categories")
cbar.ticks = ([-0.66, 0, 0.66], ["-1", "0", "1"])
fig
Figure 24: Categorical Colormap.

最后,分类值的颜色栏标签默认不在每种颜色间居中。 添加自定义标签可修复此问题,即 cbar.ticks = (positions, ticks)。 最后一种情况是传递颜色的元组给 colormap,其中颜色可以通过 SymbolString 或它们的混合指定。 然后将会得到这两组颜色间的插值颜色图。

另外,也支持十六进制编码的颜色作为输入。因此作为示范,下例将在热力图上放置一个半透明的标记。

figure = (; resolution=(600, 400), font="CMU Serif")
axis = (; xlabel=L"x", ylabel=L"y", aspect=DataAspect())
fig, ax, pltobj = heatmap(rand(20, 20); colorrange=(0, 1),
    colormap=(:red, "black"), axis=axis, figure=figure)
scatter!(ax, [11], [11], color=("#C0C0C0", 0.5), markersize=150)
Colorbar(fig[1, 2], pltobj, label="2 colors")
fig
Figure 25: Colormap from two colors.

5.5.1 自定义颜色循环

可以通过新的颜色循环定义全局 Theme ,但通常 不建议 这样做。 更好的做法是定义新的主题并像上节那样使用它。 定义带有 :color:linestyle:marker 属性的新 cycle 和默认的 colormap 。 下面为之前的 publication_theme 增加一些新的属性。

function new_cycle_theme()
    # https://nanx.me/ggsci/reference/pal_locuszoom.html
    my_colors = ["#D43F3AFF", "#EEA236FF", "#5CB85CFF", "#46B8DAFF",
        "#357EBDFF", "#9632B8FF", "#B8B8B8FF"]
    cycle = Cycle([:color, :linestyle, :marker], covary=true) # alltogether
    my_markers = [:circle, :rect, :utriangle, :dtriangle, :diamond,
        :pentagon, :cross, :xcross]
    my_linestyle = [nothing, :dash, :dot, :dashdot, :dashdotdot]
    Theme(
        fontsize=16, font="CMU Serif",
        colormap=:linear_bmy_10_95_c78_n256,
        palette=(color=my_colors, marker=my_markers, linestyle=my_linestyle),
        Lines=(cycle=cycle,), Scatter=(cycle=cycle,),
        Axis=(xlabelsize=20, xgridstyle=:dash, ygridstyle=:dash,
            xtickalign=1, ytickalign=1, yticksize=10, xticksize=10,
            xlabelpadding=-5, xlabel="x", ylabel="y"),
        Legend=(framecolor=(:black, 0.5), bgcolor=(:white, 0.5)),
        Colorbar=(ticksize=16, tickalign=1, spinewidth=0.5),
    )
end

然后将它应用到绘图函数中,如下所示:

function scatters_and_lines()
    x = collect(0:10)
    xh = LinRange(4, 6, 25)
    yh = LinRange(70, 95, 25)
    h = randn(25, 25)
    fig = Figure(resolution=(600, 400), font="CMU Serif")
    ax = Axis(fig[1, 1], xlabel=L"x", ylabel=L"f(x,a)")
    for i in x
        lines!(ax, x, i .* x; label=latexstring("$(i) x"))
        scatter!(ax, x, i .* x; markersize=13, strokewidth=0.25,
            label=latexstring("$(i) x"))
    end
    hm = heatmap!(xh, yh, h)
    axislegend(L"f(x)"; merge=true, position=:lt, nbanks=2, labelsize=14)
    Colorbar(fig[1, 2], hm, label="new default colormap")
    limits!(ax, -0.5, 10.5, -5, 105)
    colgap!(fig.layout, 5)
    fig
end
with_theme(scatters_and_lines, new_cycle_theme())
Figure 26: Custom theme with new cycle and colormap.

此时,通过颜色,曲线样式,标记和颜色图,你已经能够 完全控制 绘图结果。 下一部分将讨论如何管理和控制 布局



CC BY-NC-SA 4.0 Jose Storopoli, Rik Huijzer, Lazaro Alonso, 刘贵欣 (中文翻译), 田俊 (中文审校)