How to make a Pair Plot in Plots.jl?

Hey everyone,

I feel like I am totally overthinking this but am running into a wall. I was able to make a pairplot using some code from a Makie discussion here. I have been trying to do the same thing using Plots.jl. Instead, I am having some errors translating my logic to Plots.jl. Here is my current attempt:

using DataFrames
using Plots

# Nonsense Data
df = DataFrame([1 2 3; 2 4 6; 3 6 9])

function pairplot(df)
    rows, cols = size(df)
    scatter([row for row in 1:rows], [col for col in 1:cols],
        layout = (rows, cols))
end

pairplot(df)

Which produces the following:

It’s the right form but only the first plot is plotted. Going from there, I used the documentation from Plot.jl’s documentation where about layouts where it discussed incrementally generating plots and adding them to a final plot:

using DataFrames
using Plots

df = DataFrame([1 2 3; 2 4 6; 3 6 9])

function pairplot(df)
    rows, cols = size(df)
    plots = []
    for row = 1:rows, col = 1:cols
        push!(plots, scatter(row, col))
    end
    plot([p for p in plots], layout = (rows, cols))
end

pairplot(df)

This throws an error which somewhat surprised me based on the documentation.

ERROR: LoadError: MethodError: no method matching Plots.Plot{Plots.GRBackend}(::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::C
har, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Ch
ar, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char, ::Char)
Closest candidates are:
  Plots.Plot{Plots.GRBackend}(::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any) where T<:AbstractBackend at /home/cedarprince/.julia/packages/Plots/lmp2A/src/types.j
l:68
Stacktrace:
 [1] apply_recipe(::Dict{Symbol,Any}, ::Type{Plots.Plot{Plots.GRBackend}}, ::Plots.Plot{Plots.GRBackend}) at /home/cedarprince/.julia/packages/Plots/lmp2A/src/recipes.jl:50
 [2] _apply_type_recipe(::Any, ::AbstractArray, ::Any) at /home/cedarprince/.julia/packages/RecipesPipeline/uPBKQ/src/type_recipe.jl:39
 [3] macro expansion at /home/cedarprince/.julia/packages/RecipesPipeline/uPBKQ/src/user_recipe.jl:149 [inlined]
 [4] apply_recipe(::AbstractDict{Symbol,Any}, ::Any) at /home/cedarprince/.julia/packages/RecipesBase/92zOw/src/RecipesBase.jl:282
 [5] _process_userrecipes!(::Any, ::Any, ::Any) at /home/cedarprince/.julia/packages/RecipesPipeline/uPBKQ/src/user_recipe.jl:36
 [6] recipe_pipeline!(::Any, ::Any, ::Any) at /home/cedarprince/.julia/packages/RecipesPipeline/uPBKQ/src/RecipesPipeline.jl:70
 [7] _plot!(::Plots.Plot, ::Any, ::Any) at /home/cedarprince/.julia/packages/Plots/lmp2A/src/plot.jl:172
 [8] plot(::Any; kw::Any) at /home/cedarprince/.julia/packages/Plots/lmp2A/src/plot.jl:58
 [9] pairplot(::DataFrame) at /home/src/Projects/COVID_MOBILITY_ANALYTICS/_research/pair.jl:12
 [10] top-level scope at /home/src/Projects/COVID_MOBILITY_ANALYTICS/_research/pair.jl:15
 [11] include(::String) at ./client.jl:457
 [12] top-level scope at REPL[34]:1
in expression starting at /home/src/Projects/COVID_MOBILITY_ANALYTICS/_research/pair.jl:15

Would anyone be willing to help me out with this? Thank you!

~ tcp :deciduous_tree:

1 Like

I think the only thing you would need to add, are three points directly after the closing brackets, like shown here.

function pairplot(df)
    rows, cols = size(df)
    plots = []
    for row = 1:rows, col = 1:cols
        push!(plots, plot(row, col))
    end
    plot([p for p in plots]..., layout = (rows, cols))
end

image

1 Like

[p for p in plots] is just a cumbersome way to write copy(plots). You don’t even need it, plots... will do what you want.

1 Like

Hey @dave7895 and @FPGro - super appreciate your answers! That did trick! For anyone else curious, here is how I plotted a pair plot

using DataFrames
using Plots

df = rand(5, 5) * 10 |> DataFrame

function pairplot(df)
    rows, cols = size(df)
    plots = []
    for row = 1:rows, col = 1:cols
        push!(
            plots,
            scatter(
                df[row],
                df[col],
                xtickfont = font(4),
                ytickfont = font(4),
                legend = false,
            ),
        )
    end
    plot(plots..., layout = (rows, cols))
end

pairplot(df)

which gives

1 Like

I started from this solution and added some elements to it:

using Plots

function pairplot(df, title="Pairplot")
    rows, cols = size(df)
    plots = []
	p_mat = Matrix(undef,cols,cols);
    for i = 1:cols, j = 1:cols
		p_mat[i,j] = i>=j ? (label = :°, blank = false) : (label = :_, blank = true)
        if i>j
			subplot = scatter(df[:,i], df[:,j], legend = false,markersize=14/cols, 
			alpha=0.4, markerstrokecolor=nothing, grid=nothing, 
			tickfontsize=round(32/cols), tick_direction=:in, 
			xrot=45,showaxis=(i==cols ? (j==1 ? true : :x) : (j==1 ? :y : false)),
			xticks=(i==cols ? :auto : nothing), yticks=(j==1 ? :auto : nothing))
			push!(plots,subplot)
		elseif i==j
			subplot = histogram(df[:,i], normalize=true, legend=false, alpha=0.4, ticks=nothing, showaxis=(i==cols ? (j==1 ? true : :x) : (j==1 ? :y : false)),xticks=(i==cols ? :auto : nothing), 
				tick_direction=:in, tickfontcolor="white", xrot=45
			)
			density!(df[:,i], grid=nothing, tickfontsize=round(32/cols), trim=true,
			xlimits=(minimum(df[:,i]),maximum(df[:,i])))
			push!(plots,subplot)
		end
    end
    return plot(plots..., layout=p_mat, margin=0mm, plot_title=title, plot_titlevspan=0.05)
end

dfs = rand(50, 5) * 100;
pairplot(dfs);

With the following result:
image

There is an older version of PairPlots.jl that targets Plots. It should be linked in the docs and hopefully still work.