Julia:网格上分类数据的可视化
Julia: Visualization of a categorical data on a grid
有时需要在规则网格上绘制分类值以显示它们如何覆盖特定区域。原则上,plot() 函数很适合这个,但是有一个问题,就是每次都需要调整图标的大小,以创建实心封面的错觉。更改图像的覆盖范围时,旧尺寸变得无关紧要,需要重新调整。有没有自动调整这个大小的技巧?
using Plots
using CategoricalArrays
a = [1, 2, 3, 1, 2, 3, 1, 2, 3]
b = [1, 1, 1, 2, 2, 2, 3, 3, 3]
c = CategoricalArray(["X", "X", "Y", "Z", "Y", "Y", "Z", "Y", "Z"])
plot(a, b, group = c, seriestype = :scatter, aspect_ratio = 1, markersize=90,
markershape=:square, markerstrokewidth=0.0, xlim = (0.5, 3.5), ylim = (0.5, 3.5))
结果各方面都很好,除了每次需要调整单元格的大小以便没有重叠区域或间隙:
作为替代方案,我考虑了 heatmap(),但它对分类数据的处理非常奇怪,通过值的连续分级设置它们自己的某种比例。我还没有遇到任何使用 heatmap() 会得到像 plot() 这样带有美丽图例的地图的例子,所以我不确定在这里使用 heatmap() 是正确的方法。
a = b = [1, 2, 3]
c = CategoricalArray(["X" "X" "Y"; "Z" "Y" "Y"; "Z" "Y" "Z"])
heatmap(a, b, c)
也许还有一些方法可以自动设置plot()的单元格大小?
有多种方法可以在 Plots.jl 中创建这样的情节。也许对你想要的最明显的解释是 shapes. For that approach, you also need to understand how to group unconnected data within the same groups。基于形状的解决方案可能如下所示:
a = [1, 2, 3, 1, 2, 3, 1, 2, 3]
b = [1, 1, 1, 2, 2, 2, 3, 3, 3]
c = CategoricalArray(["X", "X", "Y", "Z", "Y", "Y", "Z", "Y", "Z"])
groups = Dict(cat => NTuple{2,Int}[] for cat in levels(c))
for (ca, cb, cat) in zip(a,b,c)
push!(groups[cat], (ca,cb))
end
w = 1
shapes = map(collect(groups)) do (cat, vals)
cat => mapreduce(vcat, vals) do (ca, cb)
[ca cb] .+ [-.5 -.5; .5 -.5; .5 .5; -.5 .5; -.5 -.5; NaN NaN]*w
end
end
p = plot(aspect_ratio=1)
for (cat, s) in sort(shapes;by=x->x[1])
plot!(s[:,1], s[:,2], label=cat, seriestype=:shape, linewidth=0)
end
大部分代码只是简单地四处移动数据,因此我们从分类值到指定所有顶点的矩阵中得到一个对向量,例如“X”:
"X" =>
12×2 Matrix{Float64}:
0.5 0.5
1.5 0.5
1.5 1.5
0.5 1.5
0.5 0.5
NaN NaN
1.5 0.5
2.5 0.5
2.5 1.5
1.5 1.5
1.5 0.5
NaN NaN
一个可能稍微简单一些的解决方案是“欺骗”绘图以使用热图显示我们想要的内容,如下所示:
a = b = [1, 2, 3]
c = CategoricalArray(["X" "X" "Y"; "Z" "Y" "Y"; "Z" "Y" "Z"])
pal = palette(:default)
p = plot(aspect_ratio=1, size=(400,400))
heatmap!(a,b,c, c=pal, colorbar=false, clims=(1,length(pal)))
for cat in sort(collect(Set(c)))
plot!(
[], [], seriestype=:shape,
label=cat, color=pal[levelcode(cat)]
)
end
有时需要在规则网格上绘制分类值以显示它们如何覆盖特定区域。原则上,plot() 函数很适合这个,但是有一个问题,就是每次都需要调整图标的大小,以创建实心封面的错觉。更改图像的覆盖范围时,旧尺寸变得无关紧要,需要重新调整。有没有自动调整这个大小的技巧?
using Plots
using CategoricalArrays
a = [1, 2, 3, 1, 2, 3, 1, 2, 3]
b = [1, 1, 1, 2, 2, 2, 3, 3, 3]
c = CategoricalArray(["X", "X", "Y", "Z", "Y", "Y", "Z", "Y", "Z"])
plot(a, b, group = c, seriestype = :scatter, aspect_ratio = 1, markersize=90,
markershape=:square, markerstrokewidth=0.0, xlim = (0.5, 3.5), ylim = (0.5, 3.5))
结果各方面都很好,除了每次需要调整单元格的大小以便没有重叠区域或间隙:
作为替代方案,我考虑了 heatmap(),但它对分类数据的处理非常奇怪,通过值的连续分级设置它们自己的某种比例。我还没有遇到任何使用 heatmap() 会得到像 plot() 这样带有美丽图例的地图的例子,所以我不确定在这里使用 heatmap() 是正确的方法。
a = b = [1, 2, 3]
c = CategoricalArray(["X" "X" "Y"; "Z" "Y" "Y"; "Z" "Y" "Z"])
heatmap(a, b, c)
也许还有一些方法可以自动设置plot()的单元格大小?
有多种方法可以在 Plots.jl 中创建这样的情节。也许对你想要的最明显的解释是 shapes. For that approach, you also need to understand how to group unconnected data within the same groups。基于形状的解决方案可能如下所示:
a = [1, 2, 3, 1, 2, 3, 1, 2, 3]
b = [1, 1, 1, 2, 2, 2, 3, 3, 3]
c = CategoricalArray(["X", "X", "Y", "Z", "Y", "Y", "Z", "Y", "Z"])
groups = Dict(cat => NTuple{2,Int}[] for cat in levels(c))
for (ca, cb, cat) in zip(a,b,c)
push!(groups[cat], (ca,cb))
end
w = 1
shapes = map(collect(groups)) do (cat, vals)
cat => mapreduce(vcat, vals) do (ca, cb)
[ca cb] .+ [-.5 -.5; .5 -.5; .5 .5; -.5 .5; -.5 -.5; NaN NaN]*w
end
end
p = plot(aspect_ratio=1)
for (cat, s) in sort(shapes;by=x->x[1])
plot!(s[:,1], s[:,2], label=cat, seriestype=:shape, linewidth=0)
end
大部分代码只是简单地四处移动数据,因此我们从分类值到指定所有顶点的矩阵中得到一个对向量,例如“X”:
"X" =>
12×2 Matrix{Float64}:
0.5 0.5
1.5 0.5
1.5 1.5
0.5 1.5
0.5 0.5
NaN NaN
1.5 0.5
2.5 0.5
2.5 1.5
1.5 1.5
1.5 0.5
NaN NaN
一个可能稍微简单一些的解决方案是“欺骗”绘图以使用热图显示我们想要的内容,如下所示:
a = b = [1, 2, 3]
c = CategoricalArray(["X" "X" "Y"; "Z" "Y" "Y"; "Z" "Y" "Z"])
pal = palette(:default)
p = plot(aspect_ratio=1, size=(400,400))
heatmap!(a,b,c, c=pal, colorbar=false, clims=(1,length(pal)))
for cat in sort(collect(Set(c)))
plot!(
[], [], seriestype=:shape,
label=cat, color=pal[levelcode(cat)]
)
end