Skip to content
18 changes: 17 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ version = "2.0.0-DEV"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
Expand All @@ -16,30 +18,44 @@ StatsFuns = "4c63d2b9-4356-54db-8cca-17b64c39e42c"
StatsModels = "3eaba693-59b7-5ba5-a881-562e759f1c8d"
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"

[weakdeps]
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd"

[extensions]
MakieExt = "Makie"
StatsPlotsExt = "StatsPlots"

[compat]
CSV = "0.7, 0.8, 0.9, 0.10"
CategoricalArrays = "0.8, 0.9, 0.10"
DataFrames = "0.22, 1"
Distributions = "0.16, 0.17, 0.18, 0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25"
Makie = "0.21"
RDatasets = "0.5, 0.6, 0.7"
Reexport = "0.1, 0.2, 1.0"
Requires = "1.3.0"
SpecialFunctions = "0.6, 0.7, 0.8, 0.9, 0.10, 1, 2.0"
Statistics = "1"
StatsAPI = "1.4"
StatsBase = "0.33.5, 0.34"
StatsFuns = "0.6, 0.7, 0.8, 0.9, 1.0"
StatsModels = "0.7.3"
StatsPlots = "0.15"
Tables = "1"
julia = "1.6"

[extras]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597"
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
RDatasets = "ce6b1742-4840-55fa-b093-852dadbb1d8b"
StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3"
StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["CategoricalArrays", "CSV", "DataFrames", "Downloads", "RDatasets", "StableRNGs", "Test"]
test = ["CategoricalArrays", "CSV", "CairoMakie", "DataFrames", "Downloads", "RDatasets", "StableRNGs", "StatsPlots", "Test"]
179 changes: 179 additions & 0 deletions ext/MakieExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
module MakieExt
using Makie
using GLM
using Distributions
using GLM: leverage, standardized_residuals
import GLM.MakieRecipes: cooksleverageplot, cooksleverageplot!
import GLM.MakieRecipes: scalelocationplot, scalelocationplot!
import GLM.MakieRecipes: residualplot, residualplot!
import GLM.MakieRecipes: residualsleverageplot, residualsleverageplot!
import GLM.MakieRecipes: lmplot
import Makie: qqplot, qqplot!


function lmplot(obj::LinearModel; figkw...)
fig = Figure(; figkw...)
ax_1 = Axis(fig[1,1],
title = "Residuals vs Fitted Values",
xlabel = "Fitted Values",
ylabel = "Residuals"
)
residualplot!(ax_1, obj)
ax_2 = Axis(fig[1,2],
title = "Q-Q Residuals",
xlabel = "Theoretical Quantiles",
ylabel = "Standardized Residuals"
)
qqplot!(ax_2, obj)
ax_3 = Axis(fig[2,1],
title = "Scale-Location",
xlabel = "Fitted Values",
ylabel = "√|standardized residuals|"
)
scalelocationplot!(ax_3, obj)
ax_4 = Axis(fig[2,2],
title = "Residuals vs Leverage",
xlabel = "Leverage",
ylabel = "Standardized Residuals"
)
ymax = maximum(abs.(standardized_residuals(obj)))
xmax = maximum(leverage(obj))
ylims!(ax_4, -1.2*ymax, 1.2*ymax)
xlims!(ax_4, 0.0, 1.2*xmax)
axislegend(ax_4,
[LineElement(color = :gray, linestyle = :dash, linewidth=1)],
["Cook's distance"],
position = :lb,
framevisible = false,
labelsize = 10,
labelcolor = :gray,
padding = (0.0f0, 0.0f0, 0.0f0, 0.0f0)
)
residualsleverageplot!(ax_4, obj)
fig
end

Makie.@recipe(ResidualPlot, obj) do scene
Theme(
axislines = true,
axislinestyle = :dot,
axislinecolor = :gray,
axislinewidth = 1,
)

end

function Makie.plot!(rp::ResidualPlot{<:Tuple{LinearModel}})
r = residuals(rp.obj[])
y = predict(rp.obj[])

scatter!(rp, y, r)
if rp.axislines[]
hlines!(rp, [0], color = rp.axislinecolor[], linestyle = rp.axislinestyle[], linewidth = rp.axislinewidth[])
end
rp
end

Makie.@recipe(ScaleLocationPlot, obj) do scene
Theme()
end

function Makie.plot!(slp::ScaleLocationPlot{<:Tuple{LinearModel}})
r = standardized_residuals(slp.obj[])
y = predict(slp.obj[])

scatter!(slp, y, (sqrt ∘ abs).(r))
return slp
end

Makie.@recipe(ResidualsLeveragePlot, obj) do scene
Theme(
axislines = true,
axislinestyle = :dot,
axislinecolor = :gray,
axislinewidth = 1,
cookslevels = [0.5,1.0],
cookslinecolor = :gray,
cookslinestyle = :dash,
cookslinewidth = 1
)
end

function Makie.plot!(rlp::ResidualsLeveragePlot{<:Tuple{LinearModel}})
r = standardized_residuals(rlp.obj[])
h = leverage(rlp.obj[])
k = dof(rlp.obj[]) - 1

scatter!(rlp, h, r)

if rlp.axislines[]
hlines!(rlp, [0],
linestyle = rlp.axislinestyle[],
color = rlp.axislinecolor[],
linewidth = rlp.axislinewidth[]
)
vlines!(rlp, [0],
linestyle = rlp.axislinestyle[],
color = rlp.axislinecolor[],
linewidth = rlp.axislinewidth[]
)
end

if !isempty(rlp.cookslevels[])
cooksfun = (h,D,k) -> sqrt(D * k * (1-h) / h)
xmin, xmax = extrema(h)
xs = LinRange(xmin, 1.1*xmax, 50)
for D in rlp.cookslevels[]
lines!(rlp, xs, cooksfun.(xs,D,k),
linestyle = rlp.cookslinestyle[],
color = rlp.cookslinecolor[],
linewidth = rlp.cookslinewidth[]
)
text!(rlp, xs[end], cooksfun(xs[end],D,k),
text = "$D",
color = rlp.cookslinecolor[],
#align = (:left, :bottom),
offset = (1,-4),
fontsize = 10
)
lines!(rlp, xs, -cooksfun.(xs,D,k),
linestyle = rlp.cookslinestyle[],
color = rlp.cookslinecolor[],
linewidth = rlp.cookslinewidth[]
)
text!(rlp, xs[end], -cooksfun(xs[end],D,k),
text = "$D",
color = rlp.cookslinecolor[],
#align = (:left, :bottom),
offset = (1,-4),
fontsize = 10
)
end
end

return rlp
end

Makie.@recipe(CooksLeveragePlot, obj) do scene
Theme()
end

function Makie.plot!(clp::CooksLeveragePlot{<:Tuple{LinearModel}})
h = leverage(clp.obj[])
cd = cooksdistance(clp.obj[])

scatter!(clp, h,cd)
return clp
end

function Makie.plot!(qqp::QQPlot{<:Tuple{LinearModel}})
r = standardized_residuals(qqp[1][])
qqplot!(qqp, Normal, r,
qqline = :fitrobust,
linestyle = :dash,
linewidth = 1
)
return qqp
end

end
Loading