Skip to content
12 changes: 10 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,20 @@

### Maintenance

### Documentation

## v0.4.3 (2025-11-24)

### Features

- Support PrettyTables v3 (v2 temporarily still supported) ([#83](https://github.com/arviz-devs/PosteriorStats.jl/pull/83))

### Maintenance

- Store SummaryStats labels separately from data ([#81](https://github.com/arviz-devs/PosteriorStats.jl/pull/81))
- Add historical changelog ([#82](https://github.com/arviz-devs/PosteriorStats.jl/pull/82))
- Add pull request templates ([#82](https://github.com/arviz-devs/PosteriorStats.jl/pull/82))

### Documentation

## v0.4.2 (2025-10-13)

### Maintenance
Expand Down
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "PosteriorStats"
uuid = "7f36be82-ad55-44ba-a5c0-b8b5480d7aa5"
authors = ["Seth Axen <seth@sethaxen.com> and contributors"]
version = "0.4.3-DEV"
version = "0.4.3"

[deps]
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
Expand Down Expand Up @@ -51,7 +51,7 @@ Optim = "1.7.2"
OrderedCollections = "1.3.0"
PDMats = "0.11.11"
PSIS = "0.9.1"
PrettyTables = "2.1"
PrettyTables = "2.1, 3"
Printf = "1"
Random = "1"
Roots = "1, 2"
Expand Down
4 changes: 2 additions & 2 deletions src/compare.jl
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ function _show(io::IO, mime::MIME, r::ModelComparisonResult; kwargs...)
weights_method_name = _typename(r.weights_method)
weights = table.weight
digits_weights = ceil(Int, -log10(maximum(weights))) + 1
weight_formatter = PrettyTables.ft_printf(
"%.$(digits_weights)f", findfirst(==(:weight), cols)
weight_formatter = _prettytables_printf_formatter(
"%.$(digits_weights)f", [findfirst(==(:weight), cols)]
)
return _show_prettytable(
io,
Expand Down
127 changes: 89 additions & 38 deletions src/show_prettytable.jl
Original file line number Diff line number Diff line change
@@ -1,25 +1,42 @@
# Utilities for displaying tables using PrettyTables.jl

@static if pkgversion(PrettyTables).major == 2
# temporarily support PrettyTables v2 until v3 is more broadly established in the ecosystem
const IS_PRETTYTABLES_V2 = true
const PRETTYTABLES_TEXT_FORMAT = (; hlines=:none, vlines=:none)
const PRETTYTABLES_TEXT_STYLE = (; title_crayon=PrettyTables.Crayon())
_prettytables_printf_formatter(fmt::String, cols) = PrettyTables.ft_printf(fmt, cols)
else
const IS_PRETTYTABLES_V2 = false
const PRETTYTABLES_TEXT_FORMAT = PrettyTables.TextTableFormat(;
PrettyTables.@text__no_vertical_lines, PrettyTables.@text__no_horizontal_lines
)
const PRETTYTABLES_TEXT_STYLE = PrettyTables.TextTableStyle(;
title=PrettyTables.Crayon()
)
_prettytables_printf_formatter(fmt::String, cols) = PrettyTables.fmt__printf(fmt, cols)
end

# formatting functions for special columns
# see https://ronisbr.github.io/PrettyTables.jl/stable/man/usage/#Formatters

# Use Printf to format real elements to the number of `sigdigits`.
function ft_printf_sigdigits(sigdigits::Int)
function _prettytables_sigdigits_formatter(sigdigits::Int)
return (v, _, _) -> begin
v isa Real || return v
return _printf_with_sigdigits(v, sigdigits)
end
end
function ft_printf_sigdigits(sigdigits::Int, columns::AbstractVector{Int})
isempty(columns) && return ft_printf_sigdigits(sigdigits)
function _prettytables_sigdigits_formatter(sigdigits::Int, columns::AbstractVector{Int})
isempty(columns) && return _prettytables_sigdigits_formatter(sigdigits)
return (v, _, j) -> begin
(v isa Real && j ∈ columns) || return v
return _printf_with_sigdigits(v, sigdigits)
end
end

# Use Printf to format interval elements to the number of `sigdigits`.
function ft_printf_sigdigits_interval(sigdigits::Int)
function _prettytables_interval_formatter(sigdigits::Int)
return (v, _, _) -> begin
v isa IntervalSets.AbstractInterval || return v
tuple_string = map(Base.Fix2(_printf_with_sigdigits, sigdigits), extrema(v))
Expand All @@ -32,7 +49,7 @@ function _interval_delimiter(x::IntervalSets.AbstractInterval)
return occursin(" .. ", str) ? " .. " : ".."
end

function ft_printf_sigdigits_matching_se(data, col::Int, se_col::Int; kwargs...)
function _prettytables_sigdigits_from_se_formatter(data, col::Int, se_col::Int; kwargs...)
se_vals = Tables.getcolumn(data, se_col)
return (v, i, j) -> begin
(v isa Real && col == j && se_vals[i] isa Real) || return v
Expand Down Expand Up @@ -61,34 +78,36 @@ function _prettytables_se_formatters(data; sigdigits_se=2)
col ∈ col_names || continue
idx_col = Tables.columnindex(data, col)
idx_col == 0 && continue
push!(formatters, ft_printf_sigdigits_matching_se(data, idx_col, idx_se_col))
push!(
formatters, _prettytables_sigdigits_from_se_formatter(data, idx_col, idx_se_col)
)
end
if !isempty(se_cols_inds)
push!(formatters, ft_printf_sigdigits(sigdigits_se, se_cols_inds))
push!(formatters, _prettytables_sigdigits_formatter(sigdigits_se, se_cols_inds))
end
return formatters
end

function _prettytables_ess_formatter(data)
cols = findall(_is_ess_label, Tables.columnnames(data))
isempty(cols) && return nothing
return PrettyTables.ft_printf("%d", cols)
return _prettytables_printf_formatter("%d", cols)
end

function _prettytables_rhat_formatter(data)
col_names = Tables.columnnames(data)
cols = findall(x -> (x === :rhat || startswith(string(x), "rhat_")), col_names)
isempty(cols) && return nothing
return PrettyTables.ft_printf("%.2f", cols)
return _prettytables_printf_formatter("%.2f", cols)
end

function _default_prettytables_formatters(data; sigdigits_se=2, sigdigits_default=3)
function _prettytables_default_formatters(data; sigdigits_se=2, sigdigits_default=3)
formatters = Union{Function,Nothing}[]
push!(formatters, _prettytables_integer_formatter(data))
append!(formatters, _prettytables_se_formatters(data; sigdigits_se))
push!(formatters, _prettytables_ess_formatter(data))
push!(formatters, ft_printf_sigdigits(sigdigits_default))
push!(formatters, ft_printf_sigdigits_interval(sigdigits_default))
push!(formatters, _prettytables_sigdigits_formatter(sigdigits_default))
push!(formatters, _prettytables_interval_formatter(sigdigits_default))
return filter(!isnothing, formatters)
end

Expand All @@ -102,14 +121,17 @@ function _text_alignment(data)
end

function _text_alignment_anchor_regex(data)
alignment_anchor_regex = Dict{Int,Vector{Regex}}()
alignment_anchor_regex = Pair{Int,Vector{Regex}}[]
for (i, k) in enumerate(Tables.columnnames(data))
v = Tables.getcolumn(data, k)
if eltype(v) <: Real && !(eltype(v) <: Integer) && !_is_ess_label(k)
alignment_anchor_regex[i] = [r"\.", r"e", r"^NaN$", r"Inf$"]
patterns = if eltype(v) <: Real && !(eltype(v) <: Integer) && !_is_ess_label(k)
[r"\.", r"e", r"^NaN$", r"Inf$"]
elseif eltype(v) <: IntervalSets.AbstractInterval
alignment_anchor_regex[i] = [r"\.\."]
[r"\.\."]
else
continue
end
push!(alignment_anchor_regex, i => patterns)
end
return alignment_anchor_regex
end
Expand All @@ -123,25 +145,37 @@ function _show_prettytable(
sigdigits_default=3,
extra_formatters=(),
alignment=_text_alignment(data),
show_subheader=false,
vcrop_mode=:middle,
show_omitted_cell_summary=true,
row_label_alignment=:l,
show_first_column_label_only=true,
vertical_crop_mode=:middle,
row_label_column_alignment=:l,
kwargs...,
)
formatters = (
formatters = [
extra_formatters...,
_default_prettytables_formatters(data; sigdigits_se, sigdigits_default)...,
_prettytables_default_formatters(data; sigdigits_se, sigdigits_default)...,
]
IS_PRETTYTABLES_V2 && return PrettyTables.pretty_table(
io,
data;
alignment,
formatters=Tuple(formatters),
merge(
(;
show_subheader=(!show_first_column_label_only),
vcrop_mode=vertical_crop_mode,
row_label_alignment=row_label_column_alignment,
),
kwargs,
)...,
)
PrettyTables.pretty_table(
io,
data;
show_subheader,
vcrop_mode,
show_omitted_cell_summary,
row_label_alignment,
formatters,
show_first_column_label_only,
vertical_crop_mode,
row_label_column_alignment,
alignment,
formatters,
kwargs...,
)
return nothing
Expand All @@ -151,32 +185,49 @@ function _show_prettytable(
io::IO,
::MIME"text/plain",
data;
title_crayon=PrettyTables.Crayon(),
hlines=:none,
vlines=:none,
newline_at_end=false,
style=PRETTYTABLES_TEXT_STYLE,
table_format=PRETTYTABLES_TEXT_FORMAT,
new_line_at_end=false,
title_alignment=:l,
alignment_anchor_regex=_text_alignment_anchor_regex(data),
alignment_anchor_fallback=:r,
kwargs...,
)
return _show_prettytable(
IS_PRETTYTABLES_V2 && return _show_prettytable(
io,
data;
backend=Val(:text),
title_crayon,
hlines,
vlines,
newline_at_end,
title_alignment,
alignment_anchor_regex=Dict(alignment_anchor_regex),
alignment_anchor_fallback,
merge((; style..., table_format..., newline_at_end=new_line_at_end), kwargs)...,
)
return _show_prettytable(
io,
data;
backend=:text,
style,
table_format,
new_line_at_end,
title_alignment,
alignment_anchor_regex,
alignment_anchor_fallback,
kwargs...,
)
end

function _show_prettytable(
io::IO, ::MIME"text/html", data; minify=true, max_num_of_rows=25, kwargs...
io::IO, ::MIME"text/html", data; minify=true, maximum_number_of_rows=25, kwargs...
)
IS_PRETTYTABLES_V2 && return _show_prettytable(
io,
data;
backend=Val(:html),
minify,
merge((; max_num_of_rows=maximum_number_of_rows), kwargs)...,
)
return _show_prettytable(
io, data; backend=Val(:html), minify, max_num_of_rows, kwargs...
io, data; backend=:html, minify, maximum_number_of_rows, kwargs...
)
end

Expand Down
10 changes: 5 additions & 5 deletions test/show_prettytable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ using PosteriorStats
using Test

@testset "utilities for showing tables" begin
@testset "ft_printf_sigdigits" begin
@testset "_prettytables_sigdigits_formatter" begin
@testset "all columns" begin
@testset for sigdigits in 1:5
ft1 = PosteriorStats.ft_printf_sigdigits(sigdigits)
ft1 = PosteriorStats._prettytables_sigdigits_formatter(sigdigits)
for i in 1:10, j in 1:5
v = randn()
@test ft1(v, i, j) ==
Expand All @@ -16,7 +16,7 @@ using Test
end
@testset "subset of columns" begin
@testset for sigdigits in 1:5
ft = PosteriorStats.ft_printf_sigdigits(sigdigits, [2, 3])
ft = PosteriorStats._prettytables_sigdigits_formatter(sigdigits, [2, 3])
for i in 1:10, j in 1:5
v = randn()
if j ∈ [2, 3]
Expand All @@ -31,10 +31,10 @@ using Test
end
end

@testset "ft_printf_sigdigits_matching_se" begin
@testset "_prettytables_sigdigits_from_se_formatter" begin
data = (x=randn(10), x_se=rand(10), y=randn(10), y_se=rand(10))
@testset for scale in 1:3, (col, se_col) in ((1, 2), (3, 4))
ft = PosteriorStats.ft_printf_sigdigits_matching_se(data, 1, 2; scale)
ft = PosteriorStats._prettytables_sigdigits_from_se_formatter(data, 1, 2; scale)
for i in eachindex(values(data)...), j in eachindex(data)
v = data[j][i]
if j == col
Expand Down
Loading