I think this was also noted here and tracked to an upstream issue in Plots.jl if you want to follow there ![]()
In the meantime, if you feel like checking out a different plotting library, Makie makes it pretty easy to build these kinds of plots up (and is also just fun to use):
using CairoMakie, CSV, DataFrames
using Makie.Colors
const kde = Makie.KernelDensity.kde
COLORS = parse.(Colorant, [:lightsalmon, :cornflowerblue])
function pair_plot!(fig, df; color=:blue)
n = length(names(df))
data = Array(df)
for j โ 1:n, i โ 1:n
ax = fig[i, j]
if i == j
k = kde(data[:, i])
kx, kd = k.x, k.density
lines!(ax, kx, kd; linewidth=3, color)
band!(ax, kx, zero(kx), kd; color=(color, 0.5))
# density!(ax, data[:, i]; color) # "non-outlined" version
elseif i > j
scatter!(ax, data[:, j], data[:, i]; color)
else
Z = kde((data[:, i], data[:, j]))
n_levels = 4
levels = compute_levels(Z.density, n_levels)
colormap = cgrad(range(color, colorant"white", n_levels), alpha=0.5)
contourf!(ax, Z; levels, colormap)
contour!(ax, Z; levels, color)
end
end
end
# Number of levels `n` to show in contour plots
compute_levels(A, n) = reverse(
range(maximum(A), step=-maximum(A)/(n+1), length=(n+1))
)
df_all = let
N = 1_000
df = DataFrame(randn(N, 3), :auto)
df.grp = repeat(["groupA", "groupB"], N รท 2)
df[df.grp .== "groupA", Not(:grp)] .+= 1.0
df
end
fig = Figure()
# Global stuff
params = names(df_all[:, Not(:grp)])
n = length(params)
gdf = groupby(df_all, :grp)
lower, upper = [], [] # Lower and upper triangle for linking axes later
elems = MarkerElement[] # Legend markers
elem_labels = String[] # Will update legend in same order as gdf groups
# Set up grid
for j โ 1:n, i โ 1:n
ax = Axis(fig[i, j])
if i < j
hidedecorations!(ax; grid=false)
push!(upper, ax)
end
i > j && push!(lower, ax)
i == j && hideydecorations!(ax)
(i == j || i > j) && i != n && hidexdecorations!(ax, grid=false)
i > j && 1 < j < n && hideydecorations!(ax, grid=false)
end
# Plot
for (((label, ), df), color) โ zip(pairs(gdf), COLORS)
pair_plot!(fig, df[:, Not(:grp)]; color)
push!(elems, MarkerElement(; marker='โ', color, strokecolor=color))
push!(elem_labels, label)
end
# Shared legend
Legend(fig[1, end+1], elems, elem_labels;
halign = :left,
valign = :top,
markersize = 20,
markerstrokewidth = 1,
)
# Link up and square up
for (i, param) โ enumerate(params)
Label(fig[i, i], param;
halign = :right,
valign = :top,
padding = (0, 10, 0, 10),
tellwidth = false,
tellheight = false,
)
end
linkxaxes!(filter(x -> x isa Axis, fig.content)...)
linkyaxes!.(lower..., upper...)
colsize!.(Ref(fig.layout), 1:n, Ref(Aspect(1, 1.0)))
colgap!(fig.layout, 5)
rowgap!(fig.layout, 5)
resize_to_layout!(fig)
fig
