Skip to content

Store column views in CoefTable and reject offset arrays - #1026

Merged
devmotion merged 1 commit into
masterfrom
dmw/coeftable-followup
Nov 10, 2026
Merged

devmotion merged 1 commit into
masterfrom
dmw/coeftable-followup

Conversation

@devmotion

Copy link
Copy Markdown
Member

Follow-up to #1020, addressing review comments that were posted after it was merged.

Column storage. CoefTable(::AbstractMatrix, ...) now stores collect(Any, eachcol(mat)), which holds views, instead of copying each column. Before #1020, the Matrix method copied via mat[:, i], but nothing depended on that. StatsBase only reads ct.cols (in length, eltype, iterate and both show methods), and the cols::Vector constructor stores the caller's vectors without copying. #1020 had added tests that checked the copy; this PR removes them.

Offset arrays. Both constructors now call Base.require_one_based_indexing and throw an ArgumentError for offset-indexed input. Before this:

  • CoefTable(Any[offset_vector, ...], ...) built the table, and then show and iterate failed with a BoundsError, because they index 1:nr.
  • An offset matrix failed with a MethodError, because collect(Any, eachcol(mat)) returns an OffsetVector{Any}, which is not a Vector.

Tests

  • Use repr("text/plain", x) / repr("text/markdown", x), as suggested in review.
  • Check the exact ArgumentError messages.
  • Cover offset arrays for the matrix constructor (row axis and column axis offset) and for the vector-of-columns constructor.
  • Restore the indentation of a comment in the "StatsAPI model reexports" testset that Accept abstract matrices in CoefTable #1020 changed.

Tested: test/statmodels.jl passes on Julia 1.10 and 1.13, and the full test suite passes on 1.13.

🤖 Generated with Claude Code

… arrays

- Store views of the matrix columns instead of copies, as suggested in
  review. StatsBase never mutates `ct.cols`, and the `cols::Vector`
  constructor does not copy either.
- Throw an `ArgumentError` for offset-indexed inputs in both
  constructors. Previously, offset columns were accepted and `show` and
  `iterate` failed later with a `BoundsError`, and offset matrices failed
  with a `MethodError`.
- Tests: use `repr`, check exact error messages, cover offset arrays for
  both constructors, and restore indentation of a comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread src/statmodels.jl
function CoefTable(mat::AbstractMatrix,colnms::Vector,rownms::Vector,
pvalcol::Int=0,teststatcol::Int=0)
cols = Any[collect(col) for col in eachcol(mat)]
Base.require_one_based_indexing(mat)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything in this method that relies on one based indexing? Same for the other one.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Methods for CoefTable such as the show method assume 1-based indexing, eg in

mat = [j == 1 ? NoQuote(rownms[i]) :
j-1 == ct.pvalcol ? NoQuote(sprint(show, PValue(cols[j-1][i]))) :
j-1 in ct.teststatcol ? TestStat(cols[j-1][i]) :
cols[j-1][i] isa AbstractString ? NoQuote(escape_markdown_pipes(cols[j-1][i])) : cols[j-1][i]
for i in 1:nr, j in 1:nc+1]

@andreasnoack andreasnoack Nov 10, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it can safely assume that because the fields are Arrays.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fields are Arrays, but their elements are indexed as well here, and those are not guaranteed to use 1-based indexing. The assumption also shows up in other places, e.g. in iterate:

cols = getindex.(ct.cols, Ref(i))

@devmotion
devmotion merged commit 4866245 into master Nov 10, 2026
9 of 10 checks passed
@devmotion
devmotion deleted the dmw/coeftable-followup branch November 10, 2026 19:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants