Repository navigation
Accept Tables.jl tables in randomized_tukey_hsd - #48
Merged
Merged
Conversation
#41 makes the library accept Tables.jl tables, which needs Tables.jl at run time. Tables was a dependency until #29, which moved it to the test extras because nothing used it; it comes back to [deps]. The [compat] entry (Tables = "1") was kept in #29, so it needs no change. Tables.jl is small and pure Julia, and it is the interface DataFrames, CSV and others implement, so the library still depends on none of them. DataFrames becomes a test-only dependency. #41 promises that a DataFrame works, and the literal claim is worth a test; the library never loads it. Tables stays available to the tests as a dependency of the package. Refs #41 Part of #38 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds randomized_tukey_hsd([rng,] table; trials, block, treatment, value) in src/tables.jl, so a DataFrame (or any Tables.jl table) can be passed as it is and the treatment names come with the data instead of through a separate systems argument. The shape is told by which column keywords are given: - none: wide, each column is a treatment (named by the column name, as a string), each row a block; only `block` leaves out an id column; - all three of block, treatment and value: tidy, one row per block and treatment, in the order they first appear; - anything else is an ArgumentError that says what is expected. Column names may be Symbols or Strings. There are deliberately no IR default names (topic, system, score) in the library; the CLI keeps them. The matrix method is untouched and remains the core: the table methods only convert (with scores_matrix and wide_scores from #37) and call it. Julia picks the matrix method for a real matrix because it is more specific, although a matrix is itself a table. A matrix whose elements are not real (say Union{Missing,Float64}) would otherwise be read as a table with columns named Column1, Column2, ...; it is rejected with a message that suggests NaN instead. A missing score, or a pair with no row, becomes NaN and is rejected by the existing NaN check, the same as for the matrix method. The two helpers are underscored and unexported. The positional tuples that scores_matrix and friends return are left as they are; their shape is a question carried on #41. Refs #41 Part of #38 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
test/table_input.jl builds tables holding the same 4 topics x 3 systems
scores as the matrix tests and requires the same results for the same
generator state, which is a stronger check than "looks plausible":
- wide, as a NamedTuple of columns, with and without an id column left
out by `block` (given as a Symbol and as a String);
- tidy, equal to the matrix call when rows come grouped, and, with rows
in a shuffled order, the same pairs and differences up to the last bit
(the topics are summed in another order). The p-values are not compared
in that case: another order of appearance puts treatments in other
columns, so the same generator state shuffles differently. The test says
so;
- other kinds of table: a vector of named tuples, and a DataFrame both
wide and tidy (DataFrames is a test-only dependency, see the previous
commit);
- the default generator, seeded with Random.seed!;
- a missing score and an absent pair end in the NaN rejection;
- a real matrix still takes the matrix method, and a matrix that is not
real (Union{Missing,Float64}) is rejected with the NaN hint, not read
as a table with columns Column1, Column2;
- errors: not a table, unknown column (named, with the available ones),
tidy roles given partly in each way, the same column in two roles,
trials = 0 through the table path, and `systems=` is a MethodError
because the names come from the table.
Refs #41
Part of #38
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds a "Passing a table" section under "Using the library" with the wide and tidy calls, what names the treatments in each, how rows are ordered, and what happens to missing scores. It sits after the paragraph that describes the result, so that paragraph stays next to the call it is about, and the installation note now lists Tables among the library's dependencies (Random, Statistics and Tables). The examples use a DataFrame called df, as the section says any Tables.jl table works and the library does not depend on DataFrames. Refs #41 Part of #38 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The wide path converted the column names to Strings before reporting them. The reason given on the PR was that this matched the matrix path and the CLI, and on review that turned out not to hold: - the CLI never goes through this method; it reads wide data with wide_scores and the names DataFrames gives it (already Strings); - the matrix path has no preferred type, since systems= takes whatever the caller passes and defaults to the column numbers; - CSV writes a Symbol as text, so no output needed the conversion. It came from adapting the library to a habit of the CLI side, where the library should follow Julia: Tables.jl names columns with Symbols, and the tidy path already returns the treatment values as they are. The wide path now does the same and reports the names as the table gave them. The wide tests compare with a matrix call whose systems are the Symbols, and check the pairs by Symbol; tidy results are unchanged. The docstring and the README say Symbol. The CLI is not affected. Refs #41 Part of #38 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This was referenced Oct 22, 2026
jerome-white
added a commit
that referenced
this pull request
Oct 22, 2026
* Remove the command-line interface Deletes bin/rthsd.jl and bin/Project.toml (the CLI and its separate environment: ArgParse, CSV, DataFrames and a [sources] entry that needed Julia 1.11). Julia packages are libraries that users import from their own script or notebook, not programs run from a shell (Turing, SciML, JuMP, Distributions and DataFrames are all used that way), and the language's start-up and compile cost makes a CLI a poor fit: on a 4 x 3 file the CLI took about 3.7 s per run against about 0.8 s for the library alone. The CLI was also shaping the library (helpers placed in src/ only to serve it, a String conversion adopted to match it) when the library should be designed Julia-first and anything else built on it. Since the table methods of #41 the library already takes a matrix, or a wide or tidy Tables.jl table, so getting data in is a line or two of CSV.jl and DataFrames.jl in the user's own script. The README and a short example script follow in later commits. The removed code is in git history: it is the tree of the parent of this commit, and described by the README there. The helpers only the CLI used (tidy_columns, incomplete_topics) are removed in the next commit, so that this one is only the deletion of bin/. Refs #49 Part of #38 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Remove the helpers that only the CLI used tidy_columns decided which CSV columns held the block, treatment and score, with a fallback to the IR names topic, system and score; incomplete_topics flagged blocks with a NaN for the CLI's --missing drop. With the CLI gone (previous commit) nothing calls either, and the table methods of #41 ask for the columns explicitly, so they are deleted with their tests (tidy_columns: 7 groups of checks; incomplete_topics: the flags and the "dropping them leaves a matrix the test accepts" check; one assertion in the wide_scores test that used incomplete_topics). scores_matrix and wide_scores stay: the table methods use them, and their tests are unchanged. The two docstrings that pointed at incomplete_topics now say that a missing score becomes NaN, which the test rejects. A header comment in test/input.jl no longer mentions the CLI. The test suite goes from 151 to 136 passing (plus the 1 deliberately broken), all removed tests being of the deleted functions. This also removes the second IR-fallback surface noted in the review of #48 (the topic/system/score names in tidy_columns), which #42 had still to decide. Refs #49 Part of #38 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Add an example script for reading a CSV examples/run.jl reads a tidy CSV, calls randomized_tukey_hsd on the DataFrame with block, treatment and value named, and writes the results as CSV to stdout. It takes the path, the number of trials and an optional seed from ARGS: julia --project=examples examples/run.jl scores.csv 100000 [seed] It stands in for what the removed CLI did for the common case, in about ten lines of ordinary Julia that a user can copy and change, and is documented as an example, not a supported tool. examples/Project.toml has the environment (CSV, DataFrames, Random and the library through a [sources] path entry, so Julia 1.11 or newer, like benchmark/). Checked on test/data/tiny.csv with 500 trials and seed 1: the p-values (0.386, 0.0, 0.38) are the ones the removed CLI gave for the same input and seed, since both call the same library method. Refs #49 Part of #38 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * README: no CLI; read a CSV from a script, and drop incomplete blocks Replaces the "Running the CLI" section with "Reading a CSV", which points at examples/run.jl and says why there is no command-line tool (you call the function from your own script, as with other Julia packages) and that the examples environment needs Julia 1.11 (its [sources] entry). "Missing scores" keeps the explanation (a NaN poisons every p-value; the sound option is complete cases; why nanmean is not offered) and replaces the --missing flag with DataFrames recipes: dropmissing and a NaN filter for a wide table, and a groupby filter for a tidy one that also drops blocks with a missing row. The tidy recipe was run on the old test inputs (NaN, absent row, empty cell) and keeps 3 of 4 blocks with the same first p-value (0.606) the old --missing drop gave; the wide one drops the missing and the NaN row (4 rows to 2 in the check). "Threads and reproducibility" now shows the example script with -t auto and says the script takes the seed as its third argument; nothing else in it changes. The layout table lists examples/ instead of bin/, and the benchmark README no longer says "like bin/" about the Julia 1.11 requirement. Refs #49 Part of #38 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #41. Part of #38.
What this does
randomized_tukey_hsdnow accepts a Tables.jl table (aDataFrame, aNamedTupleof columns, a vector of rows, ...), so the treatment names come with the data and there is no separatesystemsargument for them:The matrix method is untouched and stays the core. The table methods (new
src/tables.jl) convert withscores_matrixandwide_scoresfrom #37, then call it. The generator stays optional as the first argument, as on the matrix method.Decisions
block: wide, skipping that column (an id). All three ofblock,treatment,value: tidy. Anything else is anArgumentErrorsaying what is expected. This is the "presence of the keywords" signal proposed on Accept Tables.jl tables in the library #41.tidy_columnsinsrc/input.jl(from CLI: choose the columns, read wide CSV, take a file argument #40, used only by the CLI) still has thetopic/system/scorefallback, and the IR words are throughout the identifiers (systems=,system1,sysmeans, ...). Neutral vocabulary: blocks and treatments in docs and output #42 is widened to cover all of it, and to move CLI-only conveniences toward the CLI.Symbols orStrings. Wide names the treatments by the column names as the table gives them,Symbols; tidy uses the values of thetreatmentcolumn as they are. Blocks and treatments are taken in the order they first appear, as in Move the CLI's input handling into the library #37. (A first version of this PR converted the wide names to strings, to "match the CLI". On review that was wrong: the CLI does not use this method, the matrix path has no preferred type, and a Julia-first design returns what Tables.jl gives. Changed in the fifth commit.)Union{Missing,Float64}, would reach the table method and be read as a table with columns namedColumn1,Column2, ...; it is rejected witha matrix must have real elements, got ...; use NaN for a missing scoreinstead of silently doing that.missing, or a block and treatment with no row) becomeNaNand are rejected by the existing NaN check, exactly as for the matrix method. The CLI's--missing dropstill handles them before calling the library.Tablesis back in[deps](the[compat]entry was kept in Test the Tables.jl interface and drop the unused Tables dependency #29), so the library depends onRandom,StatisticsandTables, and on neither DataFrames nor CSV.DataFramesbecomes a test-only dependency, so that the claim "a DataFrame works" is tested; the library never loads it. The README says so.src/input.jlare left as they are; their shape is the open question carried on Accept Tables.jl tables in the library #41, and nothing here forces it.src/beyond performance and style, with the owner's approval on Accept Tables.jl tables in the library #41 and Interaction layer: domain-neutral names and inputs #38.Commits
Make Tables a dependency of the library; DataFrames a test dependency(Project.toml)Accept Tables.jl tables in randomized_tukey_hsd(src/tables.jl,src/RandomizedTukeyHSD.jl)Test the table methods against the matrix method(test/table_input.jl)Document passing a table in the READMEName the treatments of a wide table by the Symbols the table givesVerification
] test: 151 pass + 1 deliberately broken, at default and at 4 threads (before: 130 + 1); the default-thread run was repeated after the fifth commit.test/table_input.jlrequires the table forms to equal the matrix call for the same generator state: wide, wide with an id column left out (asSymbolandString), tidy, a vector of rows, aDataFramewide and tidy, and the default generator. Tidy rows in shuffled order give the same pairs and differences up to the last bit; their p-values are deliberately not compared, because another order of appearance puts the treatments in other columns and so shuffles differently (the test says why).trials = 0through the table path,systems=(aMethodError, since the names come from the table), and the non-real matrix.benchmark/snapshot.jloutput is byte-identical to before (the matrix path is unchanged). The CLI is untouched.DataFramesas a test dependency there.Not covered
wide_scores, naming the column).🤖 Generated with Claude Code