diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..3b6d99a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + open-pull-requests-limit: 1 + labels: + - "dependencies" + - "github-actions" diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..23e3402 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,58 @@ +name: CI +on: + merge_group: + pull_request: + push: + branches: + - master + tags: + - '*' +concurrency: + # Skip intermediate builds: all builds except for builds on the `master` branch + # Cancel intermediate builds: only pull request builds + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.ref != 'refs/heads/master' || github.run_number }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +permissions: + contents: read +jobs: + finalize: + timeout-minutes: 10 + needs: [test] + if: always() + runs-on: [based-on-debian, self-hosted, linux, x64] + steps: + - run: | + echo test: ${{ needs.test.result }} + - run: exit 1 + if: | + (needs.test.result != 'success') + + test: + env: + JULIA_PKG_SERVER: ${{ secrets.JULIA_PKG_SERVER }} + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + matrix: + version: + - '1' + os: + - [based-on-debian, self-hosted, linux, x64] + - [windows, x64, inside-docker, self-hosted] + arch: + - x64 + steps: + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 + with: + persist-credentials: false + - uses: julia-actions/setup-julia@f40c4b69330df1d22e7590c12e76dc2f9c66e0bc + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: PumasAI/add-private-registry@9e7f77818be0d19044e5e752205b6ded86af2cb8 + with: + juliahub_token_encoded: ${{ secrets.JULIAPRO_TOKEN_ENCODED }} + private_registry_name: ${{ secrets.PUMASREGISTRY_NAME }} + private_registry_uuid: ${{ secrets.PUMASREGISTRY_UUID }} + - uses: julia-actions/julia-buildpkg@00f9fd6b2600be0a8d80566dd4d4d2389b3468eb + - uses: julia-actions/julia-runtest@79a7e100883947123f8263c5f06e6c0ea3eb972f diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml new file mode 100644 index 0000000..06791f1 --- /dev/null +++ b/.github/workflows/CompatHelper.yml @@ -0,0 +1,48 @@ +name: CompatHelper +on: + schedule: + - cron: 0 0 * * * + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +env: + JULIA_PKG_SERVER: ${{ secrets.JULIA_PKG_SERVER }} + +jobs: + CompatHelper: + runs-on: [based-on-debian, self-hosted, linux, x64] + steps: + - uses: julia-actions/setup-julia@f40c4b69330df1d22e7590c12e76dc2f9c66e0bc + with: + version: '1.9' + + - name: Add the Pumas Registry + uses: PumasAI/add-private-registry@93e0d6c748c851aabfeac9f27e5c6d211a05dc33 + with: + juliahub_token_encoded: ${{ secrets.JULIAPRO_TOKEN_ENCODED }} + private_registry_name: ${{ secrets.PUMASREGISTRY_NAME }} + private_registry_uuid: ${{ secrets.PUMASREGISTRY_UUID }} + + - name: "Install CompatHelper" + run: | + import Pkg + name = "CompatHelper" + uuid = "aa819f21-2bde-4658-8897-bab36330d9b7" + version = "3" + Pkg.add(; name, uuid, version) + shell: julia --color=yes {0} + + - name: "Run CompatHelper" + run: | + import CompatHelper, Pkg + CompatHelper.main( + registries = [Pkg.RegistrySpec(uuid=ri.uuid) for ri in Pkg.Registry.reachable_registries()], + use_existing_registries = true + ) + shell: julia --color=yes {0} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }} diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 0000000..7bb9010 --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,44 @@ +name: Documentation + +on: + merge_group: + pull_request: + push: + branches: + - master + tags: + - '*' +concurrency: + # Skip intermediate builds: all builds except for builds on the `master` branch + # Cancel intermediate builds: only pull request builds + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.ref != 'refs/heads/master' || github.run_number }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} + +jobs: + build: + env: + JULIA_PKG_SERVER: ${{ secrets.JULIA_PKG_SERVER }} + permissions: + contents: write + statuses: write + pages: write # to deploy to Pages + id-token: write # to verify the deployment originates from an appropriate source + pull-requests: write + runs-on: [based-on-debian, self-hosted, linux, x64] + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: julia-actions/setup-julia@f40c4b69330df1d22e7590c12e76dc2f9c66e0bc + with: + version: '1' + - uses: PumasAI/add-private-registry@9e7f77818be0d19044e5e752205b6ded86af2cb8 + with: + juliahub_token_encoded: ${{ secrets.JULIAPRO_TOKEN_ENCODED }} + private_registry_name: ${{ secrets.PUMASREGISTRY_NAME }} + private_registry_uuid: ${{ secrets.PUMASREGISTRY_UUID }} + - name: Install dependencies + run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' + - name: Build and deploy + env: + JULIA_DEBUG: Documenter + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: julia --project=docs/ docs/make.jl \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd3c7fe --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +Manifest.toml +*.pdf +/output +/cache +/docs/build +.DS_Store \ No newline at end of file diff --git a/Project.toml b/Project.toml new file mode 100644 index 0000000..de4d19b --- /dev/null +++ b/Project.toml @@ -0,0 +1,34 @@ +name = "SummaryTables" +uuid = "6ce4ecf0-73a7-4ce3-9fb4-80ebfe887b60" +authors = ["Mike ", "Julius "] +version = "0.9.6" + +[deps] +CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +EnumX = "4e289a0a-7415-4d19-859d-a7e5c4648b56" +HypothesisTests = "09f84164-cd44-5f33-b23f-e6b0d136a0d5" +MultipleTesting = "f8716d33-7c4a-5097-896f-ce0ecbd3ef6b" +NaturalSort = "c020b1a1-e9b0-503a-9c33-f039bfc54a85" +OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" +SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +WriteDocx = "d049ceea-54ee-41d7-a26f-ba29db3b6599" + +[compat] +CategoricalArrays = "0.10" +DataFrames = "1" +EnumX = "1" +HypothesisTests = "0.10, 0.11" +MultipleTesting = "0.5, 0.6" +NaturalSort = "1.0" +OrderedCollections = "1" +SHA = "0.7" +Statistics = "1" +StatsBase = "0.33, 0.34" +Tables = "1" +WriteDocx = "0.5" +julia = "1.7" diff --git a/README.md b/README.md new file mode 100644 index 0000000..f4df264 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# SummaryTables.jl + +
+ + SummaryTables.jl logo + +
+ +[![][docs-stable-img]][docs-stable-url] +[![][docs-master-img]][docs-master-url] + +[docs-stable-img]: https://img.shields.io/badge/Docs-Stable-lightgrey.svg +[docs-stable-url]: https://pumasai.github.io/SummaryTables.jl/stable/ +[docs-master-img]: https://img.shields.io/badge/Docs-Dev-blue.svg +[docs-master-url]: https://pumasai.github.io/SummaryTables.jl/dev/ diff --git a/docs/Project.toml b/docs/Project.toml new file mode 100644 index 0000000..fb32c35 --- /dev/null +++ b/docs/Project.toml @@ -0,0 +1,7 @@ +[deps] +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +SummaryTables = "6ce4ecf0-73a7-4ce3-9fb4-80ebfe887b60" +Typst_jll = "eb4b1da6-20f6-5c66-9826-fdb8ad410d0e" +WriteDocx = "d049ceea-54ee-41d7-a26f-ba29db3b6599" +tectonic_jll = "d7dd28d6-a5e6-559c-9131-7eb760cdacc5" diff --git a/docs/make.jl b/docs/make.jl new file mode 100644 index 0000000..114d29f --- /dev/null +++ b/docs/make.jl @@ -0,0 +1,24 @@ +using Documenter, SummaryTables + +makedocs( + sitename = "SummaryTables.jl", + pages = [ + "index.md", + "output.md", + "Predefined Tables" => [ + "predefined_tables/listingtable.md", + "predefined_tables/summarytable.md", + "predefined_tables/table_one.md", + ], + "Custom Tables" => [ + "custom_tables/table.md", + "custom_tables/cell.md", + "custom_tables/cellstyle.md", + ], + ] +) + +deploydocs( + repo = "github.com/PumasAI/SummaryTables.jl.git", + push_preview = true, +) \ No newline at end of file diff --git a/docs/src/api.md b/docs/src/api.md new file mode 100644 index 0000000..b801536 --- /dev/null +++ b/docs/src/api.md @@ -0,0 +1,5 @@ +# API + +```@autodocs +Modules = [SummaryTables] +``` \ No newline at end of file diff --git a/docs/src/assets/_logo.svg b/docs/src/assets/_logo.svg new file mode 100644 index 0000000..d6268c9 --- /dev/null +++ b/docs/src/assets/_logo.svg @@ -0,0 +1,150 @@ + + + + diff --git a/docs/src/assets/icon_docx.png b/docs/src/assets/icon_docx.png new file mode 100644 index 0000000..0293130 Binary files /dev/null and b/docs/src/assets/icon_docx.png differ diff --git a/docs/src/assets/icon_pdf.png b/docs/src/assets/icon_pdf.png new file mode 100644 index 0000000..f716b3c Binary files /dev/null and b/docs/src/assets/icon_pdf.png differ diff --git a/docs/src/assets/logo.png b/docs/src/assets/logo.png new file mode 100644 index 0000000..52d6682 Binary files /dev/null and b/docs/src/assets/logo.png differ diff --git a/docs/src/custom_tables/cell.md b/docs/src/custom_tables/cell.md new file mode 100644 index 0000000..ebbc6b6 --- /dev/null +++ b/docs/src/custom_tables/cell.md @@ -0,0 +1,139 @@ +# `Cell` + +## Argument 1: `value` + +This is the content of the `Cell`. +How it is rendered is decided by the output format and what `show` methods are defined for the type of `value` and the respective output `MIME` type. +If no output-specific `MIME` type has a `show` method, the fallback is always the generic text output. + +The following are some types which receive special handling by SummaryTables. + +### Special `Cell` value types + +#### Floating point numbers + +Most tables display floating point numbers, however, the formatting of these numbers can vary. +SummaryTables postprocesses every table in order to find unformatted floating point numbers. +These are then given the default, table-wide, formatting. + +```@example +using SummaryTables + +cells = [ + Cell(1.23456) Cell(12.3456) + Cell(0.123456) Cell(0.0123456) +] +Table(cells) +``` + +```@example +using SummaryTables + +cells = [ + Cell(1.23456) Cell(12.3456) + Cell(0.123456) Cell(0.0123456) +] +Table(cells; round_mode = :digits, round_digits = 5, trailing_zeros = true) +``` + +#### `Concat` + +All the arguments of `Concat` are concatenated together in the final output. +Note that this is usually preferrable to string-interpolating multiple values because you lose special handling of the value types (like floating point rounding behavior or special LaTeX formatting) if you turn them into strings. + +```@example +using SummaryTables +using Statistics + +some_numbers = [1, 2, 4, 7, 8, 13, 27] +mu = mean(some_numbers) +sd = std(some_numbers) + +cells = [ + Cell("Mean (SD) interpolated") Cell("$mu ($sd)") + Cell("Mean (SD) Concat") Cell(Concat(mu, " (", sd, ")")) +] + +Table(cells) +``` + +#### `Multiline` + +Use the `Multiline` type to force linebreaks between different values in a cell. +A `Multiline` value may not be nested inside other values in a cell, it may only be the outermost value. +All nested values retain their special behaviors, so using `Multiline` is preferred over hardcoding linebreaks in the specific output formats yourself. + +```@example +using SummaryTables + +cells = [ + Cell(Multiline("A1 a", "A1 b")) Cell("B1") + Cell("A2") Cell("B2") +] + +Table(cells) +``` + +#### `Annotated` + +To annotate elements in a table with footnotes, use the `Annotated` type. +It takes an arbitrary `value` to annotate as well as an `annotation` which becomes a footnote in the table. +You can also pass the `label` keyword if you don't want an auto-incrementing number as the label. +You can also pass `label = nothing` if you want a footnote without label. + +```@example +using SummaryTables + +cells = [ + Cell(Annotated("A1", "This is the first cell")) Cell("B1") + Cell(Annotated("A2", "A custom label", label = "x")) Cell("B2") + Cell(Annotated("-", "- A missing value", label = nothing)) Cell("B3") +] + +Table(cells) +``` + +#### `Superscript` + +Displays the wrapped value in superscript style. +Use this instead of hardcoding output format specific commands. + +```@example +using SummaryTables + +cells = [ + Cell("Without superscript") Cell(Concat("With ", Superscript("superscript"))); +] + +Table(cells) +``` + +#### `Subscript` + +Displays the wrapped value in subscript style. +Use this instead of hardcoding output format specific commands. + +```@example +using SummaryTables + +cells = [ + Cell("Without subscript") Cell(Concat("With ", Subscript("subscript"))); +] + +Table(cells) +``` + +## Optional argument 2: `cellstyle` + +You may pass the style settings of a `Cell` as a positional argument of type [`CellStyle`](@ref). +It is usually more convenient, however, to use the keyword arguments to `Cell` instead. + +```@example +using SummaryTables + + +Table([ + Cell("A1", CellStyle(bold = true)) Cell("B1", CellStyle(underline = true)) + Cell("A2", CellStyle(italic = true)) Cell("B2", CellStyle(indent_pt = 10)) +]) +``` diff --git a/docs/src/custom_tables/cellstyle.md b/docs/src/custom_tables/cellstyle.md new file mode 100644 index 0000000..8c8310e --- /dev/null +++ b/docs/src/custom_tables/cellstyle.md @@ -0,0 +1,154 @@ +# `CellStyle` + +## Keyword: `bold` + +Makes the text in the cell bold. + +```@example +using SummaryTables + +cells = reshape([ + Cell("Some text in bold", bold = true), +], :, 1) + +Table(cells) +``` + +## Keyword: `italic` + +Makes the text in the cell italic. + +```@example +using SummaryTables + +cells = reshape([ + Cell("Some text in italic", italic = true), +], :, 1) + +Table(cells) +``` + +## Keyword: `underline` + +Underlines the text in the cell. + +```@example +using SummaryTables + +cells = reshape([ + Cell(Multiline("Some", "text", "that is", "underlined"), underline = true), +], :, 1) + +Table(cells) +``` + +## Keyword: `halign` + +Aligns the cell content horizontally either at the `:left`, the `:center` or the `:right`. + +```@example +using SummaryTables + +cells = reshape([ + Cell("A wide cell"), + Cell(":left", halign = :left), + Cell(":center", halign = :center), + Cell(":right", halign = :right), +], :, 1) + +Table(cells) +``` + +## Keyword: `valign` + +Aligns the cell content vertically either at the `:top`, the `:center` or the `:bottom`. + +```@example +using SummaryTables + +cells = reshape([ + Cell(Multiline("A", "tall", "cell")), + Cell(":top", valign = :top), + Cell(":center", valign = :center), + Cell(":bottom", valign = :bottom), +], 1, :) + +Table(cells) +``` + +## Keyword: `indent_pt` + +Indents the content of the cell on the left by the given number of `pt` units. +This can be used to give hierarchical structure to adjacent rows. + +```@example +using SummaryTables + +C(value; kwargs...) = Cell(value; halign = :left, kwargs...) + +cells = [ + C("Group A") C("Group B") + C("Subgroup A", indent_pt = 6) C("Subgroup B", indent_pt = 6) + C("Subgroup A", indent_pt = 6) C("Subgroup B", indent_pt = 6) +] + +Table(cells) +``` + +## Keyword: `border_bottom` + +Adds a border at the bottom of the cell. +This option is meant for horizontally merged cells functioning as subheaders. + +```@example +using SummaryTables + +header_cell = Cell("header", border_bottom = true, merge = true) + +cells = [ + header_cell header_cell + Cell("body") Cell("body") +] +Table(cells) +``` + +## Keyword: `merge` + +All adjacent cells that are `==` equal to each other and have `merge = true` will be rendered as one merged cell. + +```@example +using SummaryTables + +merged_cell = Cell("merged", valign = :center, merge = true) + +cells = [ + Cell("A1") Cell("B1") Cell("C1") Cell("D1") + Cell("A2") merged_cell merged_cell Cell("D2") + Cell("A3") merged_cell merged_cell Cell("D3") + Cell("A4") Cell("B4") Cell("C4") Cell("D4") +] +Table(cells) +``` + + + +## Keyword: `mergegroup` + +Because adjacent cells that are `==` equal to each other are merged when `merge = true` is set, you can optionally +set the `mergegroup` keyword of adjacent cells to a different value to avoid merging them even if their values are otherwise equal. + +```@example +using SummaryTables + +merged_cell_1 = Cell("merged", valign = :center, merge = true, mergegroup = 1) +merged_cell_2 = Cell("merged", valign = :center, merge = true, mergegroup = 2) + +cells = [ + Cell("A1") Cell("B1") Cell("C1") Cell("D1") + Cell("A2") merged_cell_1 merged_cell_2 Cell("D2") + Cell("A3") merged_cell_1 merged_cell_2 Cell("D3") + Cell("A4") Cell("B4") Cell("C4") Cell("D4") +] +Table(cells) +``` + diff --git a/docs/src/custom_tables/table.md b/docs/src/custom_tables/table.md new file mode 100644 index 0000000..3ddad14 --- /dev/null +++ b/docs/src/custom_tables/table.md @@ -0,0 +1,78 @@ +# `Table` + +You can build custom tables using the `Table` type. + +## Argument 1: `cells` + +The table's content is given as an `AbstractMatrix` of `Cell`s: + +```@example +using SummaryTables + +cells = [Cell("$col$row") for row in 1:5, col in 'A':'E'] +Table(cells) +``` + +## Keyword: `header` + +You can pass an `Int` to mark the last row of the header section. +A divider line is placed after this row. + +```@example +using SummaryTables + +cells = [Cell("$col$row") for row in 1:5, col in 'A':'E'] +Table(cells; header = 1) +``` + +## Keyword: `footer` + +You can pass an `Int` to mark the first row of the footer section. +A divider line is placed before this row. + +```@example +using SummaryTables + +cells = [Cell("$col$row") for row in 1:5, col in 'A':'E'] +Table(cells; footer = 5) +``` + +## Keyword: `footnotes` + +The `footnotes` keyword allows to add custom footnotes to the table which do not correspond to specific [`Annotated`](@ref) values in the table. + +```@example +using SummaryTables + +cells = [Cell("$col$row") for row in 1:5, col in 'A':'E'] +Table(cells; footnotes = ["Custom footnote 1", "Custom footnote 2"]) +``` + +## Keyword: `rowgaps` + +It can be beneficial for the readability of larger tables to add gaps between certain rows. +These gaps can be passed as a `Vector` of `Pair`s where the first element is the index of the row gap and the second element is the gap size in `pt`. + +```@example +using SummaryTables + +cells = [Cell("$col$row") for row in 1:9, col in 'A':'E'] +Table(cells; rowgaps = [3 => 8.0, 6 => 8.0]) +``` + +## Keyword: `colgaps` + +It can be beneficial for the readability of larger tables to add gaps between certain columns. +These gaps can be passed as a `Vector` of `Pair`s where the first element is the index of the column gap and the second element is the gap size in `pt`. + +```@example +using SummaryTables + +cells = [Cell("$col$row") for row in 1:5, col in 'A':'I'] +Table(cells; colgaps = [3 => 8.0, 6 => 8.0]) +``` + +## Types of cell values + +TODO: List the different options here + diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 0000000..4065203 --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,72 @@ +# SummaryTables + +SummaryTables is focused on creating tables for publications in LaTeX, docx and HTML formats. +It offers both convenient predefined table functions that are inspired by common table formats in the pharma space, as well as an API to create completely custom tables. + +It deliberately uses an opinionated, limited styling API so that styling can be as consistent as possible across the different backends. + +```@example +using SummaryTables +using DataFrames + +data = DataFrame( + sex = ["m", "m", "m", "m", "f", "f", "f", "f", "f", "f"], + age = [27, 45, 34, 85, 55, 44, 24, 29, 37, 76], + blood_type = ["A", "0", "B", "B", "B", "A", "0", "A", "A", "B"], + smoker = [true, false, false, false, true, true, true, false, false, false], +) + +table_one( + data, + [:age => "Age (years)", :blood_type => "Blood type", :smoker => "Smoker"], + groupby = :sex => "Sex", + show_n = true +) +``` + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + concentration = [1.2, 4.5, 2.0, 1.5, 0.1, 1.8, 3.2, 1.8, 1.2, 0.2], + id = repeat([1, 2], inner = 5), + time = repeat([0, 0.5, 1, 2, 3], 2) +) + +listingtable( + data, + :concentration => "Concentration (ng/mL)", + rows = :id => "ID", + cols = :time => "Time (hr)", + summarize_rows = [ + length => "N", + mean => "Mean", + std => "SD", + ] +) +``` + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + concentration = [1.2, 4.5, 2.0, 1.5, 0.1, 1.8, 3.2, 1.8, 1.2, 0.2], + id = repeat([1, 2], inner = 5), + time = repeat([0, 0.5, 1, 2, 3], 2) +) + +summarytable( + data, + :concentration => "Concentration (ng/mL)", + cols = :time => "Time (hr)", + summary = [ + length => "N", + mean => "Mean", + std => "SD", + ] +) +``` \ No newline at end of file diff --git a/docs/src/output.md b/docs/src/output.md new file mode 100644 index 0000000..b92ee31 --- /dev/null +++ b/docs/src/output.md @@ -0,0 +1,172 @@ +# Output + +## HTML + +In IDEs that support the `MIME"text/html"` or `MIME"juliavscode/html"` types, just `display`ing a `Table` will render it in HTML for you. +All examples in this documentation are rendered this way. +Alternatively, you can print HTML to any IO object via `show(io, MIME"text/html", table)`. + +## LaTeX + +You can print LaTeX code to any IO via `show(io, MIME"text/latex", table)`. +Keep in mind that the `threeparttable`, `multirow` and `booktabs` packages need to separately be included in your preamble due to the way LaTeX documents are structured. + +```@example +using SummaryTables +using DataFrames +using tectonic_jll + + +mkpath(joinpath(@__DIR__, "outputs")) + +data = DataFrame( + sex = ["m", "m", "m", "m", "f", "f", "f", "f", "f", "f"], + age = [27, 45, 34, 85, 55, 44, 24, 29, 37, 76], + blood_type = ["A", "0", "B", "B", "B", "A", "0", "A", "A", "B"], + smoker = [true, false, false, false, true, true, true, false, false, false], +) + +tbl = table_one( + data, + [:age => "Age (years)", :blood_type => "Blood type", :smoker => "Smoker"], + groupby = :sex => "Sex", + show_n = true +) + +# render latex in a temp directory +mktempdir() do dir + texfile = joinpath(dir, "main.tex") + + open(texfile, "w") do io + # add the necessary packages in the preamble + println(io, raw""" + \documentclass{article} + \usepackage{threeparttable} + \usepackage{multirow} + \usepackage{booktabs} + \begin{document} + """) + + # print the table as latex code + show(io, MIME"text/latex"(), tbl) + + println(io, raw"\end{document}") + end + + # render the tex file to pdf + tectonic_jll.tectonic() do bin + run(`$bin $texfile`) + end + + cp(joinpath(dir, "main.pdf"), joinpath(@__DIR__, "outputs", "example.pdf")) +end + +nothing # hide +``` + +Download `example.pdf`: + +```@raw html + +``` + +## docx + +To get docx output, you need to use the WriteDocx.jl package because this format is not plain-text like LaTeX or HTML. +The table node you get out of the `to_docx` function can be placed into +sections on the same level as paragraphs. + +```@example +using SummaryTables +using DataFrames +import WriteDocx as W + + +mkpath(joinpath(@__DIR__, "outputs")) + +data = DataFrame( + sex = ["m", "m", "m", "m", "f", "f", "f", "f", "f", "f"], + age = [27, 45, 34, 85, 55, 44, 24, 29, 37, 76], + blood_type = ["A", "0", "B", "B", "B", "A", "0", "A", "A", "B"], + smoker = [true, false, false, false, true, true, true, false, false, false], +) + +tbl = table_one( + data, + [:age => "Age (years)", :blood_type => "Blood type", :smoker => "Smoker"], + groupby = :sex => "Sex", + show_n = true +) + +doc = W.Document( + W.Body([ + W.Section([ + SummaryTables.to_docx(tbl) + ]) + ]) + ) + +W.save(joinpath(@__DIR__, "outputs", "example.docx"), doc) + +nothing # hide +``` + +Download `example.docx`: + +```@raw html + +``` + +## Typst + +You can print [Typst](https://github.com/typst/typst) table code to any IO via `show(io, MIME"text/typst", table)`. +The Typst backend is using the [tablex](https://github.com/PgBiel/typst-tablex/) package. +Due to the way Typst's package manager works, you do not have to add any other information to your `.typ` file to make SummaryTables's typst code work. + +```@example +using SummaryTables +using DataFrames +using Typst_jll + + +mkpath(joinpath(@__DIR__, "outputs")) + +data = DataFrame( + sex = ["m", "m", "m", "m", "f", "f", "f", "f", "f", "f"], + age = [27, 45, 34, 85, 55, 44, 24, 29, 37, 76], + blood_type = ["A", "0", "B", "B", "B", "A", "0", "A", "A", "B"], + smoker = [true, false, false, false, true, true, true, false, false, false], +) + +tbl = table_one( + data, + [:age => "Age (years)", :blood_type => "Blood type", :smoker => "Smoker"], + groupby = :sex => "Sex", + show_n = true +) + +# render latex in a temp directory +mktempdir() do dir + typfile = joinpath(dir, "example.typ") + + open(typfile, "w") do io + # print the table as latex code + show(io, MIME"text/typst"(), tbl) + end + + # render the tex file to pdf + Typst_jll.typst() do bin + run(`$bin compile $typfile`) + end + + cp(joinpath(dir, "example.pdf"), joinpath(@__DIR__, "outputs", "example_typst.pdf")) +end + +nothing # hide +``` + +Download `example_typst.pdf`: + +```@raw html + +``` diff --git a/docs/src/predefined_tables/listingtable.md b/docs/src/predefined_tables/listingtable.md new file mode 100644 index 0000000..2f70f69 --- /dev/null +++ b/docs/src/predefined_tables/listingtable.md @@ -0,0 +1,512 @@ +# `listingtable` + +## Synopsis + +A listing table displays the raw data from one column of a source table, with optional summary sections interleaved between. +The row and column structure of the listing table is defined by grouping columns from the source table. +Each row of data has to have its own cell in the listing table, therefore the grouping applied along rows and columns must be exhaustive, i.e., no two rows may end up in the same group together. + +Here is an example of a hypothetical clinical trial with drug concentration measurements of two participants with five time points each. + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + concentration = [1.2, 4.5, 2.0, 1.5, 0.1, 1.8, 3.2, 1.8, 1.2, 0.2], + id = repeat([1, 2], inner = 5), + time = repeat([0, 0.5, 1, 2, 3], 2) +) + +listingtable( + data, + :concentration => "Concentration (ng/mL)", + rows = :id => "ID", + cols = :time => "Time (hr)", + summarize_rows = [ + length => "N", + mean => "Mean", + std => "SD", + ] +) +``` + +## Argument 1: `table` + +The first argument can be any object that is a table compatible with the `Tables.jl` API. +Here are some common examples: + +### `DataFrame` + +```@example +using DataFrames +using SummaryTables + +data = DataFrame(value = 1:6, group1 = repeat(["A", "B", "C"], 2), group2 = repeat(["D", "E"], inner = 3)) + +listingtable(data, :value, rows = :group1, cols = :group2) +``` + +### `NamedTuple` of `Vector`s + +```@example +using SummaryTables + +data = (; value = 1:6, group1 = repeat(["A", "B", "C"], 2), group2 = repeat(["D", "E"], inner = 3)) + +listingtable(data, :value, rows = :group1, cols = :group2) +``` + +### `Vector` of `NamedTuple`s + +```@example +using SummaryTables + +data = [ + (value = 1, group1 = "A", group2 = "D") + (value = 2, group1 = "B", group2 = "D") + (value = 3, group1 = "C", group2 = "D") + (value = 4, group1 = "A", group2 = "E") + (value = 5, group1 = "B", group2 = "E") + (value = 6, group1 = "C", group2 = "E") +] + +listingtable(data, :value, rows = :group1, cols = :group2) +``` + +## Argument 2: `variable` + +The second argument primarily selects the table column whose data should populate the cells of the listing table. +The column name is specified with a `Symbol`: + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value1 = 1:6, + value2 = 7:12, + group1 = repeat(["A", "B", "C"], 2), + group2 = repeat(["D", "E"], inner = 3) +) + +listingtable(data, :value1, rows = :group1, cols = :group2) +``` + +Here we choose to list column `:value2` instead: + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value1 = 1:6, + value2 = 7:12, + group1 = repeat(["A", "B", "C"], 2), + group2 = repeat(["D", "E"], inner = 3) +) + +listingtable(data, :value2, rows = :group1, cols = :group2) +``` + +By default, the variable name is used as the label as well. +You can pass a different label as the second element of a `Pair` using the `=>` operators. +The label can be of any type (refer to [Types of cell values](@ref) for a list). + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value1 = 1:6, + value2 = 7:12, + group1 = repeat(["A", "B", "C"], 2), + group2 = repeat(["D", "E"], inner = 3) +) + +listingtable(data, :value1 => "Value", rows = :group1, cols = :group2) +``` + +## Optional argument 3: `pagination` + +A listing table can grow large, in which case it may make sense to split it into multiple pages. +You can pass a `Pagination` object with `rows` and / or `cols` keyword arguments. +The `Int` you pass to `rows` and / or `cols` determines how many "sections" of the table along that dimension are included in a single page. +If there are no summary statistics, a "section" is a single row or column. +If there are summary statistics, a "section" includes all the rows or columns that are summarized together (as it would not make sense to split summarized groups across multiple pages). + +If the `pagination` argument is provided, the return type of `listingtable` changes to `PaginatedTable{ListingPageMetadata}`. +This object has an interactive HTML representation for convenience the exact form of which should not be considered stable across SummaryTables versions. +The `PaginatedTable` should be deconstructed into separate `Table`s when you want to include these in a document. + +Here is an example listing table without pagination: + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value = 1:30, + group1 = repeat(["A", "B", "C", "D", "E"], 6), + group2 = repeat(["F", "G", "H", "I", "J", "K"], inner = 5) +) + +listingtable(data, :value, rows = :group1, cols = :group2) +``` + +And here is the same table paginated into groups of 3 sections along the both the rows and columns. +Note that there are only five rows in the original table, which is not divisible by 3, so two pages have only two rows. + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value = 1:30, + group1 = repeat(["A", "B", "C", "D", "E"], 6), + group2 = repeat(["F", "G", "H", "I", "J", "K"], inner = 5) +) + +listingtable(data, :value, Pagination(rows = 3, cols = 3), rows = :group1, cols = :group2) +``` + +We can also paginate only along the rows: + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value = 1:30, + group1 = repeat(["A", "B", "C", "D", "E"], 6), + group2 = repeat(["F", "G", "H", "I", "J", "K"], inner = 5) +) + +listingtable(data, :value, Pagination(rows = 3), rows = :group1, cols = :group2) +``` + +Or only along the columns: + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value = 1:30, + group1 = repeat(["A", "B", "C", "D", "E"], 6), + group2 = repeat(["F", "G", "H", "I", "J", "K"], inner = 5) +) + +listingtable(data, :value, Pagination(cols = 3), rows = :group1, cols = :group2) +``` + +## Keyword: `rows` + +The `rows` keyword determines the grouping structure along the rows. +It can either be a `Symbol` specifying a grouping column, a `Pair{Symbol,Any}` where the second element overrides the group's label, or a `Vector` with multiple groups of the aforementioned format. + +This example uses a single group with default label. + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value = 1:5, + group = ["A", "B", "C", "D", "E"], +) + +listingtable(data, :value, rows = :group) +``` + +The label can be overridden using the `Pair` operator. + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value = 1:5, + group = ["A", "B", "C", "D", "E"], +) + +listingtable(data, :value, rows = :group => "Group") +``` + +Multiple groups are possible as well, in that case you get a nested display where the last group changes the fastest. + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value = 1:5, + group1 = ["F", "F", "G", "G", "G"], + group2 = ["A", "B", "C", "D", "E"], +) + +listingtable(data, :value, rows = [:group1, :group2 => "Group 2"]) +``` + +## Keyword: `cols` + +The `cols` keyword determines the grouping structure along the columns. +It can either be a `Symbol` specifying a grouping column, a `Pair{Symbol,Any}` where the second element overrides the group's label, or a `Vector` with multiple groups of the aforementioned format. + +This example uses a single group with default label. + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value = 1:5, + group = ["A", "B", "C", "D", "E"], +) + +listingtable(data, :value, cols = :group) +``` + +The label can be overridden using the `Pair` operator. + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value = 1:5, + group = ["A", "B", "C", "D", "E"], +) + +listingtable(data, :value, cols = :group => "Group") +``` + +Multiple groups are possible as well, in that case you get a nested display where the last group changes the fastest. + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value = 1:5, + group1 = ["F", "F", "G", "G", "G"], + group2 = ["A", "B", "C", "D", "E"], +) + +listingtable(data, :value, cols = [:group1, :group2 => "Group 2"]) +``` + + +## Keyword: `summarize_rows` + +This keyword takes a list of aggregation functions which are used to summarize the listed variable along the rows. +A summary function should take a vector of values (usually that will be numbers) and output one summary value. +This value can be of any type that SummaryTables can show in a cell (refer to [Types of cell values](@ref) for a list). + +```@example +using DataFrames +using SummaryTables +using Statistics: mean, std + +data = DataFrame( + value = 1:24, + group1 = repeat(["A", "B", "C", "D", "E", "F"], 4), + group2 = repeat(["G", "H", "I", "J"], inner = 6), +) + +mean_sd(values) = Concat(mean(values), " (", std(values), ")") + +listingtable(data, + :value, + rows = :group1, + cols = :group2, + summarize_rows = [ + mean, + std => "SD", + mean_sd => "Mean (SD)", + ] +) +``` + +By default, one summary will be calculated over all rows of a given column. +You can also choose to compute one summary for each group of a row grouping column, which makes sense if there is more than one row grouping column. + +In this example, one summary is computed for each level of the `group1` column. + +```@example +using DataFrames +using SummaryTables +using Statistics: mean, std + +data = DataFrame( + value = 1:24, + group1 = repeat(["X", "Y"], 12), + group2 = repeat(["A", "B", "C"], 8), + group3 = repeat(["G", "H", "I", "J"], inner = 6), +) + +mean_sd(values) = Concat(mean(values), " (", std(values), ")") + +listingtable(data, + :value, + rows = [:group1, :group2], + cols = :group3, + summarize_rows = :group1 => [ + mean, + std => "SD", + mean_sd => "Mean (SD)", + ] +) +``` + +## Keyword: `summarize_cols` + +This keyword takes a list of aggregation functions which are used to summarize the listed variable along the columns. +A summary function should take a vector of values (usually that will be numbers) and output one summary value. +This value can be of any type that SummaryTables can show in a cell (refer to [Types of cell values](@ref) for a list). + +```@example +using DataFrames +using SummaryTables +using Statistics: mean, std + +data = DataFrame( + value = 1:24, + group1 = repeat(["A", "B", "C", "D", "E", "F"], 4), + group2 = repeat(["G", "H", "I", "J"], inner = 6), +) + +mean_sd(values) = Concat(mean(values), " (", std(values), ")") + +listingtable(data, + :value, + rows = :group1, + cols = :group2, + summarize_cols = [ + mean, + std => "SD", + mean_sd => "Mean (SD)", + ] +) +``` + +By default, one summary will be calculated over all columns of a given row. +You can also choose to compute one summary for each group of a column grouping column, which makes sense if there is more than one column grouping column. + +In this example, one summary is computed for each level of the `group1` column. + +```@example +using DataFrames +using SummaryTables +using Statistics: mean, std + +data = DataFrame( + value = 1:24, + group1 = repeat(["X", "Y"], 12), + group2 = repeat(["A", "B", "C"], 8), + group3 = repeat(["G", "H", "I", "J"], inner = 6), +) + +mean_sd(values) = Concat(mean(values), " (", std(values), ")") + +listingtable(data, + :value, + cols = [:group1, :group2], + rows = :group3, + summarize_cols = :group1 => [ + mean, + std => "SD", + mean_sd => "Mean (SD)", + ] +) +``` + +## Keyword: `variable_header` + +If you set `variable_header = false`, you can hide the header cell with the variable label, which makes the table layout a little more compact. + +Here is a table with the header cell: + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value = 1:6, + group1 = repeat(["A", "B", "C"], 2), + group2 = repeat(["D", "E"], inner = 3) +) + +listingtable(data, :value, rows = :group1, cols = :group2, variable_header = true) +``` + +And here is a table without it: + +```@example +using DataFrames +using SummaryTables + +data = DataFrame( + value = 1:6, + group1 = repeat(["A", "B", "C"], 2), + group2 = repeat(["D", "E"], inner = 3) +) + +listingtable(data, :value, rows = :group1, cols = :group2, variable_header = false) +``` + + +## Keyword: `sort` + +By default, group entries are sorted. +If you need to maintain the order of entries from your dataset, set `sort = false`. + +Notice how in the following two examples, the group indices are `"dos"`, `"tres"`, `"uno"` when sorted, but `"uno"`, `"dos"`, `"tres"` when not sorted. +If we want to preserve the natural order of these groups ("uno", "dos", "tres" meaning "one", "two", "three" in Spanish but having a different alphabetical order) we need to set `sort = false`. + +```@example sort +using DataFrames +using SummaryTables + +data = DataFrame( + value = 1:6, + group1 = repeat(["uno", "dos", "tres"], inner = 2), + group2 = repeat(["cuatro", "cinco"], 3), +) + +listingtable(data, :value, rows = :group1, cols = :group2) +``` + +```@example sort +listingtable(data, :value, rows = :group1, cols = :group2, sort = false) +``` + +!!! warning + If you have multiple groups, `sort = false` can lead to splitting of higher-level groups if they are not correctly ordered in the source data. + +Compare the following two tables. +In the second one, the group "A" is split by "B" so the label appears twice. + +```@example bad_sort +using SummaryTables +using DataFrames + +data = DataFrame( + value = 1:4, + group1 = ["A", "B", "B", "A"], + group2 = ["C", "D", "C", "D"], +) + +listingtable(data, :value, rows = [:group1, :group2]) +``` + +```@example bad_sort +data = DataFrame( + value = 1:4, + group1 = ["A", "B", "B", "A"], + group2 = ["C", "D", "C", "D"], +) + +listingtable(data, :value, rows = [:group1, :group2], sort = false) +``` diff --git a/docs/src/predefined_tables/summarytable.md b/docs/src/predefined_tables/summarytable.md new file mode 100644 index 0000000..9b49a67 --- /dev/null +++ b/docs/src/predefined_tables/summarytable.md @@ -0,0 +1,374 @@ +# `summarytable` + +## Synopsis + +A summary table summarizes the raw data from one column of a source table for different groups defined by grouping columns. +It is similar to a [`listingtable`](@ref) without the raw values. + +Here is an example of a hypothetical clinical trial with drug concentration measurements of two participants with five time points each. + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + concentration = [1.2, 4.5, 2.0, 1.5, 0.1, 1.8, 3.2, 1.8, 1.2, 0.2], + id = repeat([1, 2], inner = 5), + time = repeat([0, 0.5, 1, 2, 3], 2) +) + +summarytable( + data, + :concentration => "Concentration (ng/mL)", + cols = :time => "Time (hr)", + summary = [ + length => "N", + mean => "Mean", + std => "SD", + ] +) +``` + +## Argument 1: `table` + +The first argument can be any object that is a table compatible with the `Tables.jl` API. +Here are some common examples: + +### `DataFrame` + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + value = 1:6, + group = repeat(["A", "B", "C"], 2), +) + +summarytable(data, :value, cols = :group, summary = [mean, std]) +``` + +### `NamedTuple` of `Vector`s + +```@example +using SummaryTables +using Statistics + +data = (; value = 1:6, group = repeat(["A", "B", "C"], 2)) + +summarytable(data, :value, cols = :group, summary = [mean, std]) +``` + +### `Vector` of `NamedTuple`s + +```@example +using SummaryTables +using Statistics + +data = [ + (value = 1, group = "A") + (value = 2, group = "B") + (value = 3, group = "C") + (value = 4, group = "A") + (value = 5, group = "B") + (value = 6, group = "C") +] + +summarytable(data, :value, cols = :group, summary = [mean, std]) +``` + +## Argument 2: `variable` + +The second argument primarily selects the table column whose data should populate the cells of the summary table. +The column name is specified with a `Symbol`: + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + value1 = 1:6, + value2 = 7:12, + group = repeat(["A", "B", "C"], 2), +) + +summarytable(data, :value1, cols = :group, summary = [mean, std]) +``` + +Here we choose to list column `:value2` instead: + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + value1 = 1:6, + value2 = 7:12, + group = repeat(["A", "B", "C"], 2), +) + +summarytable(data, :value2, cols = :group, summary = [mean, std]) +``` + +By default, the variable name is used as the label as well. +You can pass a different label as the second element of a `Pair` using the `=>` operators. +The label can be of any type (refer to [Types of cell values](@ref) for a list). + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + value1 = 1:6, + value2 = 7:12, + group = repeat(["A", "B", "C"], 2), +) + +summarytable(data, :value1 => "Value", cols = :group, summary = [mean, std]) +``` + +## Keyword: `rows` + +The `rows` keyword determines the grouping structure along the rows. +It can either be a `Symbol` specifying a grouping column, a `Pair{Symbol,Any}` where the second element overrides the group's label, or a `Vector` with multiple groups of the aforementioned format. + +This example uses a single group with default label. + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + value = 1:6, + group = repeat(["A", "B", "C"], 2), +) + +summarytable(data, :value, rows = :group, summary = [mean, std]) +``` + +The label can be overridden using the `Pair` operator. + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + value = 1:6, + group = repeat(["A", "B", "C"], 2), +) + +summarytable(data, :value, rows = :group => "Group", summary = [mean, std]) +``` + +Multiple groups are possible as well, in that case you get a nested display where the last group changes the fastest. + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + value = 1:12, + group1 = repeat(["A", "B"], inner = 6), + group2 = repeat(["C", "D", "E"], 4), +) + +summarytable(data, :value, rows = [:group1, :group2 => "Group 2"], summary = [mean, std]) +``` + +## Keyword: `cols` + +The `cols` keyword determines the grouping structure along the columns. +It can either be a `Symbol` specifying a grouping column, a `Pair{Symbol,Any}` where the second element overrides the group's label, or a `Vector` with multiple groups of the aforementioned format. + +This example uses a single group with default label. + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + value = 1:6, + group = repeat(["A", "B", "C"], 2), +) + +summarytable(data, :value, cols = :group, summary = [mean, std]) +``` + +The label can be overridden using the `Pair` operator. + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + value = 1:6, + group = repeat(["A", "B", "C"], 2), +) + +summarytable(data, :value, cols = :group => "Group", summary = [mean, std]) +``` + +Multiple groups are possible as well, in that case you get a nested display where the last group changes the fastest. + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + value = 1:12, + group1 = repeat(["A", "B"], inner = 6), + group2 = repeat(["C", "D", "E"], 4), +) + +summarytable(data, :value, cols = [:group1, :group2 => "Group 2"], summary = [mean, std]) +``` + + +## Keyword: `summary` + +This keyword takes a list of aggregation functions which are used to summarize the chosen variable. +A summary function should take a vector of values (usually that will be numbers) and output one summary value. +This value can be of any type that SummaryTables can show in a cell (refer to [Types of cell values](@ref) for a list). + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + value = 1:24, + group1 = repeat(["A", "B", "C", "D"], 6), + group2 = repeat(["E", "F", "G"], inner = 8), +) + +mean_sd(values) = Concat(mean(values), " (", std(values), ")") + +summarytable( + data, + :value, + rows = :group1, + cols = :group2, + summary = [ + mean, + std => "SD", + mean_sd => "Mean (SD)", + ] +) +``` + +## Keyword: `variable_header` + +If you set `variable_header = false`, you can hide the header cell with the variable label, which makes the table layout a little more compact. + +Here is a table with the header cell: + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + value = 1:24, + group1 = repeat(["A", "B", "C", "D"], 6), + group2 = repeat(["E", "F", "G"], inner = 8), +) + +summarytable( + data, + :value, + rows = :group1, + cols = :group2, + summary = [mean, std], +) +``` + +And here is a table without it: + +```@example +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + value = 1:24, + group1 = repeat(["A", "B", "C", "D"], 6), + group2 = repeat(["E", "F", "G"], inner = 8), +) + +summarytable( + data, + :value, + rows = :group1, + cols = :group2, + summary = [mean, std], + variable_header = false, +) +``` + + +## Keyword: `sort` + +By default, group entries are sorted. +If you need to maintain the order of entries from your dataset, set `sort = false`. + +Notice how in the following two examples, the group indices are `"dos"`, `"tres"`, `"uno"` when sorted, but `"uno"`, `"dos"`, `"tres"` when not sorted. +If we want to preserve the natural order of these groups ("uno", "dos", "tres" meaning "one", "two", "three" in Spanish but having a different alphabetical order) we need to set `sort = false`. + +```@example sort +using DataFrames +using SummaryTables +using Statistics + +data = DataFrame( + value = 1:18, + group1 = repeat(["uno", "dos", "tres"], inner = 6), + group2 = repeat(["cuatro", "cinco"], 9), +) + +summarytable(data, :value, rows = :group1, cols = :group2, summary = [mean, std]) +``` + +```@example sort +summarytable(data, :value, rows = :group1, cols = :group2, summary = [mean, std], sort = false) +``` + +!!! warning + If you have multiple groups, `sort = false` can lead to splitting of higher-level groups if they are not correctly ordered in the source data. + +Compare the following two tables. +In the second one, the group "A" is split by "B" so the label appears twice. + +```@example bad_sort +using SummaryTables +using DataFrames +using Statistics + +data = DataFrame( + value = 1:4, + group1 = ["A", "B", "B", "A"], + group2 = ["C", "D", "C", "D"], +) + +summarytable(data, :value, rows = [:group1, :group2], summary = [mean]) +``` + +```@example bad_sort +data = DataFrame( + value = 1:4, + group1 = ["A", "B", "B", "A"], + group2 = ["C", "D", "C", "D"], +) + +summarytable(data, :value, rows = [:group1, :group2], summary = [mean], sort = false) +``` diff --git a/docs/src/predefined_tables/table_one.md b/docs/src/predefined_tables/table_one.md new file mode 100644 index 0000000..01c1ffe --- /dev/null +++ b/docs/src/predefined_tables/table_one.md @@ -0,0 +1,251 @@ +# `table_one` + +## Synopsis + +"Table 1" is a common term for the first table in a paper that summarizes demographic and other individual data of the population that is being studied. +In general terms, it is a table where different columns from the source table are summarized separately, stacked along the rows. +The types of analysis can be chosen manually, or will be selected given the column types. +Optionally, there can be grouping applied along the columns as well. + +In this example, several variables of a hypothetical population are analyzed split by sex. + +```@example +using SummaryTables +using DataFrames + +data = DataFrame( + sex = ["m", "m", "m", "m", "f", "f", "f", "f", "f", "f"], + age = [27, 45, 34, 85, 55, 44, 24, 29, 37, 76], + blood_type = ["A", "0", "B", "B", "B", "A", "0", "A", "A", "B"], + smoker = [true, false, false, false, true, true, true, false, false, false], +) + +table_one( + data, + [:age => "Age (years)", :blood_type => "Blood type", :smoker => "Smoker"], + groupby = :sex => "Sex", + show_n = true +) +``` + +## Argument 1: `table` + +The first argument can be any object that is a table compatible with the `Tables.jl` API. +Here are some common examples: + +### `DataFrame` + +```@example +using DataFrames +using SummaryTables + +data = DataFrame(x = [1, 2, 3], y = ["4", "5", "6"]) + +table_one(data, [:x, :y]) +``` + +### `NamedTuple` of `Vector`s + +```@example +using SummaryTables + +data = (; x = [1, 2, 3], y = ["4", "5", "6"]) + +table_one(data, [:x, :y]) +``` + +### `Vector` of `NamedTuple`s + +```@example +using SummaryTables + +data = [(; x = 1, y = "4"), (; x = 2, y = "5"), (; x = 3, y = "6")] + +table_one(data, [:x, :y]) +``` + +## Argument 2: `analyses` + +The second argument takes a vector specifying analyses, with one entry for each "row section" of the resulting table. +If only one analysis is passed, the vector can be omitted. +Each analysis can have up to three parts: the variable, the analysis function and the label. + +The variable is passed as a `Symbol`, corresponding to a column in the input data, and must always be specified. +The other two parts are optional. + +If you specify only variables, the analysis functions are chosen automatically based on the columns, and the labels are equal to the variable names. +Number variables show the mean, standard deviation, median, minimum and maximum. +String variables or other non-numeric variables show counts and percentages of each element type. + +```@example +using SummaryTables + +data = (; x = [1, 2, 3], y = ["a", "b", "a"]) + +table_one(data, [:x, :y]) +``` + +In the next example, we rename the `x` variable by passing a `String` in a `Pair`. + +```@example +using SummaryTables + +data = (; x = [1, 2, 3], y = ["a", "b", "a"]) + +table_one(data, [:x => "Variable X", :y]) +``` + +Labels can be any type except `<:Function` (that type signals that an analysis function has been passed). +One example of a non-string label is `Concat` in conjunction with `Superscript`. + +```@example +using SummaryTables + +data = (; x = [1, 2, 3], y = ["a", "b", "a"]) + +table_one(data, [:x => Concat("X", Superscript("with superscript")), :y]) +``` + +Any object which is a subtype of `Function` is assumed to be an analysis function. +An analysis function takes a data column as input and returns a `Tuple` where each entry corresponds to one analysis row. +Each of these rows consists of a `Pair` where the left side is the analysis result and the right side the label. +Here's an example of a custom number column analysis function. +Note the use of `Concat` to build content out of multiple parts. +This is preferred to interpolating into a string because interpolation destroys the original objects and takes away the possibility for automatic rounding or other special post-processing or display behavior. + +```@example +using SummaryTables +using Statistics + +data = (; x = [1, 2, 3]) + +function custom_analysis(column) + ( + minimum(column) => "Minimum", + maximum(column) => "Maximum", + Concat(mean(column), " (", std(column), ")") => "Mean (SD)", + ) +end + +table_one(data, :x => custom_analysis) +``` + +Finally, all three parts, variable, analysis function and label can be combined as well: + +```@example +using SummaryTables +using Statistics + +data = (; x = [1, 2, 3]) + +function custom_analysis(column) + ( + minimum(column) => "Minimum", + maximum(column) => "Maximum", + Concat(mean(column), " (", std(column), ")") => "Mean (SD)", + ) +end + +table_one(data, :x => custom_analysis => "Variable X") +``` + +## Keyword: `groupby` + +The `groupby` keyword takes a vector of column name symbols with optional labels. +If there is only one grouping column, the vector can be omitted. +Each analysis is then computed separately for each group. + +```@example +using SummaryTables + +data = (; x = [1, 2, 3, 4, 5, 6], y = ["a", "a", "a", "b", "b", "b"]) + +table_one(data, :x, groupby = :y) +``` + +In this example, we rename the grouping column: + +```@example +using SummaryTables + +data = (; x = [1, 2, 3, 4, 5, 6], y = ["a", "a", "a", "b", "b", "b"]) + +table_one(data, :x, groupby = :y => "Column Y") +``` + +If there are multiple grouping columns, they are shown in a nested fashion, with the first group at the highest level: + +```@example +using SummaryTables + +data = (; + x = [1, 2, 3, 4, 5, 6], + y = ["a", "a", "b", "b", "c", "c"], + z = ["d", "e", "d", "e", "d", "e"], +) + +table_one(data, :x, groupby = [:y, :z => "Column Z"]) +``` + +## Keyword: `show_n` + +When `show_n` is set to `true`, the size of each group is shown under its name. + +```@example +using SummaryTables + +data = (; x = [1, 2, 3, 4, 5, 6], y = ["a", "a", "a", "a", "b", "b"]) + +table_one(data, :x, groupby = :y, show_n = true) +``` + +## Keyword: `show_overall` + +When `show_overall` is set to `false`, the column summarizing all groups together is hidden. +Use this only when `groupby` is set, otherwise the resulting table will be empty. + +```@example +using SummaryTables + +data = (; x = [1, 2, 3, 4, 5, 6], y = ["a", "a", "a", "a", "b", "b"]) + +table_one(data, :x, groupby = :y, show_overall = false) +``` + +## Keyword: `sort` + +By default, group entries are sorted. +If you need to maintain the order of entries from your dataset, set `sort = false`. + +Notice how in the following two examples, the group indices are `"dos"`, `"tres"`, `"uno"` when sorted, but `"uno"`, `"dos"`, `"tres"` when not sorted. +If we want to preserve the natural order of these groups ("uno", "dos", "tres" meaning "one", "two", "three" in Spanish but having a different alphabetical order) we need to set `sort = false`. + +```@example sort +using SummaryTables + +data = (; x = [1, 2, 3, 4, 5, 6], y = ["uno", "uno", "dos", "dos", "tres", "tres"]) + +table_one(data, :x, groupby = :y) +``` + +```@example sort +table_one(data, :x, groupby = :y, sort = false) +``` + +!!! warning + If you have multiple groups, `sort = false` can lead to splitting of higher-level groups if they are not correctly ordered in the source data. + +Compare the following two tables. +In the second one, the group "A" is split by "B" so the label appears twice. + +```@example bad_sort +using SummaryTables + +data = (; x = [1, 2, 3, 4, 5, 6], y = ["A", "A", "B", "B", "B", "A"], z = ["C", "C", "C", "D", "D", "D"]) + +table_one(data, :x, groupby = [:y, :z]) +``` + +```@example bad_sort +table_one(data, :x, groupby = [:y, :z], sort = false) +``` \ No newline at end of file diff --git a/notebooks/PlutoDeployment.toml b/notebooks/PlutoDeployment.toml new file mode 100644 index 0000000..d3227bc --- /dev/null +++ b/notebooks/PlutoDeployment.toml @@ -0,0 +1,8 @@ +[Export] +enabled = true +output_dir = "output" +offer_binder = false + +[Pluto] +[Pluto.compiler] +threads = 1 \ No newline at end of file diff --git a/notebooks/Project.toml b/notebooks/Project.toml new file mode 100644 index 0000000..b039758 --- /dev/null +++ b/notebooks/Project.toml @@ -0,0 +1,11 @@ +[deps] +CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" +CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +Pluto = "c3e4b0f8-55cb-11ea-2926-15256bba5781" +PlutoSliderServer = "2fc8631c-6f24-4c5b-bca7-cbb509c42db4" +RDatasets = "ce6b1742-4840-55fa-b093-852dadbb1d8b" +Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +SummaryTables = "6ce4ecf0-73a7-4ce3-9fb4-80ebfe887b60" diff --git a/notebooks/examples.jl b/notebooks/examples.jl new file mode 100644 index 0000000..fae1364 --- /dev/null +++ b/notebooks/examples.jl @@ -0,0 +1,476 @@ +### A Pluto.jl notebook ### +# v0.19.9 + +using Markdown +using InteractiveUtils + +# ╔═╡ 60f82c0a-744f-11ec-0d68-75c165356df4 +begin + import Pkg + Pkg.activate(@__DIR__) + Pkg.develop(path = joinpath(@__DIR__, "..")) + # Pkg.instantiate() + using Revise + using SummaryTables + using DataFrames + using Statistics + using RDatasets + using StatsBase + using CategoricalArrays +end + +# ╔═╡ 53a5fad9-23e6-4ab3-8887-df9b97a2987e +md""" +# SummaryTables Examples +""" + +# ╔═╡ d10208df-31f4-4000-8d00-34dfe70bc696 +md""" +We will be using the following data set for all our examples. +""" + +# ╔═╡ e23ed278-e0f4-4ba8-a61a-b07cab283db2 +lung = let + lung = dataset("survival", "lung") + lung.Sex = categorical(lung.Sex) + lung.Sex = recode(lung.Sex, 1 => "m", 2 => "f") + lung.AgeGroup = ifelse.(lung.Age .> median(lung.Age), "older", "younger") + lung.Status = categorical(lung.Status) + lung.Status = recode(lung.Status, 1 => "censored", 2 => "dead") + lung +end + +# ╔═╡ 4dda7e45-32e2-4312-94db-d77829097c7b +md""" +`SummaryTables` exports three main functions: +- `table_one` +- `listingtable` +- `summarytable` + +We will start with `table_one`, which creates a table where columns from the dataset are summarized in rows, while those statistics can be stratified or grouped by splitting into several columns. +""" + +# ╔═╡ ac1c5a5f-aa1a-497b-a847-7095c77bda56 +md""" +## `table_one` +""" + +# ╔═╡ 1e00647c-ab09-4910-90c7-9f06a679acab +macro docblock(sym) + quote + md = @doc($(esc(sym))) + Markdown.parse("!!! docs\n$(replace(string(md), r"^"m => " ")))\n") + end +end + +# ╔═╡ 437be826-795b-4f9e-a600-c9b76b43bca3 +@docblock table_one + +# ╔═╡ 9c12afea-b763-455e-9d25-e86b9f5371c2 +md""" +We start by summarizing a single variable, `:Age`. By default, for a numerical column, two rows of summary statistics with mean, standard deviation, median and min/max are generated. +""" + +# ╔═╡ b43471fb-f7a3-4085-8382-e05163a0f089 +table_one(lung, :Age) + +# ╔═╡ 072af7fd-0fca-44b0-80d8-0c2ee43d17f9 +md""" +Let's add another variable, `:Chol`. For this, we pass a `Vector` of `Symbol`s: +""" + +# ╔═╡ 2b48d4b3-866a-4cf6-81e3-6bf9a3d51355 +table_one(lung, [:Age, :WtLoss]) + +# ╔═╡ 235086e3-624a-4fd0-be09-795ab7020d55 +md""" +You can see that this variable has another row called `Missing`, which is there because the `:WtLoss` column contained missing values. + +`:WtLoss` is hard to read, so we can rename the variable using Julia's pair syntax `=>`, which is often used to pass values that belong together as one object. +""" + +# ╔═╡ 7356c9f1-aaa8-427f-ba7f-53affd1cbe97 +table_one(lung, [:Age, :WtLoss => "Weight Loss"]) + +# ╔═╡ 6a48c1a7-a6bc-4707-9ed3-b1ca7b81a8f0 +md""" +Let's add one more variable with categorical data, the column `:Sex`. You can see that the summary statistic is a count and percentage this time: +""" + +# ╔═╡ f3faef4f-bff4-47d2-811c-502258f397c2 +table_one(lung, [:Age, :WtLoss => "Weight Loss", :Sex]) + +# ╔═╡ 50bee2c2-4a2a-47c3-9016-f5f3a19acf7e +md""" +We can stratify or split the columns by one or several variables. +""" + +# ╔═╡ 4892bfe7-6f7a-4fa9-b82d-d950f2d71762 +table_one( + lung, + [:Age, :WtLoss => "Weight Loss", :Sex], + groupby = :Status +) + +# ╔═╡ 452e78fc-a0e0-4365-aba3-f6e988ee515f +table_one( + lung, + [:Age, :WtLoss => "Weight Loss", :Sex], + groupby = [:AgeGroup => "Age Group", :Status] +) + +# ╔═╡ 7badf545-48b9-40b9-af9e-17f65191b366 +md""" +To understand the counts of "f" and "m" better in relation to their groups, we can display group sizes with the keyword argument `show_n`. +""" + +# ╔═╡ 5ad31a44-0c79-4377-88f0-6bed926d7e67 +table_one( + lung, + [:Age, :WtLoss => "Weight Loss", :Sex], + groupby = [:AgeGroup => "Age Group", :Status], + show_n = true, +) + +# ╔═╡ 69ecf7b4-0d9d-4e51-9b3f-121bc30da9c8 +md""" +We can also compute simple group difference statistics across the groups we have stratified by. For this purpose, set the keyword argument `show_pvalues` to `true`: +""" + +# ╔═╡ 185ebb32-388d-4f9b-8bbb-c2718422d7a7 +table_one( + lung, + [:Age, :WtLoss => "Weight Loss", :Sex], + groupby = [:AgeGroup => "Age Group"], + show_n = true, + show_pvalues = true, +) + +# ╔═╡ b7e5fe3f-67d2-4c45-8a62-283fedc19f5b +md""" +To hide the test name column, set `show_tests = false`. +""" + +# ╔═╡ b5e17a11-2c26-49b4-8129-a187a07262be +table_one( + lung, + [:Age, :WtLoss => "Weight Loss", :Sex], + groupby = [:AgeGroup => "Age Group"], + show_n = true, + show_pvalues = true, + show_tests = false, +) + +# ╔═╡ 67ae5d07-b39c-42aa-8e59-f42111f1ec51 +md""" +You can also hide the "Overall" column by setting `show_overall = false`. +""" + +# ╔═╡ 9a479c06-b51c-4d23-93fc-c4d6b2e1972f +table_one( + lung, + [:Age, :WtLoss => "Weight Loss", :Sex], + groupby = [:AgeGroup => "Age Group"], + show_n = true, + show_pvalues = true, + show_tests = false, + show_overall = false, +) + +# ╔═╡ 89d3fb2f-8d64-4670-81ad-053b7d8528ba +md""" +If you want to specify summary statistics yourself, you can calculate them using a function that takes a column and returns a tuple of `value => name` pairs. +""" + +# ╔═╡ 07f33391-ddf2-4770-8466-03443321bc46 +begin + function summarizer(col) + q25, med, q75 = StatsBase.quantile(col, [0.25, 0.5, 0.75]) + (med => "Median", "$q25 / $q75" => "Q25 / Q75") + end + + table_one( + lung, + [:Age => summarizer] + ) +end + +# ╔═╡ e7d8b5b3-7514-4738-bc38-43b827e1d486 +md""" +# `listingtable` +""" + +# ╔═╡ b55522ac-973b-47e7-94a6-fe600a93aa1e +@docblock listingtable + +# ╔═╡ e9d7d981-1211-49e3-8ce1-0272248d7437 +md""" +The `listingtable` function is meant to display raw values from one variable for datasets that are split by multiple groups across rows and columns, with optional summaries across a chosen grouping level. + +To show this function with the `lung` dataset, we remove some data so for the remaining grouping factors there is just one value for each grouping combination. + +If we don't reduce the data, we get an error that there are too many rows for one group: +""" + +# ╔═╡ 6b619eeb-7259-4ecd-ad39-f5444eb5b7c7 +listingtable( + lung, + :Time, + rows = [:AgeGroup, :Sex], + cols = :Status +) + +# ╔═╡ 267d3eea-e4d6-40e4-879d-0a3bb3a524ae +lung_reduced = unique(lung, [:Sex, :AgeGroup, :Status]) + +# ╔═╡ 68351238-b4ae-45ca-a3fc-dd9e6c255459 +listingtable( + lung_reduced, + :Time, + rows = [:AgeGroup, :Sex], + cols = :Status +) + +# ╔═╡ 2b1fb668-1cb1-470c-9a61-fc441e0455b5 +md""" +We can rename columns with the pair syntax: +""" + +# ╔═╡ aa037656-add6-48e6-84be-70b4b039977f +listingtable( + lung_reduced, + :Time => "Survival time in days", + rows = [:AgeGroup => "Age group", :Sex], + cols = :Status +) + +# ╔═╡ 93e7c1e0-971f-4689-a169-d31a646e261b +md""" +Now we can summarize the data across groups. By default, the summaries are applied across all groups at once: +""" + +# ╔═╡ 9afee474-66e1-413f-801a-5acc88c29097 +listingtable( + lung_reduced, + :Time => "Survival time in days", + rows = [:AgeGroup => "Age group", :Sex], + cols = :Status, + summarize_rows = [mean, std], + summarize_cols = [sum], +) + +# ╔═╡ b4a761b7-dcab-46bf-bfcd-8308dab5fbe0 +md""" +The summary functions can also be renamed using the pair syntax. +""" + +# ╔═╡ 2ef759ee-3fba-498a-bea9-225e010ffebc +listingtable( + lung_reduced, + :Time => "Survival time in days", + rows = [:AgeGroup => "Age group", :Sex], + cols = :Status, + summarize_rows = [mean => "Mean", std => "SD"], + summarize_cols = [sum => "Overall"], +) + +# ╔═╡ 847bcc9c-6d6d-4c5e-86ba-4bfca36517ce +md""" +If you want separate summaries for each subgroup of a grouping variable, you can pass this grouping variable in a pair together with the summary functions. Here we summarize the rows once for each age group (`summarize_cols` works similarly): +""" + +# ╔═╡ bdc37172-74e9-4847-b0e4-bd46584b2e2d +listingtable( + lung_reduced, + :Time => "Survival time in days", + rows = [:AgeGroup => "Age group", :Sex], + cols = :Status, + summarize_rows = :AgeGroup => [mean => "Mean", std => "SD"], + summarize_cols = [sum => "Overall"], +) + +# ╔═╡ c9d5468c-df98-4fd2-9d16-a8d8638e980e +md""" +You can choose not to show the name of the raw value variable, in case it is already clear from context, using the keyword argument `variable_header = false`: +""" + +# ╔═╡ 5e95cb18-0e5b-4c57-b383-b2d66cb81be9 +listingtable( + lung_reduced, + :Time => "Survival time in days", + rows = [:AgeGroup => "Age group", :Sex], + cols = :Status, + summarize_rows = :AgeGroup => [mean => "Mean", std => "SD"], + summarize_cols = [sum => "Overall"], + variable_header = false, +) + +# ╔═╡ a9afc1bc-1de5-4544-9e16-9d93f0b31d67 +md""" +## `summarytable` + +The function `summarytable` is somewhat similar in structure to `listingtable` in that only one variable is analysed, but without showing the raw values. You can pass a vector of analysis functions to the keyword argument `summary`, and group rows and columns with the `rows` and `cols` keywords. The set of summaries is applied to each group. + +A first basic example summarizes without grouping: +""" + +# ╔═╡ 332d00be-a34f-4cd6-8dc8-2f63ef7895ea +summarytable( + lung, + :Time, + summary = [mean, std] +) + +# ╔═╡ cd387c02-d1c8-481c-9df3-c922b6184879 +md""" +We can add a row grouping factor and rename the summary functions: +""" + +# ╔═╡ 96f48319-f29c-4e8a-a56f-04a4e5d2d27a +summarytable( + lung, + :Time, + rows = [:AgeGroup => "Age Group"], + summary = [mean => "Mean", std => "SD"] +) + +# ╔═╡ 2a1caf28-7537-4ff2-a6f5-2d9695a58009 +md""" +We can add another row grouping factor, a column factor, and number of samples as well: +""" + +# ╔═╡ 31319e94-8945-410d-b493-13a028138493 +summarytable( + lung, + :Time, + rows = [:AgeGroup => "Age Group", :Sex], + cols = [:Status], + summary = [length => "N", mean => "Mean", std => "SD"] +) + +# ╔═╡ 5f16d3e3-35e9-46a8-bcbb-d5412be8b486 +md""" +## Annotations +""" + +# ╔═╡ 1ff7bbb4-1d3c-4250-a80a-7e31af8f3a46 +md""" +SummaryTables.jl exports the `Annotated` type. +Whenever a table cell contains a value of this type, the table will get a footnote with an automatic or manually defined label attached to the value wrapped inside the `Annotated`. +""" + +# ╔═╡ 2e7cdb76-19ac-435d-a5e2-6d7c790721d7 +summarytable( + lung, + :Time => Annotated("Time", "Time was measured using a stopwatch."), + rows = [:AgeGroup => "Age Group", :Sex], + cols = [:Status], + summary = [length => "N", mean => "Mean", std => "SD"] +) + +# ╔═╡ 44ac2295-5b5f-472d-84d4-03ebbe424564 +md""" +You can also pass a label manually using the `label` keyword argument: +""" + +# ╔═╡ 62ff0490-f2e7-45e8-aac3-039b48d55d3b +summarytable( + lung, + :Time => Annotated("Time", "Time was measured using a stopwatch.", label = "T"), + rows = [:AgeGroup => "Age Group", :Sex], + cols = [:Status], + summary = [length => "N", mean => "Mean", std => "SD"] +) + +# ╔═╡ 77a8297c-04f6-4821-8cd8-763d5acd2c35 +md""" +Any cell in the final table can contain an `Annotated` value. +You can also use the `Concat` type if you need to construct cell content from multiple parts, where a specific part should carry the annotation. +""" + +# ╔═╡ 4755a90c-7c5b-44f1-a212-5e12517c2680 +function length_with_annotation(x) + l = length(x) + l == 12 ? Annotated(l, "This value deserves special attention.", label = "X") : l +end + +# ╔═╡ 134a201a-9048-489e-82a3-48992023c5d0 +summarytable( + lung, + :Time => Concat( + Annotated("Time", "Time was measured using a stopwatch.", label = "T"), + " (hours)" + ), + rows = [:AgeGroup => "Age Group", :Sex], + cols = [:Status], + summary = [length_with_annotation => "N", mean => "Mean", std => "SD"] +) + +# ╔═╡ ff8e167c-b388-42aa-b4db-d96f868ac860 +md""" +# Appendix + +Packages used: +""" + +# ╔═╡ Cell order: +# ╟─53a5fad9-23e6-4ab3-8887-df9b97a2987e +# ╟─d10208df-31f4-4000-8d00-34dfe70bc696 +# ╠═e23ed278-e0f4-4ba8-a61a-b07cab283db2 +# ╟─4dda7e45-32e2-4312-94db-d77829097c7b +# ╟─ac1c5a5f-aa1a-497b-a847-7095c77bda56 +# ╟─1e00647c-ab09-4910-90c7-9f06a679acab +# ╠═437be826-795b-4f9e-a600-c9b76b43bca3 +# ╟─9c12afea-b763-455e-9d25-e86b9f5371c2 +# ╠═b43471fb-f7a3-4085-8382-e05163a0f089 +# ╟─072af7fd-0fca-44b0-80d8-0c2ee43d17f9 +# ╠═2b48d4b3-866a-4cf6-81e3-6bf9a3d51355 +# ╟─235086e3-624a-4fd0-be09-795ab7020d55 +# ╠═7356c9f1-aaa8-427f-ba7f-53affd1cbe97 +# ╟─6a48c1a7-a6bc-4707-9ed3-b1ca7b81a8f0 +# ╠═f3faef4f-bff4-47d2-811c-502258f397c2 +# ╟─50bee2c2-4a2a-47c3-9016-f5f3a19acf7e +# ╠═4892bfe7-6f7a-4fa9-b82d-d950f2d71762 +# ╠═452e78fc-a0e0-4365-aba3-f6e988ee515f +# ╟─7badf545-48b9-40b9-af9e-17f65191b366 +# ╠═5ad31a44-0c79-4377-88f0-6bed926d7e67 +# ╟─69ecf7b4-0d9d-4e51-9b3f-121bc30da9c8 +# ╠═185ebb32-388d-4f9b-8bbb-c2718422d7a7 +# ╟─b7e5fe3f-67d2-4c45-8a62-283fedc19f5b +# ╠═b5e17a11-2c26-49b4-8129-a187a07262be +# ╟─67ae5d07-b39c-42aa-8e59-f42111f1ec51 +# ╠═9a479c06-b51c-4d23-93fc-c4d6b2e1972f +# ╟─89d3fb2f-8d64-4670-81ad-053b7d8528ba +# ╠═07f33391-ddf2-4770-8466-03443321bc46 +# ╟─e7d8b5b3-7514-4738-bc38-43b827e1d486 +# ╠═b55522ac-973b-47e7-94a6-fe600a93aa1e +# ╠═e9d7d981-1211-49e3-8ce1-0272248d7437 +# ╠═6b619eeb-7259-4ecd-ad39-f5444eb5b7c7 +# ╠═267d3eea-e4d6-40e4-879d-0a3bb3a524ae +# ╠═68351238-b4ae-45ca-a3fc-dd9e6c255459 +# ╟─2b1fb668-1cb1-470c-9a61-fc441e0455b5 +# ╠═aa037656-add6-48e6-84be-70b4b039977f +# ╟─93e7c1e0-971f-4689-a169-d31a646e261b +# ╠═9afee474-66e1-413f-801a-5acc88c29097 +# ╟─b4a761b7-dcab-46bf-bfcd-8308dab5fbe0 +# ╠═2ef759ee-3fba-498a-bea9-225e010ffebc +# ╟─847bcc9c-6d6d-4c5e-86ba-4bfca36517ce +# ╠═bdc37172-74e9-4847-b0e4-bd46584b2e2d +# ╟─c9d5468c-df98-4fd2-9d16-a8d8638e980e +# ╠═5e95cb18-0e5b-4c57-b383-b2d66cb81be9 +# ╟─a9afc1bc-1de5-4544-9e16-9d93f0b31d67 +# ╠═332d00be-a34f-4cd6-8dc8-2f63ef7895ea +# ╟─cd387c02-d1c8-481c-9df3-c922b6184879 +# ╠═96f48319-f29c-4e8a-a56f-04a4e5d2d27a +# ╟─2a1caf28-7537-4ff2-a6f5-2d9695a58009 +# ╠═31319e94-8945-410d-b493-13a028138493 +# ╟─5f16d3e3-35e9-46a8-bcbb-d5412be8b486 +# ╟─1ff7bbb4-1d3c-4250-a80a-7e31af8f3a46 +# ╠═2e7cdb76-19ac-435d-a5e2-6d7c790721d7 +# ╟─44ac2295-5b5f-472d-84d4-03ebbe424564 +# ╠═62ff0490-f2e7-45e8-aac3-039b48d55d3b +# ╟─77a8297c-04f6-4821-8cd8-763d5acd2c35 +# ╠═4755a90c-7c5b-44f1-a212-5e12517c2680 +# ╠═134a201a-9048-489e-82a3-48992023c5d0 +# ╟─ff8e167c-b388-42aa-b4db-d96f868ac860 +# ╠═60f82c0a-744f-11ec-0d68-75c165356df4 diff --git a/notebooks/pluto.jl b/notebooks/pluto.jl new file mode 100644 index 0000000..2346c4f --- /dev/null +++ b/notebooks/pluto.jl @@ -0,0 +1,14 @@ +import Pkg + +Pkg.activate(@__DIR__) +Pkg.develop(url=joinpath(@__DIR__, "..")) +Pkg.instantiate() + +using PlutoSliderServer + +# Invalidate the notebook cache when either of the manifests or Julia version change: +let fn = (file) -> string(hash(read(file)); base = 62), + root = fn(joinpath(@__DIR__, "Manifest.toml")), + cache = "$VERSION-$root" + PlutoSliderServer.export_directory("notebooks"; Export_create_index = false, Export_cache_dir = joinpath("cache", cache)) +end diff --git a/src/SummaryTables.jl b/src/SummaryTables.jl new file mode 100644 index 0000000..5c87386 --- /dev/null +++ b/src/SummaryTables.jl @@ -0,0 +1,48 @@ +module SummaryTables + +# +# Imports and exports. +# + +using Tables +using CategoricalArrays +using DataFrames +using Statistics + +import EnumX +import HypothesisTests +import OrderedCollections +import MultipleTesting +import StatsBase +import Printf +import NaturalSort +import WriteDocx +import SHA + +export table_one +export listingtable +export summarytable +export Cell +export CellStyle +export Table +export Annotated +export Concat +export Multiline +export Pagination +export ReplaceMissing +export Replace +export Superscript +export Subscript + +const DEFAULT_ROWGAP = 6.0 + +include("cells.jl") +include("table_one.jl") +include("table.jl") +include("helpers.jl") +include("latex.jl") +include("html.jl") +include("docx.jl") +include("typst.jl") + +end # module diff --git a/src/cells.jl b/src/cells.jl new file mode 100644 index 0000000..5715bdc --- /dev/null +++ b/src/cells.jl @@ -0,0 +1,561 @@ +""" + CellStyle(; + bold::Bool = false, + italic::Bool = false, + underline::Bool = false, + halign::Symbol = :center, + valign::Symbol = :top, + indent_pt::Float64 = 0.0, + border_bottom::Bool = false, + merge::Bool = false, + mergegroup::UInt8 = 0, + ) + +Create a `CellStyle` object which determines the visual appearance of `Cell`s. + +Keyword arguments: + +- `bold` renders text `bold` if `true`. +- `italic` renders text `italic` if `true`. +- `underline` underlines text if `true`. +- `halign` determines the horizontal alignment within the cell, either `:left`, `:center` or `:right`. +- `valign` determines the vertical alignment within the cell, either `:top`, `:center` or `:bottom`. +- `indent_pt` adds left indentation in points to the cell text. +- `border_bottom` adds a bottom border to the cell if `true`. +- `merge` causes adjacent cells which are `==` equal to be rendered as a single merged cell. +- `mergegroup` is a number that can be used to differentiate between two otherwise equal adjacent groups of cells that should not be merged together. +""" +Base.@kwdef struct CellStyle + indent_pt::Float64 = 0.0 + bold::Bool = false + italic::Bool = false + underline::Bool = false + border_bottom::Bool = false + halign::Symbol = :center + valign::Symbol = :top + merge::Bool = false + mergegroup::UInt8 = 0 +end + +@eval function CellStyle(c::CellStyle; kwargs...) + Base.Cartesian.@ncall $(length(fieldnames(CellStyle))) CellStyle i -> begin + name = $(fieldnames(CellStyle))[i] + get(kwargs, name, getfield(c, name)) + end +end + +struct SpannedCell + span::Tuple{UnitRange{Int64},UnitRange{Int64}} + value + style::CellStyle + + function SpannedCell(span::Tuple{UnitRange{Int64},UnitRange{Int64}}, value, style) + rowstart = span[1].start + colstart = span[2].start + if rowstart < 1 + error("SpannedCell must not begin at a row lower than 1, but begins at row $(rowstart).") + end + if colstart < 1 + error("SpannedCell must not begin at a column lower than 1, but begins at column $(colstart).") + end + new(span, value, style) + end +end + +SpannedCell(rows::Union{Int,UnitRange{Int}}, cols::Union{Int,UnitRange{Int}}, value, style = CellStyle()) = SpannedCell((_to_range(rows), _to_range(cols)), value, style) +_to_range(i::Int) = i:i +_to_range(ur::UnitRange{Int}) = ur + +# the old type never did anything, so now we just make any old use of this a no-op basically +const CellList = Vector{SpannedCell} + +""" + Cell(value, style::CellStyle) + Cell(value; [bold, italic, underline, halign, valign, border_bottom, indent_pt, merge, mergegroup]) + +Construct a `Cell` with value `value` and `CellStyle` `style`, which can also be created implicitly with keyword arguments. +For explanations of the styling options, refer to `CellStyle`. +A cell with value `nothing` is displayed as an empty cell (styles might still apply). +The type of `value` can be anything. + +Some types with special behavior are: + - `Multiline` for content broken over multiple lines in a cell. This object may not be used nested in other values, only as the top-level value. + - `Concat` for stringing together multiple values without having to interpolate them into a `String`, which keeps their own special behaviors intact. + - `Superscript` and `Subscript` + - `Annotated` for a value with an optional superscript label and a footnote annotation. +""" +struct Cell + value + style::CellStyle +end + +Base.adjoint(c::Cell) = c # simplifies making row vectors out of column vectors of Cells with ' + +Cell(value; kwargs...) = Cell(value, CellStyle(; kwargs...)) +Cell(cell::Cell; kwargs...) = Cell(cell.value, CellStyle(cell.style; kwargs...)) +Cell(cell::Cell, value; kwargs...) = Cell(value, CellStyle(cell.style; kwargs...)) + +Base.broadcastable(c::Cell) = Ref(c) +@inline Base.getproperty(c::Cell, s::Symbol) = hasfield(Cell, s) ? getfield(c, s) : getproperty(c.style, s) +Base.propertynames(c::Cell) = (fieldnames(Cell)..., propertynames(c.style)...) + +struct Table + cells::Matrix{Cell} + header::Union{Nothing, Int} + footer::Union{Nothing, Int} + footnotes::Vector{Any} + rowgaps::Vector{Pair{Int,Float64}} + colgaps::Vector{Pair{Int,Float64}} + postprocess::Vector{Any} + round_digits::Int + round_mode::Union{Nothing,Symbol} + trailing_zeros::Bool +end + +function Table(cells, header, footer; + round_digits = 3, + round_mode = :auto, + trailing_zeros = false, + footnotes = [], + postprocess = [], + rowgaps = Pair{Int,Float64}[], + colgaps = Pair{Int,Float64}[], + ) + Table(cells, header, footer, footnotes, rowgaps, colgaps, postprocess, round_digits, round_mode, trailing_zeros) +end + +""" + function Table(cells; + header = nothing, + footer = nothing, + round_digits = 3, + round_mode = :auto, + trailing_zeros = false, + footnotes = [], + postprocess = [], + rowgaps = Pair{Int,Float64}[], + colgaps = Pair{Int,Float64}[], + ) + +Create a `Table` which can be rendered in multiple formats, such as HTML or LaTeX. + +## Arguments +- `cells::AbstractMatrix{<:Cell}`: The matrix of `Cell`s that make up the table. + +## Keyword arguments +- `header`: The index of the last row of the header, `nothing` if no header is specified. +- `footer`: The index of the first row of the footer, `nothing` if no footer is specified. +- `footnotes`: A vector of objects printed as footnotes that are not derived from `Annotated` + values and therefore don't get labels with counterparts inside the table. +- `round_digits = 3`: Float values will be rounded to this precision before printing. +- `round_mode = :auto`: How the float values are rounded, options are `:auto`, `:digits` or `:sigdigits`. + If `round_mode === nothing`, no rounding will be applied and `round_digits` and `trailing_zeros` + will have no effect. +- `trailing_zeros = false`: Controls if float values keep trailing zeros, for example `4.0` vs `4`. +- `postprocess = []`: A list of post-processors which will be applied left to right to the table before displaying the table. + A post-processor can either work element-wise or on the whole table object. See the `postprocess_table` and + `postprocess_cell` functions for defining custom postprocessors. +- `rowgaps = Pair{Int,Float64}[]`: A list of pairs `index => gap_pt`. For each pair, a visual gap + the size of `gap_pt` is added between the rows `index` and `index+1`. +- `colgaps = Pair{Int,Float64}[]`: A list of pairs `index => gap_pt`. For each pair, a visual gap + the size of `gap_pt` is added between the columns `index` and `index+1`. + +## Round mode + +Consider the numbers `0.006789`, `23.4567`, `456.789` or `12345.0`. + +Here is how these numbers are formatted with the different available rounding modes: + +- `:auto` rounds to `n` significant digits but doesn't zero out additional digits before the comma unlike `:sigdigits`. + For example, `round_digits = 3` would result in `0.00679`, `23.5`, `457.0` or `12345.0`. + Numbers at orders of magnitude >= 6 or <= -5 are displayed in exponential notation as in Julia. +- `:digits` rounds to `n` digits after the comma and shows possibly multiple trailing zeros. + For example, `round_digits = 3` would result in `0.007`, `23.457` or `456.789` or `12345.000`. + Numbers are never shown with exponential notation. +- `:sigdigits` rounds to `n` significant digits and zeros out additional digits before the comma unlike `:auto`. + For example, `round_digits = 3` would result in `0.00679`, `23.5`, `457.0` or `12300.0`. + Numbers at orders of magnitude >= 6 or <= -5 are displayed in exponential notation as in Julia. +""" +Table(cells; header = nothing, footer = nothing, kwargs...) = Table(cells, header, footer; kwargs...) + +# non-public-API method to keep old code working in the meantime +function Table(cells::AbstractVector{SpannedCell}, args...; kwargs...) + sz = reduce(cells; init = (0, 0)) do sz, cell + max.(sz, (cell.span[1].stop, cell.span[2].stop)) + end + m = fill(Cell(nothing), sz...) + visited = zeros(Bool, sz...) + mergegroup = 0 + for cell in cells + is_spanned = length(cell.span[1]) > 1 || length(cell.span[2]) > 1 + if is_spanned + mergegroup = mod(mergegroup + 1, 255) + end + for row in cell.span[1] + for col in cell.span[2] + if visited[row, col] + error("Tried to fill cell $row,$col twice. First value was $(m[row, col].value) and second $(cell.value).") + end + visited[row, col] = true + if is_spanned + m[row, col] = Cell(cell.value, CellStyle(cell.style; merge = true, mergegroup)) + else + m[row, col] = Cell(cell.value, cell.style) + end + end + end + end + return Table(m, args...; kwargs...) +end + +function to_spanned_cells(m::AbstractMatrix{<:Cell}) + cells = Vector{SpannedCell}() + sizehint!(cells, length(m)) + visited = zeros(Bool, size(m)) + nrow, ncol = size(m) + for row in 1:nrow + for col in 1:ncol + visited[row, col] && continue + + c = m[row, col] + + lastrow = row + for _row in row+1:nrow + if !visited[_row, col] && c.merge && m[_row, col] == c + lastrow = _row + else + break + end + end + lastcol = col + for _col in col+1:ncol + if !visited[row, _col] && c.merge && m[row, _col] == c + lastcol = _col + else + break + end + end + for _row in row+1:lastrow + for _col in col+1:lastcol + _c = m[_row, _col] + if _c != c + error("Cell $c was detected to span over [$(row:lastrow),$(col:lastcol)] but at $_row,$_col the value was $_c. This is not allowed. Cells spanning multiple rows and columns must always span a full rectangle.") + end + end + end + push!(cells, SpannedCell((row:lastrow,col:lastcol), c.value, c.style)) + visited[row:lastrow,col:lastcol] .= true + end + end + return cells +end + +""" + Multiline(args...) + +Create a `Multiline` object which renders each `arg` on a separate line. +A `Multiline` value may only be used as the top-level value of a cell, so +`Cell(Multiline(...))` is allowed but `Cell(Concat(Multiline(...), ...))` is not. +""" +struct Multiline + values::Vector{Any} +end +Multiline(args...) = Multiline(Any[args...]) + +""" + Concat(args...) + +Create a `Concat` object which can be used to concatenate the representations +of multiple values in a single table cell while keeping the conversion semantics +of each `arg` in `args` intact. + +## Example + +```julia +Concat( + "Some text and an ", + Annotated("annotated", "Some annotation"), + " value", +) +# will be rendered as "Some text and an annotated¹ value" +``` +""" +struct Concat + args::Tuple + Concat(args...) = new(args) +end + +struct Annotated + value + annotation + label +end + +struct AutoNumbering end + +""" + Annotated(value, annotation; label = AutoNumbering()) + +Create an `Annotated` object which will be given a footnote annotation +in the `Table` where it is used. +If the `label` keyword is `AutoNumbering()`, annotations will be given number labels +from 1 to N in the order of their appearance. If it is `nothing`, no label will be +shown. Any other `label` will be used directly as the footnote label. + +Each unique label must be paired with a unique annotation, but the same +combination can exist multiple times in a single table. +""" +Annotated(value, annotation; label = AutoNumbering()) = Annotated(value, annotation, label) + +struct ResolvedAnnotation + value + label +end + +# Signals that a given annotation should have no label. +# This is useful for cases where the value itself is the label +# for example when printing NA or - for a missing value. +# You would not want a superscript label for every one of those. +struct NoLabel end + +function resolve_annotations(cells::AbstractVector{<:SpannedCell}) + annotations = collect_annotations(cells) + + k = 1 + for (annotation, label) in annotations + if label === AutoNumbering() + annotations[annotation] = k + k += 1 + elseif label === nothing + annotations[annotation] = NoLabel() + end + end + + labels = Set() + for label in values(annotations) + label === NoLabel() && continue + label ∈ labels && error("Found the same label $(repr(label)) twice with different annotations.") + push!(labels, label) + end + + # put all non-integer labels (so all manual labels) behind the auto-incremented labels + # the remaining order will be corresponding to the elements in the list + annotations = OrderedCollections.OrderedDict(sort(collect(annotations), by = x -> !(last(x) isa Int))) + + cells = map(cells) do cell + SpannedCell(cell.span, resolve_annotation(cell.value, annotations), cell.style) + end + + return cells, annotations +end + +function collect_annotations(cells) + annotations = OrderedCollections.OrderedDict() + for cell in cells + collect_annotations!(annotations, cell.value) + end + return annotations +end + +collect_annotations!(annotations, x) = nothing + +function collect_annotations!(annotations, c::Concat) + for arg in c.args + collect_annotations!(annotations, arg) + end +end + +function collect_annotations!(annotations, x::Annotated) + if haskey(annotations, x.annotation) + if annotations[x.annotation] != x.label + error("Found the same annotation $(repr(x.annotation)) with two different labels: $(repr(x.label)) and $(repr(annotations[x.annotation])).") + end + else + annotations[x.annotation] = x.label + end + return +end + +resolve_annotation(x, annotations) = x +function resolve_annotation(a::Annotated, annotations) + ResolvedAnnotation(a.value, annotations[a.annotation]) +end + +function resolve_annotation(c::Concat, annotations) + new_args = map(c.args) do arg + resolve_annotation(arg, annotations) + end + Concat(new_args...) +end + +function create_cell_matrix(cells) + nrows = 0 + ncols = 0 + for cell in cells + nrows = max(nrows, cell.span[1].stop) + ncols = max(ncols, cell.span[2].stop) + end + matrix = zeros(Int, nrows, ncols) + for (i, cell) in enumerate(cells) + enter_cell!(matrix, cell, i) + end + matrix +end + +function enter_cell!(matrix, cell, i) + for row in cell.span[1], col in cell.span[2] + v = matrix[row, col] + if v == 0 + matrix[row, col] = i + else + error( + """ + Can't place cell $i in [$row, $col] as cell $v is already there. + Value of cell $i: $(cell.value) + """ + ) + end + end +end + +""" + postprocess_table + +Overload `postprocess_table(t::Table, postprocessor::YourPostProcessor)` +to enable using `YourPostProcessor` as a table postprocessor by passing +it to the `postprocess` keyword argument of `Table`. + +The function must always return a `Table`. + +Use `postprocess_cell` instead if you do not need to modify table attributes +during postprocessing but only individual cells. +""" +function postprocess_table end + +""" + postprocess_cell + +Overload `postprocess_cell(c::Cell, postprocessor::YourPostProcessor)` +to enable using `YourPostProcessor` as a cell postprocessor by passing +it to the `postprocess` keyword argument of `Table`. + +The function must always return a `Cell`. It will be applied on every cell +of the table that is being postprocessed, all other table attributes will +be left unmodified. + +Use `postprocess_table` instead if you need to modify table attributes +during postprocessing. +""" +function postprocess_cell end + +function postprocess_cell(cell::Cell, any) + error(""" + `postprocess_cell` is not implemented for postprocessor type `$(typeof(any))`. + To use this object for postprocessing, either implement `postprocess_table(::Table, ::$(typeof(any)))` or + `postprocess_cell(::Cell, ::$(typeof(any)))` for it. + """) +end + +function postprocess_table(ct::Table, any) + new_cl = map(ct.cells) do cell + new_cell = postprocess_cell(cell, any) + if !(new_cell isa Cell) + error("`postprocess_cell` called with `$(any)` returned an object of type `$(typeof(new_cell))` instead of `Cell`.") + end + return new_cell + end + Table(new_cl, ct.header, ct.footer, ct.footnotes, ct.rowgaps, ct.colgaps, [], ct.round_digits, ct.round_mode, ct.trailing_zeros) +end + +function postprocess_table(ct::Table, v::AbstractVector) + for postprocessor in v + ct = postprocess_table(ct, postprocessor) + !(ct isa Table) && error("Postprocessor $postprocessor caused `postprocess_table` not to return a `Table` but a `$(typeof(ct))`") + end + return ct +end + +""" + Replace(f, with) + Replace(f; with) + +This postprocessor replaces all cell values for which `f(value) === true` +with the value `with`. +If `with <: Function` then the new value will be `with(value)`, instead. + +## Examples + +``` +Replace(x -> x isa String, "A string was here") +Replace(x -> x isa String, uppercase) +Replace(x -> x isa Int && iseven(x), "An even Int was here") +``` +""" +struct Replace{F,W} + f::F + with::W +end + +Replace(f; with) = Replace(f, with) + +""" + ReplaceMissing(; with = Annotated("-", "- No value"; label = NoLabel())) + +This postprocessor replaces all `missing` cell values with the value in `with`. +""" +ReplaceMissing(; with = Annotated("-", "- No value"; label = NoLabel())) = + Replace(ismissing, with) + +function postprocess_cell(cell::Cell, r::Replace) + matches = r.f(cell.value) + if !(matches isa Bool) + error("`Replace` predicate `$(r.f)` did not return a `Bool` but a value of type `$(typeof(matches))`.") + end + fn(_, with) = with + fn(x, with::Function) = with(x) + value = matches ? fn(cell.value, r.with) : cell.value + return Cell(value, cell.style) +end + +struct Rounder + round_digits::Int + round_mode::Symbol + trailing_zeros::Bool +end +struct RoundedFloat + f::Float64 + round_digits::Int + round_mode::Symbol + trailing_zeros::Bool +end + +apply_rounder(x, r::Rounder) = x +apply_rounder(x::AbstractFloat, r::Rounder) = RoundedFloat(x, r.round_digits, r.round_mode, r.trailing_zeros) +apply_rounder(x::Concat, r::Rounder) = Concat(map(arg -> apply_rounder(arg, r), x.args)...) +apply_rounder(x::Multiline, r::Rounder) = Multiline(map(arg -> apply_rounder(arg, r), x.values)) +apply_rounder(x::Annotated, r::Rounder) = Annotated(apply_rounder(x.value, r), x.annotation, x.label) + +function postprocess_cell(cell::Cell, r::Rounder) + Cell(apply_rounder(cell.value, r), cell.style) +end + +struct Superscript + super +end + +struct Subscript + sub +end + +apply_rounder(x::Superscript, r::Rounder) = Superscript(apply_rounder(x.super, r)) +apply_rounder(x::Subscript, r::Rounder) = Subscript(apply_rounder(x.sub, r)) + +function postprocess(ct::Table) + # every table has float rounding / formatting applied as the very last step + pp = ct.postprocess + if ct.round_mode !== nothing + rounder = Rounder(ct.round_digits, ct.round_mode, ct.trailing_zeros) + pp = [ct.postprocess; rounder] + end + return postprocess_table(ct, pp) +end diff --git a/src/docx.jl b/src/docx.jl new file mode 100644 index 0000000..4d53ee6 --- /dev/null +++ b/src/docx.jl @@ -0,0 +1,299 @@ +const DOCX_OUTER_RULE_SIZE = 8 * WriteDocx.eighthpt +const DOCX_INNER_RULE_SIZE = 4 * WriteDocx.eighthpt +const DOCX_ANNOTATION_FONTSIZE = 8 * WriteDocx.pt + +""" + to_docx(ct::Table) + +Creates a `WriteDocx.Table` node for `Table` `ct` which can be inserted into +a `WriteDocx` document. +""" +function to_docx(ct::Table) + ct = postprocess(ct) + + cells = sort(to_spanned_cells(ct.cells), by = x -> (x.span[1].start, x.span[2].start)) + + cells, annotations = resolve_annotations(cells) + + matrix = create_cell_matrix(cells) + + running_index = 0 + tablerows = WriteDocx.TableRow[] + + function full_width_border_row(sz) + WriteDocx.TableRow( + [WriteDocx.TableCell([WriteDocx.Paragraph([])], + WriteDocx.TableCellProperties( + gridspan = size(matrix, 2), + borders = WriteDocx.TableCellBorders( + bottom = WriteDocx.TableCellBorder( + color = WriteDocx.automatic, + size = sz, + style = WriteDocx.BorderStyle.single, + ), + start = WriteDocx.TableCellBorder(color = WriteDocx.automatic, size = sz, style = WriteDocx.BorderStyle.none), + stop = WriteDocx.TableCellBorder(color = WriteDocx.automatic, size = sz, style = WriteDocx.BorderStyle.none), + ), + hide_mark = true, + ))] + ) + end + + push!(tablerows, full_width_border_row(DOCX_OUTER_RULE_SIZE)) + + validate_rowgaps(ct.rowgaps, size(matrix, 1)) + validate_colgaps(ct.colgaps, size(matrix, 2)) + rowgaps = Dict(ct.rowgaps) + colgaps = Dict(ct.colgaps) + + for row in 1:size(matrix, 1) + rowcells = WriteDocx.TableCell[] + + for col in 1:size(matrix, 2) + index = matrix[row, col] + if index == 0 + push!(rowcells, WriteDocx.TableCell([ + WriteDocx.Paragraph([ + WriteDocx.Run([ + WriteDocx.Text("") + ]) + ]) + ])) + else + cell = cells[index] + is_firstcol = col == cell.span[2].start + if !is_firstcol + continue + end + push!(rowcells, docx_cell(row, col, cell, rowgaps, colgaps)) + running_index = index + + end + end + push!(tablerows, WriteDocx.TableRow(rowcells)) + + if row == ct.header + push!(tablerows, full_width_border_row(DOCX_INNER_RULE_SIZE)) + end + end + + push!(tablerows, full_width_border_row(DOCX_OUTER_RULE_SIZE)) + + if !isempty(annotations) || !isempty(ct.footnotes) + elements = [] + for (i, (annotation, label)) in enumerate(annotations) + i > 1 && push!(elements, WriteDocx.Run([WriteDocx.Text(" ")])) + if label !== NoLabel() + push!(elements, WriteDocx.Run([WriteDocx.Text(docx_sprint(label)), WriteDocx.Text(" ")], + WriteDocx.RunProperties(valign = WriteDocx.VerticalAlignment.superscript))) + end + push!(elements, WriteDocx.Run([WriteDocx.Text(docx_sprint(annotation))], + WriteDocx.RunProperties(size = DOCX_ANNOTATION_FONTSIZE))) + end + for (i, footnote) in enumerate(ct.footnotes) + (!isempty(annotations) || i > 1) && push!(elements, WriteDocx.Run([WriteDocx.Text(" ")])) + push!(elements, WriteDocx.Run([WriteDocx.Text(docx_sprint(footnote))], + WriteDocx.RunProperties(size = DOCX_ANNOTATION_FONTSIZE))) + end + annotation_row = WriteDocx.TableRow([WriteDocx.TableCell( + [WriteDocx.Paragraph(elements)], + WriteDocx.TableCellProperties(gridspan = size(matrix, 2)) + )]) + push!(tablerows, annotation_row) + end + + tablenode = WriteDocx.Table(tablerows, + WriteDocx.TableProperties( + margins = WriteDocx.TableLevelCellMargins( + # Word already has relatively broadly spaced tables, + # so we keep margins to a minimum. A little bit on the left + # and right is needed to separate the columns from each other + top = WriteDocx.pt * 0, + bottom = WriteDocx.pt * 0, + start = WriteDocx.pt * 1.5, + stop = WriteDocx.pt * 1.5, + ), + # this spacing allows adjacent column underlines to be ever-so-slightly spaced apart, + # which is otherwise not possible to achieve in Word (aside from adding empty spacing columns maybe) + spacing = 1 * WriteDocx.pt, + ) + ) + + return tablenode +end + +function paragraph_and_run_properties(st::CellStyle) + para = WriteDocx.ParagraphProperties( + justification = st.halign === :center ? WriteDocx.Justification.center : + st.halign === :left ? WriteDocx.Justification.start : + st.halign === :right ? WriteDocx.Justification.stop : + error("Unhandled halign $(st.halign)"), + ) + run = WriteDocx.RunProperties( + bold = st.bold ? true : nothing, # TODO: fix bug in WriteDocx? + italic = st.italic ? true : nothing, # TODO: fix bug in WriteDocx? + ) + return para, run +end + +function hardcoded_styles(class::Nothing) + WriteDocx.ParagraphProperties(), (;) +end + +function cell_properties(cell::SpannedCell, row, col, vertical_merge, gridspan, rowgaps, colgaps) + cs = cell.style + + pt = WriteDocx.pt + + bottom_rowgap = get(rowgaps, cell.span[1].stop, nothing) + if bottom_rowgap === nothing + if cs.border_bottom # borders need a bit of spacing to look ok + bottom_margin = 2.0 * pt + else + bottom_margin = nothing + end + else + bottom_margin = 0.5 * bottom_rowgap * pt + end + + top_rowgap = get(rowgaps, cell.span[1].start-1, nothing) + top_margin = top_rowgap === nothing ? nothing : 0.5 * top_rowgap * pt + + left_colgap = get(colgaps, cell.span[2].start-1, nothing) + if left_colgap === nothing + if cs.indent_pt != 0 + left_margin = cs.indent_pt * pt + else + left_margin = nothing + end + else + if cs.indent_pt != 0 + left_margin = (cs.indent_pt + 0.5 * left_colgap) * pt + else + left_margin = 0.5 * left_colgap * pt + end + end + + right_colgap = get(colgaps, cell.span[2].stop, nothing) + right_margin = right_colgap === nothing ? nothing : 0.5 * right_colgap * pt + + left_end = col == cell.span[2].start + right_end = col == cell.span[2].stop + top_end = row == cell.span[1].start + bottom_end = row == cell.span[1].stop + + # spanned cells cannot have margins in the interior + if !right_end + right_margin = nothing + end + if !left_end + left_margin = nothing + end + if !top_end + top_margin = nothing + end + if !bottom_end + bottom_margin = nothing + end + + WriteDocx.TableCellProperties(; + margins = WriteDocx.TableCellMargins( + start = left_margin, + bottom = bottom_margin, + top = top_margin, + stop = right_margin, + ), + borders = cs.border_bottom ? WriteDocx.TableCellBorders( + bottom = WriteDocx.TableCellBorder(color = WriteDocx.automatic, size = DOCX_INNER_RULE_SIZE, style = WriteDocx.BorderStyle.single), + start = WriteDocx.TableCellBorder(color = WriteDocx.automatic, size = DOCX_INNER_RULE_SIZE, style = WriteDocx.BorderStyle.none), # the left/right none styles keep adjacent cells' bottom borders from merging together + stop = WriteDocx.TableCellBorder(color = WriteDocx.automatic, size = DOCX_INNER_RULE_SIZE, style = WriteDocx.BorderStyle.none), + ) : nothing, + valign = cs.valign === :center ? WriteDocx.VerticalAlign.center : + cs.valign === :bottom ? WriteDocx.VerticalAlign.bottom : + cs.valign === :top ? WriteDocx.VerticalAlign.top : + error("Unhandled valign $(cs.valign)"), + vertical_merge, + gridspan, + ) +end + +function docx_cell(row, col, cell, rowgaps, colgaps) + ncols = length(cell.span[2]) + is_firstrow = row == cell.span[1].start + is_firstcol = col == cell.span[2].start + vertical_merge = length(cell.span[1]) == 1 ? nothing : is_firstrow + gridspan = ncols > 1 ? ncols : nothing + + paraproperties, runproperties = paragraph_and_run_properties(cell.style) + + runs = if is_firstrow && is_firstcol + if cell.value === nothing + WriteDocx.Run[] + else + to_runs(cell.value, runproperties) + end + else + [WriteDocx.Run([WriteDocx.Text("")], runproperties)] + end + cellprops = cell_properties(cell, row, col, vertical_merge, gridspan, rowgaps, colgaps) + + WriteDocx.TableCell([ + WriteDocx.Paragraph(runs, paraproperties), + ], cellprops) +end + +to_runs(x, props) = [WriteDocx.Run([WriteDocx.Text(docx_sprint(x))], props)] + +function to_runs(c::Concat, props) + runs = WriteDocx.Run[] + for arg in c.args + append!(runs, to_runs(arg, props)) + end + return runs +end + +# make a new property object where each field that's not nothing in x2 replaces the equivalent +# from x1, however, if the elements are both also property objects, merge those separately +@generated function merge_props(x1::T, x2::T) where {T<:Union{WriteDocx.TableCellProperties,WriteDocx.RunProperties,WriteDocx.ParagraphProperties,WriteDocx.TableCellBorders,WriteDocx.TableCellMargins}} + FN = fieldnames(T) + N = fieldcount(T) + quote + Base.Cartesian.@ncall $N $T i -> begin + f1 = getfield(x1, $FN[i]) + f2 = getfield(x2, $FN[i]) + merge_props(f1, f2) + end + end +end + +merge_props(x, y) = y === nothing ? x : y + +function to_runs(s::Superscript, props::WriteDocx.RunProperties) + props = merge_props(props, WriteDocx.RunProperties(valign = WriteDocx.VerticalAlignment.superscript)) + return to_runs(s.super, props) +end + +function to_runs(s::Subscript, props::WriteDocx.RunProperties) + props = merge_props(props, WriteDocx.RunProperties(valign = WriteDocx.VerticalAlignment.subscript)) + return to_runs(s.sub, props) +end + +function to_runs(m::Multiline, props) + runs = WriteDocx.Run[] + for (i, val) in enumerate(m.values) + i > 1 && push!(runs, WriteDocx.Run([WriteDocx.Break()])), + append!(runs, to_runs(val, props)) + end + return runs +end +function to_runs(r::ResolvedAnnotation, props) + runs = to_runs(r.value, props) + if r.label !== NoLabel() + props = merge_props(props, WriteDocx.RunProperties(valign = WriteDocx.VerticalAlignment.superscript)) + push!(runs, WriteDocx.Run([WriteDocx.Text(docx_sprint(r.label))], props)) + end + return runs +end +docx_sprint(x) = sprint(x) do io, x + _showas(io, MIME"text"(), x) +end \ No newline at end of file diff --git a/src/helpers.jl b/src/helpers.jl new file mode 100644 index 0000000..6c1c131 --- /dev/null +++ b/src/helpers.jl @@ -0,0 +1,105 @@ +function _showas(io::IO, mime::MIME, value) + fn(io::IO, ::MIME"text/html", value::AbstractString) = _str_html_escaped(io, value) + fn(io::IO, ::MIME"text/html", value) = _str_html_escaped(io, repr(value)) + fn(io::IO, ::MIME"text/latex", value::AbstractString) = _str_latex_escaped(io, value) + fn(io::IO, ::MIME"text/latex", value) = _str_latex_escaped(io, repr(value)) + fn(io::IO, ::MIME"text/typst", value::AbstractString) = _str_typst_escaped(io, value) + fn(io::IO, ::MIME"text/typst", value) = _str_typst_escaped(io, repr(value)) + fn(io::IO, ::MIME, value) = print(io, value) + return showable(mime, value) ? show(io, mime, value) : fn(io, mime, value) +end +function _showas(io::IO, m::MIME, r::RoundedFloat) + f = r.f + mode = r.round_mode + digits = r.round_digits + s = if mode === :auto + string(auto_round(f, target_digits = digits)) + elseif mode === :sigdigits + string(round(f, sigdigits = digits)) + elseif mode === :digits + fmt = Printf.Format("%.$(digits)f") + Printf.format(fmt, f) + else + error("Unknown round mode $mode") + end + if !r.trailing_zeros + s = replace(s, r"^(\d+)$|^(\d+)\.0*$|^(\d+\.[1-9]*?)0*$" => s"\1\2\3") + end + _showas(io, m, s) +end +_showas(io::IO, m::MIME, c::CategoricalValue) = _showas(io, m, CategoricalArrays.DataAPI.unwrap(c)) + +function _showas(io::IO, m::MIME, c::Concat) + for arg in c.args + _showas(io, m, arg) + end +end + +format_value(x) = x + +""" + auto_round(number; target_digits) + +Rounds a floating point number to a target number of digits that are not leading zeros. +For example, with 3 target digits, desirable numbers would be 123.0, 12.3, 1.23, +0.123, 0.0123 etc. Numbers larger than the number of digits are only rounded to the next integer +(compare with `round(1234, sigdigits = 3)` which rounds to `1230.0`). +Numbers are rounded to `target_digits` significant digits when the floored base 10 +exponent is -5 and lower or 6 and higher, as these numbers print with `e` notation by default in Julia. + +``` +auto_round( 1234567, target_digits = 4) = 1.235e6 +auto_round( 123456.7, target_digits = 4) = 123457.0 +auto_round( 12345.67, target_digits = 4) = 12346.0 +auto_round( 1234.567, target_digits = 4) = 1235.0 +auto_round( 123.4567, target_digits = 4) = 123.5 +auto_round( 12.34567, target_digits = 4) = 12.35 +auto_round( 1.234567, target_digits = 4) = 1.235 +auto_round( 0.1234567, target_digits = 4) = 0.1235 +auto_round( 0.01234567, target_digits = 4) = 0.01235 +auto_round( 0.001234567, target_digits = 4) = 0.001235 +auto_round( 0.0001234567, target_digits = 4) = 0.0001235 +auto_round( 0.00001234567, target_digits = 4) = 1.235e-5 +auto_round( 0.000001234567, target_digits = 4) = 1.235e-6 +auto_round(0.0000001234567, target_digits = 4) = 1.235e-7 +``` +""" +function auto_round(number; target_digits::Int) + !isfinite(number) && return number + target_digits < 1 && throw(ArgumentError("target_digits needs to be 1 or more")) + order_of_magnitude = number == 0 ? 0 : log10(abs(number)) + oom = floor(Int, order_of_magnitude) + ndigits = max(0, -oom + target_digits - 1) + if -5 < oom < 6 + round(number, digits = ndigits) + else + # this relies on Base printing e notation >= 6 and <= -5 + round(number, sigdigits = target_digits) + end +end + +natural_lt(x::AbstractString, y::AbstractString) = NaturalSort.natural(x, y) +natural_lt(x, y) = x < y + +function validate_rowgaps(rowgaps, nrows) + nrows == 1 && !isempty(rowgaps) && error("No row gaps allowed for a table with one row.") + for (m, _) in rowgaps + if m < 1 + error("A row gap index of $m is invalid, must be at least 1.") + end + if m >= nrows + error("A row gap index of $m is invalid for a table with $nrows rows. The maximum allowed is $(nrows - 1).") + end + end +end +function validate_colgaps(colgaps, ncols) + ncols == 1 && !isempty(colgaps) && error("No column gaps allowed for a table with one column.") + for (m, _) in colgaps + if m < 1 + error("A column gap index of $m is invalid, must be at least 1.") + end + if m >= ncols + error("A column gap index of $m is invalid for a table with $ncols columns. The maximum allowed is $(ncols - 1).") + end + end +end \ No newline at end of file diff --git a/src/html.jl b/src/html.jl new file mode 100644 index 0000000..97699db --- /dev/null +++ b/src/html.jl @@ -0,0 +1,278 @@ +Base.show(io::IO, ::MIME"juliavscode/html", ct::Table) = show(io, MIME"text/html"(), ct) + +function Base.show(io::IO, ::MIME"text/html", ct::Table) + ct = postprocess(ct) + + cells = sort(to_spanned_cells(ct.cells), by = x -> (x.span[1].start, x.span[2].start)) + + cells, annotations = resolve_annotations(cells) + + matrix = create_cell_matrix(cells) + + _io = IOBuffer() + + # The final table has a hash-based class name so that several different renderings (maybe even across + # SummaryTables.jl versions) don't conflict and influence each other. + hash_placeholder = "<>" # should not collide because it's not valid HTML and <> are not allowed otherwise + + println(_io, "") + + print(_io, """ + + """) + # border-collapse requires a separate row/cell to insert a border, it can't be put on + println(_io, " ") + + validate_rowgaps(ct.rowgaps, size(matrix, 1)) + validate_colgaps(ct.colgaps, size(matrix, 2)) + rowgaps = Dict(ct.rowgaps) + colgaps = Dict(ct.colgaps) + + running_index = 0 + for row in 1:size(matrix, 1) + if row == ct.footer + print(_io, " \n") + # border-collapse requires a separate row/cell to insert a border, it can't be put on + print(_io, " ") + end + print(_io, " \n") + for col in 1:size(matrix, 2) + index = matrix[row, col] + if index > running_index + print(_io, " ") + print_html_cell(_io, cells[index], rowgaps, colgaps) + running_index = index + print(_io, "\n") + elseif index == 0 + print(_io, " ") + print_empty_html_cell(_io) + print(_io, "\n") + end + end + print(_io, " \n") + if row == ct.header + # border-collapse requires a separate row/cell to insert a border, it can't be put on + print(_io, " ") + end + end + + # border-collapse requires a separate row/cell to insert a border, it can't be put on + println(_io, " ") + + if !isempty(annotations) || !isempty(ct.footnotes) + print(_io, " ") + end + + print(_io, "
") + for (i, (annotation, label)) in enumerate(annotations) + i > 1 && print(_io, "    ") + if label !== NoLabel() + print(_io, "") + _showas(_io, MIME"text/html"(), label) + print(_io, " ") + end + _showas(_io, MIME"text/html"(), annotation) + end + for (i, footnote) in enumerate(ct.footnotes) + (!isempty(annotations) || i > 1) && print(_io, "    ") + _showas(_io, MIME"text/html"(), footnote) + end + println(_io, "
") + + s = String(take!(_io)) + + short_hash = first(bytes2hex(SHA.sha256(s)), 8) + s2 = replace(s, hash_placeholder => short_hash) + print(io, s2) +end + +function _showas(io::IO, ::MIME"text/html", m::Multiline) + for (i, value) in enumerate(m.values) + i > 1 && print(io, "
") + _showas(io, MIME"text/html"(), value) + end +end + +function _showas(io::IO, ::MIME"text/html", r::ResolvedAnnotation) + _showas(io, MIME"text/html"(), r.value) + if r.label !== NoLabel() + print(io, "") + _showas(io, MIME"text/html"(), r.label) + print(io, "") + end +end + +function _showas(io::IO, ::MIME"text/html", s::Superscript) + print(io, "") + _showas(io, MIME"text/html"(), s.super) + print(io, "") +end + +function _showas(io::IO, ::MIME"text/html", s::Subscript) + print(io, "") + _showas(io, MIME"text/html"(), s.sub) + print(io, "") +end + +function print_html_cell(io, cell::SpannedCell, rowgaps, colgaps) + print(io, " 1 + print(io, " rowspan=\"$nrows\"") + end + if ncols > 1 + print(io, " colspan=\"$ncols\"") + end + print(io, " style=\"") + if cell.style.bold + print(io, "font-weight:bold;") + end + if cell.style.italic + print(io, "font-style:italic;") + end + if cell.style.underline + print(io, "text-decoration:underline;") + end + padding_left = get(colgaps, cell.span[2].start-1, nothing) + if cell.style.indent_pt != 0 || padding_left !== nothing + pl = something(padding_left, 0.0) / 2 + cell.style.indent_pt + print(io, "padding-left:$(pl)pt;") + end + padding_right = get(colgaps, cell.span[2].stop, nothing) + if padding_right !== nothing + print(io, "padding-right:$(padding_right/2)pt;") + end + if cell.style.border_bottom + print(io, "border-bottom:1px solid black; ") + end + padding_bottom = get(rowgaps, cell.span[1].stop, nothing) + if padding_bottom !== nothing + print(io, "padding-bottom: $(padding_bottom/2)pt;") + elseif cell.style.border_bottom + print(io, "padding-bottom: 0.25em;") # needed to make border bottoms look less cramped + end + padding_top = get(rowgaps, cell.span[1].start-1, nothing) + if padding_top !== nothing + print(io, "padding-top: $(padding_top/2)pt;") + end + if cell.style.valign ∉ (:top, :center, :bottom) + error("Invalid valign $(repr(cell.style.valign)). Options are :top, :center, :bottom.") + end + if cell.style.valign !== :top + v = cell.style.valign === :center ? "middle" : "bottom" + print(io, "vertical-align:$v;") + end + if cell.style.halign ∉ (:left, :center, :right) + error("Invalid halign $(repr(cell.style.halign)). Options are :left, :center, :right.") + end + print(io, "text-align:$(cell.style.halign);") + + print(io, "\">") + if cell.value !== nothing + _showas(io, MIME"text/html"(), cell.value) + end + print(io, "") + return +end + +function print_empty_html_cell(io) + print(io, "") +end + +function print_html_styles(io, table_styles) + println(io, "") +end + +function _sorted_dict(d) + ps = collect(pairs(d)) + sort!(ps, by = first) +end + +# Escaping functions, copied from PrettyTables, MIT licensed. + +function _str_html_escaped( + io::IO, + s::AbstractString, + replace_newline::Bool = false, + escape_html_chars::Bool = true, +) + a = Iterators.Stateful(s) + for c in a + if isascii(c) + c == '\n' ? (replace_newline ? print(io, "
") : print(io, "\\n")) : + c == '&' ? (escape_html_chars ? print(io, "&") : print(io, c)) : + c == '<' ? (escape_html_chars ? print(io, "<") : print(io, c)) : + c == '>' ? (escape_html_chars ? print(io, ">") : print(io, c)) : + c == '"' ? (escape_html_chars ? print(io, """) : print(io, c)) : + c == '\'' ? (escape_html_chars ? print(io, "'") : print(io, c)) : + c == '\0' ? print(io, escape_nul(peek(a))) : + c == '\e' ? print(io, "\\e") : + c == '\\' ? print(io, "\\\\") : + '\a' <= c <= '\r' ? print(io, '\\', "abtnvfr"[Int(c)-6]) : + # c == '%' ? print(io, "\\%") : + isprint(c) ? print(io, c) : + print(io, "\\x", string(UInt32(c), base = 16, pad = 2)) + elseif !Base.isoverlong(c) && !Base.ismalformed(c) + isprint(c) ? print(io, c) : + c <= '\x7f' ? print(io, "\\x", string(UInt32(c), base = 16, pad = 2)) : + c <= '\uffff' ? print(io, "\\u", string(UInt32(c), base = 16, pad = Base.need_full_hex(peek(a)) ? 4 : 2)) : + print(io, "\\U", string(UInt32(c), base = 16, pad = Base.need_full_hex(peek(a)) ? 8 : 4)) + else # malformed or overlong + u = bswap(reinterpret(UInt32, c)) + while true + print(io, "\\x", string(u % UInt8, base = 16, pad = 2)) + (u >>= 8) == 0 && break + end + end + end +end + +function _str_html_escaped( + s::AbstractString, + replace_newline::Bool = false, + escape_html_chars::Bool = true +) + return sprint( + _str_html_escaped, + s, + replace_newline, + escape_html_chars; + sizehint = lastindex(s) + ) +end diff --git a/src/latex.jl b/src/latex.jl new file mode 100644 index 0000000..eca32e5 --- /dev/null +++ b/src/latex.jl @@ -0,0 +1,249 @@ +function Base.show(io::IO, ::MIME"text/latex", ct::Table) + ct = postprocess(ct) + + cells = sort(to_spanned_cells(ct.cells), by = x -> (x.span[1].start, x.span[2].start)) + + cells, annotations = resolve_annotations(cells) + + matrix = create_cell_matrix(cells) + + validate_rowgaps(ct.rowgaps, size(matrix, 1)) + validate_colgaps(ct.colgaps, size(matrix, 2)) + rowgaps = Dict(ct.rowgaps) + colgaps = Dict(ct.colgaps) + + column_alignment_counts = StatsBase.countmap((cell.span[2], cell.style.halign) for cell in cells if cell.value !== nothing) + + alignments = (:center, :left, :right) + column_alignments = map(1:size(matrix,2)) do i_col + i_max = argmax(get(column_alignment_counts, (i_col:i_col, al), 0) for al in alignments) + return alignments[i_max] + end + colspec = let + iob = IOBuffer() + for (icol, al) in enumerate(column_alignments) + char = al === :center ? 'c' : + al === :right ? 'r' : + al === :left ? 'l' : error("Invalid align $al") + print(iob, char) + if haskey(colgaps, icol) + print(iob, "@{\\hskip $(colgaps[icol])pt}") + end + end + String(take!(iob)) + end + + print(io, """ + \\begin{table}[!ht] + \\setlength\\tabcolsep{0pt} + \\centering + \\begin{threeparttable} + \\begin{tabular}{@{\\extracolsep{2ex}}*{$(size(matrix, 2))}{$colspec}} + \\toprule + """) + + running_index = 0 + bottom_borders = Dict{Int, Vector{UnitRange}}() + + for row in axes(matrix, 1) + for col in axes(matrix, 2) + index = matrix[row, col] + column_align = column_alignments[col] + if index == 0 + col > 1 && print(io, " & ") + print_empty_latex_cell(io) + else + cell = cells[index] + + if cell.style.border_bottom && col == cell.span[2].start + lastrow = cell.span[1].stop + ranges = get!(bottom_borders, lastrow) do + UnitRange[] + end + border_columns = cell.span[2] + push!(ranges, border_columns) + end + + halign_char = cell.style.halign === :left ? 'l' : + cell.style.halign === :center ? 'c' : + cell.style.halign === :right ? 'r' : + error("Unknown halign $(cell.style.halign)") + valign_char = cell.style.valign === :top ? 't' : + cell.style.valign === :center ? 'c' : + cell.style.valign === :bottom ? 'b' : + error("Unknown valign $(cell.style.valign)") + + nrow = length(cell.span[1]) + ncol = length(cell.span[2]) + use_multicolumn = ncol > 1 || cell.style.halign !== column_align + + if index > running_index + # this is the top-left part of a new cell which can be a single or multicolumn/row cell + col > 1 && print(io, " & ") + if cell.value !== nothing + use_multicolumn && print(io, "\\multicolumn{$ncol}{$halign_char}{") + nrow > 1 && print(io, "\\multirow[$valign_char]{$nrow}{*}{") + print_latex_cell(io, cell) + nrow > 1 && print(io, "}") + use_multicolumn && print(io, "}") + end + running_index = index + elseif col == cell.span[2][begin] + # we need to print additional multicolumn statements in the second to last + # row of a multirow + col > 1 && print(io, " & ") + if ncol > 1 + print(io, "\\multicolumn{$ncol}{$halign_char}{}") + end + end + end + end + + print(io, " \\\\") + if haskey(rowgaps, row) + print(io, "[$(rowgaps[row])pt]") + end + println(io) + # draw any bottom borders that have been registered to be drawn below this row + if haskey(bottom_borders, row) + for range in bottom_borders[row] + print(io, "\\cmidrule{$(range.start)-$(range.stop)}") + end + print(io, "\n") + end + + if row == ct.header + print(io, "\\midrule\n") + end + if row + 1 == ct.footer + print(io, "\\midrule\n") + end + end + print(io, "\\bottomrule\n") + print(io, raw""" + \end{tabular} + """) + if !isempty(annotations) || !isempty(ct.footnotes) + println(io, raw"\begin{tablenotes}[flushleft,para]") + println(io, raw"\footnotesize") + for (annotation, label) in annotations + if label !== NoLabel() + print(io, raw"\item[") + _showas(io, MIME"text/latex"(), label) + print(io, "]") + end + _showas(io, MIME"text/latex"(), annotation) + println(io) + end + for footnote in ct.footnotes + print(io, raw"\item[]") + _showas(io, MIME"text/latex"(), footnote) + println(io) + end + println(io, raw"\end{tablenotes}") + end + + print(io, raw""" + \end{threeparttable} + \end{table} + """) + + # after end{tabular}: + + + return +end + +function get_class_styles(class, table_styles) + properties = Dict{Symbol, Any}() + if haskey(table_styles, class) + merge!(properties, table_styles[class]) + end + return properties +end + +print_empty_latex_cell(io) = nothing + +function print_latex_cell(io, cell::SpannedCell) + cell.value === nothing && return + + st = cell.style + st.indent_pt > 0 && print(io, "\\hspace{$(st.indent_pt)pt}") + st.bold && print(io, "\\textbf{") + st.italic && print(io, "\\textit{") + st.underline && print(io, "\\underline{") + _showas(io, MIME"text/latex"(), cell.value) + st.underline && print(io, "}") + st.italic && print(io, "}") + st.bold && print(io, "}") + return +end + +function _showas(io::IO, ::MIME"text/latex", m::Multiline) + print(io, "\\begin{tabular}{@{}c@{}}") + for (i, value) in enumerate(m.values) + i > 1 && print(io, " \\\\ ") + _showas(io, MIME"text/latex"(), value) + end + print(io, "\\end{tabular}") +end + +function _showas(io::IO, m::MIME"text/latex", s::Superscript) + print(io, "\\textsuperscript{") + _showas(io, m, s.super) + print(io, "}") +end + +function _showas(io::IO, m::MIME"text/latex", s::Subscript) + print(io, "\\textsubscript{") + _showas(io, m, s.sub) + print(io, "}") +end + +function _showas(io::IO, ::MIME"text/latex", r::ResolvedAnnotation) + _showas(io, MIME"text/latex"(), r.value) + if r.label !== NoLabel() + print(io, "\\tnote{") + _showas(io, MIME"text/latex"(), r.label) + print(io, "}") + end +end + +function _str_latex_escaped(io::IO, s::AbstractString) + escapable_special_chars = raw"&%$#_{}" + a = Iterators.Stateful(s) + for c in a + if c in escapable_special_chars + print(io, '\\', c) + elseif c === '\\' + print(io, "\\textbackslash{}") + elseif c === '~' + print(io, "\\textasciitilde{}") + elseif c === '^' + print(io, "\\textasciicircum{}") + elseif isascii(c) + c == '\0' ? print(io, Base.escape_nul(peek(a))) : + c == '\e' ? print(io, "\\e") : + # c == '\\' ? print(io, "\\\\") : + '\a' <= c <= '\r' ? print(io, '\\', "abtnvfr"[Int(c)-6]) : + c == '%' ? print(io, "\\%") : + isprint(c) ? print(io, c) : + print(io, "\\x", string(UInt32(c), base = 16, pad = 2)) + elseif !Base.isoverlong(c) && !Base.ismalformed(c) + isprint(c) ? print(io, c) : + c <= '\x7f' ? print(io, "\\x", string(UInt32(c), base = 16, pad = 2)) : + c <= '\uffff' ? print(io, "\\u", string(UInt32(c), base = 16, pad = Base.need_full_hex(peek(a)) ? 4 : 2)) : + print(io, "\\U", string(UInt32(c), base = 16, pad = Base.need_full_hex(peek(a)) ? 8 : 4)) + else # malformed or overlong + u = bswap(reinterpret(UInt32, c)) + while true + print(io, "\\x", string(u % UInt8, base = 16, pad = 2)) + (u >>= 8) == 0 && break + end + end + end +end + +function _str_latex_escaped(s::AbstractString) + return sprint(_str_latex_escaped, s, sizehint=lastindex(s)) +end diff --git a/src/table.jl b/src/table.jl new file mode 100644 index 0000000..c20e40d --- /dev/null +++ b/src/table.jl @@ -0,0 +1,969 @@ +""" +Specifies one variable to group over and an associated name for display. +""" +struct Group + symbol::Symbol + name +end + +Group(s::Symbol) = Group(s, string(s)) +Group(p::Pair{Symbol, <:Any}) = Group(p[1], p[2]) +make_groups(v::AbstractVector) = map(Group, v) +make_groups(x) = [Group(x)] + +""" +Specifies one function to summarize the raw values of one group with, +and an associated name for display. +""" +struct SummaryAnalysis + func + name +end + +SummaryAnalysis(p::Pair{<:Function, <:Any}) = SummaryAnalysis(p[1], p[2]) +SummaryAnalysis(f::Function) = SummaryAnalysis(f, string(f)) + +""" +Stores the index of the grouping variable under which the summaries defined in +`analyses` should be run. An index of `0` means that one summary block is appended +after all columns or rows, an index of `1` means on summary block after each group +from the first grouping key of rows or columns, and so on. +""" +struct Summary + groupindex::Int + analyses::Vector{SummaryAnalysis} +end + +function Summary(p::Pair{Symbol, <:Vector}, symbols) + sym = p[1] + summary_index = findfirst(==(sym), symbols) + if summary_index === nothing + error("Summary variable :$(sym) is not a grouping variable.") + end + Summary(summary_index, SummaryAnalysis.(p[2])) +end + +function Summary(v::Vector, _) + summary_index = 0 + Summary(summary_index, SummaryAnalysis.(v)) +end + +# The variable that is used to populate the raw-value cells. +struct Variable + symbol::Symbol + name +end + +Variable(s::Symbol) = Variable(s, string(s)) +Variable(p::Pair{Symbol, <:Any}) = Variable(p[1], p[2]) + +struct ListingTable + gdf::DataFrames.GroupedDataFrame + variable::Variable + row_keys::Vector{<:Tuple} + col_keys::Vector{<:Tuple} + rows::Vector{Group} + columns::Vector{Group} + rowsummary::Summary + gdf_rowsummary::DataFrames.GroupedDataFrame + colsummary::Summary + gdf_colsummary::DataFrames.GroupedDataFrame +end + +struct Pagination{T<:NamedTuple} + options::T +end +Pagination(; kwargs...) = Pagination(NamedTuple(sort(collect(pairs(kwargs)), by = first))) + +""" + Page{M} + +Represents one page of a `PaginatedTable`. + +It has two public fields: + +- `table::Table`: A part of the full table, created according to the chosen `Pagination`. +- `metadata::M`: Information about which part of the full table this page contains. This is different for each + table function that takes a `Pagination` argument because each such function may use its own logic + for how to split pages. +""" +struct Page{M} + metadata::M + table::Table +end + +function Base.show(io::IO, M::MIME"text/plain", p::Page) + indent = " " ^ get(io, :indent, 0) + i_page = get(io, :i_page, nothing) + print(io, indent, "Page") + i_page !== nothing && print(io, " $i_page") + println(io) + show(IOContext(io, :indent => get(io, :indent, 0) + 2), M, p.metadata) +end + +""" + GroupKey + +Holds the group column names and values for one group of a dataset. +This struct has only one field: + +- `entries::Vector{Pair{Symbol,Any}}`: A vector of `column_name => group_value` pairs. +""" +struct GroupKey + entries::Vector{Pair{Symbol,Any}} +end + +GroupKey(g::DataFrames.GroupKey) = GroupKey(collect(pairs(g))) + +""" + ListingPageMetadata + +Describes which row and column group sections of a full listing table +are included in a given page. There are two fields: + +- `rows::Vector{GroupKey}` +- `cols::Vector{GroupKey}` + +Each `Vector{GroupKey}` holds all group keys that were relevant for pagination +along that side of the listing table. A vector is empty if the table was not +paginated along that side. +""" +Base.@kwdef struct ListingPageMetadata + rows::Vector{GroupKey} = [] + cols::Vector{GroupKey} = [] +end + +function Base.show(io::IO, M::MIME"text/plain", p::ListingPageMetadata) + indent = " " ^ get(io, :indent, 0) + println(io, indent, "ListingPageMetadata") + print(io, indent, " rows:") + isempty(p.rows) && print(io, " no pagination") + for r in p.rows + print(io, "\n ", indent,) + print(io, "[", join(("$key => $value" for (key, value) in r.entries), ", "), "]") + end + print(io, "\n", indent, " cols:") + isempty(p.cols) && print(io, " no pagination") + for c in p.cols + print(io, "\n ", indent) + print(io, "[", join(("$key => $value" for (key, value) in c.entries), ", "), "]") + end +end + +""" + PaginatedTable{M} + +The return type for all table functions that take a `Pagination` argument to split the table +into pages according to table-specific pagination rules. + +This type only has one field: + +- `pages::Vector{Page{M}}`: Each `Page` holds a table and metadata of type `M` which depends on the table function that creates the `PaginatedTable`. + +To get the table of page 2, for a `PaginatedTable` stored in variable `p`, access `p.pages[2].table`. +""" +struct PaginatedTable{M} + pages::Vector{Page{M}} +end + +function Base.show(io::IO, M::MIME"text/plain", p::PaginatedTable) + len = length(p.pages) + print(io, "PaginatedTable with $(len) page$(len == 1 ? "" : "s")") + for (i, page) in enumerate(p.pages) + print(io, "\n") + show(IOContext(io, :indent => 2, :i_page => i), M, page) + end +end + +# a basic interactive display of the different pages in the PaginatedTable, which is much +# nicer than just having the textual overview that you get printed out in the REPL +function Base.show(io::IO, M::Union{MIME"text/html",MIME"juliavscode/html"}, p::PaginatedTable) + println(io, "
") + println(io, """ + + """) + for i in 1:length(p.pages) + println(io, """ + + """) + end + println(io, "
") + for (i, page) in enumerate(p.pages) + println(io, "
") + println(io, "

Page $i

") + show(io, M, page.table) + println(io, "\n
") + end + println(io, "
") + println(io, "
") + return +end + +""" + listingtable(table, variable, [pagination]; + rows = [], + cols = [], + summarize_rows = [], + summarize_cols = [], + variable_header = true, + table_kwargs... + ) + +Create a listing table `Table` from `table` which displays raw values from column `variable`. + +## Arguments +- `table`: Data source which must be convertible to a `DataFrames.DataFrame`. +- `variable`: Determines which variable's raw values are shown. Can either be a `Symbol` such as `:ColumnA`, or alternatively a `Pair` where the second element is the display name, such as `:ColumnA => "Column A"`. +- `pagination::Pagination`: If a pagination object is passed, the return type changes to `PaginatedTable`. + The `Pagination` object may be created with keywords `rows` and/or `cols`. + These must be set to `Int`s that determine how many group sections along each side are included in one page. + These group sections are determined by the summary structure, because pagination never splits a listing table + within rows or columns that are being summarized together. + If `summarize_rows` or `summarize_cols` is empty or unset, each group along that side is its own section. + If `summarize_rows` or `summarize_cols` has a group passed via the `column => ...` syntax, the group sections + along that side are determined by `column`. If no such `column` is passed (i.e., the summary + along that side applies to the all groups) there is only one section along that side, which means + that this side cannot be paginated into more than one page. + + +## Keyword arguments +- `rows = []`: Grouping structure along the rows. Should be a `Vector` where each element is a grouping variable, specified as a `Symbol` such as `:Column1`, or a `Pair`, where the first element is the symbol and the second a display name, such as `:Column1 => "Column 1"`. Specifying multiple grouping variables creates nested groups, with the last variable changing the fastest. +- `cols = []`: Grouping structure along the columns. Follows the same structure as `rows`. +- `summarize_rows = []`: Specifies functions to summarize `variable` with along the rows. + Should be a `Vector`, where each entry is one separate summary. + Each summary can be given as a `Function` such as `mean` or `maximum`, in which case the display name is the function's name. + Alternatively, a display name can be given using the pair syntax, such as `mean => "Average"`. + By default, one summary is computed over all groups. + You can also pass `Symbol => [...]` where `Symbol` is a grouping column, to compute one summary for each level of that group. +- `summarize_cols = []`: Specifies functions to summarize `variable` with along the columns. Follows the same structure as `summarize_rows`. +- `variable_header = true`: Controls if the cell with the name of the summarized `variable` is shown. +- `sort = true`: Sort the input table before grouping. Pre-sort as desired and set to `false` when you want to maintain a specific group order or are using non-sortable objects as group keys. + +All other keywords are forwarded to the `Table` constructor, refer to its docstring for details. + +## Example + +``` +using Statistics + +tbl = [ + :Apples => [1, 2, 3, 4, 5, 6, 7, 8], + :Batch => [1, 1, 1, 1, 2, 2, 2, 2], + :Checked => [true, false, true, false, true, false, true, false], + :Delivery => ['a', 'a', 'b', 'b', 'a', 'a', 'b', 'b'], +] + +listingtable( + tbl, + :Apples => "Number of apples", + rows = [:Batch, :Checked => "Checked for spots"], + cols = [:Delivery], + summarize_cols = [sum => "overall"], + summarize_rows = :Batch => [mean => "average", sum] +) +``` +""" +function listingtable(table, variable, pagination::Union{Nothing,Pagination} = nothing; rows = [], + cols = [], + summarize_rows = [], + summarize_cols = [], + variable_header = true, + sort = true, + table_kwargs...) + + + df = DataFrames.DataFrame(table) + + var = Variable(variable) + + rowgroups = make_groups(rows) + colgroups = make_groups(cols) + + rowsymbols = [r.symbol for r in rowgroups] + rowsummary = Summary(summarize_rows, rowsymbols) + + colsymbols = [c.symbol for c in colgroups] + colsummary = Summary(summarize_cols, colsymbols) + + if pagination === nothing + return _listingtable(df, var, rowgroups, colgroups, rowsummary, colsummary; variable_header, sort, table_kwargs...) + else + sd = setdiff(keys(pagination.options), [:rows, :cols]) + if !isempty(sd) + throw(ArgumentError("`listingtable` only accepts `rows` and `cols` as pagination arguments. Found $(join(sd, ", ", " and "))")) + end + + paginate_cols = get(pagination.options, :cols, nothing) + paginate_rows = get(pagination.options, :rows, nothing) + + paginated_colgroupers = colsymbols[1:(isempty(colsummary.analyses) ? end : colsummary.groupindex)] + paginated_rowgroupers = rowsymbols[1:(isempty(rowsummary.analyses) ? end : rowsummary.groupindex)] + + pages = Page{ListingPageMetadata}[] + rowgrouped = DataFrames.groupby(df, paginated_rowgroupers; sort) + rowgroup_indices = 1:length(rowgrouped) + for r_indices in Iterators.partition(rowgroup_indices, something(paginate_rows, length(rowgroup_indices))) + colgrouped = DataFrames.groupby(DataFrame(rowgrouped[r_indices]), paginated_colgroupers; sort) + colgroup_indices = 1:length(colgrouped) + for c_indices in Iterators.partition(colgroup_indices, something(paginate_cols, length(colgroup_indices))) + t = _listingtable(DataFrame(colgrouped[c_indices]), var, rowgroups, colgroups, rowsummary, colsummary; variable_header, sort, table_kwargs...) + push!(pages, Page( + ListingPageMetadata( + cols = paginate_cols === nothing ? GroupKey[] : GroupKey.(keys(colgrouped)[c_indices]), + rows = paginate_rows === nothing ? GroupKey[] : GroupKey.(keys(rowgrouped)[r_indices]), + ), + t, + )) + end + end + return PaginatedTable(pages) + end +end + +struct TooManyRowsError <: Exception + msg::String +end +Base.show(io::IO, t::TooManyRowsError) = print(io, "TooManyRowsError: ", t.msg) + +struct SortingError <: Exception end + +function Base.showerror(io::IO, ::SortingError) + print(io, """ + Sorting the input dataframe for grouping failed. + This can happen when a column contains special objects intended for table formatting which are not sortable, for example `Concat`, `Multiline`, `Subscript` or `Superscript`. + Consider pre-sorting your dataframe and retrying with `sort = false`. + Note that group keys will appear in the order they are present in the dataframe, so usually you should sort in the same order that the groups are given to the table function. + """) +end + +function _listingtable( + df::DataFrames.DataFrame, + variable::Variable, + rowgroups::Vector{Group}, + colgroups::Vector{Group}, + rowsummary::Summary, + colsummary::Summary; + variable_header::Bool, + sort::Bool, + celltable_kws...) + + rowsymbols = [r.symbol for r in rowgroups] + colsymbols = [c.symbol for c in colgroups] + + groups = vcat(rowsymbols, colsymbols) + + # remove unneeded columns from the dataframe + used_columns = [variable.symbol; rowsymbols; colsymbols] + if sort && !isempty(groups) + try + df = Base.sort(df, groups, lt = natural_lt) + catch e + throw(SortingError()) + end + end + + gdf = DataFrames.groupby(df, groups, sort = false) + + for group in gdf + if size(group, 1) > 1 + nonuniform_columns = filter(names(df, DataFrames.Not(used_columns))) do name + length(Set((getproperty(group, name)))) > 1 + end + throw(TooManyRowsError(""" + Found a group which has more than one value. This is not allowed, only one value of "$(variable.symbol)" per table cell may exist. + + $(repr(DataFrames.select(group, used_columns), context = :limit => true)) + + Filter your dataset or use additional row or column grouping factors. + + $(!isempty(nonuniform_columns) ? + "The following columns in the dataset are not uniform in this group and could potentially be used: $nonuniform_columns." : + "There are no other non-uniform columns in this dataset.") + """)) + end + end + + rowsummary_groups = vcat(rowsymbols[1:rowsummary.groupindex], colsymbols) + gdf_rowsummary = DataFrames.combine( + DataFrames.groupby(df, rowsummary_groups), + [variable.symbol => a.func => "____$i" for (i, a) in enumerate(rowsummary.analyses)]..., + ungroup = false + ) + + colsummary_groups = vcat(rowsymbols, colsymbols[1:colsummary.groupindex]) + gdf_colsummary = DataFrames.combine( + DataFrames.groupby(df, colsummary_groups), + [variable.symbol => a.func => "____$i" for (i, a) in enumerate(colsummary.analyses)]..., + ungroup = false + ) + + gdf_rows = DataFrames.groupby(df, rowsymbols, sort = false) + row_keys = Tuple.(keys(gdf_rows)) + gdf_cols = DataFrames.groupby(df, colsymbols, sort = false) + col_keys = Tuple.(keys(gdf_cols)) + + lt = ListingTable( + gdf, + variable, + row_keys, + col_keys, + rowgroups, + colgroups, + rowsummary, + gdf_rowsummary, + colsummary, + gdf_colsummary, + ) + + cl, i_header, rowgap_indices = get_cells(lt; variable_header) + Table(cl, i_header, nothing; rowgaps = rowgap_indices .=> DEFAULT_ROWGAP, celltable_kws...) +end + +function get_cells(l::ListingTable; variable_header::Bool) + cells = SpannedCell[] + + row_summaryindex = l.rowsummary.groupindex + col_summaryindex = l.colsummary.groupindex + + rowparts = partition(l.row_keys, by = x -> x[1:row_summaryindex]) + colparts = partition(l.col_keys, by = x -> x[1:col_summaryindex]) + + lengths_rowparts = map(length, rowparts) + cumsum_lengths_rowparts = cumsum(lengths_rowparts) + n_row_summaries = length(l.rowsummary.analyses) + + lengths_colparts = map(length, colparts) + cumsum_lengths_colparts = cumsum(lengths_colparts) + n_col_summaries = length(l.colsummary.analyses) + + n_rowgroups = length(l.rows) + n_colgroups = length(l.columns) + + colheader_offset = 2 * n_colgroups + (variable_header ? 1 : 0) + rowheader_offset = n_rowgroups + + rowgap_indices = Int[] + + # group headers for row groups + for (i_rowgroup, rowgroup) in enumerate(l.rows) + cell = SpannedCell(colheader_offset, i_rowgroup, rowgroup.name, listingtable_row_header()) + push!(cells, cell) + end + + for (i_colpart, colpart) in enumerate(colparts) + coloffset = rowheader_offset + + (i_colpart == 1 ? 0 : cumsum_lengths_colparts[i_colpart-1]) + + (i_colpart-1) * n_col_summaries + colrange = coloffset .+ (1:length(colpart)) + + # variable headers on top of each column part + if variable_header + cell = SpannedCell(colheader_offset, colrange, l.variable.name, listingtable_variable_header()) + push!(cells, cell) + end + + values_spans = nested_run_length_encodings(colpart) + all_spanranges = [spanranges(spans) for (values, spans) in values_spans] + + # column headers on top of each column part + for i_colgroupkey in 1:n_colgroups + headerspanranges = i_colgroupkey == 1 ? [1:length(colpart)] : all_spanranges[i_colgroupkey-1] + for headerspanrange in headerspanranges + header_offset_range = headerspanrange .+ coloffset + class = length(headerspanrange) > 1 ? listingtable_column_header_spanned() : listingtable_column_header() + cell = SpannedCell(i_colgroupkey * 2 - 1, header_offset_range, l.columns[i_colgroupkey].name, class) + push!(cells, cell) + end + values, _ = values_spans[i_colgroupkey] + ranges = all_spanranges[i_colgroupkey] + for (value, range) in zip(values, ranges) + label_offset_range = range .+ coloffset + cell = SpannedCell(i_colgroupkey * 2, label_offset_range, format_value(value), listingtable_column_header_key()) + push!(cells, cell) + end + end + + # column analysis headers after each column part + for (i_colsumm, summ_ana) in enumerate(l.colsummary.analyses) + summ_coloffset = coloffset + length(colpart) + + push!(cells, SpannedCell( + colheader_offset, + summ_coloffset + i_colsumm, + summ_ana.name, + listingtable_column_analysis_header() + )) + end + end + + for (i_rowpart, rowpart) in enumerate(rowparts) + rowgroupoffset = i_rowpart == 1 ? 0 : cumsum_lengths_rowparts[i_rowpart-1] + rowsummoffset = (i_rowpart - 1) * n_row_summaries + rowoffset = rowgroupoffset + rowsummoffset + colheader_offset + + all_rowspans = nested_run_length_encodings(rowpart) + + # row groups to the left of each row part + for i_rowgroupkey in 1:n_rowgroups + values, spans = all_rowspans[i_rowgroupkey] + ranges = spanranges(spans) + for (value, range) in zip(values, ranges) + offset_range = range .+ rowoffset + cell = SpannedCell(offset_range, i_rowgroupkey, format_value(value), listingtable_row_key()) + push!(cells, cell) + end + end + + summ_rowoffset = rowoffset + length(rowpart) + if !isempty(l.rowsummary.analyses) + push!(rowgap_indices, summ_rowoffset) + if i_rowpart < length(rowparts) + push!(rowgap_indices, summ_rowoffset + length(l.rowsummary.analyses)) + end + end + + # row analysis headers below each row part + for (i_rowsumm, summ_ana) in enumerate(l.rowsummary.analyses) + push!(cells, SpannedCell( + summ_rowoffset + i_rowsumm, + n_rowgroups, + summ_ana.name, + listingtable_row_analysis_header() + )) + end + + # this loop goes over each block of rowparts x colparts + for (i_colpart, colpart) in enumerate(colparts) + + colgroupoffset = i_colpart == 1 ? 0 : cumsum_lengths_colparts[i_colpart-1] + colsummoffset = (i_colpart - 1) * n_col_summaries + coloffset = colgroupoffset + colsummoffset + rowheader_offset + + # populate raw value cells for the current block + for (i_row, rowkey) in enumerate(rowpart) + for (i_col, colkey) in enumerate(colpart) + fullkey = (rowkey..., colkey...) + data = get(l.gdf, fullkey, nothing) + + if data === nothing + value = "" + else + value = only(getproperty(data, l.variable.symbol)) + end + row = rowoffset + i_row + col = coloffset + i_col + cell = SpannedCell(row, col, format_value(value), listingtable_body()) + push!(cells, cell) + end + end + + # populate row analysis cells for the current block + for i_rowsumm in eachindex(l.rowsummary.analyses) + + summ_rowoffset = rowoffset + length(rowpart) + + for (i_col, colkey) in enumerate(colpart) + partial_rowkey = first(rowpart)[1:row_summaryindex] + summkey = (partial_rowkey..., colkey...) + datacol_index = length(summkey) + i_rowsumm + + data = get(l.gdf_rowsummary, summkey, nothing) + if data === nothing + value = "" + else + value = only(data[!, datacol_index]) + end + + cell = SpannedCell( + summ_rowoffset + i_rowsumm, + coloffset + i_col, + format_value(value), + listingtable_row_analysis_body() + ) + push!(cells, cell) + end + end + + # populate column analysis cells for the current block + for i_colsumm in eachindex(l.colsummary.analyses) + + summ_coloffset = coloffset + length(colpart) + + for (i_row, rowkey) in enumerate(rowpart) + partial_colkey = first(colpart)[1:col_summaryindex] + summkey = (rowkey..., partial_colkey...) + datacol_index = length(summkey) + i_colsumm + + data = get(l.gdf_colsummary, summkey, nothing) + if data === nothing + value = "" + else + value = only(data[!, datacol_index]) + end + + cell = SpannedCell( + rowoffset + i_row, + summ_coloffset + i_colsumm, + format_value(value), + listingtable_column_analysis_body() + ) + push!(cells, cell) + end + end + end + end + + cells, colheader_offset, rowgap_indices +end + +listingtable_row_header() = CellStyle(halign = :left, bold = true) +listingtable_variable_header() = CellStyle(bold = true) +listingtable_row_key() = CellStyle(halign = :left) +listingtable_body() = CellStyle() +listingtable_column_header() = CellStyle(bold = true) +listingtable_column_header_spanned() = CellStyle(border_bottom = true, bold = true) +listingtable_column_header_key() = CellStyle() +listingtable_row_analysis_header() = CellStyle(halign = :left, bold = true) +listingtable_row_analysis_body() = CellStyle() +listingtable_column_analysis_header() = CellStyle(halign = :right, bold = true) +listingtable_column_analysis_body() = CellStyle(halign = :right) + +function nested_run_length_encodings(gdf_keys) + n_entries = length(gdf_keys) + n_levels = length(first(gdf_keys)) + spans = Tuple{Vector{Any},Vector{Int}}[] + for level in 1:n_levels + keys = Any[] + lengths = Int[] + + prev_key = first(gdf_keys)[level] + current_length = 1 + + starts_of_previous_level = level == 1 ? Int[] : cumsum([1; spans[level-1][2][1:end-1]]) + + for (i, entrykeys) in zip(2:length(gdf_keys), gdf_keys[2:end]) + key = entrykeys[level] + is_previous_level_start = i in starts_of_previous_level + if !is_previous_level_start && key == prev_key + current_length += 1 + else + push!(lengths, current_length) + push!(keys, prev_key) + current_length = 1 + end + prev_key = key + end + push!(lengths, current_length) + push!(keys, prev_key) + push!(spans, (keys, lengths)) + end + return spans +end + +function spanranges(spans) + start = 1 + stop = 0 + map(spans) do span + stop = start + span - 1 + range = start:stop + start += span + return range + end +end + +# split a collection into parts where each element in a part `isequal` for `by(element)` +function partition(collection; by) + parts = Vector{eltype(collection)}[] + part = eltype(collection)[] + for element in collection + if isempty(part) + push!(part, element) + else + if isequal(by(last(part)), by(element)) + push!(part, element) + else + push!(parts, part) + part = eltype(collection)[element] + end + end + end + push!(parts, part) + parts +end + +struct SummaryTable + gdf::DataFrames.GroupedDataFrame + variable::Variable + row_keys::Vector{<:Tuple} + col_keys::Vector{<:Tuple} + rows::Vector{Group} + columns::Vector{Group} + summary::Summary + gdf_summary::DataFrames.GroupedDataFrame +end + + +""" + summarytable(table, variable; + rows = [], + cols = [], + summary = [], + variable_header = true, + celltable_kws... + ) + +Create a summary table `Table` from `table`, which summarizes values from column `variable`. + +## Arguments +- `table`: Data source which must be convertible to a `DataFrames.DataFrame`. +- `variable`: Determines which variable from `table` is summarized. Can either be a `Symbol` such as `:ColumnA`, or alternatively a `Pair` where the second element is the display name, such as `:ColumnA => "Column A"`. + +## Keyword arguments +- `rows = []`: Grouping structure along the rows. Should be a `Vector` where each element is a grouping variable, specified as a `Symbol` such as `:Column1`, or a `Pair`, where the first element is the symbol and the second a display name, such as `:Column1 => "Column 1"`. Specifying multiple grouping variables creates nested groups, with the last variable changing the fastest. +- `cols = []`: Grouping structure along the columns. Follows the same structure as `rows`. +- `summary = []`: Specifies functions to summarize `variable` with. + Should be a `Vector`, where each entry is one separate summary. + Each summary can be given as a `Function` such as `mean` or `maximum`, in which case the display name is the function's name. + Alternatively, a display name can be given using the pair syntax, such as `mean => "Average"`. + By default, one summary is computed over all groups. + You can also pass `Symbol => [...]` where `Symbol` is a grouping column, to compute one summary for each level of that group. +- `variable_header = true`: Controls if the cell with the name of the summarized `variable` is shown. +- `sort = true`: Sort the input table before grouping. Pre-sort as desired and set to `false` when you want to maintain a specific group order or are using non-sortable objects as group keys. + + +All other keywords are forwarded to the `Table` constructor, refer to its docstring for details. + +## Example + +``` +using Statistics + +tbl = [ + :Apples => [1, 2, 3, 4, 5, 6, 7, 8], + :Batch => [1, 1, 1, 1, 2, 2, 2, 2], + :Delivery => ['a', 'a', 'b', 'b', 'a', 'a', 'b', 'b'], +] + +summarytable( + tbl, + :Apples => "Number of apples", + rows = [:Batch], + cols = [:Delivery], + summary = [length => "N", mean => "average", sum] +) +``` +""" +function summarytable( + table, variable; + rows = [], + cols = [], + summary = [], + variable_header = true, + celltable_kws... +) + + df = DataFrames.DataFrame(table) + + var = Variable(variable) + + rowgroups = make_groups(rows) + colgroups = make_groups(cols) + + rowsymbols = [r.symbol for r in rowgroups] + _summary = Summary(summary, rowsymbols) + + if isempty(_summary.analyses) + throw(ArgumentError("No summary analyses defined.")) + end + + _summarytable(df, var, rowgroups, colgroups, _summary; variable_header, celltable_kws...) +end + +function _summarytable( + df::DataFrames.DataFrame, + variable::Variable, + rowgroups::Vector{Group}, + colgroups::Vector{Group}, + summary::Summary; + variable_header::Bool, + sort = true, + celltable_kws...) + + rowsymbols = [r.symbol for r in rowgroups] + colsymbols = [c.symbol for c in colgroups] + + groups = vcat(rowsymbols, colsymbols) + + # remove unneeded columns from the dataframe + used_columns = [variable.symbol; rowsymbols; colsymbols] + + + + _df = DataFrames.select(df, used_columns) + if !isempty(groups) && sort + try + Base.sort!(_df, groups, lt = natural_lt) + catch e + throw(SortingError()) + end + end + + gdf = DataFrames.groupby(_df, groups, sort = false) + + gdf_summary = DataFrames.combine( + DataFrames.groupby(_df, groups), + [variable.symbol => a.func => "____$i" for (i, a) in enumerate(summary.analyses)]..., + ungroup = false + ) + + gdf_rows = DataFrames.groupby(_df, rowsymbols, sort = false) + row_keys = Tuple.(keys(gdf_rows)) + gdf_cols = DataFrames.groupby(_df, colsymbols, sort = false) + col_keys = Tuple.(keys(gdf_cols)) + + st = SummaryTable( + gdf, + variable, + row_keys, + col_keys, + rowgroups, + colgroups, + summary, + gdf_summary, + ) + + cl, i_header = get_cells(st; variable_header) + Table(cl, i_header, nothing; celltable_kws...) +end + + +function get_cells(l::SummaryTable; variable_header::Bool) + cells = SpannedCell[] + + n_row_summaries = length(l.summary.analyses) + + n_rowgroups = length(l.rows) + n_colgroups = length(l.columns) + + colheader_offset = if n_colgroups == 0 && n_rowgroups > 0 + 1 + else + 2 * n_colgroups + (variable_header ? 1 : 0) + end + + rowheader_offset = n_rowgroups + 1 + + # group headers for row groups + for (i_rowgroup, rowgroup) in enumerate(l.rows) + cell = SpannedCell(colheader_offset, i_rowgroup, rowgroup.name, summarytable_row_header()) + push!(cells, cell) + end + + # variable headers on top of each column part + if variable_header + colrange = rowheader_offset .+ (1:length(l.col_keys)) + cell = SpannedCell(colheader_offset, colrange, l.variable.name, summarytable_column_header()) + push!(cells, cell) + end + + values_spans_cols = nested_run_length_encodings(l.col_keys) + all_spanranges_cols = [spanranges(spans) for (values, spans) in values_spans_cols] + + # column headers on top of each column part + for i_colgroupkey in 1:n_colgroups + headerspanranges = i_colgroupkey == 1 ? [1:length(l.col_keys)] : all_spanranges_cols[i_colgroupkey-1] + for headerspanrange in headerspanranges + header_offset_range = headerspanrange .+ rowheader_offset + class = length(headerspanrange) > 1 ? summarytable_column_header_spanned() : summarytable_column_header() + cell = SpannedCell(i_colgroupkey * 2 - 1, header_offset_range, l.columns[i_colgroupkey].name, class) + push!(cells, cell) + end + values, _ = values_spans_cols[i_colgroupkey] + ranges = all_spanranges_cols[i_colgroupkey] + for (value, range) in zip(values, ranges) + label_offset_range = range .+ rowheader_offset + cell = SpannedCell(i_colgroupkey * 2, label_offset_range, format_value(value), summarytable_body()) + push!(cells, cell) + end + end + + values_spans_rows = nested_run_length_encodings(l.row_keys) + all_spanranges_rows = [spanranges(spans) for (values, spans) in values_spans_rows] + + for (i_rowkey, rowkey) in enumerate(l.row_keys) + rowgroupoffset = (i_rowkey - 1) * n_row_summaries + rowoffset = rowgroupoffset + colheader_offset + + # row group keys to the left + for i_rowgroupkey in 1:n_rowgroups + # show key only once per span + spanranges = all_spanranges_rows[i_rowgroupkey] + ith_span = findfirst(spanrange -> first(spanrange) == i_rowkey, spanranges) + if ith_span === nothing + continue + end + spanrange = spanranges[ith_span] + range = 1:n_row_summaries * length(spanrange) + offset_range = range .+ rowoffset + key = rowkey[i_rowgroupkey] + cell = SpannedCell(offset_range, i_rowgroupkey, format_value(key), summarytable_row_key()) + push!(cells, cell) + end + + # row analysis headers to the right of each row key + for (i_rowsumm, summ_ana) in enumerate(l.summary.analyses) + summ_rowoffset = rowoffset + 1 + push!(cells, SpannedCell( + summ_rowoffset + i_rowsumm - 1, + n_rowgroups + 1, + summ_ana.name, + summarytable_analysis_header() + )) + end + + # populate row analysis cells + for i_rowsumm in eachindex(l.summary.analyses) + + summ_rowoffset = rowoffset + + for (i_col, colkey) in enumerate(l.col_keys) + summkey = (rowkey..., colkey...) + datacol_index = length(summkey) + i_rowsumm + + data = get(l.gdf_summary, summkey, nothing) + if data === nothing + value = "" + else + value = only(data[!, datacol_index]) + end + + cell = SpannedCell( + summ_rowoffset + i_rowsumm, + rowheader_offset + i_col, + format_value(value), + summarytable_body() + ) + push!(cells, cell) + end + end + end + + cells, colheader_offset +end + +summarytable_column_header() = CellStyle(halign = :center, bold = true) +summarytable_column_header_spanned() = CellStyle(halign = :center, bold = true, border_bottom = true) +summarytable_analysis_header() = CellStyle(halign = :left, bold = true) +summarytable_body() = CellStyle() +summarytable_row_header() = CellStyle(halign = :left, bold = true) +summarytable_row_key() = CellStyle(halign = :left) + diff --git a/src/table_one.jl b/src/table_one.jl new file mode 100644 index 0000000..e9af28b --- /dev/null +++ b/src/table_one.jl @@ -0,0 +1,466 @@ +default_tests() = ( + categorical = HypothesisTests.ChisqTest, + nonnormal = HypothesisTests.KruskalWallisTest, + minmax = HypothesisTests.UnequalVarianceTTest, + normal = HypothesisTests.UnequalVarianceTTest, +) + +hformatter(num::Real) = num < 0.001 ? "<0.001" : string(round(num; digits = 3)) +hformatter((a, b)::Tuple{<:Real,<:Real}; digits = 3) = "($(round(a; digits)), $(round(b; digits)))" +hformatter(::Tuple{Nothing,Nothing}) = "" +hformatter(::Vector) = "" # TODO +hformatter(other) = "" + +## Categorical: + +function htester(data::Matrix, test, combine) + data = identity.(data) + try + if size(data) == (2, 2) + a, c, b, d = data + test = HypothesisTests.FisherExactTest + return test, test(a, b, c, d) + else + return test, test(data) + end + catch _ + return nothing, nothing + end +end + +## Continuous: + +function htester(data::Vector, test::Type{HypothesisTests.KruskalWallisTest}, combine) + try + return test, test(data...) + catch _ + return nothing, nothing + end +end + +function htester(data::Vector, test, combine) + try + if length(data) > 2 + # test each unique pair of vectors from data + results = [test(a, b) for (i, a) in pairs(data) for b in data[i+1:end]] + pvalues = HypothesisTests.pvalue.(results) + return test, MultipleTesting.combine(pvalues, combine) + else + return test, test(data...) + end + catch _ + return nothing, nothing + end +end + +## P-Values: + +get_pvalue(n::Real) = n +get_pvalue(::Nothing) = nothing +get_pvalue(result) = HypothesisTests.pvalue(result) + +## CI: + +function get_confint(result) + try + return HypothesisTests.confint(result) + catch _ + return nothing, nothing + end +end +get_confint(::Real) = (nothing, nothing) +get_confint(::Nothing) = (nothing, nothing) + +## Test name: + +get_testname(test) = string(nameof(test)) +get_testname(::Nothing) = "" + +## + +struct Analysis + variable::Symbol + func::Function + name +end + +function Analysis(s::Symbol, df::DataFrames.DataFrame) + Analysis(s, default_analysis(df[!, s]), string(s)) +end + +function Analysis(p::Pair{Symbol, <:Any}, df::DataFrames.DataFrame) + sym, rest = p + Analysis(sym, rest, df) +end + +function Analysis(sym::Symbol, name, df::DataFrames.DataFrame) + Analysis(sym, default_analysis(df[!, sym]), name) +end + +function Analysis(sym::Symbol, funcvec::AbstractVector, df::DataFrames.DataFrame) + Analysis(sym, to_func(funcvec), df) +end + +function Analysis(sym::Symbol, f::Function, df::DataFrames.DataFrame) + Analysis(sym, f, string(sym)) +end + +function Analysis(s::Symbol, f::Function, name, df::DataFrames.DataFrame) + Analysis(s, f, name) +end + +function Analysis(sym::Symbol, p::Pair, df::DataFrames.DataFrame) + funcs, name = p + Analysis(sym, funcs, name, df) +end + +make_analyses(v::AbstractVector, df::DataFrame) = map(x -> Analysis(x, df), v) +make_analyses(x, df::DataFrame) = [Analysis(x, df)] + +to_func(f::Function) = f +function to_func(v::AbstractVector) + return function(col) + result_name_pairs = map(v) do el + f, name = func_and_name(el) + f(col) => name + end + Tuple(result_name_pairs) + end +end +func_and_name(p::Pair{<:Function, <:Any}) = p +func_and_name(f::Function) = f => string(f) + +not_computable_annotation() = Annotated("NC", "NC - Not computable", label = nothing) + +function guard_statistic(stat) + function (vec) + sm = skipmissing(vec) + if isempty(sm) + missing + else + stat(sm) + end + end +end + +function default_analysis(v::AbstractVector{<:Union{Missing, <:Real}}) + + anymissing = any(ismissing, v) + + function (col) + allmissing = isempty(skipmissing(col)) + + _mean = guard_statistic(mean)(col) + _sd = guard_statistic(std)(col) + mean_sd = if allmissing + not_computable_annotation() + else + Concat(_mean, " (", _sd, ")") + end + _median = guard_statistic(median)(col) + _min = guard_statistic(minimum)(col) + _max = guard_statistic(maximum)(col) + med_min_max = if allmissing + not_computable_annotation() + else + Concat(_median, " [", _min, ", ", _max, "]") + end + + if anymissing + nm = count(ismissing, col) + _mis = Concat(nm, " (", nm / length(col) * 100, "%)") + end + + ( + mean_sd => "Mean (SD)", + med_min_max => "Median [Min, Max]", + (anymissing ? (_mis => "Missing",) : ())... + ) + end +end + +default_analysis(c::CategoricalArray) = level_analyses(c) +default_analysis(v::AbstractVector{<:Union{Missing, Bool}}) = level_analyses(v) +# by default we just count levels for all datatypes that are not known +default_analysis(v) = level_analyses(v) + +function level_analyses(c) + has_missing = any(ismissing, c) # if there's any missing, we report them for every col in c + function (col) + _levels = levels(c) # levels are computed for the whole column, not per group, so they are always exhaustive + lvls = tuple(_levels...) + cm = StatsBase.countmap(col) + n = length(col) + + _entry(n_lvl) = Concat(n_lvl, " (", n_lvl / n * 100, "%)") + + entries = map(lvls) do lvl + n_lvl = get(cm, lvl, 0) + s = _entry(n_lvl) + s => lvl + end + if has_missing + n_missing = count(ismissing, col) + entries = (entries..., _entry(n_missing) => "Missing") + end + return entries + end +end + +""" + table_one(table, analyses; keywords...) + +Construct a "Table 1" which summarises the patient baseline +characteristics from the provided `table` dataset. This table is commonly used +in biomedical research papers. + +It can handle both continuous and categorical columns in `table` and summary +statistics and hypothesis testing are able to be customised by the user. Tables +can be stratified by one, or more, variables using the `groupby` keyword. + +## Keywords + + - `groupby`: Which columns to stratify the dataset with, as a `Vector{Symbol}`. + - `nonnormal`: A vector of column names where hypothesis tests for the `:nonnormal` type are chosen. + - `minmax`: A vector of column names where hypothesis tests for the `:minmax` type are chosen. + - `tests`: a `NamedTuple` of hypothesis test types to use for `categorical`, `nonnormal`, `minmax`, and `normal` variables. + - `combine`: an object from `MultipleTesting` to use when combining p-values. + - `show_overall`: display the "Overall" column summary. Default is `true`. + - `show_n`: Display the number of rows for each group key next to its label. + - `show_pvalues`: display the `P-Value` column. Default is `false`. + - `show_testnames`: display the `Test` column. Default is `false`. + - `show_confints`: display the `CI` column. Default is `false`. + - `sort`: Sort the input table before grouping. Default is `true`. Pre-sort as desired and set to `false` when you want to maintain a specific group order or are using non-sortable objects as group keys. + +All other keywords are forwarded to the `Table` constructor, refer to its docstring for details. + +""" +function table_one( + table, + analyses; + groupby = [], + show_overall = true, + show_pvalues = false, + show_tests = true, + show_confints = false, + show_n = false, + compare_groups::Vector = [], + nonnormal = [], + minmax = [], + tests = default_tests(), + combine = MultipleTesting.Fisher(), + sort = true, + celltable_kws... +) + + df = DataFrames.DataFrame(table) + + groups = make_groups(groupby) + n_groups = length(groups) + + show_overall || n_groups > 0 || error("Overall can't be false if there are no groups.") + + _analyses = make_analyses(analyses, df) + + typedict = Dict(map(_analyses) do analysis + type = if getproperty(df, analysis.variable) isa CategoricalVector + :categorical + elseif analysis.variable in nonnormal + :nonnormal + elseif analysis.variable in minmax + :minmax + else + :normal + end + analysis.variable => type + end) + + cells = SpannedCell[] + + groupsymbols = [g.symbol for g in groups] + if sort && !isempty(groupsymbols) + try + Base.sort!(df, groupsymbols, lt = natural_lt) + catch e + throw(SortingError()) + end + end + gdf = DataFrames.groupby(df, groupsymbols, sort = false) + + calculate_comparisons = length(gdf) >= 2 && show_pvalues + if calculate_comparisons + compare_groups = [make_testfunction(show_pvalues, show_tests, show_confints, typedict, merge(default_tests(), tests), combine); compare_groups] + end + + rows_per_group = [nrow(g) for g in gdf] + + funcvector = [a.variable => a.func for a in _analyses] + df_analyses = DataFrames.combine(gdf, funcvector; ungroup = false) + if show_overall + df_overall = DataFrames.combine(df, funcvector) + end + + analysis_labels = map(n_groups+1:n_groups+length(_analyses)) do i_col + col = df_analyses[1][!, i_col] + x = only(col) + if x isa Tuple + map(last, x) + else + error("Expected a tuple") + end + end + + n_values_per_analysis = map(length, analysis_labels) + analysis_offsets = [0; cumsum(1 .+ n_values_per_analysis)[1:end-1]] + + header_offset = n_groups == 0 ? 2 : n_groups * 2 + 1 + + values_spans = nested_run_length_encodings(keys(gdf)) + all_spanranges = [spanranges(spans) for (values, spans) in values_spans] + + n_groupcols = n_groups == 0 ? 0 : maximum(x -> x.stop, all_spanranges[1]) + + if show_overall + coloffset = 1 + title = if show_n + Multiline("Overall", "(n=$(nrow(df)))") + else + "Overall" + end + cell = SpannedCell(n_groups == 0 ? 1 : 2 * n_groups, 2, title, tableone_column_header()) + push!(cells, cell) + else + coloffset = 0 + end + + # add column headers for groups + for i_groupkey in 1:n_groups + headerspanranges = i_groupkey == 1 ? [1:length(keys(gdf))] : all_spanranges[i_groupkey-1] + for headerspanrange in headerspanranges + header_offset_range = headerspanrange .+ 1 .+ coloffset + + cell = SpannedCell(i_groupkey * 2 - 1, header_offset_range, groups[i_groupkey].name, tableone_column_header_spanned()) + push!(cells, cell) + end + values, _ = values_spans[i_groupkey] + ranges = all_spanranges[i_groupkey] + for (value, range) in zip(values, ranges) + label_offset_range = range .+ 1 .+ coloffset + n_entries = sum(rows_per_group[range]) + label = if show_n + Multiline(format_value(value), "(n=$n_entries)") + else + format_value(value) + end + cell = SpannedCell(i_groupkey * 2, label_offset_range, label, tableone_column_header_key()) + push!(cells, cell) + end + end + + for (i_ana, analysis) in enumerate(_analyses) + + offset = header_offset + analysis_offsets[i_ana] + cell = SpannedCell(offset, 1, analysis.name, tableone_variable_header()) + push!(cells, cell) + + for (i_func, funcname) in enumerate(analysis_labels[i_ana]) + cell = SpannedCell(offset + i_func, 1, funcname, tableone_analysis_name()) + push!(cells, cell) + end + + if show_overall + val = only(df_overall[!, i_ana]) + for i_func in 1:n_values_per_analysis[i_ana] + cell = SpannedCell(offset + i_func, 2, val[i_func][1], tableone_body()) + push!(cells, cell) + end + end + + if n_groups > 0 + for (i_group, ggdf) in enumerate(df_analyses) + val = only(ggdf[!, n_groups + i_ana]) + for i_func in 1:n_values_per_analysis[i_ana] + cell = SpannedCell(offset + i_func, coloffset + 1 + i_group, val[i_func][1], tableone_body()) + push!(cells, cell) + end + end + end + end + + compcolumn_offset = n_groupcols + (show_overall ? 1 : 0) + 1 + for comp in compare_groups + # the logic here is much less clean than it could be because of the way + # column names have to be passed via pairs, and it cannot be guaranteed from typing + # that all are compatible, so it has to be runtime checked + values = map(_analyses) do analysis + val = comp(analysis.variable, [getproperty(g, analysis.variable) for g in gdf]) + @assert val isa Tuple && all(x -> x isa Pair, val) "A comparison function has to return a tuple of value => name pairs. Function $comp returned $val" + val + end + + nvalues = length(first(values)) + @assert all(==(nvalues), map(length, values)) "All comparison tuples must have the same length. Found\n$values" + colnames = [map(last, v) for v in values] + unique_colnames = unique(colnames) + @assert length(unique_colnames) == 1 "All column names must be the same, found $colnames" + + # set column headers + for (ival, name) in enumerate(only(unique_colnames)) + cell = SpannedCell(header_offset-1, compcolumn_offset+ival, name, tableone_column_header()) + push!(cells, cell) + end + + for (j, val) in enumerate(values) + # set column values + for (ival, (value, _)) in enumerate(val) + cell = SpannedCell(analysis_offsets[j] + header_offset, compcolumn_offset+ival, value, tableone_body()) + push!(cells, cell) + end + end + compcolumn_offset += nvalues + end + + Table(cells, header_offset-1, nothing; celltable_kws...) +end + +tableone_column_header() = CellStyle(halign = :center, bold = true) +tableone_column_header_spanned() = CellStyle(halign = :center, bold = true, border_bottom = true) +tableone_column_header_key() = CellStyle(halign = :center) +tableone_variable_header() = CellStyle(bold = true, halign = :left) +tableone_body() = CellStyle() +tableone_analysis_name() = CellStyle(indent_pt = 12, halign = :left) + +formatted(f::Function, s::String) = formatted((f), s) +function formatted(fs::Tuple, s::String) + function (col) + values = map(fs) do f + f(col) + end + Printf.format(Printf.Format(s), values...) + end +end + +function make_testfunction(show_pvalues::Bool, show_tests::Bool, show_confint::Bool, typedict, testdict, combine) + function testfunction(variable, cols) + cols_nomissing = map(collect ∘ skipmissing, cols) + variabletype = typedict[variable] + test = testdict[variabletype] + if variabletype === :categorical + # concatenate the level counts into a matrix which Chi Square Test needs + matrix = hcat([map(l -> count(==(l), col), levels(col)) for col in cols_nomissing]...) + used_test, result = htester(matrix, test, combine) + else + used_test, result = htester(cols_nomissing, test, combine) + end + testname = get_testname(used_test) + pvalue = hformatter(get_pvalue(result)) + confint = hformatter(get_confint(result)) + + ( + (show_pvalues ? (pvalue => "P-Value",) : ())..., + (show_tests ? (testname => "Test",) : ())..., + (show_confint ? (confint => "CI",) : ())..., + ) + end +end + diff --git a/src/typst.jl b/src/typst.jl new file mode 100644 index 0000000..abbc8f6 --- /dev/null +++ b/src/typst.jl @@ -0,0 +1,141 @@ +function Base.show(io::IO, M::MIME"text/typst", ct::Table) + ct = postprocess(ct) + + cells = sort(to_spanned_cells(ct.cells), by = x -> (x.span[1].start, x.span[2].start)) + + cells, annotations = resolve_annotations(cells) + + matrix = create_cell_matrix(cells) + + validate_rowgaps(ct.rowgaps, size(matrix, 1)) + validate_colgaps(ct.colgaps, size(matrix, 2)) + rowgaps = Dict(ct.rowgaps) + colgaps = Dict(ct.colgaps) + + print(io, """ + + #[ + #import "@preview/tablex:0.0.8": tablex, cellx, hlinex + + #tablex( + columns: $(size(matrix, 2)), + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + """) + + println(io, " hlinex(y: 0, stroke: 1pt),") + + running_index = 0 + for row in 1:size(matrix, 1) + if row == ct.footer + println(io, " hlinex(y: $(row-1), stroke: 0.75pt),") + end + for col in 1:size(matrix, 2) + index = matrix[row, col] + if index > running_index + cell = cells[index] + if cell.value !== nothing + print(io, " cellx(colspan: $(length(cell.span[2])), x: $(col-1), y: $(row-1), align: $(typst_align(cell.style)))[") + cell.style.bold && print(io, "*") + cell.style.italic && print(io, "_") + cell.style.underline && print(io, "#underline[") + cell.style.indent_pt > 0 && print(io, "#h($(cell.style.indent_pt)pt)") + _showas(io, M, cell.value) + cell.style.underline && print(io, "]") + cell.style.italic && print(io, "_") + cell.style.bold && print(io, "*") + print(io, "],\n") + end + if cell.style.border_bottom + println(io, " hlinex(y: $(row), start: $(cell.span[2].start-1), end: $(cell.span[2].stop), stroke: 0.75pt),") + end + running_index = index + end + end + if row == ct.header + println(io, " hlinex(y: $(row), stroke: 0.75pt),") + end + end + + println(io, " hlinex(y: $(size(matrix, 1)), stroke: 1pt),") + + if !isempty(annotations) || !isempty(ct.footnotes) + print(io, " cellx(colspan: $(size(matrix, 2)), x: 0, y: $(size(matrix, 1)))[") + + for (i, (annotation, label)) in enumerate(annotations) + i > 1 && print(io, "#h(1.5em, weak: true)") + if label !== NoLabel() + print(io, "#super[") + _showas(io, MIME"text/typst"(), label) + print(io, "]") + end + print(io, "#text(size: 0.8em)[") + _showas(io, MIME"text/typst"(), annotation) + print(io, "]") + end + + for (i, footnote) in enumerate(ct.footnotes) + (!isempty(annotations) || i > 1) && print(io, "#h(1.5em, weak: true)") + _showas(io, MIME"text/typst"(), footnote) + end + + print(io, "],") # cellx()[ + end + + println(io, "\n)") # tablex( + println(io, "]") # #[ + return +end + +function _showas(io::IO, M::MIME"text/typst", m::Multiline) + for (i, v) in enumerate(m.values) + i > 1 && print(io, " #linebreak() ") + _showas(io, M, v) + end +end + +function typst_align(s::CellStyle) + string( + s.halign === :left ? "left" : s.halign === :right ? "right" : s.halign === :center ? "center" : error("Invalid halign $(s.halign)"), + " + ", + s.valign === :top ? "top" : s.valign === :bottom ? "bottom" : s.valign === :center ? "horizon" : error("Invalid valign $(s.valign)"), + ) +end + +function _showas(io::IO, ::MIME"text/typst", r::ResolvedAnnotation) + _showas(io, MIME"text/typst"(), r.value) + if r.label !== NoLabel() + print(io, "#super[") + _showas(io, MIME"text/typst"(), r.label) + print(io, "]") + end +end + +function _showas(io::IO, ::MIME"text/typst", s::Superscript) + print(io, "#super[") + _showas(io, MIME"text/typst"(), s.super) + print(io, "]") +end + +function _showas(io::IO, ::MIME"text/typst", s::Subscript) + print(io, "#sub[") + _showas(io, MIME"text/typst"(), s.sub) + print(io, "]") +end + +function _str_typst_escaped(io::IO, s::AbstractString) + escapable_special_chars = raw"\$#*_" + a = Iterators.Stateful(s) + for c in a + if c in escapable_special_chars + print(io, '\\', c) + else + print(io, c) + end + end +end + +function _str_typst_escaped(s::AbstractString) + return sprint(_str_typst_escaped, s, sizehint=lastindex(s)) +end diff --git a/test.tex b/test.tex new file mode 100644 index 0000000..43c8ef9 --- /dev/null +++ b/test.tex @@ -0,0 +1,28 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular*}{\textwidth}{@{\extracolsep{2ex}}*{4}{c}} + & & \multicolumn{2}{c}{\textbf{Group\tnote{1}}} \\ +\cmidrule{3-4}\cmidrule{3-4} + & \textbf{Overall} & a & b \\ +\multicolumn{1}{l}{\textbf{C\tnote{1}}} & & & \\ +\multicolumn{1}{l}{\hspace{1em}Mean (SD)} & 4.5 (2.45) & 2.5 (1.29) & 6.5 (1.29) \\ +\multicolumn{1}{l}{\hspace{1em}Median [Min, Max]} & 4.5 [1, 8] & 2.5 [1, 4] & 6.5 [5, 8] \\ +\multicolumn{1}{l}{\textbf{D\tnote{2}}} & & & \\ +\multicolumn{1}{l}{\hspace{1em}a} & 3 (37.5\%) & 2 (50\%) & 1 (25\%) \\ +\multicolumn{1}{l}{\hspace{1em}b} & 3 (37.5\%) & 1 (25\%) & 2 (50\%) \\ +\multicolumn{1}{l}{\hspace{1em}c} & 2 (25\%) & 1 (25\%) & 1 (25\%) \\ +\end{tabular*} +\begin{tablenotes} +\item[1]This is a C +\item[2]And this is a D +\end{tablenotes} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/Project.toml b/test/Project.toml new file mode 100644 index 0000000..e4d3cc8 --- /dev/null +++ b/test/Project.toml @@ -0,0 +1,8 @@ +[deps] +CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +ReferenceTests = "324d217c-45ce-50fc-942e-d289b448e8cf" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +Typst_jll = "eb4b1da6-20f6-5c66-9826-fdb8ad410d0e" +tectonic_jll = "d7dd28d6-a5e6-559c-9131-7eb760cdacc5" diff --git a/test/references/annotations/annotated_float.latex.txt b/test/references/annotations/annotated_float.latex.txt new file mode 100644 index 0000000..4310bfd --- /dev/null +++ b/test/references/annotations/annotated_float.latex.txt @@ -0,0 +1,21 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{1}{c}} +\toprule +0.124\tnote{A} \\ +\bottomrule +\end{tabular} +\begin{tablenotes}[flushleft,para] +\footnotesize +\item[A]Note 1 +\end{tablenotes} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/annotations/annotated_float.txt b/test/references/annotations/annotated_float.txt new file mode 100644 index 0000000..37ff3fe --- /dev/null +++ b/test/references/annotations/annotated_float.txt @@ -0,0 +1,37 @@ + + + + + + + + +
0.124A
A Note 1
\ No newline at end of file diff --git a/test/references/annotations/annotated_float.typ.txt b/test/references/annotations/annotated_float.typ.txt new file mode 100644 index 0000000..0501ad9 --- /dev/null +++ b/test/references/annotations/annotated_float.typ.txt @@ -0,0 +1,15 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 1, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[0.124#super[A]], + hlinex(y: 1, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 1)[#super[A]#text(size: 0.8em)[Note 1]], +) +] diff --git a/test/references/annotations/automatic_annotations.latex.txt b/test/references/annotations/automatic_annotations.latex.txt new file mode 100644 index 0000000..aa70845 --- /dev/null +++ b/test/references/annotations/automatic_annotations.latex.txt @@ -0,0 +1,24 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +A\tnote{1} & B\tnote{2} \\ +C\tnote{3} & D\tnote{1} \\ +\bottomrule +\end{tabular} +\begin{tablenotes}[flushleft,para] +\footnotesize +\item[1]Note 1 +\item[2]Note 2 +\item[3]Note 3 +\end{tablenotes} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/annotations/automatic_annotations.txt b/test/references/annotations/automatic_annotations.txt new file mode 100644 index 0000000..8001c2d --- /dev/null +++ b/test/references/annotations/automatic_annotations.txt @@ -0,0 +1,42 @@ + + + + + + + + + + + + + +
A1B2
C3D1
1 Note 1    2 Note 2    3 Note 3
\ No newline at end of file diff --git a/test/references/annotations/automatic_annotations.typ.txt b/test/references/annotations/automatic_annotations.typ.txt new file mode 100644 index 0000000..f708015 --- /dev/null +++ b/test/references/annotations/automatic_annotations.typ.txt @@ -0,0 +1,18 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[A#super[1]], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[B#super[2]], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[C#super[3]], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[D#super[1]], + hlinex(y: 2, stroke: 1pt), + cellx(colspan: 2, x: 0, y: 2)[#super[1]#text(size: 0.8em)[Note 1]#h(1.5em, weak: true)#super[2]#text(size: 0.8em)[Note 2]#h(1.5em, weak: true)#super[3]#text(size: 0.8em)[Note 3]], +) +] diff --git a/test/references/annotations/manual_annotations.latex.txt b/test/references/annotations/manual_annotations.latex.txt new file mode 100644 index 0000000..57aeebd --- /dev/null +++ b/test/references/annotations/manual_annotations.latex.txt @@ -0,0 +1,25 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +A\tnote{X} & B\tnote{Y} \\ +C\tnote{1} & D\tnote{2} \\ +\bottomrule +\end{tabular} +\begin{tablenotes}[flushleft,para] +\footnotesize +\item[1]Note 3 +\item[2]Note 4 +\item[X]Note 1 +\item[Y]Note 2 +\end{tablenotes} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/annotations/manual_annotations.txt b/test/references/annotations/manual_annotations.txt new file mode 100644 index 0000000..f7ed7a5 --- /dev/null +++ b/test/references/annotations/manual_annotations.txt @@ -0,0 +1,42 @@ + + + + + + + + + + + + + +
AXBY
C1D2
1 Note 3    2 Note 4    X Note 1    Y Note 2
\ No newline at end of file diff --git a/test/references/annotations/manual_annotations.typ.txt b/test/references/annotations/manual_annotations.typ.txt new file mode 100644 index 0000000..72365ec --- /dev/null +++ b/test/references/annotations/manual_annotations.typ.txt @@ -0,0 +1,18 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[A#super[X]], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[B#super[Y]], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[C#super[1]], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[D#super[2]], + hlinex(y: 2, stroke: 1pt), + cellx(colspan: 2, x: 0, y: 2)[#super[1]#text(size: 0.8em)[Note 3]#h(1.5em, weak: true)#super[2]#text(size: 0.8em)[Note 4]#h(1.5em, weak: true)#super[X]#text(size: 0.8em)[Note 1]#h(1.5em, weak: true)#super[Y]#text(size: 0.8em)[Note 2]], +) +] diff --git a/test/references/character_escaping/problematic_characters.latex.txt b/test/references/character_escaping/problematic_characters.latex.txt new file mode 100644 index 0000000..46e815a --- /dev/null +++ b/test/references/character_escaping/problematic_characters.latex.txt @@ -0,0 +1,17 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{1}{c}} +\toprule +\& \% \$ \# \_ \{ \} \textasciitilde{} \textasciicircum{} \textbackslash{} < > " ' \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/character_escaping/problematic_characters.txt b/test/references/character_escaping/problematic_characters.txt new file mode 100644 index 0000000..b1a2f66 --- /dev/null +++ b/test/references/character_escaping/problematic_characters.txt @@ -0,0 +1,36 @@ + + + + + + + +
& % $ # _ { } ~ ^ \\ < > " '
\ No newline at end of file diff --git a/test/references/character_escaping/problematic_characters.typ.txt b/test/references/character_escaping/problematic_characters.typ.txt new file mode 100644 index 0000000..f691a69 --- /dev/null +++ b/test/references/character_escaping/problematic_characters.typ.txt @@ -0,0 +1,15 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 1, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[& % \$ \# \_ { } ~ ^ \\ < > " ' ], + hlinex(y: 1, stroke: 1pt), + +) +] diff --git a/test/references/global_rounding/auto_false_1.latex.txt b/test/references/global_rounding/auto_false_1.latex.txt new file mode 100644 index 0000000..4df8e27 --- /dev/null +++ b/test/references/global_rounding/auto_false_1.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +1 & 1.0e7 \\ +1.0e8 & 0.001 \\ +1 \& 0.001 & \begin{tabular}{@{}c@{}}1 \\ 0.001\end{tabular} \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/global_rounding/auto_false_1.txt b/test/references/global_rounding/auto_false_1.txt new file mode 100644 index 0000000..5edb8c0 --- /dev/null +++ b/test/references/global_rounding/auto_false_1.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
11.0e7
1.0e80.001
1 & 0.0011
0.001
\ No newline at end of file diff --git a/test/references/global_rounding/auto_false_1.typ.txt b/test/references/global_rounding/auto_false_1.typ.txt new file mode 100644 index 0000000..f49c6eb --- /dev/null +++ b/test/references/global_rounding/auto_false_1.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[1.0e7], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[1.0e8], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[0.001], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[1 & 0.001], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[1 #linebreak() 0.001], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/global_rounding/auto_false_3.latex.txt b/test/references/global_rounding/auto_false_3.latex.txt new file mode 100644 index 0000000..1039eac --- /dev/null +++ b/test/references/global_rounding/auto_false_3.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +1.41 & 1.24e7 \\ +1.06e8 & 0.00111 \\ +1.23 \& 0.00123 & \begin{tabular}{@{}c@{}}1.23 \\ 0.00123\end{tabular} \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/global_rounding/auto_false_3.txt b/test/references/global_rounding/auto_false_3.txt new file mode 100644 index 0000000..08f1752 --- /dev/null +++ b/test/references/global_rounding/auto_false_3.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
1.411.24e7
1.06e80.00111
1.23 & 0.001231.23
0.00123
\ No newline at end of file diff --git a/test/references/global_rounding/auto_false_3.typ.txt b/test/references/global_rounding/auto_false_3.typ.txt new file mode 100644 index 0000000..50fc035 --- /dev/null +++ b/test/references/global_rounding/auto_false_3.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1.41], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[1.24e7], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[1.06e8], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[0.00111], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[1.23 & 0.00123], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[1.23 #linebreak() 0.00123], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/global_rounding/auto_true_1.latex.txt b/test/references/global_rounding/auto_true_1.latex.txt new file mode 100644 index 0000000..7891bb5 --- /dev/null +++ b/test/references/global_rounding/auto_true_1.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +1.0 & 1.0e7 \\ +1.0e8 & 0.001 \\ +1.0 \& 0.001 & \begin{tabular}{@{}c@{}}1.0 \\ 0.001\end{tabular} \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/global_rounding/auto_true_1.txt b/test/references/global_rounding/auto_true_1.txt new file mode 100644 index 0000000..b254498 --- /dev/null +++ b/test/references/global_rounding/auto_true_1.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
1.01.0e7
1.0e80.001
1.0 & 0.0011.0
0.001
\ No newline at end of file diff --git a/test/references/global_rounding/auto_true_1.typ.txt b/test/references/global_rounding/auto_true_1.typ.txt new file mode 100644 index 0000000..b0bc45c --- /dev/null +++ b/test/references/global_rounding/auto_true_1.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1.0], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[1.0e7], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[1.0e8], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[0.001], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[1.0 & 0.001], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[1.0 #linebreak() 0.001], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/global_rounding/auto_true_3.latex.txt b/test/references/global_rounding/auto_true_3.latex.txt new file mode 100644 index 0000000..1039eac --- /dev/null +++ b/test/references/global_rounding/auto_true_3.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +1.41 & 1.24e7 \\ +1.06e8 & 0.00111 \\ +1.23 \& 0.00123 & \begin{tabular}{@{}c@{}}1.23 \\ 0.00123\end{tabular} \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/global_rounding/auto_true_3.txt b/test/references/global_rounding/auto_true_3.txt new file mode 100644 index 0000000..08f1752 --- /dev/null +++ b/test/references/global_rounding/auto_true_3.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
1.411.24e7
1.06e80.00111
1.23 & 0.001231.23
0.00123
\ No newline at end of file diff --git a/test/references/global_rounding/auto_true_3.typ.txt b/test/references/global_rounding/auto_true_3.typ.txt new file mode 100644 index 0000000..50fc035 --- /dev/null +++ b/test/references/global_rounding/auto_true_3.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1.41], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[1.24e7], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[1.06e8], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[0.00111], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[1.23 & 0.00123], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[1.23 #linebreak() 0.00123], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/global_rounding/default.latex.txt b/test/references/global_rounding/default.latex.txt new file mode 100644 index 0000000..1039eac --- /dev/null +++ b/test/references/global_rounding/default.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +1.41 & 1.24e7 \\ +1.06e8 & 0.00111 \\ +1.23 \& 0.00123 & \begin{tabular}{@{}c@{}}1.23 \\ 0.00123\end{tabular} \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/global_rounding/default.txt b/test/references/global_rounding/default.txt new file mode 100644 index 0000000..08f1752 --- /dev/null +++ b/test/references/global_rounding/default.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
1.411.24e7
1.06e80.00111
1.23 & 0.001231.23
0.00123
\ No newline at end of file diff --git a/test/references/global_rounding/default.typ.txt b/test/references/global_rounding/default.typ.txt new file mode 100644 index 0000000..50fc035 --- /dev/null +++ b/test/references/global_rounding/default.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1.41], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[1.24e7], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[1.06e8], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[0.00111], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[1.23 & 0.00123], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[1.23 #linebreak() 0.00123], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/global_rounding/digits_false_1.latex.txt b/test/references/global_rounding/digits_false_1.latex.txt new file mode 100644 index 0000000..f00ce00 --- /dev/null +++ b/test/references/global_rounding/digits_false_1.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +1.4 & 12352131 \\ +106071821.2 & 0 \\ +1.2 \& 0 & \begin{tabular}{@{}c@{}}1.2 \\ 0\end{tabular} \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/global_rounding/digits_false_1.txt b/test/references/global_rounding/digits_false_1.txt new file mode 100644 index 0000000..4926eb4 --- /dev/null +++ b/test/references/global_rounding/digits_false_1.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
1.412352131
106071821.20
1.2 & 01.2
0
\ No newline at end of file diff --git a/test/references/global_rounding/digits_false_1.typ.txt b/test/references/global_rounding/digits_false_1.typ.txt new file mode 100644 index 0000000..e196669 --- /dev/null +++ b/test/references/global_rounding/digits_false_1.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1.4], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[12352131], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[106071821.2], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[0], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[1.2 & 0], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[1.2 #linebreak() 0], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/global_rounding/digits_false_3.latex.txt b/test/references/global_rounding/digits_false_3.latex.txt new file mode 100644 index 0000000..c7f7f60 --- /dev/null +++ b/test/references/global_rounding/digits_false_3.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +1.414 & 12352131 \\ +106071821.193 & 0.001 \\ +1.235 \& 0.001 & \begin{tabular}{@{}c@{}}1.235 \\ 0.001\end{tabular} \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/global_rounding/digits_false_3.txt b/test/references/global_rounding/digits_false_3.txt new file mode 100644 index 0000000..1e86c1e --- /dev/null +++ b/test/references/global_rounding/digits_false_3.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
1.41412352131
106071821.1930.001
1.235 & 0.0011.235
0.001
\ No newline at end of file diff --git a/test/references/global_rounding/digits_false_3.typ.txt b/test/references/global_rounding/digits_false_3.typ.txt new file mode 100644 index 0000000..0789bdd --- /dev/null +++ b/test/references/global_rounding/digits_false_3.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1.414], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[12352131], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[106071821.193], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[0.001], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[1.235 & 0.001], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[1.235 #linebreak() 0.001], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/global_rounding/digits_true_1.latex.txt b/test/references/global_rounding/digits_true_1.latex.txt new file mode 100644 index 0000000..88d0c7c --- /dev/null +++ b/test/references/global_rounding/digits_true_1.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +1.4 & 12352131.0 \\ +106071821.2 & 0.0 \\ +1.2 \& 0.0 & \begin{tabular}{@{}c@{}}1.2 \\ 0.0\end{tabular} \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/global_rounding/digits_true_1.txt b/test/references/global_rounding/digits_true_1.txt new file mode 100644 index 0000000..44f683e --- /dev/null +++ b/test/references/global_rounding/digits_true_1.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
1.412352131.0
106071821.20.0
1.2 & 0.01.2
0.0
\ No newline at end of file diff --git a/test/references/global_rounding/digits_true_1.typ.txt b/test/references/global_rounding/digits_true_1.typ.txt new file mode 100644 index 0000000..d7410f9 --- /dev/null +++ b/test/references/global_rounding/digits_true_1.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1.4], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[12352131.0], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[106071821.2], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[0.0], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[1.2 & 0.0], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[1.2 #linebreak() 0.0], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/global_rounding/digits_true_3.latex.txt b/test/references/global_rounding/digits_true_3.latex.txt new file mode 100644 index 0000000..64024da --- /dev/null +++ b/test/references/global_rounding/digits_true_3.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +1.414 & 12352131.000 \\ +106071821.193 & 0.001 \\ +1.235 \& 0.001 & \begin{tabular}{@{}c@{}}1.235 \\ 0.001\end{tabular} \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/global_rounding/digits_true_3.txt b/test/references/global_rounding/digits_true_3.txt new file mode 100644 index 0000000..c43b5d9 --- /dev/null +++ b/test/references/global_rounding/digits_true_3.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
1.41412352131.000
106071821.1930.001
1.235 & 0.0011.235
0.001
\ No newline at end of file diff --git a/test/references/global_rounding/digits_true_3.typ.txt b/test/references/global_rounding/digits_true_3.typ.txt new file mode 100644 index 0000000..3d3a48c --- /dev/null +++ b/test/references/global_rounding/digits_true_3.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1.414], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[12352131.000], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[106071821.193], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[0.001], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[1.235 & 0.001], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[1.235 #linebreak() 0.001], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/global_rounding/no_rounding.latex.txt b/test/references/global_rounding/no_rounding.latex.txt new file mode 100644 index 0000000..6d5e0b1 --- /dev/null +++ b/test/references/global_rounding/no_rounding.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +1.4142135623730951 & 1.2352131000001e7 \\ +1.0607182119320439e8 & 0.0011096125449903673 \\ +1.23456 \& 0.0012345 & \begin{tabular}{@{}c@{}}1.23456 \\ 0.0012345\end{tabular} \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/global_rounding/no_rounding.txt b/test/references/global_rounding/no_rounding.txt new file mode 100644 index 0000000..0eee1af --- /dev/null +++ b/test/references/global_rounding/no_rounding.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
1.41421356237309511.2352131000001e7
1.0607182119320439e80.0011096125449903673
1.23456 & 0.00123451.23456
0.0012345
\ No newline at end of file diff --git a/test/references/global_rounding/no_rounding.typ.txt b/test/references/global_rounding/no_rounding.typ.txt new file mode 100644 index 0000000..e392100 --- /dev/null +++ b/test/references/global_rounding/no_rounding.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1.4142135623730951], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[1.2352131000001e7], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[1.0607182119320439e8], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[0.0011096125449903673], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[1.23456 & 0.0012345], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[1.23456 #linebreak() 0.0012345], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/global_rounding/sigdigits_false_1.latex.txt b/test/references/global_rounding/sigdigits_false_1.latex.txt new file mode 100644 index 0000000..4df8e27 --- /dev/null +++ b/test/references/global_rounding/sigdigits_false_1.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +1 & 1.0e7 \\ +1.0e8 & 0.001 \\ +1 \& 0.001 & \begin{tabular}{@{}c@{}}1 \\ 0.001\end{tabular} \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/global_rounding/sigdigits_false_1.txt b/test/references/global_rounding/sigdigits_false_1.txt new file mode 100644 index 0000000..5edb8c0 --- /dev/null +++ b/test/references/global_rounding/sigdigits_false_1.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
11.0e7
1.0e80.001
1 & 0.0011
0.001
\ No newline at end of file diff --git a/test/references/global_rounding/sigdigits_false_1.typ.txt b/test/references/global_rounding/sigdigits_false_1.typ.txt new file mode 100644 index 0000000..f49c6eb --- /dev/null +++ b/test/references/global_rounding/sigdigits_false_1.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[1.0e7], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[1.0e8], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[0.001], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[1 & 0.001], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[1 #linebreak() 0.001], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/global_rounding/sigdigits_false_3.latex.txt b/test/references/global_rounding/sigdigits_false_3.latex.txt new file mode 100644 index 0000000..1039eac --- /dev/null +++ b/test/references/global_rounding/sigdigits_false_3.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +1.41 & 1.24e7 \\ +1.06e8 & 0.00111 \\ +1.23 \& 0.00123 & \begin{tabular}{@{}c@{}}1.23 \\ 0.00123\end{tabular} \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/global_rounding/sigdigits_false_3.txt b/test/references/global_rounding/sigdigits_false_3.txt new file mode 100644 index 0000000..08f1752 --- /dev/null +++ b/test/references/global_rounding/sigdigits_false_3.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
1.411.24e7
1.06e80.00111
1.23 & 0.001231.23
0.00123
\ No newline at end of file diff --git a/test/references/global_rounding/sigdigits_false_3.typ.txt b/test/references/global_rounding/sigdigits_false_3.typ.txt new file mode 100644 index 0000000..50fc035 --- /dev/null +++ b/test/references/global_rounding/sigdigits_false_3.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1.41], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[1.24e7], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[1.06e8], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[0.00111], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[1.23 & 0.00123], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[1.23 #linebreak() 0.00123], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/global_rounding/sigdigits_true_1.latex.txt b/test/references/global_rounding/sigdigits_true_1.latex.txt new file mode 100644 index 0000000..7891bb5 --- /dev/null +++ b/test/references/global_rounding/sigdigits_true_1.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +1.0 & 1.0e7 \\ +1.0e8 & 0.001 \\ +1.0 \& 0.001 & \begin{tabular}{@{}c@{}}1.0 \\ 0.001\end{tabular} \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/global_rounding/sigdigits_true_1.txt b/test/references/global_rounding/sigdigits_true_1.txt new file mode 100644 index 0000000..b254498 --- /dev/null +++ b/test/references/global_rounding/sigdigits_true_1.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
1.01.0e7
1.0e80.001
1.0 & 0.0011.0
0.001
\ No newline at end of file diff --git a/test/references/global_rounding/sigdigits_true_1.typ.txt b/test/references/global_rounding/sigdigits_true_1.typ.txt new file mode 100644 index 0000000..b0bc45c --- /dev/null +++ b/test/references/global_rounding/sigdigits_true_1.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1.0], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[1.0e7], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[1.0e8], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[0.001], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[1.0 & 0.001], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[1.0 #linebreak() 0.001], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/global_rounding/sigdigits_true_3.latex.txt b/test/references/global_rounding/sigdigits_true_3.latex.txt new file mode 100644 index 0000000..1039eac --- /dev/null +++ b/test/references/global_rounding/sigdigits_true_3.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +1.41 & 1.24e7 \\ +1.06e8 & 0.00111 \\ +1.23 \& 0.00123 & \begin{tabular}{@{}c@{}}1.23 \\ 0.00123\end{tabular} \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/global_rounding/sigdigits_true_3.txt b/test/references/global_rounding/sigdigits_true_3.txt new file mode 100644 index 0000000..08f1752 --- /dev/null +++ b/test/references/global_rounding/sigdigits_true_3.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
1.411.24e7
1.06e80.00111
1.23 & 0.001231.23
0.00123
\ No newline at end of file diff --git a/test/references/global_rounding/sigdigits_true_3.typ.txt b/test/references/global_rounding/sigdigits_true_3.typ.txt new file mode 100644 index 0000000..50fc035 --- /dev/null +++ b/test/references/global_rounding/sigdigits_true_3.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1.41], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[1.24e7], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[1.06e8], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[0.00111], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[1.23 & 0.00123], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[1.23 #linebreak() 0.00123], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/cols_only.latex.txt b/test/references/listingtable/cols_only.latex.txt new file mode 100644 index 0000000..a5f249c --- /dev/null +++ b/test/references/listingtable/cols_only.latex.txt @@ -0,0 +1,28 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{8}{cccccccc}} +\toprule +\multicolumn{8}{c}{\textbf{group1}} \\ +\cmidrule{1-8} +\multicolumn{4}{c}{a} & \multicolumn{4}{c}{b} \\ +\multicolumn{4}{c}{\textbf{group2}} & \multicolumn{4}{c}{\textbf{group2}} \\ +\cmidrule{1-4}\cmidrule{5-8} +\multicolumn{2}{c}{e} & \multicolumn{2}{c}{f} & \multicolumn{2}{c}{e} & \multicolumn{2}{c}{f} \\ +\multicolumn{2}{c}{\textbf{group3}} & \multicolumn{2}{c}{\textbf{group3}} & \multicolumn{2}{c}{\textbf{group3}} & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{1-2}\cmidrule{3-4}\cmidrule{5-6}\cmidrule{7-8} +c & d & c & d & c & d & c & d \\ +\multicolumn{8}{c}{\textbf{value1}} \\ +\midrule +1 & 3 & 2 & 4 & 5 & 7 & 6 & 8 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/cols_only.txt b/test/references/listingtable/cols_only.txt new file mode 100644 index 0000000..99bae5f --- /dev/null +++ b/test/references/listingtable/cols_only.txt @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
ab
group2group2
efef
group3group3group3group3
cdcdcdcd
value1
13245768
\ No newline at end of file diff --git a/test/references/listingtable/cols_only.typ.txt b/test/references/listingtable/cols_only.typ.txt new file mode 100644 index 0000000..cbc9bc0 --- /dev/null +++ b/test/references/listingtable/cols_only.typ.txt @@ -0,0 +1,52 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 8, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 8, x: 0, y: 0, align: center + top)[*group1*], + hlinex(y: 1, start: 0, end: 8, stroke: 0.75pt), + cellx(colspan: 4, x: 0, y: 1, align: center + top)[a], + cellx(colspan: 4, x: 4, y: 1, align: center + top)[b], + cellx(colspan: 4, x: 0, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 0, end: 4, stroke: 0.75pt), + cellx(colspan: 4, x: 4, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 4, end: 8, stroke: 0.75pt), + cellx(colspan: 2, x: 0, y: 3, align: center + top)[e], + cellx(colspan: 2, x: 2, y: 3, align: center + top)[f], + cellx(colspan: 2, x: 4, y: 3, align: center + top)[e], + cellx(colspan: 2, x: 6, y: 3, align: center + top)[f], + cellx(colspan: 2, x: 0, y: 4, align: center + top)[*group3*], + hlinex(y: 5, start: 0, end: 2, stroke: 0.75pt), + cellx(colspan: 2, x: 2, y: 4, align: center + top)[*group3*], + hlinex(y: 5, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 2, x: 4, y: 4, align: center + top)[*group3*], + hlinex(y: 5, start: 4, end: 6, stroke: 0.75pt), + cellx(colspan: 2, x: 6, y: 4, align: center + top)[*group3*], + hlinex(y: 5, start: 6, end: 8, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: center + top)[c], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[d], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[d], + cellx(colspan: 1, x: 4, y: 5, align: center + top)[c], + cellx(colspan: 1, x: 5, y: 5, align: center + top)[d], + cellx(colspan: 1, x: 6, y: 5, align: center + top)[c], + cellx(colspan: 1, x: 7, y: 5, align: center + top)[d], + cellx(colspan: 8, x: 0, y: 6, align: center + top)[*value1*], + hlinex(y: 7, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 7, align: center + top)[1], + cellx(colspan: 1, x: 1, y: 7, align: center + top)[3], + cellx(colspan: 1, x: 2, y: 7, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 7, align: center + top)[4], + cellx(colspan: 1, x: 4, y: 7, align: center + top)[5], + cellx(colspan: 1, x: 5, y: 7, align: center + top)[7], + cellx(colspan: 1, x: 6, y: 7, align: center + top)[6], + cellx(colspan: 1, x: 7, y: 7, align: center + top)[8], + hlinex(y: 8, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/natural_sort_order.latex.txt b/test/references/listingtable/natural_sort_order.latex.txt new file mode 100644 index 0000000..7a057fd --- /dev/null +++ b/test/references/listingtable/natural_sort_order.latex.txt @@ -0,0 +1,30 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{3}{llc}} +\toprule +\textbf{id} & \textbf{dose} & \textbf{value} \\ +\midrule +\multirow[t]{2}{*}{1} & 1 mg & 5 \\ + & 50 mg & 2 \\ +\multirow[t]{2}{*}{5} & 1 mg & 1 \\ + & 50 mg & 2 \\ +\multirow[t]{2}{*}{8} & 1 mg & 2 \\ + & 50 mg & 3 \\ +\multirow[t]{2}{*}{10} & 5 mg & 4 \\ + & 10 mg & 5 \\ +\multirow[t]{2}{*}{50} & 5 mg & 3 \\ + & 10 mg & 4 \\ +\multirow[t]{2}{*}{80} & 5 mg & 1 \\ + & 10 mg & 4 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/natural_sort_order.txt b/test/references/listingtable/natural_sort_order.txt new file mode 100644 index 0000000..00b18fd --- /dev/null +++ b/test/references/listingtable/natural_sort_order.txt @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
iddosevalue
11 mg5
50 mg2
51 mg1
50 mg2
81 mg2
50 mg3
105 mg4
10 mg5
505 mg3
10 mg4
805 mg1
10 mg4
\ No newline at end of file diff --git a/test/references/listingtable/natural_sort_order.typ.txt b/test/references/listingtable/natural_sort_order.typ.txt new file mode 100644 index 0000000..ed1f7da --- /dev/null +++ b/test/references/listingtable/natural_sort_order.typ.txt @@ -0,0 +1,48 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 3, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: left + top)[*id*], + cellx(colspan: 1, x: 1, y: 0, align: left + top)[*dose*], + cellx(colspan: 1, x: 2, y: 0, align: center + top)[*value*], + hlinex(y: 1, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 1, align: left + top)[1], + cellx(colspan: 1, x: 1, y: 1, align: left + top)[1 mg], + cellx(colspan: 1, x: 2, y: 1, align: center + top)[5], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[50 mg], + cellx(colspan: 1, x: 2, y: 2, align: center + top)[2], + cellx(colspan: 1, x: 0, y: 3, align: left + top)[5], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[1 mg], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[50 mg], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[8], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[1 mg], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[50 mg], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[3], + cellx(colspan: 1, x: 0, y: 7, align: left + top)[10], + cellx(colspan: 1, x: 1, y: 7, align: left + top)[5 mg], + cellx(colspan: 1, x: 2, y: 7, align: center + top)[4], + cellx(colspan: 1, x: 1, y: 8, align: left + top)[10 mg], + cellx(colspan: 1, x: 2, y: 8, align: center + top)[5], + cellx(colspan: 1, x: 0, y: 9, align: left + top)[50], + cellx(colspan: 1, x: 1, y: 9, align: left + top)[5 mg], + cellx(colspan: 1, x: 2, y: 9, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 10, align: left + top)[10 mg], + cellx(colspan: 1, x: 2, y: 10, align: center + top)[4], + cellx(colspan: 1, x: 0, y: 11, align: left + top)[80], + cellx(colspan: 1, x: 1, y: 11, align: left + top)[5 mg], + cellx(colspan: 1, x: 2, y: 11, align: center + top)[1], + cellx(colspan: 1, x: 1, y: 12, align: left + top)[10 mg], + cellx(colspan: 1, x: 2, y: 12, align: center + top)[4], + hlinex(y: 13, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/no_variable_header.latex.txt b/test/references/listingtable/no_variable_header.latex.txt new file mode 100644 index 0000000..4068d1e --- /dev/null +++ b/test/references/listingtable/no_variable_header.latex.txt @@ -0,0 +1,25 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{5}{lcccc}} +\toprule + & \multicolumn{4}{c}{\textbf{group2}} \\ +\cmidrule{2-5} + & \multicolumn{2}{c}{e} & \multicolumn{2}{c}{f} \\ + & \multicolumn{2}{c}{\textbf{group3}} & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{2-3}\cmidrule{4-5} +\textbf{group1} & c & d & c & d \\ +\midrule +a & 1 & 3 & 2 & 4 \\ +b & 5 & 7 & 6 & 8 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/no_variable_header.txt b/test/references/listingtable/no_variable_header.txt new file mode 100644 index 0000000..ce5a6ca --- /dev/null +++ b/test/references/listingtable/no_variable_header.txt @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group2
ef
group3group3
group1cdcd
a1324
b5768
\ No newline at end of file diff --git a/test/references/listingtable/no_variable_header.typ.txt b/test/references/listingtable/no_variable_header.typ.txt new file mode 100644 index 0000000..08c3b96 --- /dev/null +++ b/test/references/listingtable/no_variable_header.typ.txt @@ -0,0 +1,38 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 5, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 4, x: 1, y: 0, align: center + top)[*group2*], + hlinex(y: 1, start: 1, end: 5, stroke: 0.75pt), + cellx(colspan: 2, x: 1, y: 1, align: center + top)[e], + cellx(colspan: 2, x: 3, y: 1, align: center + top)[f], + cellx(colspan: 2, x: 1, y: 2, align: center + top)[*group3*], + hlinex(y: 3, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 2, x: 3, y: 2, align: center + top)[*group3*], + hlinex(y: 3, start: 3, end: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[c], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[d], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[c], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[d], + hlinex(y: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 4, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 4, align: center + top)[1], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[3], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[2], + cellx(colspan: 1, x: 4, y: 4, align: center + top)[4], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[5], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[7], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[6], + cellx(colspan: 1, x: 4, y: 5, align: center + top)[8], + hlinex(y: 6, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/one_row_two_cols.latex.txt b/test/references/listingtable/one_row_two_cols.latex.txt new file mode 100644 index 0000000..c23df24 --- /dev/null +++ b/test/references/listingtable/one_row_two_cols.latex.txt @@ -0,0 +1,26 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{5}{lcccc}} +\toprule + & \multicolumn{4}{c}{\textbf{group2}} \\ +\cmidrule{2-5} + & \multicolumn{2}{c}{e} & \multicolumn{2}{c}{f} \\ + & \multicolumn{2}{c}{\textbf{group3}} & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{2-3}\cmidrule{4-5} + & c & d & c & d \\ +\textbf{group1} & \multicolumn{4}{c}{\textbf{value1}} \\ +\midrule +a & 1 & 3 & 2 & 4 \\ +b & 5 & 7 & 6 & 8 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/one_row_two_cols.txt b/test/references/listingtable/one_row_two_cols.txt new file mode 100644 index 0000000..19a27c9 --- /dev/null +++ b/test/references/listingtable/one_row_two_cols.txt @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group2
ef
group3group3
cdcd
group1value1
a1324
b5768
\ No newline at end of file diff --git a/test/references/listingtable/one_row_two_cols.typ.txt b/test/references/listingtable/one_row_two_cols.typ.txt new file mode 100644 index 0000000..5ede4bf --- /dev/null +++ b/test/references/listingtable/one_row_two_cols.typ.txt @@ -0,0 +1,39 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 5, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 4, x: 1, y: 0, align: center + top)[*group2*], + hlinex(y: 1, start: 1, end: 5, stroke: 0.75pt), + cellx(colspan: 2, x: 1, y: 1, align: center + top)[e], + cellx(colspan: 2, x: 3, y: 1, align: center + top)[f], + cellx(colspan: 2, x: 1, y: 2, align: center + top)[*group3*], + hlinex(y: 3, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 2, x: 3, y: 2, align: center + top)[*group3*], + hlinex(y: 3, start: 3, end: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 3, align: center + top)[c], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[d], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[c], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*group1*], + cellx(colspan: 4, x: 1, y: 4, align: center + top)[*value1*], + hlinex(y: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[1], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[3], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 4, y: 5, align: center + top)[4], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[5], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[7], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[6], + cellx(colspan: 1, x: 4, y: 6, align: center + top)[8], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_cols=1_1.latex.txt b/test/references/listingtable/pagination_cols=1_1.latex.txt new file mode 100644 index 0000000..6b554d6 --- /dev/null +++ b/test/references/listingtable/pagination_cols=1_1.latex.txt @@ -0,0 +1,24 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{lc}} +\toprule + & \textbf{group1} \\ + & a \\ + & \textbf{group2} \\ + & e \\ +\textbf{group3} & \textbf{value1} \\ +\midrule +c & 1 \\ +d & 3 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_cols=1_1.txt b/test/references/listingtable/pagination_cols=1_1.txt new file mode 100644 index 0000000..4627662 --- /dev/null +++ b/test/references/listingtable/pagination_cols=1_1.txt @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
a
group2
e
group3value1
c1
d3
\ No newline at end of file diff --git a/test/references/listingtable/pagination_cols=1_1.typ.txt b/test/references/listingtable/pagination_cols=1_1.typ.txt new file mode 100644 index 0000000..3f145ae --- /dev/null +++ b/test/references/listingtable/pagination_cols=1_1.typ.txt @@ -0,0 +1,25 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 1, y: 0, align: center + top)[*group1*], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[a], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[*group2*], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*group3*], + cellx(colspan: 1, x: 1, y: 4, align: center + top)[*value1*], + hlinex(y: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: left + top)[c], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[1], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[d], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[3], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_cols=1_2.latex.txt b/test/references/listingtable/pagination_cols=1_2.latex.txt new file mode 100644 index 0000000..26775ec --- /dev/null +++ b/test/references/listingtable/pagination_cols=1_2.latex.txt @@ -0,0 +1,24 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{lc}} +\toprule + & \textbf{group1} \\ + & a \\ + & \textbf{group2} \\ + & f \\ +\textbf{group3} & \textbf{value1} \\ +\midrule +c & 2 \\ +d & 4 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_cols=1_2.txt b/test/references/listingtable/pagination_cols=1_2.txt new file mode 100644 index 0000000..58ac5c0 --- /dev/null +++ b/test/references/listingtable/pagination_cols=1_2.txt @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
a
group2
f
group3value1
c2
d4
\ No newline at end of file diff --git a/test/references/listingtable/pagination_cols=1_2.typ.txt b/test/references/listingtable/pagination_cols=1_2.typ.txt new file mode 100644 index 0000000..d6c53ae --- /dev/null +++ b/test/references/listingtable/pagination_cols=1_2.typ.txt @@ -0,0 +1,25 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 1, y: 0, align: center + top)[*group1*], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[a], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[*group2*], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*group3*], + cellx(colspan: 1, x: 1, y: 4, align: center + top)[*value1*], + hlinex(y: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: left + top)[c], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[d], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[4], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_cols=1_3.latex.txt b/test/references/listingtable/pagination_cols=1_3.latex.txt new file mode 100644 index 0000000..7766d7d --- /dev/null +++ b/test/references/listingtable/pagination_cols=1_3.latex.txt @@ -0,0 +1,24 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{lc}} +\toprule + & \textbf{group1} \\ + & b \\ + & \textbf{group2} \\ + & e \\ +\textbf{group3} & \textbf{value1} \\ +\midrule +c & 5 \\ +d & 7 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_cols=1_3.txt b/test/references/listingtable/pagination_cols=1_3.txt new file mode 100644 index 0000000..215335a --- /dev/null +++ b/test/references/listingtable/pagination_cols=1_3.txt @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
b
group2
e
group3value1
c5
d7
\ No newline at end of file diff --git a/test/references/listingtable/pagination_cols=1_3.typ.txt b/test/references/listingtable/pagination_cols=1_3.typ.txt new file mode 100644 index 0000000..0796b19 --- /dev/null +++ b/test/references/listingtable/pagination_cols=1_3.typ.txt @@ -0,0 +1,25 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 1, y: 0, align: center + top)[*group1*], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[b], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[*group2*], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*group3*], + cellx(colspan: 1, x: 1, y: 4, align: center + top)[*value1*], + hlinex(y: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: left + top)[c], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[5], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[d], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[7], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_cols=1_4.latex.txt b/test/references/listingtable/pagination_cols=1_4.latex.txt new file mode 100644 index 0000000..c8ef41e --- /dev/null +++ b/test/references/listingtable/pagination_cols=1_4.latex.txt @@ -0,0 +1,24 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{lc}} +\toprule + & \textbf{group1} \\ + & b \\ + & \textbf{group2} \\ + & f \\ +\textbf{group3} & \textbf{value1} \\ +\midrule +c & 6 \\ +d & 8 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_cols=1_4.txt b/test/references/listingtable/pagination_cols=1_4.txt new file mode 100644 index 0000000..2bdd6ba --- /dev/null +++ b/test/references/listingtable/pagination_cols=1_4.txt @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
b
group2
f
group3value1
c6
d8
\ No newline at end of file diff --git a/test/references/listingtable/pagination_cols=1_4.typ.txt b/test/references/listingtable/pagination_cols=1_4.typ.txt new file mode 100644 index 0000000..c24df58 --- /dev/null +++ b/test/references/listingtable/pagination_cols=1_4.typ.txt @@ -0,0 +1,25 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 1, y: 0, align: center + top)[*group1*], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[b], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[*group2*], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*group3*], + cellx(colspan: 1, x: 1, y: 4, align: center + top)[*value1*], + hlinex(y: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: left + top)[c], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[6], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[d], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[8], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_cols=2_1.latex.txt b/test/references/listingtable/pagination_cols=2_1.latex.txt new file mode 100644 index 0000000..3f39d91 --- /dev/null +++ b/test/references/listingtable/pagination_cols=2_1.latex.txt @@ -0,0 +1,26 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{3}{lcc}} +\toprule + & \multicolumn{2}{c}{\textbf{group1}} \\ +\cmidrule{2-3} + & \multicolumn{2}{c}{a} \\ + & \multicolumn{2}{c}{\textbf{group2}} \\ +\cmidrule{2-3} + & e & f \\ +\textbf{group3} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +c & 1 & 2 \\ +d & 3 & 4 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_cols=2_1.txt b/test/references/listingtable/pagination_cols=2_1.txt new file mode 100644 index 0000000..1747eb3 --- /dev/null +++ b/test/references/listingtable/pagination_cols=2_1.txt @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
a
group2
ef
group3value1
c12
d34
\ No newline at end of file diff --git a/test/references/listingtable/pagination_cols=2_1.typ.txt b/test/references/listingtable/pagination_cols=2_1.typ.txt new file mode 100644 index 0000000..992d9fd --- /dev/null +++ b/test/references/listingtable/pagination_cols=2_1.typ.txt @@ -0,0 +1,30 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 3, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 1, y: 0, align: center + top)[*group1*], + hlinex(y: 1, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 2, x: 1, y: 1, align: center + top)[a], + cellx(colspan: 2, x: 1, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*group3*], + cellx(colspan: 2, x: 1, y: 4, align: center + top)[*value1*], + hlinex(y: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: left + top)[c], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[1], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[d], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[3], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[4], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_cols=2_2.latex.txt b/test/references/listingtable/pagination_cols=2_2.latex.txt new file mode 100644 index 0000000..6c97df9 --- /dev/null +++ b/test/references/listingtable/pagination_cols=2_2.latex.txt @@ -0,0 +1,26 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{3}{lcc}} +\toprule + & \multicolumn{2}{c}{\textbf{group1}} \\ +\cmidrule{2-3} + & \multicolumn{2}{c}{b} \\ + & \multicolumn{2}{c}{\textbf{group2}} \\ +\cmidrule{2-3} + & e & f \\ +\textbf{group3} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +c & 5 & 6 \\ +d & 7 & 8 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_cols=2_2.txt b/test/references/listingtable/pagination_cols=2_2.txt new file mode 100644 index 0000000..956ea1d --- /dev/null +++ b/test/references/listingtable/pagination_cols=2_2.txt @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
b
group2
ef
group3value1
c56
d78
\ No newline at end of file diff --git a/test/references/listingtable/pagination_cols=2_2.typ.txt b/test/references/listingtable/pagination_cols=2_2.typ.txt new file mode 100644 index 0000000..c8675ad --- /dev/null +++ b/test/references/listingtable/pagination_cols=2_2.typ.txt @@ -0,0 +1,30 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 3, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 1, y: 0, align: center + top)[*group1*], + hlinex(y: 1, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 2, x: 1, y: 1, align: center + top)[b], + cellx(colspan: 2, x: 1, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*group3*], + cellx(colspan: 2, x: 1, y: 4, align: center + top)[*value1*], + hlinex(y: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: left + top)[c], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[5], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[6], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[d], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[7], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[8], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=1_1.latex.txt b/test/references/listingtable/pagination_rows=1_1.latex.txt new file mode 100644 index 0000000..5b223a4 --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_1.latex.txt @@ -0,0 +1,22 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +a & e & 1 & 3 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_1.txt b/test/references/listingtable/pagination_rows=1_1.txt new file mode 100644 index 0000000..3ab42fb --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_1.txt @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
ae13
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_1.typ.txt b/test/references/listingtable/pagination_rows=1_1.typ.txt new file mode 100644 index 0000000..05479ad --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_1.typ.txt @@ -0,0 +1,26 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[3], + hlinex(y: 4, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=1_2.latex.txt b/test/references/listingtable/pagination_rows=1_2.latex.txt new file mode 100644 index 0000000..22d2bca --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_2.latex.txt @@ -0,0 +1,22 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +a & f & 2 & 4 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_2.txt b/test/references/listingtable/pagination_rows=1_2.txt new file mode 100644 index 0000000..ef514ca --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_2.txt @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
af24
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_2.typ.txt b/test/references/listingtable/pagination_rows=1_2.typ.txt new file mode 100644 index 0000000..3890b2e --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_2.typ.txt @@ -0,0 +1,26 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[4], + hlinex(y: 4, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=1_3.latex.txt b/test/references/listingtable/pagination_rows=1_3.latex.txt new file mode 100644 index 0000000..c5636c4 --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_3.latex.txt @@ -0,0 +1,22 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +b & e & 5 & 7 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_3.txt b/test/references/listingtable/pagination_rows=1_3.txt new file mode 100644 index 0000000..b3e3f93 --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_3.txt @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
be57
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_3.typ.txt b/test/references/listingtable/pagination_rows=1_3.typ.txt new file mode 100644 index 0000000..9eb9103 --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_3.typ.txt @@ -0,0 +1,26 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[5], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[7], + hlinex(y: 4, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=1_4.latex.txt b/test/references/listingtable/pagination_rows=1_4.latex.txt new file mode 100644 index 0000000..fa2f35f --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_4.latex.txt @@ -0,0 +1,22 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +b & f & 6 & 8 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_4.txt b/test/references/listingtable/pagination_rows=1_4.txt new file mode 100644 index 0000000..684fc0a --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_4.txt @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
bf68
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_4.typ.txt b/test/references/listingtable/pagination_rows=1_4.typ.txt new file mode 100644 index 0000000..eb4bd19 --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_4.typ.txt @@ -0,0 +1,26 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[6], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[8], + hlinex(y: 4, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=1_cols=2_1.latex.txt b/test/references/listingtable/pagination_rows=1_cols=2_1.latex.txt new file mode 100644 index 0000000..92a70a3 --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_cols=2_1.latex.txt @@ -0,0 +1,25 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{3}{lcc}} +\toprule + & \multicolumn{2}{c}{\textbf{group1}} \\ +\cmidrule{2-3} + & \multicolumn{2}{c}{a} \\ + & \multicolumn{2}{c}{\textbf{group2}} \\ +\cmidrule{2-3} + & e & f \\ +\textbf{group3} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +c & 1 & 2 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_cols=2_1.txt b/test/references/listingtable/pagination_rows=1_cols=2_1.txt new file mode 100644 index 0000000..4743ab8 --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_cols=2_1.txt @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
a
group2
ef
group3value1
c12
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_cols=2_1.typ.txt b/test/references/listingtable/pagination_rows=1_cols=2_1.typ.txt new file mode 100644 index 0000000..88a7693 --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_cols=2_1.typ.txt @@ -0,0 +1,27 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 3, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 1, y: 0, align: center + top)[*group1*], + hlinex(y: 1, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 2, x: 1, y: 1, align: center + top)[a], + cellx(colspan: 2, x: 1, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*group3*], + cellx(colspan: 2, x: 1, y: 4, align: center + top)[*value1*], + hlinex(y: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: left + top)[c], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[1], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[2], + hlinex(y: 6, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=1_cols=2_2.latex.txt b/test/references/listingtable/pagination_rows=1_cols=2_2.latex.txt new file mode 100644 index 0000000..1e59e1b --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_cols=2_2.latex.txt @@ -0,0 +1,25 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{3}{lcc}} +\toprule + & \multicolumn{2}{c}{\textbf{group1}} \\ +\cmidrule{2-3} + & \multicolumn{2}{c}{b} \\ + & \multicolumn{2}{c}{\textbf{group2}} \\ +\cmidrule{2-3} + & e & f \\ +\textbf{group3} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +c & 5 & 6 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_cols=2_2.txt b/test/references/listingtable/pagination_rows=1_cols=2_2.txt new file mode 100644 index 0000000..0acafee --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_cols=2_2.txt @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
b
group2
ef
group3value1
c56
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_cols=2_2.typ.txt b/test/references/listingtable/pagination_rows=1_cols=2_2.typ.txt new file mode 100644 index 0000000..5ff65ba --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_cols=2_2.typ.txt @@ -0,0 +1,27 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 3, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 1, y: 0, align: center + top)[*group1*], + hlinex(y: 1, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 2, x: 1, y: 1, align: center + top)[b], + cellx(colspan: 2, x: 1, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*group3*], + cellx(colspan: 2, x: 1, y: 4, align: center + top)[*value1*], + hlinex(y: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: left + top)[c], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[5], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[6], + hlinex(y: 6, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=1_cols=2_3.latex.txt b/test/references/listingtable/pagination_rows=1_cols=2_3.latex.txt new file mode 100644 index 0000000..bbbdaa4 --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_cols=2_3.latex.txt @@ -0,0 +1,25 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{3}{lcc}} +\toprule + & \multicolumn{2}{c}{\textbf{group1}} \\ +\cmidrule{2-3} + & \multicolumn{2}{c}{a} \\ + & \multicolumn{2}{c}{\textbf{group2}} \\ +\cmidrule{2-3} + & e & f \\ +\textbf{group3} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +d & 3 & 4 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_cols=2_3.txt b/test/references/listingtable/pagination_rows=1_cols=2_3.txt new file mode 100644 index 0000000..8b39b82 --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_cols=2_3.txt @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
a
group2
ef
group3value1
d34
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_cols=2_3.typ.txt b/test/references/listingtable/pagination_rows=1_cols=2_3.typ.txt new file mode 100644 index 0000000..7de50c5 --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_cols=2_3.typ.txt @@ -0,0 +1,27 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 3, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 1, y: 0, align: center + top)[*group1*], + hlinex(y: 1, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 2, x: 1, y: 1, align: center + top)[a], + cellx(colspan: 2, x: 1, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*group3*], + cellx(colspan: 2, x: 1, y: 4, align: center + top)[*value1*], + hlinex(y: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: left + top)[d], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[3], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[4], + hlinex(y: 6, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=1_cols=2_4.latex.txt b/test/references/listingtable/pagination_rows=1_cols=2_4.latex.txt new file mode 100644 index 0000000..986032c --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_cols=2_4.latex.txt @@ -0,0 +1,25 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{3}{lcc}} +\toprule + & \multicolumn{2}{c}{\textbf{group1}} \\ +\cmidrule{2-3} + & \multicolumn{2}{c}{b} \\ + & \multicolumn{2}{c}{\textbf{group2}} \\ +\cmidrule{2-3} + & e & f \\ +\textbf{group3} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +d & 7 & 8 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_cols=2_4.txt b/test/references/listingtable/pagination_rows=1_cols=2_4.txt new file mode 100644 index 0000000..726dffa --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_cols=2_4.txt @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
b
group2
ef
group3value1
d78
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=1_cols=2_4.typ.txt b/test/references/listingtable/pagination_rows=1_cols=2_4.typ.txt new file mode 100644 index 0000000..4ba7726 --- /dev/null +++ b/test/references/listingtable/pagination_rows=1_cols=2_4.typ.txt @@ -0,0 +1,27 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 3, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 1, y: 0, align: center + top)[*group1*], + hlinex(y: 1, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 2, x: 1, y: 1, align: center + top)[b], + cellx(colspan: 2, x: 1, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*group3*], + cellx(colspan: 2, x: 1, y: 4, align: center + top)[*value1*], + hlinex(y: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: left + top)[d], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[7], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[8], + hlinex(y: 6, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=2_1.latex.txt b/test/references/listingtable/pagination_rows=2_1.latex.txt new file mode 100644 index 0000000..45348de --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_1.latex.txt @@ -0,0 +1,23 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +\multirow[t]{2}{*}{a} & e & 1 & 3 \\ + & f & 2 & 4 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_1.txt b/test/references/listingtable/pagination_rows=2_1.txt new file mode 100644 index 0000000..57fb30d --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_1.txt @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
ae13
f24
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_1.typ.txt b/test/references/listingtable/pagination_rows=2_1.typ.txt new file mode 100644 index 0000000..b281652 --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_1.typ.txt @@ -0,0 +1,29 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[4], + hlinex(y: 5, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=2_2.latex.txt b/test/references/listingtable/pagination_rows=2_2.latex.txt new file mode 100644 index 0000000..7da9529 --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_2.latex.txt @@ -0,0 +1,23 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +\multirow[t]{2}{*}{b} & e & 5 & 7 \\ + & f & 6 & 8 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_2.txt b/test/references/listingtable/pagination_rows=2_2.txt new file mode 100644 index 0000000..57d505c --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_2.txt @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
be57
f68
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_2.typ.txt b/test/references/listingtable/pagination_rows=2_2.typ.txt new file mode 100644 index 0000000..70c5351 --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_2.typ.txt @@ -0,0 +1,29 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[5], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[7], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[6], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[8], + hlinex(y: 5, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=2_summarized_1.latex.txt b/test/references/listingtable/pagination_rows=2_summarized_1.latex.txt new file mode 100644 index 0000000..7203014 --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_1.latex.txt @@ -0,0 +1,27 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +\multirow[t]{2}{*}{a} & e & 1 & 3 \\ + & f & 2 & 4 \\ +\multirow[t]{2}{*}{b} & e & 5 & 7 \\ + & f & 6 & 8 \\[6.0pt] + & \textbf{mean} & 3.5 & 5.5 \\ + & \textbf{std} & 2.38 & 2.38 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_summarized_1.txt b/test/references/listingtable/pagination_rows=2_summarized_1.txt new file mode 100644 index 0000000..c79e50b --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_1.txt @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
ae13
f24
be57
f68
mean3.55.5
std2.382.38
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_summarized_1.typ.txt b/test/references/listingtable/pagination_rows=2_summarized_1.typ.txt new file mode 100644 index 0000000..bc4fec8 --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_1.typ.txt @@ -0,0 +1,42 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[4], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[5], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[7], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[6], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[8], + cellx(colspan: 1, x: 1, y: 7, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 7, align: center + top)[3.5], + cellx(colspan: 1, x: 3, y: 7, align: center + top)[5.5], + cellx(colspan: 1, x: 1, y: 8, align: left + top)[*std*], + cellx(colspan: 1, x: 2, y: 8, align: center + top)[2.38], + cellx(colspan: 1, x: 3, y: 8, align: center + top)[2.38], + hlinex(y: 9, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_1.latex.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_1.latex.txt new file mode 100644 index 0000000..6f6f4c6 --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_1.latex.txt @@ -0,0 +1,25 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{5}{llccc}} +\toprule + & & \multicolumn{3}{c}{\textbf{parameters}} \\ +\cmidrule{3-5} + & & T\textsubscript{max} & C\textsuperscript{max} & \begin{tabular}{@{}c@{}}One Line \\ Another Line\end{tabular} \\ +\textbf{group2} & \textbf{group} & \multicolumn{3}{c}{\textbf{value}} \\ +\midrule +\multirow[t]{2}{*}{1} & 1 & 1 & 5 & 9 \\ + & 3 & 3 & 7 & 11 \\ +\multirow[t]{2}{*}{2} & 2 & 2 & 6 & 10 \\ + & 4 & 4 & 8 & 12 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_1.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_1.txt new file mode 100644 index 0000000..e2608ea --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_1.txt @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
parameters
TmaxCmaxOne Line
Another Line
group2groupvalue
11159
33711
222610
44812
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_1.typ.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_1.typ.txt new file mode 100644 index 0000000..008ae21 --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_1.typ.txt @@ -0,0 +1,41 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 5, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 3, x: 2, y: 0, align: center + top)[*parameters*], + hlinex(y: 1, start: 2, end: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[T#sub[max]], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[C#super[max]], + cellx(colspan: 1, x: 4, y: 1, align: center + top)[One Line #linebreak() Another Line], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group2*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group*], + cellx(colspan: 3, x: 2, y: 2, align: center + top)[*value*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[1], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[1], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[5], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[9], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[3], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[3], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[7], + cellx(colspan: 1, x: 4, y: 4, align: center + top)[11], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[2], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[2], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[6], + cellx(colspan: 1, x: 4, y: 5, align: center + top)[10], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[4], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[4], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[8], + cellx(colspan: 1, x: 4, y: 6, align: center + top)[12], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_2.latex.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_2.latex.txt new file mode 100644 index 0000000..6f6f4c6 --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_2.latex.txt @@ -0,0 +1,25 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{5}{llccc}} +\toprule + & & \multicolumn{3}{c}{\textbf{parameters}} \\ +\cmidrule{3-5} + & & T\textsubscript{max} & C\textsuperscript{max} & \begin{tabular}{@{}c@{}}One Line \\ Another Line\end{tabular} \\ +\textbf{group2} & \textbf{group} & \multicolumn{3}{c}{\textbf{value}} \\ +\midrule +\multirow[t]{2}{*}{1} & 1 & 1 & 5 & 9 \\ + & 3 & 3 & 7 & 11 \\ +\multirow[t]{2}{*}{2} & 2 & 2 & 6 & 10 \\ + & 4 & 4 & 8 & 12 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_2.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_2.txt new file mode 100644 index 0000000..e2608ea --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_2.txt @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
parameters
TmaxCmaxOne Line
Another Line
group2groupvalue
11159
33711
222610
44812
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_2.typ.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_2.typ.txt new file mode 100644 index 0000000..008ae21 --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_1_2.typ.txt @@ -0,0 +1,41 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 5, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 3, x: 2, y: 0, align: center + top)[*parameters*], + hlinex(y: 1, start: 2, end: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[T#sub[max]], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[C#super[max]], + cellx(colspan: 1, x: 4, y: 1, align: center + top)[One Line #linebreak() Another Line], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group2*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group*], + cellx(colspan: 3, x: 2, y: 2, align: center + top)[*value*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[1], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[1], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[5], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[9], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[3], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[3], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[7], + cellx(colspan: 1, x: 4, y: 4, align: center + top)[11], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[2], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[2], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[6], + cellx(colspan: 1, x: 4, y: 5, align: center + top)[10], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[4], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[4], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[8], + cellx(colspan: 1, x: 4, y: 6, align: center + top)[12], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_1.latex.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_1.latex.txt new file mode 100644 index 0000000..ce5de95 --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_1.latex.txt @@ -0,0 +1,24 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +a & e & 1 & 3 \\[6.0pt] + & \textbf{mean} & 1 & 3 \\ + & \textbf{std} & NaN & NaN \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_1.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_1.txt new file mode 100644 index 0000000..db89a3a --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_1.txt @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
ae13
mean13
stdNaNNaN
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_1.typ.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_1.typ.txt new file mode 100644 index 0000000..a86dd86 --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_1.typ.txt @@ -0,0 +1,32 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[*std*], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[NaN], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[NaN], + hlinex(y: 6, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_2.latex.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_2.latex.txt new file mode 100644 index 0000000..78f113a --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_2.latex.txt @@ -0,0 +1,24 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +a & f & 2 & 4 \\[6.0pt] + & \textbf{mean} & 2 & 4 \\ + & \textbf{std} & NaN & NaN \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_2.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_2.txt new file mode 100644 index 0000000..51a52b3 --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_2.txt @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
af24
mean24
stdNaNNaN
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_2.typ.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_2.typ.txt new file mode 100644 index 0000000..97921fe --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_2.typ.txt @@ -0,0 +1,32 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[4], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[4], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[*std*], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[NaN], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[NaN], + hlinex(y: 6, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_3.latex.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_3.latex.txt new file mode 100644 index 0000000..f80170e --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_3.latex.txt @@ -0,0 +1,24 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +b & e & 5 & 7 \\[6.0pt] + & \textbf{mean} & 5 & 7 \\ + & \textbf{std} & NaN & NaN \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_3.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_3.txt new file mode 100644 index 0000000..9d083bc --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_3.txt @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
be57
mean57
stdNaNNaN
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_3.typ.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_3.typ.txt new file mode 100644 index 0000000..01a80de --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_3.typ.txt @@ -0,0 +1,32 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[5], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[7], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[5], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[7], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[*std*], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[NaN], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[NaN], + hlinex(y: 6, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_4.latex.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_4.latex.txt new file mode 100644 index 0000000..37b28e6 --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_4.latex.txt @@ -0,0 +1,24 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +b & f & 6 & 8 \\[6.0pt] + & \textbf{mean} & 6 & 8 \\ + & \textbf{std} & NaN & NaN \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_4.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_4.txt new file mode 100644 index 0000000..d2bad10 --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_4.txt @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
bf68
mean68
stdNaNNaN
\ No newline at end of file diff --git a/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_4.typ.txt b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_4.typ.txt new file mode 100644 index 0000000..212348f --- /dev/null +++ b/test/references/listingtable/pagination_rows=2_summarized_grouplevel_2_4.typ.txt @@ -0,0 +1,32 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[6], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[8], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[6], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[8], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[*std*], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[NaN], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[NaN], + hlinex(y: 6, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/renaming.latex.txt b/test/references/listingtable/renaming.latex.txt new file mode 100644 index 0000000..01c4a47 --- /dev/null +++ b/test/references/listingtable/renaming.latex.txt @@ -0,0 +1,27 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{Group 3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{Group 1} & \textbf{Group 2} & \multicolumn{2}{c}{\textbf{Value 1}} \\ +\midrule +\multirow[t]{2}{*}{a} & e & 1 & 3 \\ + & f & 2 & 4 \\ +\multirow[t]{2}{*}{b} & e & 5 & 7 \\ + & f & 6 & 8 \\[6.0pt] + & \textbf{Mean} & 3.5 & 5.5 \\ + & \textbf{Minimum} & 1 & 3 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/renaming.txt b/test/references/listingtable/renaming.txt new file mode 100644 index 0000000..599769a --- /dev/null +++ b/test/references/listingtable/renaming.txt @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Group 3
cd
Group 1Group 2Value 1
ae13
f24
be57
f68
Mean3.55.5
Minimum13
\ No newline at end of file diff --git a/test/references/listingtable/renaming.typ.txt b/test/references/listingtable/renaming.typ.txt new file mode 100644 index 0000000..ef13c74 --- /dev/null +++ b/test/references/listingtable/renaming.typ.txt @@ -0,0 +1,42 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*Group 3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*Group 1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*Group 2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*Value 1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[4], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[5], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[7], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[6], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[8], + cellx(colspan: 1, x: 1, y: 7, align: left + top)[*Mean*], + cellx(colspan: 1, x: 2, y: 7, align: center + top)[3.5], + cellx(colspan: 1, x: 3, y: 7, align: center + top)[5.5], + cellx(colspan: 1, x: 1, y: 8, align: left + top)[*Minimum*], + cellx(colspan: 1, x: 2, y: 8, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 8, align: center + top)[3], + hlinex(y: 9, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/rows_only.latex.txt b/test/references/listingtable/rows_only.latex.txt new file mode 100644 index 0000000..2820f06 --- /dev/null +++ b/test/references/listingtable/rows_only.latex.txt @@ -0,0 +1,26 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{lllc}} +\toprule +\textbf{group1} & \textbf{group2} & \textbf{group3} & \textbf{value1} \\ +\midrule +\multirow[t]{4}{*}{a} & \multirow[t]{2}{*}{e} & c & 1 \\ + & & d & 3 \\ + & \multirow[t]{2}{*}{f} & c & 2 \\ + & & d & 4 \\ +\multirow[t]{4}{*}{b} & \multirow[t]{2}{*}{e} & c & 5 \\ + & & d & 7 \\ + & \multirow[t]{2}{*}{f} & c & 6 \\ + & & d & 8 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/rows_only.txt b/test/references/listingtable/rows_only.txt new file mode 100644 index 0000000..d7f2e04 --- /dev/null +++ b/test/references/listingtable/rows_only.txt @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1group2group3value1
aec1
d3
fc2
d4
bec5
d7
fc6
d8
\ No newline at end of file diff --git a/test/references/listingtable/rows_only.typ.txt b/test/references/listingtable/rows_only.typ.txt new file mode 100644 index 0000000..3570bfc --- /dev/null +++ b/test/references/listingtable/rows_only.typ.txt @@ -0,0 +1,41 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 0, align: left + top)[*group2*], + cellx(colspan: 1, x: 2, y: 0, align: left + top)[*group3*], + cellx(colspan: 1, x: 3, y: 0, align: center + top)[*value1*], + hlinex(y: 1, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 1, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 1, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 1, align: left + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[1], + cellx(colspan: 1, x: 2, y: 2, align: left + top)[d], + cellx(colspan: 1, x: 3, y: 2, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 3, align: left + top)[c], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[2], + cellx(colspan: 1, x: 2, y: 4, align: left + top)[d], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[4], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 5, align: left + top)[c], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[5], + cellx(colspan: 1, x: 2, y: 6, align: left + top)[d], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[7], + cellx(colspan: 1, x: 1, y: 7, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 7, align: left + top)[c], + cellx(colspan: 1, x: 3, y: 7, align: center + top)[6], + cellx(colspan: 1, x: 2, y: 8, align: left + top)[d], + cellx(colspan: 1, x: 3, y: 8, align: center + top)[8], + hlinex(y: 9, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/sort_false.latex.txt b/test/references/listingtable/sort_false.latex.txt new file mode 100644 index 0000000..6f6f4c6 --- /dev/null +++ b/test/references/listingtable/sort_false.latex.txt @@ -0,0 +1,25 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{5}{llccc}} +\toprule + & & \multicolumn{3}{c}{\textbf{parameters}} \\ +\cmidrule{3-5} + & & T\textsubscript{max} & C\textsuperscript{max} & \begin{tabular}{@{}c@{}}One Line \\ Another Line\end{tabular} \\ +\textbf{group2} & \textbf{group} & \multicolumn{3}{c}{\textbf{value}} \\ +\midrule +\multirow[t]{2}{*}{1} & 1 & 1 & 5 & 9 \\ + & 3 & 3 & 7 & 11 \\ +\multirow[t]{2}{*}{2} & 2 & 2 & 6 & 10 \\ + & 4 & 4 & 8 & 12 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/sort_false.txt b/test/references/listingtable/sort_false.txt new file mode 100644 index 0000000..e2608ea --- /dev/null +++ b/test/references/listingtable/sort_false.txt @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
parameters
TmaxCmaxOne Line
Another Line
group2groupvalue
11159
33711
222610
44812
\ No newline at end of file diff --git a/test/references/listingtable/sort_false.typ.txt b/test/references/listingtable/sort_false.typ.txt new file mode 100644 index 0000000..008ae21 --- /dev/null +++ b/test/references/listingtable/sort_false.typ.txt @@ -0,0 +1,41 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 5, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 3, x: 2, y: 0, align: center + top)[*parameters*], + hlinex(y: 1, start: 2, end: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[T#sub[max]], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[C#super[max]], + cellx(colspan: 1, x: 4, y: 1, align: center + top)[One Line #linebreak() Another Line], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group2*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group*], + cellx(colspan: 3, x: 2, y: 2, align: center + top)[*value*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[1], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[1], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[5], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[9], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[3], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[3], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[7], + cellx(colspan: 1, x: 4, y: 4, align: center + top)[11], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[2], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[2], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[6], + cellx(colspan: 1, x: 4, y: 5, align: center + top)[10], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[4], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[4], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[8], + cellx(colspan: 1, x: 4, y: 6, align: center + top)[12], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/summarize_end_cols_two_funcs.latex.txt b/test/references/listingtable/summarize_end_cols_two_funcs.latex.txt new file mode 100644 index 0000000..dbfe525 --- /dev/null +++ b/test/references/listingtable/summarize_end_cols_two_funcs.latex.txt @@ -0,0 +1,26 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{7}{lccccrr}} +\toprule + & \multicolumn{4}{c}{\textbf{group1}} & & \\ +\cmidrule{2-5} + & \multicolumn{2}{c}{a} & \multicolumn{2}{c}{b} & & \\ + & \multicolumn{2}{c}{\textbf{group2}} & \multicolumn{2}{c}{\textbf{group2}} & & \\ +\cmidrule{2-3}\cmidrule{4-5} + & e & f & e & f & & \\ +\textbf{group3} & \multicolumn{4}{c}{\textbf{value1}} & \textbf{mean} & \textbf{std} \\ +\midrule +c & 1 & 2 & 5 & 6 & 3.5 & 2.38 \\ +d & 3 & 4 & 7 & 8 & 5.5 & 2.38 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/summarize_end_cols_two_funcs.txt b/test/references/listingtable/summarize_end_cols_two_funcs.txt new file mode 100644 index 0000000..95d46d4 --- /dev/null +++ b/test/references/listingtable/summarize_end_cols_two_funcs.txt @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
ab
group2group2
efef
group3value1meanstd
c12563.52.38
d34785.52.38
\ No newline at end of file diff --git a/test/references/listingtable/summarize_end_cols_two_funcs.typ.txt b/test/references/listingtable/summarize_end_cols_two_funcs.typ.txt new file mode 100644 index 0000000..f5ff06c --- /dev/null +++ b/test/references/listingtable/summarize_end_cols_two_funcs.typ.txt @@ -0,0 +1,45 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 7, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 4, x: 1, y: 0, align: center + top)[*group1*], + hlinex(y: 1, start: 1, end: 5, stroke: 0.75pt), + cellx(colspan: 2, x: 1, y: 1, align: center + top)[a], + cellx(colspan: 2, x: 3, y: 1, align: center + top)[b], + cellx(colspan: 2, x: 1, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 2, x: 3, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 3, end: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*group3*], + cellx(colspan: 4, x: 1, y: 4, align: center + top)[*value1*], + cellx(colspan: 1, x: 5, y: 4, align: right + top)[*mean*], + cellx(colspan: 1, x: 6, y: 4, align: right + top)[*std*], + hlinex(y: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: left + top)[c], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[1], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[5], + cellx(colspan: 1, x: 4, y: 5, align: center + top)[6], + cellx(colspan: 1, x: 5, y: 5, align: right + top)[3.5], + cellx(colspan: 1, x: 6, y: 5, align: right + top)[2.38], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[d], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[3], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[4], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[7], + cellx(colspan: 1, x: 4, y: 6, align: center + top)[8], + cellx(colspan: 1, x: 5, y: 6, align: right + top)[5.5], + cellx(colspan: 1, x: 6, y: 6, align: right + top)[2.38], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/summarize_end_rows.latex.txt b/test/references/listingtable/summarize_end_rows.latex.txt new file mode 100644 index 0000000..a8ebc9d --- /dev/null +++ b/test/references/listingtable/summarize_end_rows.latex.txt @@ -0,0 +1,26 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +\multirow[t]{2}{*}{a} & e & 1 & 3 \\ + & f & 2 & 4 \\ +\multirow[t]{2}{*}{b} & e & 5 & 7 \\ + & f & 6 & 8 \\[6.0pt] + & \textbf{mean} & 3.5 & 5.5 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/summarize_end_rows.txt b/test/references/listingtable/summarize_end_rows.txt new file mode 100644 index 0000000..e8b97cc --- /dev/null +++ b/test/references/listingtable/summarize_end_rows.txt @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
ae13
f24
be57
f68
mean3.55.5
\ No newline at end of file diff --git a/test/references/listingtable/summarize_end_rows.typ.txt b/test/references/listingtable/summarize_end_rows.typ.txt new file mode 100644 index 0000000..796104d --- /dev/null +++ b/test/references/listingtable/summarize_end_rows.typ.txt @@ -0,0 +1,39 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[4], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[5], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[7], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[6], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[8], + cellx(colspan: 1, x: 1, y: 7, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 7, align: center + top)[3.5], + cellx(colspan: 1, x: 3, y: 7, align: center + top)[5.5], + hlinex(y: 8, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/summarize_end_rows_two_funcs.latex.txt b/test/references/listingtable/summarize_end_rows_two_funcs.latex.txt new file mode 100644 index 0000000..7203014 --- /dev/null +++ b/test/references/listingtable/summarize_end_rows_two_funcs.latex.txt @@ -0,0 +1,27 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +\multirow[t]{2}{*}{a} & e & 1 & 3 \\ + & f & 2 & 4 \\ +\multirow[t]{2}{*}{b} & e & 5 & 7 \\ + & f & 6 & 8 \\[6.0pt] + & \textbf{mean} & 3.5 & 5.5 \\ + & \textbf{std} & 2.38 & 2.38 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/summarize_end_rows_two_funcs.txt b/test/references/listingtable/summarize_end_rows_two_funcs.txt new file mode 100644 index 0000000..c79e50b --- /dev/null +++ b/test/references/listingtable/summarize_end_rows_two_funcs.txt @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
ae13
f24
be57
f68
mean3.55.5
std2.382.38
\ No newline at end of file diff --git a/test/references/listingtable/summarize_end_rows_two_funcs.typ.txt b/test/references/listingtable/summarize_end_rows_two_funcs.typ.txt new file mode 100644 index 0000000..bc4fec8 --- /dev/null +++ b/test/references/listingtable/summarize_end_rows_two_funcs.typ.txt @@ -0,0 +1,42 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[4], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[5], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[7], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[6], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[8], + cellx(colspan: 1, x: 1, y: 7, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 7, align: center + top)[3.5], + cellx(colspan: 1, x: 3, y: 7, align: center + top)[5.5], + cellx(colspan: 1, x: 1, y: 8, align: left + top)[*std*], + cellx(colspan: 1, x: 2, y: 8, align: center + top)[2.38], + cellx(colspan: 1, x: 3, y: 8, align: center + top)[2.38], + hlinex(y: 9, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/summarize_first_group_cols.latex.txt b/test/references/listingtable/summarize_first_group_cols.latex.txt new file mode 100644 index 0000000..3e572fa --- /dev/null +++ b/test/references/listingtable/summarize_first_group_cols.latex.txt @@ -0,0 +1,26 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{7}{lccrccr}} +\toprule + & \multicolumn{2}{c}{\textbf{group1}} & & \multicolumn{2}{c}{\textbf{group1}} & \\ +\cmidrule{2-3}\cmidrule{5-6} + & \multicolumn{2}{c}{a} & & \multicolumn{2}{c}{b} & \\ + & \multicolumn{2}{c}{\textbf{group2}} & & \multicolumn{2}{c}{\textbf{group2}} & \\ +\cmidrule{2-3}\cmidrule{5-6} + & e & f & & e & f & \\ +\textbf{group3} & \multicolumn{2}{c}{\textbf{value1}} & \textbf{mean} & \multicolumn{2}{c}{\textbf{value1}} & \textbf{mean} \\ +\midrule +c & 1 & 2 & 1.5 & 5 & 6 & 5.5 \\ +d & 3 & 4 & 3.5 & 7 & 8 & 7.5 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/summarize_first_group_cols.txt b/test/references/listingtable/summarize_first_group_cols.txt new file mode 100644 index 0000000..c7e8f01 --- /dev/null +++ b/test/references/listingtable/summarize_first_group_cols.txt @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1group1
ab
group2group2
efef
group3value1meanvalue1mean
c121.5565.5
d343.5787.5
\ No newline at end of file diff --git a/test/references/listingtable/summarize_first_group_cols.typ.txt b/test/references/listingtable/summarize_first_group_cols.typ.txt new file mode 100644 index 0000000..aa4cab5 --- /dev/null +++ b/test/references/listingtable/summarize_first_group_cols.typ.txt @@ -0,0 +1,48 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 7, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 1, y: 0, align: center + top)[*group1*], + hlinex(y: 1, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 2, x: 4, y: 0, align: center + top)[*group1*], + hlinex(y: 1, start: 4, end: 6, stroke: 0.75pt), + cellx(colspan: 2, x: 1, y: 1, align: center + top)[a], + cellx(colspan: 2, x: 4, y: 1, align: center + top)[b], + cellx(colspan: 2, x: 1, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 1, end: 3, stroke: 0.75pt), + cellx(colspan: 2, x: 4, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 4, end: 6, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 5, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*group3*], + cellx(colspan: 2, x: 1, y: 4, align: center + top)[*value1*], + cellx(colspan: 1, x: 3, y: 4, align: right + top)[*mean*], + cellx(colspan: 2, x: 4, y: 4, align: center + top)[*value1*], + cellx(colspan: 1, x: 6, y: 4, align: right + top)[*mean*], + hlinex(y: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: left + top)[c], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[1], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 5, align: right + top)[1.5], + cellx(colspan: 1, x: 4, y: 5, align: center + top)[5], + cellx(colspan: 1, x: 5, y: 5, align: center + top)[6], + cellx(colspan: 1, x: 6, y: 5, align: right + top)[5.5], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[d], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[3], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[4], + cellx(colspan: 1, x: 3, y: 6, align: right + top)[3.5], + cellx(colspan: 1, x: 4, y: 6, align: center + top)[7], + cellx(colspan: 1, x: 5, y: 6, align: center + top)[8], + cellx(colspan: 1, x: 6, y: 6, align: right + top)[7.5], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/summarize_first_group_rows.latex.txt b/test/references/listingtable/summarize_first_group_rows.latex.txt new file mode 100644 index 0000000..0890014 --- /dev/null +++ b/test/references/listingtable/summarize_first_group_rows.latex.txt @@ -0,0 +1,27 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +\multirow[t]{2}{*}{a} & e & 1 & 3 \\ + & f & 2 & 4 \\[6.0pt] + & \textbf{mean} & 1.5 & 3.5 \\[6.0pt] +\multirow[t]{2}{*}{b} & e & 5 & 7 \\ + & f & 6 & 8 \\[6.0pt] + & \textbf{mean} & 5.5 & 7.5 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/summarize_first_group_rows.txt b/test/references/listingtable/summarize_first_group_rows.txt new file mode 100644 index 0000000..b2bd23f --- /dev/null +++ b/test/references/listingtable/summarize_first_group_rows.txt @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
ae13
f24
mean1.53.5
be57
f68
mean5.57.5
\ No newline at end of file diff --git a/test/references/listingtable/summarize_first_group_rows.typ.txt b/test/references/listingtable/summarize_first_group_rows.typ.txt new file mode 100644 index 0000000..426aa74 --- /dev/null +++ b/test/references/listingtable/summarize_first_group_rows.typ.txt @@ -0,0 +1,42 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[4], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[1.5], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[3.5], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[5], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[7], + cellx(colspan: 1, x: 1, y: 7, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 7, align: center + top)[6], + cellx(colspan: 1, x: 3, y: 7, align: center + top)[8], + cellx(colspan: 1, x: 1, y: 8, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 8, align: center + top)[5.5], + cellx(colspan: 1, x: 3, y: 8, align: center + top)[7.5], + hlinex(y: 9, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/summarize_last_group_cols.latex.txt b/test/references/listingtable/summarize_last_group_cols.latex.txt new file mode 100644 index 0000000..de16ec3 --- /dev/null +++ b/test/references/listingtable/summarize_last_group_cols.latex.txt @@ -0,0 +1,24 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{9}{lcrcrcrcr}} +\toprule + & \textbf{group1} & & \textbf{group1} & & \textbf{group1} & & \textbf{group1} & \\ + & a & & a & & b & & b & \\ + & \textbf{group2} & & \textbf{group2} & & \textbf{group2} & & \textbf{group2} & \\ + & e & & f & & e & & f & \\ +\textbf{group3} & \textbf{value1} & \textbf{mean} & \textbf{value1} & \textbf{mean} & \textbf{value1} & \textbf{mean} & \textbf{value1} & \textbf{mean} \\ +\midrule +c & 1 & 1 & 2 & 2 & 5 & 5 & 6 & 6 \\ +d & 3 & 3 & 4 & 4 & 7 & 7 & 8 & 8 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/summarize_last_group_cols.txt b/test/references/listingtable/summarize_last_group_cols.txt new file mode 100644 index 0000000..fccd840 --- /dev/null +++ b/test/references/listingtable/summarize_last_group_cols.txt @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1group1group1group1
aabb
group2group2group2group2
efef
group3value1meanvalue1meanvalue1meanvalue1mean
c11225566
d33447788
\ No newline at end of file diff --git a/test/references/listingtable/summarize_last_group_cols.typ.txt b/test/references/listingtable/summarize_last_group_cols.typ.txt new file mode 100644 index 0000000..e1ea55d --- /dev/null +++ b/test/references/listingtable/summarize_last_group_cols.typ.txt @@ -0,0 +1,58 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 9, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 1, y: 0, align: center + top)[*group1*], + cellx(colspan: 1, x: 3, y: 0, align: center + top)[*group1*], + cellx(colspan: 1, x: 5, y: 0, align: center + top)[*group1*], + cellx(colspan: 1, x: 7, y: 0, align: center + top)[*group1*], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[a], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[a], + cellx(colspan: 1, x: 5, y: 1, align: center + top)[b], + cellx(colspan: 1, x: 7, y: 1, align: center + top)[b], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[*group2*], + cellx(colspan: 1, x: 3, y: 2, align: center + top)[*group2*], + cellx(colspan: 1, x: 5, y: 2, align: center + top)[*group2*], + cellx(colspan: 1, x: 7, y: 2, align: center + top)[*group2*], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 5, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 7, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*group3*], + cellx(colspan: 1, x: 1, y: 4, align: center + top)[*value1*], + cellx(colspan: 1, x: 2, y: 4, align: right + top)[*mean*], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[*value1*], + cellx(colspan: 1, x: 4, y: 4, align: right + top)[*mean*], + cellx(colspan: 1, x: 5, y: 4, align: center + top)[*value1*], + cellx(colspan: 1, x: 6, y: 4, align: right + top)[*mean*], + cellx(colspan: 1, x: 7, y: 4, align: center + top)[*value1*], + cellx(colspan: 1, x: 8, y: 4, align: right + top)[*mean*], + hlinex(y: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 5, align: left + top)[c], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[1], + cellx(colspan: 1, x: 2, y: 5, align: right + top)[1], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 4, y: 5, align: right + top)[2], + cellx(colspan: 1, x: 5, y: 5, align: center + top)[5], + cellx(colspan: 1, x: 6, y: 5, align: right + top)[5], + cellx(colspan: 1, x: 7, y: 5, align: center + top)[6], + cellx(colspan: 1, x: 8, y: 5, align: right + top)[6], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[d], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[3], + cellx(colspan: 1, x: 2, y: 6, align: right + top)[3], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[4], + cellx(colspan: 1, x: 4, y: 6, align: right + top)[4], + cellx(colspan: 1, x: 5, y: 6, align: center + top)[7], + cellx(colspan: 1, x: 6, y: 6, align: right + top)[7], + cellx(colspan: 1, x: 7, y: 6, align: center + top)[8], + cellx(colspan: 1, x: 8, y: 6, align: right + top)[8], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/summarize_last_group_rows.latex.txt b/test/references/listingtable/summarize_last_group_rows.latex.txt new file mode 100644 index 0000000..9bf1c4e --- /dev/null +++ b/test/references/listingtable/summarize_last_group_rows.latex.txt @@ -0,0 +1,29 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +a & e & 1 & 3 \\[6.0pt] + & \textbf{mean} & 1 & 3 \\[6.0pt] +a & f & 2 & 4 \\[6.0pt] + & \textbf{mean} & 2 & 4 \\[6.0pt] +b & e & 5 & 7 \\[6.0pt] + & \textbf{mean} & 5 & 7 \\[6.0pt] +b & f & 6 & 8 \\[6.0pt] + & \textbf{mean} & 6 & 8 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/summarize_last_group_rows.txt b/test/references/listingtable/summarize_last_group_rows.txt new file mode 100644 index 0000000..04c455f --- /dev/null +++ b/test/references/listingtable/summarize_last_group_rows.txt @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
ae13
mean13
af24
mean24
be57
mean57
bf68
mean68
\ No newline at end of file diff --git a/test/references/listingtable/summarize_last_group_rows.typ.txt b/test/references/listingtable/summarize_last_group_rows.typ.txt new file mode 100644 index 0000000..98a782c --- /dev/null +++ b/test/references/listingtable/summarize_last_group_rows.typ.txt @@ -0,0 +1,50 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[3], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[4], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[4], + cellx(colspan: 1, x: 0, y: 7, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 7, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 7, align: center + top)[5], + cellx(colspan: 1, x: 3, y: 7, align: center + top)[7], + cellx(colspan: 1, x: 1, y: 8, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 8, align: center + top)[5], + cellx(colspan: 1, x: 3, y: 8, align: center + top)[7], + cellx(colspan: 1, x: 0, y: 9, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 9, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 9, align: center + top)[6], + cellx(colspan: 1, x: 3, y: 9, align: center + top)[8], + cellx(colspan: 1, x: 1, y: 10, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 10, align: center + top)[6], + cellx(colspan: 1, x: 3, y: 10, align: center + top)[8], + hlinex(y: 11, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/two_rows_one_col.latex.txt b/test/references/listingtable/two_rows_one_col.latex.txt new file mode 100644 index 0000000..334c082 --- /dev/null +++ b/test/references/listingtable/two_rows_one_col.latex.txt @@ -0,0 +1,25 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group3}} \\ +\cmidrule{3-4} + & & c & d \\ +\textbf{group1} & \textbf{group2} & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +\multirow[t]{2}{*}{a} & e & 1 & 3 \\ + & f & 2 & 4 \\ +\multirow[t]{2}{*}{b} & e & 5 & 7 \\ + & f & 6 & 8 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/two_rows_one_col.txt b/test/references/listingtable/two_rows_one_col.txt new file mode 100644 index 0000000..42466b4 --- /dev/null +++ b/test/references/listingtable/two_rows_one_col.txt @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group3
cd
group1group2value1
ae13
f24
be57
f68
\ No newline at end of file diff --git a/test/references/listingtable/two_rows_one_col.typ.txt b/test/references/listingtable/two_rows_one_col.typ.txt new file mode 100644 index 0000000..8070f7f --- /dev/null +++ b/test/references/listingtable/two_rows_one_col.typ.txt @@ -0,0 +1,36 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group3*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[4], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[5], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[7], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[6], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[8], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/listingtable/two_same_summarizers.latex.txt b/test/references/listingtable/two_same_summarizers.latex.txt new file mode 100644 index 0000000..ab0708b --- /dev/null +++ b/test/references/listingtable/two_same_summarizers.latex.txt @@ -0,0 +1,32 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{5}{llcrr}} +\toprule +\textbf{id} & \textbf{dose} & \textbf{value} & \textbf{mean} & \textbf{mean} \\ +\midrule +\multirow[t]{2}{*}{1} & 1 mg & 5 & 5 & 5 \\ + & 50 mg & 2 & 2 & 2 \\ +\multirow[t]{2}{*}{5} & 1 mg & 1 & 1 & 1 \\ + & 50 mg & 2 & 2 & 2 \\ +\multirow[t]{2}{*}{8} & 1 mg & 2 & 2 & 2 \\ + & 50 mg & 3 & 3 & 3 \\ +\multirow[t]{2}{*}{10} & 5 mg & 4 & 4 & 4 \\ + & 10 mg & 5 & 5 & 5 \\ +\multirow[t]{2}{*}{50} & 5 mg & 3 & 3 & 3 \\ + & 10 mg & 4 & 4 & 4 \\ +\multirow[t]{2}{*}{80} & 5 mg & 1 & 1 & 1 \\ + & 10 mg & 4 & 4 & 4 \\[6.0pt] + & \textbf{mean} & 3 & & \\ + & \textbf{mean} & 3 & & \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/listingtable/two_same_summarizers.txt b/test/references/listingtable/two_same_summarizers.txt new file mode 100644 index 0000000..07d37a6 --- /dev/null +++ b/test/references/listingtable/two_same_summarizers.txt @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
iddosevaluemeanmean
11 mg555
50 mg222
51 mg111
50 mg222
81 mg222
50 mg333
105 mg444
10 mg555
505 mg333
10 mg444
805 mg111
10 mg444
mean3
mean3
\ No newline at end of file diff --git a/test/references/listingtable/two_same_summarizers.typ.txt b/test/references/listingtable/two_same_summarizers.typ.txt new file mode 100644 index 0000000..3803918 --- /dev/null +++ b/test/references/listingtable/two_same_summarizers.typ.txt @@ -0,0 +1,78 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 5, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: left + top)[*id*], + cellx(colspan: 1, x: 1, y: 0, align: left + top)[*dose*], + cellx(colspan: 1, x: 2, y: 0, align: center + top)[*value*], + cellx(colspan: 1, x: 3, y: 0, align: right + top)[*mean*], + cellx(colspan: 1, x: 4, y: 0, align: right + top)[*mean*], + hlinex(y: 1, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 1, align: left + top)[1], + cellx(colspan: 1, x: 1, y: 1, align: left + top)[1 mg], + cellx(colspan: 1, x: 2, y: 1, align: center + top)[5], + cellx(colspan: 1, x: 3, y: 1, align: right + top)[5], + cellx(colspan: 1, x: 4, y: 1, align: right + top)[5], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[50 mg], + cellx(colspan: 1, x: 2, y: 2, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 2, align: right + top)[2], + cellx(colspan: 1, x: 4, y: 2, align: right + top)[2], + cellx(colspan: 1, x: 0, y: 3, align: left + top)[5], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[1 mg], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 3, align: right + top)[1], + cellx(colspan: 1, x: 4, y: 3, align: right + top)[1], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[50 mg], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 4, align: right + top)[2], + cellx(colspan: 1, x: 4, y: 4, align: right + top)[2], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[8], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[1 mg], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 5, align: right + top)[2], + cellx(colspan: 1, x: 4, y: 5, align: right + top)[2], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[50 mg], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[3], + cellx(colspan: 1, x: 3, y: 6, align: right + top)[3], + cellx(colspan: 1, x: 4, y: 6, align: right + top)[3], + cellx(colspan: 1, x: 0, y: 7, align: left + top)[10], + cellx(colspan: 1, x: 1, y: 7, align: left + top)[5 mg], + cellx(colspan: 1, x: 2, y: 7, align: center + top)[4], + cellx(colspan: 1, x: 3, y: 7, align: right + top)[4], + cellx(colspan: 1, x: 4, y: 7, align: right + top)[4], + cellx(colspan: 1, x: 1, y: 8, align: left + top)[10 mg], + cellx(colspan: 1, x: 2, y: 8, align: center + top)[5], + cellx(colspan: 1, x: 3, y: 8, align: right + top)[5], + cellx(colspan: 1, x: 4, y: 8, align: right + top)[5], + cellx(colspan: 1, x: 0, y: 9, align: left + top)[50], + cellx(colspan: 1, x: 1, y: 9, align: left + top)[5 mg], + cellx(colspan: 1, x: 2, y: 9, align: center + top)[3], + cellx(colspan: 1, x: 3, y: 9, align: right + top)[3], + cellx(colspan: 1, x: 4, y: 9, align: right + top)[3], + cellx(colspan: 1, x: 1, y: 10, align: left + top)[10 mg], + cellx(colspan: 1, x: 2, y: 10, align: center + top)[4], + cellx(colspan: 1, x: 3, y: 10, align: right + top)[4], + cellx(colspan: 1, x: 4, y: 10, align: right + top)[4], + cellx(colspan: 1, x: 0, y: 11, align: left + top)[80], + cellx(colspan: 1, x: 1, y: 11, align: left + top)[5 mg], + cellx(colspan: 1, x: 2, y: 11, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 11, align: right + top)[1], + cellx(colspan: 1, x: 4, y: 11, align: right + top)[1], + cellx(colspan: 1, x: 1, y: 12, align: left + top)[10 mg], + cellx(colspan: 1, x: 2, y: 12, align: center + top)[4], + cellx(colspan: 1, x: 3, y: 12, align: right + top)[4], + cellx(colspan: 1, x: 4, y: 12, align: right + top)[4], + cellx(colspan: 1, x: 1, y: 13, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 13, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 14, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 14, align: center + top)[3], + hlinex(y: 15, stroke: 1pt), + +) +] diff --git a/test/references/manual_footnotes/footnotes.latex.txt b/test/references/manual_footnotes/footnotes.latex.txt new file mode 100644 index 0000000..0aaf424 --- /dev/null +++ b/test/references/manual_footnotes/footnotes.latex.txt @@ -0,0 +1,22 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +Cell 1 & Cell 2 \\ +\bottomrule +\end{tabular} +\begin{tablenotes}[flushleft,para] +\footnotesize +\item[]First footnote. +\item[]Second footnote. +\end{tablenotes} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/manual_footnotes/footnotes.txt b/test/references/manual_footnotes/footnotes.txt new file mode 100644 index 0000000..16916a7 --- /dev/null +++ b/test/references/manual_footnotes/footnotes.txt @@ -0,0 +1,38 @@ + + + + + + + + + +
Cell 1Cell 2
First footnote.    Second footnote.
\ No newline at end of file diff --git a/test/references/manual_footnotes/footnotes.typ.txt b/test/references/manual_footnotes/footnotes.typ.txt new file mode 100644 index 0000000..843c1d3 --- /dev/null +++ b/test/references/manual_footnotes/footnotes.typ.txt @@ -0,0 +1,16 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[Cell 1], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[Cell 2], + hlinex(y: 1, stroke: 1pt), + cellx(colspan: 2, x: 0, y: 1)[First footnote.#h(1.5em, weak: true)Second footnote.], +) +] diff --git a/test/references/manual_footnotes/footnotes_and_annotated.latex.txt b/test/references/manual_footnotes/footnotes_and_annotated.latex.txt new file mode 100644 index 0000000..88a6195 --- /dev/null +++ b/test/references/manual_footnotes/footnotes_and_annotated.latex.txt @@ -0,0 +1,23 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +Cell 1\tnote{1} & Cell 2 \\ +\bottomrule +\end{tabular} +\begin{tablenotes}[flushleft,para] +\footnotesize +\item[1]Note 1 +\item[]First footnote. +\item[]Second footnote. +\end{tablenotes} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/manual_footnotes/footnotes_and_annotated.txt b/test/references/manual_footnotes/footnotes_and_annotated.txt new file mode 100644 index 0000000..a4c618f --- /dev/null +++ b/test/references/manual_footnotes/footnotes_and_annotated.txt @@ -0,0 +1,38 @@ + + + + + + + + + +
Cell 11Cell 2
1 Note 1    First footnote.    Second footnote.
\ No newline at end of file diff --git a/test/references/manual_footnotes/footnotes_and_annotated.typ.txt b/test/references/manual_footnotes/footnotes_and_annotated.typ.txt new file mode 100644 index 0000000..9687fd8 --- /dev/null +++ b/test/references/manual_footnotes/footnotes_and_annotated.typ.txt @@ -0,0 +1,16 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[Cell 1#super[1]], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[Cell 2], + hlinex(y: 1, stroke: 1pt), + cellx(colspan: 2, x: 0, y: 1)[#super[1]#text(size: 0.8em)[Note 1]#h(1.5em, weak: true)First footnote.#h(1.5em, weak: true)Second footnote.], +) +] diff --git a/test/references/paginated_table_interactive.txt b/test/references/paginated_table_interactive.txt new file mode 100644 index 0000000..f1d426d --- /dev/null +++ b/test/references/paginated_table_interactive.txt @@ -0,0 +1,277 @@ +
+ + + + + + + + + + +
+
+

Page 1

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
a
group2
ef
group3value1
c12
+
+
+

Page 2

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
b
group2
ef
group3value1
c56
+
+
+

Page 3

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
a
group2
ef
group3value1
d34
+
+
+

Page 4

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
b
group2
ef
group3value1
d78
+
+
+
diff --git a/test/references/replace/replace_predicate_function.latex.txt b/test/references/replace/replace_predicate_function.latex.txt new file mode 100644 index 0000000..d6a2d81 --- /dev/null +++ b/test/references/replace/replace_predicate_function.latex.txt @@ -0,0 +1,18 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +missing & \\ +11 & 12 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/replace/replace_predicate_function.txt b/test/references/replace/replace_predicate_function.txt new file mode 100644 index 0000000..3760f8c --- /dev/null +++ b/test/references/replace/replace_predicate_function.txt @@ -0,0 +1,41 @@ + + + + + + + + + + + + +
missing
1112
\ No newline at end of file diff --git a/test/references/replace/replace_predicate_function.typ.txt b/test/references/replace/replace_predicate_function.typ.txt new file mode 100644 index 0000000..9f17ab5 --- /dev/null +++ b/test/references/replace/replace_predicate_function.typ.txt @@ -0,0 +1,17 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[missing], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[11], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[12], + hlinex(y: 2, stroke: 1pt), + +) +] diff --git a/test/references/replace/replace_predicate_value.latex.txt b/test/references/replace/replace_predicate_value.latex.txt new file mode 100644 index 0000000..969be68 --- /dev/null +++ b/test/references/replace/replace_predicate_value.latex.txt @@ -0,0 +1,18 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +missing & \\ +an Int was here & an Int was here \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/replace/replace_predicate_value.txt b/test/references/replace/replace_predicate_value.txt new file mode 100644 index 0000000..0728424 --- /dev/null +++ b/test/references/replace/replace_predicate_value.txt @@ -0,0 +1,41 @@ + + + + + + + + + + + + +
missing
an Int was herean Int was here
\ No newline at end of file diff --git a/test/references/replace/replace_predicate_value.typ.txt b/test/references/replace/replace_predicate_value.typ.txt new file mode 100644 index 0000000..7ec7e00 --- /dev/null +++ b/test/references/replace/replace_predicate_value.typ.txt @@ -0,0 +1,17 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[missing], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[an Int was here], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[an Int was here], + hlinex(y: 2, stroke: 1pt), + +) +] diff --git a/test/references/replace/replacemissing_custom.latex.txt b/test/references/replace/replacemissing_custom.latex.txt new file mode 100644 index 0000000..1a7444f --- /dev/null +++ b/test/references/replace/replacemissing_custom.latex.txt @@ -0,0 +1,18 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +??? & \\ +1 & 2 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/replace/replacemissing_custom.txt b/test/references/replace/replacemissing_custom.txt new file mode 100644 index 0000000..3ae13bb --- /dev/null +++ b/test/references/replace/replacemissing_custom.txt @@ -0,0 +1,41 @@ + + + + + + + + + + + + +
???
12
\ No newline at end of file diff --git a/test/references/replace/replacemissing_custom.typ.txt b/test/references/replace/replacemissing_custom.typ.txt new file mode 100644 index 0000000..21196b6 --- /dev/null +++ b/test/references/replace/replacemissing_custom.typ.txt @@ -0,0 +1,17 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[???], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[1], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[2], + hlinex(y: 2, stroke: 1pt), + +) +] diff --git a/test/references/replace/replacemissing_default.latex.txt b/test/references/replace/replacemissing_default.latex.txt new file mode 100644 index 0000000..5ae42c2 --- /dev/null +++ b/test/references/replace/replacemissing_default.latex.txt @@ -0,0 +1,22 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{cc}} +\toprule +- & - \\ +1 & 2 \\ +\bottomrule +\end{tabular} +\begin{tablenotes}[flushleft,para] +\footnotesize +- No value +\end{tablenotes} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/replace/replacemissing_default.txt b/test/references/replace/replacemissing_default.txt new file mode 100644 index 0000000..694fb07 --- /dev/null +++ b/test/references/replace/replacemissing_default.txt @@ -0,0 +1,42 @@ + + + + + + + + + + + + + +
--
12
- No value
\ No newline at end of file diff --git a/test/references/replace/replacemissing_default.typ.txt b/test/references/replace/replacemissing_default.typ.txt new file mode 100644 index 0000000..504eb84 --- /dev/null +++ b/test/references/replace/replacemissing_default.typ.txt @@ -0,0 +1,18 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[-], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[-], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[1], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[2], + hlinex(y: 2, stroke: 1pt), + cellx(colspan: 2, x: 0, y: 2)[#text(size: 0.8em)[- No value]], +) +] diff --git a/test/references/row_and_column_gaps/singlecell.latex.txt b/test/references/row_and_column_gaps/singlecell.latex.txt new file mode 100644 index 0000000..f954b2d --- /dev/null +++ b/test/references/row_and_column_gaps/singlecell.latex.txt @@ -0,0 +1,20 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{cc@{\hskip 4.0pt}c@{\hskip 8.0pt}c}} +\toprule +1, 1 & 1, 2 & 1, 3 & 1, 4 \\[4.0pt] +2, 1 & 2, 2 & 2, 3 & 2, 4 \\[8.0pt] +3, 1 & 3, 2 & 3, 3 & 3, 4 \\ +4, 1 & 4, 2 & 4, 3 & 4, 4 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/row_and_column_gaps/singlecell.txt b/test/references/row_and_column_gaps/singlecell.txt new file mode 100644 index 0000000..e389c24 --- /dev/null +++ b/test/references/row_and_column_gaps/singlecell.txt @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1, 11, 21, 31, 4
2, 12, 22, 32, 4
3, 13, 23, 33, 4
4, 14, 24, 34, 4
\ No newline at end of file diff --git a/test/references/row_and_column_gaps/singlecell.typ.txt b/test/references/row_and_column_gaps/singlecell.typ.txt new file mode 100644 index 0000000..6fdab81 --- /dev/null +++ b/test/references/row_and_column_gaps/singlecell.typ.txt @@ -0,0 +1,30 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[1, 1], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[1, 2], + cellx(colspan: 1, x: 2, y: 0, align: center + top)[1, 3], + cellx(colspan: 1, x: 3, y: 0, align: center + top)[1, 4], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[2, 1], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[2, 2], + cellx(colspan: 1, x: 2, y: 1, align: center + top)[2, 3], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[2, 4], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[3, 1], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[3, 2], + cellx(colspan: 1, x: 2, y: 2, align: center + top)[3, 3], + cellx(colspan: 1, x: 3, y: 2, align: center + top)[3, 4], + cellx(colspan: 1, x: 0, y: 3, align: center + top)[4, 1], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[4, 2], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[4, 3], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[4, 4], + hlinex(y: 4, stroke: 1pt), + +) +] diff --git a/test/references/row_and_column_gaps/spanned_cells.latex.txt b/test/references/row_and_column_gaps/spanned_cells.latex.txt new file mode 100644 index 0000000..48cb9da --- /dev/null +++ b/test/references/row_and_column_gaps/spanned_cells.latex.txt @@ -0,0 +1,20 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{cc@{\hskip 4.0pt}cc}} +\toprule + & \multicolumn{3}{c}{Spanned columns} \\[4.0pt] +\multirow[t]{3}{*}{Spanned rows} & & & \\ + & & & \\ + & & & \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/row_and_column_gaps/spanned_cells.txt b/test/references/row_and_column_gaps/spanned_cells.txt new file mode 100644 index 0000000..307bb40 --- /dev/null +++ b/test/references/row_and_column_gaps/spanned_cells.txt @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + +
Spanned columns
Spanned rows
\ No newline at end of file diff --git a/test/references/row_and_column_gaps/spanned_cells.typ.txt b/test/references/row_and_column_gaps/spanned_cells.typ.txt new file mode 100644 index 0000000..ac63391 --- /dev/null +++ b/test/references/row_and_column_gaps/spanned_cells.typ.txt @@ -0,0 +1,16 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 3, x: 1, y: 0, align: center + top)[Spanned columns], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[Spanned rows], + hlinex(y: 4, stroke: 1pt), + +) +] diff --git a/test/references/styles/valign.latex.txt b/test/references/styles/valign.latex.txt new file mode 100644 index 0000000..6a4bb46 --- /dev/null +++ b/test/references/styles/valign.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{cccc}} +\toprule +Row 1 & \multirow[t]{3}{*}{top} & \multirow[c]{3}{*}{center} & \multirow[b]{3}{*}{bottom} \\ +Row 2 & & & \\ +Row 3 & & & \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/styles/valign.txt b/test/references/styles/valign.txt new file mode 100644 index 0000000..6fd5bb8 --- /dev/null +++ b/test/references/styles/valign.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
Row 1topcenterbottom
Row 2
Row 3
\ No newline at end of file diff --git a/test/references/styles/valign.typ.txt b/test/references/styles/valign.typ.txt new file mode 100644 index 0000000..95e5d1e --- /dev/null +++ b/test/references/styles/valign.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: center + top)[Row 1], + cellx(colspan: 1, x: 1, y: 0, align: center + top)[top], + cellx(colspan: 1, x: 2, y: 0, align: center + horizon)[center], + cellx(colspan: 1, x: 3, y: 0, align: center + bottom)[bottom], + cellx(colspan: 1, x: 0, y: 1, align: center + top)[Row 2], + cellx(colspan: 1, x: 0, y: 2, align: center + top)[Row 3], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/summarytable/natural_sort_order.latex.txt b/test/references/summarytable/natural_sort_order.latex.txt new file mode 100644 index 0000000..d30e1b3 --- /dev/null +++ b/test/references/summarytable/natural_sort_order.latex.txt @@ -0,0 +1,30 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{lllc}} +\toprule +\textbf{id} & \textbf{dose} & & \textbf{value} \\ +\midrule +\multirow[t]{2}{*}{1} & 1 mg & \textbf{mean} & 5 \\ + & 50 mg & \textbf{mean} & 2 \\ +\multirow[t]{2}{*}{5} & 1 mg & \textbf{mean} & 1 \\ + & 50 mg & \textbf{mean} & 2 \\ +\multirow[t]{2}{*}{8} & 1 mg & \textbf{mean} & 2 \\ + & 50 mg & \textbf{mean} & 3 \\ +\multirow[t]{2}{*}{10} & 5 mg & \textbf{mean} & 4 \\ + & 10 mg & \textbf{mean} & 5 \\ +\multirow[t]{2}{*}{50} & 5 mg & \textbf{mean} & 3 \\ + & 10 mg & \textbf{mean} & 4 \\ +\multirow[t]{2}{*}{80} & 5 mg & \textbf{mean} & 1 \\ + & 10 mg & \textbf{mean} & 4 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/summarytable/natural_sort_order.txt b/test/references/summarytable/natural_sort_order.txt new file mode 100644 index 0000000..b3a06ce --- /dev/null +++ b/test/references/summarytable/natural_sort_order.txt @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
iddosevalue
11 mgmean5
50 mgmean2
51 mgmean1
50 mgmean2
81 mgmean2
50 mgmean3
105 mgmean4
10 mgmean5
505 mgmean3
10 mgmean4
805 mgmean1
10 mgmean4
\ No newline at end of file diff --git a/test/references/summarytable/natural_sort_order.typ.txt b/test/references/summarytable/natural_sort_order.typ.txt new file mode 100644 index 0000000..7bbb77b --- /dev/null +++ b/test/references/summarytable/natural_sort_order.typ.txt @@ -0,0 +1,60 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: left + top)[*id*], + cellx(colspan: 1, x: 1, y: 0, align: left + top)[*dose*], + cellx(colspan: 1, x: 3, y: 0, align: center + top)[*value*], + hlinex(y: 1, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 1, align: left + top)[1], + cellx(colspan: 1, x: 1, y: 1, align: left + top)[1 mg], + cellx(colspan: 1, x: 2, y: 1, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[5], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[50 mg], + cellx(colspan: 1, x: 2, y: 2, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 2, align: center + top)[2], + cellx(colspan: 1, x: 0, y: 3, align: left + top)[5], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[1 mg], + cellx(colspan: 1, x: 2, y: 3, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[50 mg], + cellx(colspan: 1, x: 2, y: 4, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[2], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[8], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[1 mg], + cellx(colspan: 1, x: 2, y: 5, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[50 mg], + cellx(colspan: 1, x: 2, y: 6, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[3], + cellx(colspan: 1, x: 0, y: 7, align: left + top)[10], + cellx(colspan: 1, x: 1, y: 7, align: left + top)[5 mg], + cellx(colspan: 1, x: 2, y: 7, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 7, align: center + top)[4], + cellx(colspan: 1, x: 1, y: 8, align: left + top)[10 mg], + cellx(colspan: 1, x: 2, y: 8, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 8, align: center + top)[5], + cellx(colspan: 1, x: 0, y: 9, align: left + top)[50], + cellx(colspan: 1, x: 1, y: 9, align: left + top)[5 mg], + cellx(colspan: 1, x: 2, y: 9, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 9, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 10, align: left + top)[10 mg], + cellx(colspan: 1, x: 2, y: 10, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 10, align: center + top)[4], + cellx(colspan: 1, x: 0, y: 11, align: left + top)[80], + cellx(colspan: 1, x: 1, y: 11, align: left + top)[5 mg], + cellx(colspan: 1, x: 2, y: 11, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 11, align: center + top)[1], + cellx(colspan: 1, x: 1, y: 12, align: left + top)[10 mg], + cellx(colspan: 1, x: 2, y: 12, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 12, align: center + top)[4], + hlinex(y: 13, stroke: 1pt), + +) +] diff --git a/test/references/summarytable/no_group_one_summary.latex.txt b/test/references/summarytable/no_group_one_summary.latex.txt new file mode 100644 index 0000000..a47cc98 --- /dev/null +++ b/test/references/summarytable/no_group_one_summary.latex.txt @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{lc}} +\toprule + & \textbf{value1} \\ +\midrule +\textbf{mean} & 4.5 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/summarytable/no_group_one_summary.txt b/test/references/summarytable/no_group_one_summary.txt new file mode 100644 index 0000000..32bca01 --- /dev/null +++ b/test/references/summarytable/no_group_one_summary.txt @@ -0,0 +1,41 @@ + + + + + + + + + + + + +
value1
mean4.5
\ No newline at end of file diff --git a/test/references/summarytable/no_group_one_summary.typ.txt b/test/references/summarytable/no_group_one_summary.typ.txt new file mode 100644 index 0000000..898ac0d --- /dev/null +++ b/test/references/summarytable/no_group_one_summary.typ.txt @@ -0,0 +1,18 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 1, y: 0, align: center + top)[*value1*], + hlinex(y: 1, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 1, align: left + top)[*mean*], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[4.5], + hlinex(y: 2, stroke: 1pt), + +) +] diff --git a/test/references/summarytable/no_group_two_summaries.latex.txt b/test/references/summarytable/no_group_two_summaries.latex.txt new file mode 100644 index 0000000..58a490a --- /dev/null +++ b/test/references/summarytable/no_group_two_summaries.latex.txt @@ -0,0 +1,20 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{lc}} +\toprule + & \textbf{value1} \\ +\midrule +\textbf{mean} & 4.5 \\ +\textbf{SD} & 2.45 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/summarytable/no_group_two_summaries.txt b/test/references/summarytable/no_group_two_summaries.txt new file mode 100644 index 0000000..612c993 --- /dev/null +++ b/test/references/summarytable/no_group_two_summaries.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
value1
mean4.5
SD2.45
\ No newline at end of file diff --git a/test/references/summarytable/no_group_two_summaries.typ.txt b/test/references/summarytable/no_group_two_summaries.typ.txt new file mode 100644 index 0000000..65d6e74 --- /dev/null +++ b/test/references/summarytable/no_group_two_summaries.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 1, y: 0, align: center + top)[*value1*], + hlinex(y: 1, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 1, align: left + top)[*mean*], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[4.5], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*SD*], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[2.45], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/summarytable/one_rowgroup_one_colgroup_one_summary.latex.txt b/test/references/summarytable/one_rowgroup_one_colgroup_one_summary.latex.txt new file mode 100644 index 0000000..cf5dba8 --- /dev/null +++ b/test/references/summarytable/one_rowgroup_one_colgroup_one_summary.latex.txt @@ -0,0 +1,23 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{Group 2}} \\ +\cmidrule{3-4} + & & e & f \\ +\textbf{Group 1} & & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +a & \textbf{mean} & 2 & 3 \\ +b & \textbf{mean} & 6 & 7 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/summarytable/one_rowgroup_one_colgroup_one_summary.txt b/test/references/summarytable/one_rowgroup_one_colgroup_one_summary.txt new file mode 100644 index 0000000..176618b --- /dev/null +++ b/test/references/summarytable/one_rowgroup_one_colgroup_one_summary.txt @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Group 2
ef
Group 1value1
amean23
bmean67
\ No newline at end of file diff --git a/test/references/summarytable/one_rowgroup_one_colgroup_one_summary.typ.txt b/test/references/summarytable/one_rowgroup_one_colgroup_one_summary.typ.txt new file mode 100644 index 0000000..88c5bd1 --- /dev/null +++ b/test/references/summarytable/one_rowgroup_one_colgroup_one_summary.typ.txt @@ -0,0 +1,29 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*Group 2*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[e], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[f], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*Group 1*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[3], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[6], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[7], + hlinex(y: 5, stroke: 1pt), + +) +] diff --git a/test/references/summarytable/one_rowgroup_one_colgroup_two_summaries.latex.txt b/test/references/summarytable/one_rowgroup_one_colgroup_two_summaries.latex.txt new file mode 100644 index 0000000..555330f --- /dev/null +++ b/test/references/summarytable/one_rowgroup_one_colgroup_two_summaries.latex.txt @@ -0,0 +1,25 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{llcc}} +\toprule + & & \multicolumn{2}{c}{\textbf{Group 2}} \\ +\cmidrule{3-4} + & & e & f \\ +\textbf{Group 1} & & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +\multirow[t]{2}{*}{a} & \textbf{mean} & 2 & 3 \\ + & \textbf{std} & 1.41 & 1.41 \\ +\multirow[t]{2}{*}{b} & \textbf{mean} & 6 & 7 \\ + & \textbf{std} & 1.41 & 1.41 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/summarytable/one_rowgroup_one_colgroup_two_summaries.txt b/test/references/summarytable/one_rowgroup_one_colgroup_two_summaries.txt new file mode 100644 index 0000000..3443aa9 --- /dev/null +++ b/test/references/summarytable/one_rowgroup_one_colgroup_two_summaries.txt @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Group 2
ef
Group 1value1
amean23
std1.411.41
bmean67
std1.411.41
\ No newline at end of file diff --git a/test/references/summarytable/one_rowgroup_one_colgroup_two_summaries.typ.txt b/test/references/summarytable/one_rowgroup_one_colgroup_two_summaries.typ.txt new file mode 100644 index 0000000..52e6f58 --- /dev/null +++ b/test/references/summarytable/one_rowgroup_one_colgroup_two_summaries.typ.txt @@ -0,0 +1,35 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*Group 2*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[e], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[f], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*Group 1*], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[2], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[3], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[*std*], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[1.41], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[1.41], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[6], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[7], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[*std*], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[1.41], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[1.41], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/summarytable/one_rowgroup_one_summary.latex.txt b/test/references/summarytable/one_rowgroup_one_summary.latex.txt new file mode 100644 index 0000000..390aea8 --- /dev/null +++ b/test/references/summarytable/one_rowgroup_one_summary.latex.txt @@ -0,0 +1,20 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{3}{llc}} +\toprule +\textbf{Group 1} & & \textbf{value1} \\ +\midrule +a & \textbf{mean} & 2.5 \\ +b & \textbf{mean} & 6.5 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/summarytable/one_rowgroup_one_summary.txt b/test/references/summarytable/one_rowgroup_one_summary.txt new file mode 100644 index 0000000..759051a --- /dev/null +++ b/test/references/summarytable/one_rowgroup_one_summary.txt @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + +
Group 1value1
amean2.5
bmean6.5
\ No newline at end of file diff --git a/test/references/summarytable/one_rowgroup_one_summary.typ.txt b/test/references/summarytable/one_rowgroup_one_summary.typ.txt new file mode 100644 index 0000000..a83464b --- /dev/null +++ b/test/references/summarytable/one_rowgroup_one_summary.typ.txt @@ -0,0 +1,23 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 3, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: left + top)[*Group 1*], + cellx(colspan: 1, x: 2, y: 0, align: center + top)[*value1*], + hlinex(y: 1, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 1, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 1, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 1, align: center + top)[2.5], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 2, align: center + top)[6.5], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/summarytable/one_rowgroup_two_summaries.latex.txt b/test/references/summarytable/one_rowgroup_two_summaries.latex.txt new file mode 100644 index 0000000..62aff86 --- /dev/null +++ b/test/references/summarytable/one_rowgroup_two_summaries.latex.txt @@ -0,0 +1,22 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{3}{llc}} +\toprule +\textbf{Group 1} & & \textbf{value1} \\ +\midrule +\multirow[t]{2}{*}{a} & \textbf{mean} & 2.5 \\ + & \textbf{std} & 1.29 \\ +\multirow[t]{2}{*}{b} & \textbf{mean} & 6.5 \\ + & \textbf{std} & 1.29 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/summarytable/one_rowgroup_two_summaries.txt b/test/references/summarytable/one_rowgroup_two_summaries.txt new file mode 100644 index 0000000..90206df --- /dev/null +++ b/test/references/summarytable/one_rowgroup_two_summaries.txt @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Group 1value1
amean2.5
std1.29
bmean6.5
std1.29
\ No newline at end of file diff --git a/test/references/summarytable/one_rowgroup_two_summaries.typ.txt b/test/references/summarytable/one_rowgroup_two_summaries.typ.txt new file mode 100644 index 0000000..32b030c --- /dev/null +++ b/test/references/summarytable/one_rowgroup_two_summaries.typ.txt @@ -0,0 +1,27 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 3, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 0, y: 0, align: left + top)[*Group 1*], + cellx(colspan: 1, x: 2, y: 0, align: center + top)[*value1*], + hlinex(y: 1, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 1, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 1, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 1, align: center + top)[2.5], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*std*], + cellx(colspan: 1, x: 2, y: 2, align: center + top)[1.29], + cellx(colspan: 1, x: 0, y: 3, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[*mean*], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[6.5], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[*std*], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[1.29], + hlinex(y: 5, stroke: 1pt), + +) +] diff --git a/test/references/summarytable/sort_false.latex.txt b/test/references/summarytable/sort_false.latex.txt new file mode 100644 index 0000000..e017f73 --- /dev/null +++ b/test/references/summarytable/sort_false.latex.txt @@ -0,0 +1,25 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{6}{lllccc}} +\toprule + & & & \multicolumn{3}{c}{\textbf{parameters}} \\ +\cmidrule{4-6} + & & & T\textsubscript{max} & C\textsuperscript{max} & \begin{tabular}{@{}c@{}}One Line \\ Another Line\end{tabular} \\ +\textbf{group2} & \textbf{group} & & \multicolumn{3}{c}{\textbf{value}} \\ +\midrule +\multirow[t]{2}{*}{1} & 1 & \textbf{mean} & 1 & 5 & 9 \\ + & 3 & \textbf{mean} & 3 & 7 & 11 \\ +\multirow[t]{2}{*}{2} & 2 & \textbf{mean} & 2 & 6 & 10 \\ + & 4 & \textbf{mean} & 4 & 8 & 12 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/summarytable/sort_false.txt b/test/references/summarytable/sort_false.txt new file mode 100644 index 0000000..8593eb5 --- /dev/null +++ b/test/references/summarytable/sort_false.txt @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
parameters
TmaxCmaxOne Line
Another Line
group2groupvalue
11mean159
3mean3711
22mean2610
4mean4812
\ No newline at end of file diff --git a/test/references/summarytable/sort_false.typ.txt b/test/references/summarytable/sort_false.typ.txt new file mode 100644 index 0000000..2a14eaf --- /dev/null +++ b/test/references/summarytable/sort_false.typ.txt @@ -0,0 +1,45 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 6, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 3, x: 3, y: 0, align: center + top)[*parameters*], + hlinex(y: 1, start: 3, end: 6, stroke: 0.75pt), + cellx(colspan: 1, x: 3, y: 1, align: center + top)[T#sub[max]], + cellx(colspan: 1, x: 4, y: 1, align: center + top)[C#super[max]], + cellx(colspan: 1, x: 5, y: 1, align: center + top)[One Line #linebreak() Another Line], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*group2*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group*], + cellx(colspan: 3, x: 3, y: 2, align: center + top)[*value*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[1], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[1], + cellx(colspan: 1, x: 2, y: 3, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[5], + cellx(colspan: 1, x: 5, y: 3, align: center + top)[9], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[3], + cellx(colspan: 1, x: 2, y: 4, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[3], + cellx(colspan: 1, x: 4, y: 4, align: center + top)[7], + cellx(colspan: 1, x: 5, y: 4, align: center + top)[11], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[2], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[2], + cellx(colspan: 1, x: 2, y: 5, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 4, y: 5, align: center + top)[6], + cellx(colspan: 1, x: 5, y: 5, align: center + top)[10], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[4], + cellx(colspan: 1, x: 2, y: 6, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[4], + cellx(colspan: 1, x: 4, y: 6, align: center + top)[8], + cellx(colspan: 1, x: 5, y: 6, align: center + top)[12], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries.latex.txt b/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries.latex.txt new file mode 100644 index 0000000..3876bc2 --- /dev/null +++ b/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries.latex.txt @@ -0,0 +1,29 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{5}{lllcc}} +\toprule + & & & \multicolumn{2}{c}{\textbf{Group 3}} \\ +\cmidrule{4-5} + & & & c & d \\ +\textbf{Group 1} & \textbf{group2} & & \multicolumn{2}{c}{\textbf{value1}} \\ +\midrule +\multirow[t]{4}{*}{a} & \multirow[t]{2}{*}{e} & \textbf{mean} & 1 & 3 \\ + & & \textbf{std} & NaN & NaN \\ + & \multirow[t]{2}{*}{f} & \textbf{mean} & 2 & 4 \\ + & & \textbf{std} & NaN & NaN \\ +\multirow[t]{4}{*}{b} & \multirow[t]{2}{*}{e} & \textbf{mean} & 5 & 7 \\ + & & \textbf{std} & NaN & NaN \\ + & \multirow[t]{2}{*}{f} & \textbf{mean} & 6 & 8 \\ + & & \textbf{std} & NaN & NaN \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries.txt b/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries.txt new file mode 100644 index 0000000..1eb5fe8 --- /dev/null +++ b/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries.txt @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Group 3
cd
Group 1group2value1
aemean13
stdNaNNaN
fmean24
stdNaNNaN
bemean57
stdNaNNaN
fmean68
stdNaNNaN
\ No newline at end of file diff --git a/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries.typ.txt b/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries.typ.txt new file mode 100644 index 0000000..48e8ef2 --- /dev/null +++ b/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries.typ.txt @@ -0,0 +1,52 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 5, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 3, y: 0, align: center + top)[*Group 3*], + hlinex(y: 1, start: 3, end: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 3, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 4, y: 1, align: center + top)[d], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*Group 1*], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[*group2*], + cellx(colspan: 2, x: 3, y: 2, align: center + top)[*value1*], + hlinex(y: 3, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 3, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 3, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 3, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[1], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[3], + cellx(colspan: 1, x: 2, y: 4, align: left + top)[*std*], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[NaN], + cellx(colspan: 1, x: 4, y: 4, align: center + top)[NaN], + cellx(colspan: 1, x: 1, y: 5, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 5, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[2], + cellx(colspan: 1, x: 4, y: 5, align: center + top)[4], + cellx(colspan: 1, x: 2, y: 6, align: left + top)[*std*], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[NaN], + cellx(colspan: 1, x: 4, y: 6, align: center + top)[NaN], + cellx(colspan: 1, x: 0, y: 7, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 7, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 7, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 7, align: center + top)[5], + cellx(colspan: 1, x: 4, y: 7, align: center + top)[7], + cellx(colspan: 1, x: 2, y: 8, align: left + top)[*std*], + cellx(colspan: 1, x: 3, y: 8, align: center + top)[NaN], + cellx(colspan: 1, x: 4, y: 8, align: center + top)[NaN], + cellx(colspan: 1, x: 1, y: 9, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 9, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 9, align: center + top)[6], + cellx(colspan: 1, x: 4, y: 9, align: center + top)[8], + cellx(colspan: 1, x: 2, y: 10, align: left + top)[*std*], + cellx(colspan: 1, x: 3, y: 10, align: center + top)[NaN], + cellx(colspan: 1, x: 4, y: 10, align: center + top)[NaN], + hlinex(y: 11, stroke: 1pt), + +) +] diff --git a/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries_no_header.latex.txt b/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries_no_header.latex.txt new file mode 100644 index 0000000..d94270a --- /dev/null +++ b/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries_no_header.latex.txt @@ -0,0 +1,28 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{5}{lllcc}} +\toprule + & & & \multicolumn{2}{c}{\textbf{Group 3}} \\ +\cmidrule{4-5} +\textbf{Group 1} & \textbf{group2} & & c & d \\ +\midrule +\multirow[t]{4}{*}{a} & \multirow[t]{2}{*}{e} & \textbf{mean} & 1 & 3 \\ + & & \textbf{std} & NaN & NaN \\ + & \multirow[t]{2}{*}{f} & \textbf{mean} & 2 & 4 \\ + & & \textbf{std} & NaN & NaN \\ +\multirow[t]{4}{*}{b} & \multirow[t]{2}{*}{e} & \textbf{mean} & 5 & 7 \\ + & & \textbf{std} & NaN & NaN \\ + & \multirow[t]{2}{*}{f} & \textbf{mean} & 6 & 8 \\ + & & \textbf{std} & NaN & NaN \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries_no_header.txt b/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries_no_header.txt new file mode 100644 index 0000000..57fab66 --- /dev/null +++ b/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries_no_header.txt @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Group 3
Group 1group2cd
aemean13
stdNaNNaN
fmean24
stdNaNNaN
bemean57
stdNaNNaN
fmean68
stdNaNNaN
\ No newline at end of file diff --git a/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries_no_header.typ.txt b/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries_no_header.typ.txt new file mode 100644 index 0000000..f2980a7 --- /dev/null +++ b/test/references/summarytable/two_rowgroups_one_colgroup_two_summaries_no_header.typ.txt @@ -0,0 +1,51 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 5, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 3, y: 0, align: center + top)[*Group 3*], + hlinex(y: 1, start: 3, end: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 1, align: left + top)[*Group 1*], + cellx(colspan: 1, x: 1, y: 1, align: left + top)[*group2*], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[c], + cellx(colspan: 1, x: 4, y: 1, align: center + top)[d], + hlinex(y: 2, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 2, align: left + top)[a], + cellx(colspan: 1, x: 1, y: 2, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 2, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 2, align: center + top)[1], + cellx(colspan: 1, x: 4, y: 2, align: center + top)[3], + cellx(colspan: 1, x: 2, y: 3, align: left + top)[*std*], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[NaN], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[NaN], + cellx(colspan: 1, x: 1, y: 4, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 4, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[2], + cellx(colspan: 1, x: 4, y: 4, align: center + top)[4], + cellx(colspan: 1, x: 2, y: 5, align: left + top)[*std*], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[NaN], + cellx(colspan: 1, x: 4, y: 5, align: center + top)[NaN], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[b], + cellx(colspan: 1, x: 1, y: 6, align: left + top)[e], + cellx(colspan: 1, x: 2, y: 6, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[5], + cellx(colspan: 1, x: 4, y: 6, align: center + top)[7], + cellx(colspan: 1, x: 2, y: 7, align: left + top)[*std*], + cellx(colspan: 1, x: 3, y: 7, align: center + top)[NaN], + cellx(colspan: 1, x: 4, y: 7, align: center + top)[NaN], + cellx(colspan: 1, x: 1, y: 8, align: left + top)[f], + cellx(colspan: 1, x: 2, y: 8, align: left + top)[*mean*], + cellx(colspan: 1, x: 3, y: 8, align: center + top)[6], + cellx(colspan: 1, x: 4, y: 8, align: center + top)[8], + cellx(colspan: 1, x: 2, y: 9, align: left + top)[*std*], + cellx(colspan: 1, x: 3, y: 9, align: center + top)[NaN], + cellx(colspan: 1, x: 4, y: 9, align: center + top)[NaN], + hlinex(y: 10, stroke: 1pt), + +) +] diff --git a/test/references/summarytable/two_same_summaries.latex.txt b/test/references/summarytable/two_same_summaries.latex.txt new file mode 100644 index 0000000..a6c3225 --- /dev/null +++ b/test/references/summarytable/two_same_summaries.latex.txt @@ -0,0 +1,20 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{lc}} +\toprule + & \textbf{value1} \\ +\midrule +\textbf{mean} & 4.5 \\ +\textbf{mean} & 4.5 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/summarytable/two_same_summaries.txt b/test/references/summarytable/two_same_summaries.txt new file mode 100644 index 0000000..7ebf937 --- /dev/null +++ b/test/references/summarytable/two_same_summaries.txt @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + +
value1
mean4.5
mean4.5
\ No newline at end of file diff --git a/test/references/summarytable/two_same_summaries.typ.txt b/test/references/summarytable/two_same_summaries.typ.txt new file mode 100644 index 0000000..0cf8f1a --- /dev/null +++ b/test/references/summarytable/two_same_summaries.typ.txt @@ -0,0 +1,20 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 1, y: 0, align: center + top)[*value1*], + hlinex(y: 1, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 1, align: left + top)[*mean*], + cellx(colspan: 1, x: 1, y: 1, align: center + top)[4.5], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*mean*], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[4.5], + hlinex(y: 3, stroke: 1pt), + +) +] diff --git a/test/references/table_one/all_missing_group.latex.txt b/test/references/table_one/all_missing_group.latex.txt new file mode 100644 index 0000000..e95eb36 --- /dev/null +++ b/test/references/table_one/all_missing_group.latex.txt @@ -0,0 +1,28 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{lccc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group}} \\ +\cmidrule{3-4} + & \textbf{Overall} & 1 & 2 \\ +\midrule +\textbf{empty} & & & \\ +\hspace{12.0pt}Mean (SD) & 2 (1) & NC & 2 (1) \\ +\hspace{12.0pt}Median [Min, Max] & 2 [1, 3] & NC & 2 [1, 3] \\ +\hspace{12.0pt}Missing & 3 (50\%) & 3 (100\%) & 0 (0\%) \\ +\bottomrule +\end{tabular} +\begin{tablenotes}[flushleft,para] +\footnotesize +NC - Not computable +\end{tablenotes} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/table_one/all_missing_group.txt b/test/references/table_one/all_missing_group.txt new file mode 100644 index 0000000..61acbe6 --- /dev/null +++ b/test/references/table_one/all_missing_group.txt @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group
Overall12
empty
Mean (SD)2 (1)NC2 (1)
Median [Min, Max]2 [1, 3]NC2 [1, 3]
Missing3 (50%)3 (100%)0 (0%)
NC - Not computable
\ No newline at end of file diff --git a/test/references/table_one/all_missing_group.typ.txt b/test/references/table_one/all_missing_group.typ.txt new file mode 100644 index 0000000..7259851 --- /dev/null +++ b/test/references/table_one/all_missing_group.typ.txt @@ -0,0 +1,33 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 1, align: center + top)[*Overall*], + cellx(colspan: 1, x: 2, y: 1, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[2], + hlinex(y: 2, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*empty*], + cellx(colspan: 1, x: 0, y: 3, align: left + top)[#h(12.0pt)Mean (SD)], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[2 (1)], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[NC], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[2 (1)], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[#h(12.0pt)Median [Min, Max]], + cellx(colspan: 1, x: 1, y: 4, align: center + top)[2 [1, 3]], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[NC], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[2 [1, 3]], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[#h(12.0pt)Missing], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[3 (50%)], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[3 (100%)], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[0 (0%)], + hlinex(y: 6, stroke: 1pt), + cellx(colspan: 4, x: 0, y: 6)[#text(size: 0.8em)[NC - Not computable]], +) +] diff --git a/test/references/table_one/category_with_missing.latex.txt b/test/references/table_one/category_with_missing.latex.txt new file mode 100644 index 0000000..b5dd920 --- /dev/null +++ b/test/references/table_one/category_with_missing.latex.txt @@ -0,0 +1,25 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{lccc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group}} \\ +\cmidrule{3-4} + & \textbf{Overall} & 1 & 2 \\ +\midrule +\textbf{category} & & & \\ +\hspace{12.0pt}a & 1 (12.5\%) & 1 (25\%) & 0 (0\%) \\ +\hspace{12.0pt}b & 3 (37.5\%) & 2 (50\%) & 1 (25\%) \\ +\hspace{12.0pt}c & 3 (37.5\%) & 1 (25\%) & 2 (50\%) \\ +\hspace{12.0pt}Missing & 1 (12.5\%) & 0 (0\%) & 1 (25\%) \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/table_one/category_with_missing.txt b/test/references/table_one/category_with_missing.txt new file mode 100644 index 0000000..fdb7698 --- /dev/null +++ b/test/references/table_one/category_with_missing.txt @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group
Overall12
category
a1 (12.5%)1 (25%)0 (0%)
b3 (37.5%)2 (50%)1 (25%)
c3 (37.5%)1 (25%)2 (50%)
Missing1 (12.5%)0 (0%)1 (25%)
\ No newline at end of file diff --git a/test/references/table_one/category_with_missing.typ.txt b/test/references/table_one/category_with_missing.typ.txt new file mode 100644 index 0000000..65ca1ca --- /dev/null +++ b/test/references/table_one/category_with_missing.typ.txt @@ -0,0 +1,37 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 1, align: center + top)[*Overall*], + cellx(colspan: 1, x: 2, y: 1, align: center + top)[1], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[2], + hlinex(y: 2, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*category*], + cellx(colspan: 1, x: 0, y: 3, align: left + top)[#h(12.0pt)a], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[1 (12.5%)], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[1 (25%)], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[0 (0%)], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[#h(12.0pt)b], + cellx(colspan: 1, x: 1, y: 4, align: center + top)[3 (37.5%)], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2 (50%)], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[1 (25%)], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[#h(12.0pt)c], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[3 (37.5%)], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[1 (25%)], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[2 (50%)], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[#h(12.0pt)Missing], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[1 (12.5%)], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[0 (0%)], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[1 (25%)], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/table_one/natural_sort_order.latex.txt b/test/references/table_one/natural_sort_order.latex.txt new file mode 100644 index 0000000..1154d1e --- /dev/null +++ b/test/references/table_one/natural_sort_order.latex.txt @@ -0,0 +1,23 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{6}{lccccc}} +\toprule + & & \multicolumn{4}{c}{\textbf{dose}} \\ +\cmidrule{3-6} + & \textbf{Overall} & 1 mg & 5 mg & 10 mg & 50 mg \\ +\midrule +\textbf{value} & & & & & \\ +\hspace{12.0pt}Mean (SD) & 3 (1.41) & 2.67 (2.08) & 2.67 (1.53) & 4.33 (0.577) & 2.33 (0.577) \\ +\hspace{12.0pt}Median [Min, Max] & 3 [1, 5] & 2 [1, 5] & 3 [1, 4] & 4 [4, 5] & 2 [2, 3] \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/table_one/natural_sort_order.txt b/test/references/table_one/natural_sort_order.txt new file mode 100644 index 0000000..b1d51ff --- /dev/null +++ b/test/references/table_one/natural_sort_order.txt @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
dose
Overall1 mg5 mg10 mg50 mg
value
Mean (SD)3 (1.41)2.67 (2.08)2.67 (1.53)4.33 (0.577)2.33 (0.577)
Median [Min, Max]3 [1, 5]2 [1, 5]3 [1, 4]4 [4, 5]2 [2, 3]
\ No newline at end of file diff --git a/test/references/table_one/natural_sort_order.typ.txt b/test/references/table_one/natural_sort_order.typ.txt new file mode 100644 index 0000000..92f54a4 --- /dev/null +++ b/test/references/table_one/natural_sort_order.typ.txt @@ -0,0 +1,35 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 6, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 4, x: 2, y: 0, align: center + top)[*dose*], + hlinex(y: 1, start: 2, end: 6, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 1, align: center + top)[*Overall*], + cellx(colspan: 1, x: 2, y: 1, align: center + top)[1 mg], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[5 mg], + cellx(colspan: 1, x: 4, y: 1, align: center + top)[10 mg], + cellx(colspan: 1, x: 5, y: 1, align: center + top)[50 mg], + hlinex(y: 2, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*value*], + cellx(colspan: 1, x: 0, y: 3, align: left + top)[#h(12.0pt)Mean (SD)], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[3 (1.41)], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[2.67 (2.08)], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[2.67 (1.53)], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[4.33 (0.577)], + cellx(colspan: 1, x: 5, y: 3, align: center + top)[2.33 (0.577)], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[#h(12.0pt)Median [Min, Max]], + cellx(colspan: 1, x: 1, y: 4, align: center + top)[3 [1, 5]], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2 [1, 5]], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[3 [1, 4]], + cellx(colspan: 1, x: 4, y: 4, align: center + top)[4 [4, 5]], + cellx(colspan: 1, x: 5, y: 4, align: center + top)[2 [2, 3]], + hlinex(y: 5, stroke: 1pt), + +) +] diff --git a/test/references/table_one/nested_spans_bad_sort.latex.txt b/test/references/table_one/nested_spans_bad_sort.latex.txt new file mode 100644 index 0000000..8cc28ad --- /dev/null +++ b/test/references/table_one/nested_spans_bad_sort.latex.txt @@ -0,0 +1,26 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{6}{lccccc}} +\toprule + & & \multicolumn{4}{c}{\textbf{y}} \\ +\cmidrule{3-6} + & & A & \multicolumn{2}{c}{B} & A \\ + & & \textbf{z} & \multicolumn{2}{c}{\textbf{z}} & \textbf{z} \\ +\cmidrule{3-3}\cmidrule{4-5}\cmidrule{6-6} + & \textbf{Overall} & C & C & D & D \\ +\midrule +\textbf{x} & & & & & \\ +\hspace{12.0pt}Mean (SD) & 3.5 (1.87) & 1.5 (0.707) & 3 (NaN) & 4.5 (0.707) & 6 (NaN) \\ +\hspace{12.0pt}Median [Min, Max] & 3.5 [1, 6] & 1.5 [1, 2] & 3 [3, 3] & 4.5 [4, 5] & 6 [6, 6] \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/table_one/nested_spans_bad_sort.txt b/test/references/table_one/nested_spans_bad_sort.txt new file mode 100644 index 0000000..0131661 --- /dev/null +++ b/test/references/table_one/nested_spans_bad_sort.txt @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
y
ABA
zzz
OverallCCDD
x
Mean (SD)3.5 (1.87)1.5 (0.707)3 (NaN)4.5 (0.707)6 (NaN)
Median [Min, Max]3.5 [1, 6]1.5 [1, 2]3 [3, 3]4.5 [4, 5]6 [6, 6]
\ No newline at end of file diff --git a/test/references/table_one/nested_spans_bad_sort.typ.txt b/test/references/table_one/nested_spans_bad_sort.typ.txt new file mode 100644 index 0000000..0629055 --- /dev/null +++ b/test/references/table_one/nested_spans_bad_sort.typ.txt @@ -0,0 +1,44 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 6, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 4, x: 2, y: 0, align: center + top)[*y*], + hlinex(y: 1, start: 2, end: 6, stroke: 0.75pt), + cellx(colspan: 1, x: 2, y: 1, align: center + top)[A], + cellx(colspan: 2, x: 3, y: 1, align: center + top)[B], + cellx(colspan: 1, x: 5, y: 1, align: center + top)[A], + cellx(colspan: 1, x: 2, y: 2, align: center + top)[*z*], + hlinex(y: 3, start: 2, end: 3, stroke: 0.75pt), + cellx(colspan: 2, x: 3, y: 2, align: center + top)[*z*], + hlinex(y: 3, start: 3, end: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 5, y: 2, align: center + top)[*z*], + hlinex(y: 3, start: 5, end: 6, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 3, align: center + top)[*Overall*], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[C], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[C], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[D], + cellx(colspan: 1, x: 5, y: 3, align: center + top)[D], + hlinex(y: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*x*], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[#h(12.0pt)Mean (SD)], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[3.5 (1.87)], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[1.5 (0.707)], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[3 (NaN)], + cellx(colspan: 1, x: 4, y: 5, align: center + top)[4.5 (0.707)], + cellx(colspan: 1, x: 5, y: 5, align: center + top)[6 (NaN)], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[#h(12.0pt)Median [Min, Max]], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[3.5 [1, 6]], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[1.5 [1, 2]], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[3 [3, 3]], + cellx(colspan: 1, x: 4, y: 6, align: center + top)[4.5 [4, 5]], + cellx(colspan: 1, x: 5, y: 6, align: center + top)[6 [6, 6]], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/table_one/one_row.latex.txt b/test/references/table_one/one_row.latex.txt new file mode 100644 index 0000000..0ba99a2 --- /dev/null +++ b/test/references/table_one/one_row.latex.txt @@ -0,0 +1,21 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{lc}} +\toprule + & \textbf{Overall} \\ +\midrule +\textbf{value1} & \\ +\hspace{12.0pt}Mean (SD) & 4.5 (2.45) \\ +\hspace{12.0pt}Median [Min, Max] & 4.5 [1, 8] \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/table_one/one_row.txt b/test/references/table_one/one_row.txt new file mode 100644 index 0000000..4ee0cc9 --- /dev/null +++ b/test/references/table_one/one_row.txt @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + +
Overall
value1
Mean (SD)4.5 (2.45)
Median [Min, Max]4.5 [1, 8]
\ No newline at end of file diff --git a/test/references/table_one/one_row.typ.txt b/test/references/table_one/one_row.typ.txt new file mode 100644 index 0000000..3f85cd8 --- /dev/null +++ b/test/references/table_one/one_row.typ.txt @@ -0,0 +1,21 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 1, y: 0, align: center + top)[*Overall*], + hlinex(y: 1, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 1, align: left + top)[*value1*], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[#h(12.0pt)Mean (SD)], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[4.5 (2.45)], + cellx(colspan: 1, x: 0, y: 3, align: left + top)[#h(12.0pt)Median [Min, Max]], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[4.5 [1, 8]], + hlinex(y: 4, stroke: 1pt), + +) +] diff --git a/test/references/table_one/one_row_no_group.txt b/test/references/table_one/one_row_no_group.txt new file mode 100644 index 0000000..8bd687b --- /dev/null +++ b/test/references/table_one/one_row_no_group.txt @@ -0,0 +1,63 @@ + +
Overall
value1
Mean (SD)4.5 (2.4)
Median [Min, Max]4.5 [1.0, 8.0]
\ No newline at end of file diff --git a/test/references/table_one/one_row_one_group_pvalues_tests_confints.latex.txt b/test/references/table_one/one_row_one_group_pvalues_tests_confints.latex.txt new file mode 100644 index 0000000..f04627b --- /dev/null +++ b/test/references/table_one/one_row_one_group_pvalues_tests_confints.latex.txt @@ -0,0 +1,23 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{7}{lcccccc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group1}} & & & \\ +\cmidrule{3-4} + & \textbf{Overall} & a & b & \textbf{P-Value} & \textbf{Test} & \textbf{CI} \\ +\midrule +\textbf{value1} & & & & 0.005 & UnequalVarianceTTest & (-6.234, -1.766) \\ +\hspace{12.0pt}Mean (SD) & 4.5 (2.45) & 2.5 (1.29) & 6.5 (1.29) & & & \\ +\hspace{12.0pt}Median [Min, Max] & 4.5 [1, 8] & 2.5 [1, 4] & 6.5 [5, 8] & & & \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/table_one/one_row_one_group_pvalues_tests_confints.txt b/test/references/table_one/one_row_one_group_pvalues_tests_confints.txt new file mode 100644 index 0000000..ac1cc80 --- /dev/null +++ b/test/references/table_one/one_row_one_group_pvalues_tests_confints.txt @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
OverallabP-ValueTestCI
value10.005UnequalVarianceTTest(-6.234, -1.766)
Mean (SD)4.5 (2.45)2.5 (1.29)6.5 (1.29)
Median [Min, Max]4.5 [1, 8]2.5 [1, 4]6.5 [5, 8]
\ No newline at end of file diff --git a/test/references/table_one/one_row_one_group_pvalues_tests_confints.typ.txt b/test/references/table_one/one_row_one_group_pvalues_tests_confints.typ.txt new file mode 100644 index 0000000..a2390e3 --- /dev/null +++ b/test/references/table_one/one_row_one_group_pvalues_tests_confints.typ.txt @@ -0,0 +1,35 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 7, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group1*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 1, align: center + top)[*Overall*], + cellx(colspan: 1, x: 2, y: 1, align: center + top)[a], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[b], + cellx(colspan: 1, x: 4, y: 1, align: center + top)[*P-Value*], + cellx(colspan: 1, x: 5, y: 1, align: center + top)[*Test*], + cellx(colspan: 1, x: 6, y: 1, align: center + top)[*CI*], + hlinex(y: 2, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*value1*], + cellx(colspan: 1, x: 4, y: 2, align: center + top)[0.005], + cellx(colspan: 1, x: 5, y: 2, align: center + top)[UnequalVarianceTTest], + cellx(colspan: 1, x: 6, y: 2, align: center + top)[(-6.234, -1.766)], + cellx(colspan: 1, x: 0, y: 3, align: left + top)[#h(12.0pt)Mean (SD)], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[4.5 (2.45)], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[2.5 (1.29)], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[6.5 (1.29)], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[#h(12.0pt)Median [Min, Max]], + cellx(colspan: 1, x: 1, y: 4, align: center + top)[4.5 [1, 8]], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2.5 [1, 4]], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[6.5 [5, 8]], + hlinex(y: 5, stroke: 1pt), + +) +] diff --git a/test/references/table_one/one_row_renamed.latex.txt b/test/references/table_one/one_row_renamed.latex.txt new file mode 100644 index 0000000..c6391d3 --- /dev/null +++ b/test/references/table_one/one_row_renamed.latex.txt @@ -0,0 +1,21 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{lc}} +\toprule + & \textbf{Overall} \\ +\midrule +\textbf{Value 1} & \\ +\hspace{12.0pt}Mean (SD) & 4.5 (2.45) \\ +\hspace{12.0pt}Median [Min, Max] & 4.5 [1, 8] \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/table_one/one_row_renamed.txt b/test/references/table_one/one_row_renamed.txt new file mode 100644 index 0000000..3f58307 --- /dev/null +++ b/test/references/table_one/one_row_renamed.txt @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + +
Overall
Value 1
Mean (SD)4.5 (2.45)
Median [Min, Max]4.5 [1, 8]
\ No newline at end of file diff --git a/test/references/table_one/one_row_renamed.typ.txt b/test/references/table_one/one_row_renamed.typ.txt new file mode 100644 index 0000000..7daf7c8 --- /dev/null +++ b/test/references/table_one/one_row_renamed.typ.txt @@ -0,0 +1,21 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 1, y: 0, align: center + top)[*Overall*], + hlinex(y: 1, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 1, align: left + top)[*Value 1*], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[#h(12.0pt)Mean (SD)], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[4.5 (2.45)], + cellx(colspan: 1, x: 0, y: 3, align: left + top)[#h(12.0pt)Median [Min, Max]], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[4.5 [1, 8]], + hlinex(y: 4, stroke: 1pt), + +) +] diff --git a/test/references/table_one/one_row_two_groups_pvalues.latex.txt b/test/references/table_one/one_row_two_groups_pvalues.latex.txt new file mode 100644 index 0000000..3e25f1b --- /dev/null +++ b/test/references/table_one/one_row_two_groups_pvalues.latex.txt @@ -0,0 +1,26 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{8}{lccccccc}} +\toprule + & & \multicolumn{4}{c}{\textbf{group1}} & & \\ +\cmidrule{3-6} + & & \multicolumn{2}{c}{a} & \multicolumn{2}{c}{b} & & \\ + & & \multicolumn{2}{c}{\textbf{group2}} & \multicolumn{2}{c}{\textbf{group2}} & & \\ +\cmidrule{3-4}\cmidrule{5-6} + & \textbf{Overall} & e & f & e & f & \textbf{P-Value} & \textbf{Test} \\ +\midrule +\textbf{value1} & & & & & & 0.063 & UnequalVarianceTTest \\ +\hspace{12.0pt}Mean (SD) & 4.5 (2.45) & 2 (1.41) & 3 (1.41) & 6 (1.41) & 7 (1.41) & & \\ +\hspace{12.0pt}Median [Min, Max] & 4.5 [1, 8] & 2 [1, 3] & 3 [2, 4] & 6 [5, 7] & 7 [6, 8] & & \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/table_one/one_row_two_groups_pvalues.txt b/test/references/table_one/one_row_two_groups_pvalues.txt new file mode 100644 index 0000000..ce174b3 --- /dev/null +++ b/test/references/table_one/one_row_two_groups_pvalues.txt @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
ab
group2group2
OverallefefP-ValueTest
value10.063UnequalVarianceTTest
Mean (SD)4.5 (2.45)2 (1.41)3 (1.41)6 (1.41)7 (1.41)
Median [Min, Max]4.5 [1, 8]2 [1, 3]3 [2, 4]6 [5, 7]7 [6, 8]
\ No newline at end of file diff --git a/test/references/table_one/one_row_two_groups_pvalues.typ.txt b/test/references/table_one/one_row_two_groups_pvalues.typ.txt new file mode 100644 index 0000000..52453cd --- /dev/null +++ b/test/references/table_one/one_row_two_groups_pvalues.typ.txt @@ -0,0 +1,45 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 8, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 4, x: 2, y: 0, align: center + top)[*group1*], + hlinex(y: 1, start: 2, end: 6, stroke: 0.75pt), + cellx(colspan: 2, x: 2, y: 1, align: center + top)[a], + cellx(colspan: 2, x: 4, y: 1, align: center + top)[b], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 2, x: 4, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 4, end: 6, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 3, align: center + top)[*Overall*], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 5, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 6, y: 3, align: center + top)[*P-Value*], + cellx(colspan: 1, x: 7, y: 3, align: center + top)[*Test*], + hlinex(y: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*value1*], + cellx(colspan: 1, x: 6, y: 4, align: center + top)[0.063], + cellx(colspan: 1, x: 7, y: 4, align: center + top)[UnequalVarianceTTest], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[#h(12.0pt)Mean (SD)], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[4.5 (2.45)], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[2 (1.41)], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[3 (1.41)], + cellx(colspan: 1, x: 4, y: 5, align: center + top)[6 (1.41)], + cellx(colspan: 1, x: 5, y: 5, align: center + top)[7 (1.41)], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[#h(12.0pt)Median [Min, Max]], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[4.5 [1, 8]], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[2 [1, 3]], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[3 [2, 4]], + cellx(colspan: 1, x: 4, y: 6, align: center + top)[6 [5, 7]], + cellx(colspan: 1, x: 5, y: 6, align: center + top)[7 [6, 8]], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/references/table_one/sort_false.latex.txt b/test/references/table_one/sort_false.latex.txt new file mode 100644 index 0000000..3f88dc1 --- /dev/null +++ b/test/references/table_one/sort_false.latex.txt @@ -0,0 +1,23 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{5}{lcccc}} +\toprule + & & \multicolumn{3}{c}{\textbf{parameters}} \\ +\cmidrule{3-5} + & \textbf{Overall} & T\textsubscript{max} & C\textsuperscript{max} & \begin{tabular}{@{}c@{}}One Line \\ Another Line\end{tabular} \\ +\midrule +\textbf{value} & & & & \\ +\hspace{12.0pt}Mean (SD) & 6.5 (3.61) & 2.5 (1.29) & 6.5 (1.29) & 10.5 (1.29) \\ +\hspace{12.0pt}Median [Min, Max] & 6.5 [1, 12] & 2.5 [1, 4] & 6.5 [5, 8] & 10.5 [9, 12] \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/table_one/sort_false.txt b/test/references/table_one/sort_false.txt new file mode 100644 index 0000000..4f4c6f4 --- /dev/null +++ b/test/references/table_one/sort_false.txt @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
parameters
OverallTmaxCmaxOne Line
Another Line
value
Mean (SD)6.5 (3.61)2.5 (1.29)6.5 (1.29)10.5 (1.29)
Median [Min, Max]6.5 [1, 12]2.5 [1, 4]6.5 [5, 8]10.5 [9, 12]
\ No newline at end of file diff --git a/test/references/table_one/sort_false.typ.txt b/test/references/table_one/sort_false.typ.txt new file mode 100644 index 0000000..5d7b88c --- /dev/null +++ b/test/references/table_one/sort_false.typ.txt @@ -0,0 +1,32 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 5, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 3, x: 2, y: 0, align: center + top)[*parameters*], + hlinex(y: 1, start: 2, end: 5, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 1, align: center + top)[*Overall*], + cellx(colspan: 1, x: 2, y: 1, align: center + top)[T#sub[max]], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[C#super[max]], + cellx(colspan: 1, x: 4, y: 1, align: center + top)[One Line #linebreak() Another Line], + hlinex(y: 2, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*value*], + cellx(colspan: 1, x: 0, y: 3, align: left + top)[#h(12.0pt)Mean (SD)], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[6.5 (3.61)], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[2.5 (1.29)], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[6.5 (1.29)], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[10.5 (1.29)], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[#h(12.0pt)Median [Min, Max]], + cellx(colspan: 1, x: 1, y: 4, align: center + top)[6.5 [1, 12]], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2.5 [1, 4]], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[6.5 [5, 8]], + cellx(colspan: 1, x: 4, y: 4, align: center + top)[10.5 [9, 12]], + hlinex(y: 5, stroke: 1pt), + +) +] diff --git a/test/references/table_one/two_rows.latex.txt b/test/references/table_one/two_rows.latex.txt new file mode 100644 index 0000000..ec28d5d --- /dev/null +++ b/test/references/table_one/two_rows.latex.txt @@ -0,0 +1,25 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{lc}} +\toprule + & \textbf{Overall} \\ +\midrule +\textbf{value1} & \\ +\hspace{12.0pt}Mean (SD) & 4.5 (2.45) \\ +\hspace{12.0pt}Median [Min, Max] & 4.5 [1, 8] \\ +\textbf{value2} & \\ +\hspace{12.0pt}a & 3 (37.5\%) \\ +\hspace{12.0pt}b & 3 (37.5\%) \\ +\hspace{12.0pt}c & 2 (25\%) \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/table_one/two_rows.txt b/test/references/table_one/two_rows.txt new file mode 100644 index 0000000..c9381dd --- /dev/null +++ b/test/references/table_one/two_rows.txt @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Overall
value1
Mean (SD)4.5 (2.45)
Median [Min, Max]4.5 [1, 8]
value2
a3 (37.5%)
b3 (37.5%)
c2 (25%)
\ No newline at end of file diff --git a/test/references/table_one/two_rows.typ.txt b/test/references/table_one/two_rows.typ.txt new file mode 100644 index 0000000..2e793d0 --- /dev/null +++ b/test/references/table_one/two_rows.typ.txt @@ -0,0 +1,28 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 1, y: 0, align: center + top)[*Overall*], + hlinex(y: 1, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 1, align: left + top)[*value1*], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[#h(12.0pt)Mean (SD)], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[4.5 (2.45)], + cellx(colspan: 1, x: 0, y: 3, align: left + top)[#h(12.0pt)Median [Min, Max]], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[4.5 [1, 8]], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*value2*], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[#h(12.0pt)a], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[3 (37.5%)], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[#h(12.0pt)b], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[3 (37.5%)], + cellx(colspan: 1, x: 0, y: 7, align: left + top)[#h(12.0pt)c], + cellx(colspan: 1, x: 1, y: 7, align: center + top)[2 (25%)], + hlinex(y: 8, stroke: 1pt), + +) +] diff --git a/test/references/table_one/two_rows_no_group.txt b/test/references/table_one/two_rows_no_group.txt new file mode 100644 index 0000000..a812fb0 --- /dev/null +++ b/test/references/table_one/two_rows_no_group.txt @@ -0,0 +1,63 @@ + +
Overall
value1
Mean (SD)4.5 (2.4)
Median [Min, Max]4.5 [1.0, 8.0]
value2
a3 (37.5%)
b3 (37.5%)
c2 (25.0%)
\ No newline at end of file diff --git a/test/references/table_one/two_rows_one_group.latex.txt b/test/references/table_one/two_rows_one_group.latex.txt new file mode 100644 index 0000000..2f3a821 --- /dev/null +++ b/test/references/table_one/two_rows_one_group.latex.txt @@ -0,0 +1,27 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{4}{lccc}} +\toprule + & & \multicolumn{2}{c}{\textbf{group1}} \\ +\cmidrule{3-4} + & \textbf{Overall} & a & b \\ +\midrule +\textbf{value1} & & & \\ +\hspace{12.0pt}Mean (SD) & 4.5 (2.45) & 2.5 (1.29) & 6.5 (1.29) \\ +\hspace{12.0pt}Median [Min, Max] & 4.5 [1, 8] & 2.5 [1, 4] & 6.5 [5, 8] \\ +\textbf{value2} & & & \\ +\hspace{12.0pt}a & 3 (37.5\%) & 2 (50\%) & 1 (25\%) \\ +\hspace{12.0pt}b & 3 (37.5\%) & 1 (25\%) & 2 (50\%) \\ +\hspace{12.0pt}c & 2 (25\%) & 1 (25\%) & 1 (25\%) \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/table_one/two_rows_one_group.txt b/test/references/table_one/two_rows_one_group.txt new file mode 100644 index 0000000..26d621b --- /dev/null +++ b/test/references/table_one/two_rows_one_group.txt @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
Overallab
value1
Mean (SD)4.5 (2.45)2.5 (1.29)6.5 (1.29)
Median [Min, Max]4.5 [1, 8]2.5 [1, 4]6.5 [5, 8]
value2
a3 (37.5%)2 (50%)1 (25%)
b3 (37.5%)1 (25%)2 (50%)
c2 (25%)1 (25%)1 (25%)
\ No newline at end of file diff --git a/test/references/table_one/two_rows_one_group.typ.txt b/test/references/table_one/two_rows_one_group.typ.txt new file mode 100644 index 0000000..a008420 --- /dev/null +++ b/test/references/table_one/two_rows_one_group.typ.txt @@ -0,0 +1,42 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 4, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 2, x: 2, y: 0, align: center + top)[*group1*], + hlinex(y: 1, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 1, align: center + top)[*Overall*], + cellx(colspan: 1, x: 2, y: 1, align: center + top)[a], + cellx(colspan: 1, x: 3, y: 1, align: center + top)[b], + hlinex(y: 2, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 2, align: left + top)[*value1*], + cellx(colspan: 1, x: 0, y: 3, align: left + top)[#h(12.0pt)Mean (SD)], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[4.5 (2.45)], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[2.5 (1.29)], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[6.5 (1.29)], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[#h(12.0pt)Median [Min, Max]], + cellx(colspan: 1, x: 1, y: 4, align: center + top)[4.5 [1, 8]], + cellx(colspan: 1, x: 2, y: 4, align: center + top)[2.5 [1, 4]], + cellx(colspan: 1, x: 3, y: 4, align: center + top)[6.5 [5, 8]], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[*value2*], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[#h(12.0pt)a], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[3 (37.5%)], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[2 (50%)], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[1 (25%)], + cellx(colspan: 1, x: 0, y: 7, align: left + top)[#h(12.0pt)b], + cellx(colspan: 1, x: 1, y: 7, align: center + top)[3 (37.5%)], + cellx(colspan: 1, x: 2, y: 7, align: center + top)[1 (25%)], + cellx(colspan: 1, x: 3, y: 7, align: center + top)[2 (50%)], + cellx(colspan: 1, x: 0, y: 8, align: left + top)[#h(12.0pt)c], + cellx(colspan: 1, x: 1, y: 8, align: center + top)[2 (25%)], + cellx(colspan: 1, x: 2, y: 8, align: center + top)[1 (25%)], + cellx(colspan: 1, x: 3, y: 8, align: center + top)[1 (25%)], + hlinex(y: 9, stroke: 1pt), + +) +] diff --git a/test/references/table_one/two_rows_two_groups.latex.txt b/test/references/table_one/two_rows_two_groups.latex.txt new file mode 100644 index 0000000..b7368cb --- /dev/null +++ b/test/references/table_one/two_rows_two_groups.latex.txt @@ -0,0 +1,30 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{6}{lccccc}} +\toprule + & & \multicolumn{4}{c}{\textbf{group1}} \\ +\cmidrule{3-6} + & & \multicolumn{2}{c}{a} & \multicolumn{2}{c}{b} \\ + & & \multicolumn{2}{c}{\textbf{group2}} & \multicolumn{2}{c}{\textbf{group2}} \\ +\cmidrule{3-4}\cmidrule{5-6} + & \textbf{Overall} & e & f & e & f \\ +\midrule +\textbf{value1} & & & & & \\ +\hspace{12.0pt}Mean (SD) & 4.5 (2.45) & 2 (1.41) & 3 (1.41) & 6 (1.41) & 7 (1.41) \\ +\hspace{12.0pt}Median [Min, Max] & 4.5 [1, 8] & 2 [1, 3] & 3 [2, 4] & 6 [5, 7] & 7 [6, 8] \\ +\textbf{value2} & & & & & \\ +\hspace{12.0pt}a & 3 (37.5\%) & 1 (50\%) & 1 (50\%) & 1 (50\%) & 0 (0\%) \\ +\hspace{12.0pt}b & 3 (37.5\%) & 0 (0\%) & 1 (50\%) & 1 (50\%) & 1 (50\%) \\ +\hspace{12.0pt}c & 2 (25\%) & 1 (50\%) & 0 (0\%) & 0 (0\%) & 1 (50\%) \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/table_one/two_rows_two_groups.txt b/test/references/table_one/two_rows_two_groups.txt new file mode 100644 index 0000000..985baaa --- /dev/null +++ b/test/references/table_one/two_rows_two_groups.txt @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
group1
ab
group2group2
Overallefef
value1
Mean (SD)4.5 (2.45)2 (1.41)3 (1.41)6 (1.41)7 (1.41)
Median [Min, Max]4.5 [1, 8]2 [1, 3]3 [2, 4]6 [5, 7]7 [6, 8]
value2
a3 (37.5%)1 (50%)1 (50%)1 (50%)0 (0%)
b3 (37.5%)0 (0%)1 (50%)1 (50%)1 (50%)
c2 (25%)1 (50%)0 (0%)0 (0%)1 (50%)
\ No newline at end of file diff --git a/test/references/table_one/two_rows_two_groups.typ.txt b/test/references/table_one/two_rows_two_groups.typ.txt new file mode 100644 index 0000000..e0f9508 --- /dev/null +++ b/test/references/table_one/two_rows_two_groups.typ.txt @@ -0,0 +1,60 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 6, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 4, x: 2, y: 0, align: center + top)[*group1*], + hlinex(y: 1, start: 2, end: 6, stroke: 0.75pt), + cellx(colspan: 2, x: 2, y: 1, align: center + top)[a], + cellx(colspan: 2, x: 4, y: 1, align: center + top)[b], + cellx(colspan: 2, x: 2, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 2, end: 4, stroke: 0.75pt), + cellx(colspan: 2, x: 4, y: 2, align: center + top)[*group2*], + hlinex(y: 3, start: 4, end: 6, stroke: 0.75pt), + cellx(colspan: 1, x: 1, y: 3, align: center + top)[*Overall*], + cellx(colspan: 1, x: 2, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 3, y: 3, align: center + top)[f], + cellx(colspan: 1, x: 4, y: 3, align: center + top)[e], + cellx(colspan: 1, x: 5, y: 3, align: center + top)[f], + hlinex(y: 4, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*value1*], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[#h(12.0pt)Mean (SD)], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[4.5 (2.45)], + cellx(colspan: 1, x: 2, y: 5, align: center + top)[2 (1.41)], + cellx(colspan: 1, x: 3, y: 5, align: center + top)[3 (1.41)], + cellx(colspan: 1, x: 4, y: 5, align: center + top)[6 (1.41)], + cellx(colspan: 1, x: 5, y: 5, align: center + top)[7 (1.41)], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[#h(12.0pt)Median [Min, Max]], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[4.5 [1, 8]], + cellx(colspan: 1, x: 2, y: 6, align: center + top)[2 [1, 3]], + cellx(colspan: 1, x: 3, y: 6, align: center + top)[3 [2, 4]], + cellx(colspan: 1, x: 4, y: 6, align: center + top)[6 [5, 7]], + cellx(colspan: 1, x: 5, y: 6, align: center + top)[7 [6, 8]], + cellx(colspan: 1, x: 0, y: 7, align: left + top)[*value2*], + cellx(colspan: 1, x: 0, y: 8, align: left + top)[#h(12.0pt)a], + cellx(colspan: 1, x: 1, y: 8, align: center + top)[3 (37.5%)], + cellx(colspan: 1, x: 2, y: 8, align: center + top)[1 (50%)], + cellx(colspan: 1, x: 3, y: 8, align: center + top)[1 (50%)], + cellx(colspan: 1, x: 4, y: 8, align: center + top)[1 (50%)], + cellx(colspan: 1, x: 5, y: 8, align: center + top)[0 (0%)], + cellx(colspan: 1, x: 0, y: 9, align: left + top)[#h(12.0pt)b], + cellx(colspan: 1, x: 1, y: 9, align: center + top)[3 (37.5%)], + cellx(colspan: 1, x: 2, y: 9, align: center + top)[0 (0%)], + cellx(colspan: 1, x: 3, y: 9, align: center + top)[1 (50%)], + cellx(colspan: 1, x: 4, y: 9, align: center + top)[1 (50%)], + cellx(colspan: 1, x: 5, y: 9, align: center + top)[1 (50%)], + cellx(colspan: 1, x: 0, y: 10, align: left + top)[#h(12.0pt)c], + cellx(colspan: 1, x: 1, y: 10, align: center + top)[2 (25%)], + cellx(colspan: 1, x: 2, y: 10, align: center + top)[1 (50%)], + cellx(colspan: 1, x: 3, y: 10, align: center + top)[0 (0%)], + cellx(colspan: 1, x: 4, y: 10, align: center + top)[0 (0%)], + cellx(colspan: 1, x: 5, y: 10, align: center + top)[1 (50%)], + hlinex(y: 11, stroke: 1pt), + +) +] diff --git a/test/references/table_one/vector_and_function_arguments.latex.txt b/test/references/table_one/vector_and_function_arguments.latex.txt new file mode 100644 index 0000000..92aa182 --- /dev/null +++ b/test/references/table_one/vector_and_function_arguments.latex.txt @@ -0,0 +1,24 @@ +\documentclass{article} +\usepackage{threeparttable} +\usepackage{multirow} +\usepackage{booktabs} +\begin{document} +\begin{table}[!ht] +\setlength\tabcolsep{0pt} +\centering +\begin{threeparttable} +\begin{tabular}{@{\extracolsep{2ex}}*{2}{lc}} +\toprule + & \textbf{Overall} \\ +\midrule +\textbf{value1} & \\ +\hspace{12.0pt}mean & 4.5 \\ +\hspace{12.0pt}SD & 2.45 \\ +\textbf{value1} & \\ +\hspace{12.0pt}Mean & 4.5 \\ +\hspace{12.0pt}SD & 2.45 \\ +\bottomrule +\end{tabular} +\end{threeparttable} +\end{table} +\end{document} \ No newline at end of file diff --git a/test/references/table_one/vector_and_function_arguments.txt b/test/references/table_one/vector_and_function_arguments.txt new file mode 100644 index 0000000..3919c3f --- /dev/null +++ b/test/references/table_one/vector_and_function_arguments.txt @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Overall
value1
mean4.5
SD2.45
value1
Mean4.5
SD2.45
\ No newline at end of file diff --git a/test/references/table_one/vector_and_function_arguments.typ.txt b/test/references/table_one/vector_and_function_arguments.typ.txt new file mode 100644 index 0000000..7bf38c1 --- /dev/null +++ b/test/references/table_one/vector_and_function_arguments.typ.txt @@ -0,0 +1,26 @@ + +#[ +#import "@preview/tablex:0.0.8": tablex, cellx, hlinex + +#tablex( + columns: 2, + auto-vlines: false, + auto-hlines: false, + column-gutter: 0.25em, + hlinex(y: 0, stroke: 1pt), + cellx(colspan: 1, x: 1, y: 0, align: center + top)[*Overall*], + hlinex(y: 1, stroke: 0.75pt), + cellx(colspan: 1, x: 0, y: 1, align: left + top)[*value1*], + cellx(colspan: 1, x: 0, y: 2, align: left + top)[#h(12.0pt)mean], + cellx(colspan: 1, x: 1, y: 2, align: center + top)[4.5], + cellx(colspan: 1, x: 0, y: 3, align: left + top)[#h(12.0pt)SD], + cellx(colspan: 1, x: 1, y: 3, align: center + top)[2.45], + cellx(colspan: 1, x: 0, y: 4, align: left + top)[*value1*], + cellx(colspan: 1, x: 0, y: 5, align: left + top)[#h(12.0pt)Mean], + cellx(colspan: 1, x: 1, y: 5, align: center + top)[4.5], + cellx(colspan: 1, x: 0, y: 6, align: left + top)[#h(12.0pt)SD], + cellx(colspan: 1, x: 1, y: 6, align: center + top)[2.45], + hlinex(y: 7, stroke: 1pt), + +) +] diff --git a/test/runtests.jl b/test/runtests.jl new file mode 100644 index 0000000..0bdbfee --- /dev/null +++ b/test/runtests.jl @@ -0,0 +1,682 @@ +using SummaryTables +using SummaryTables: Table, SpannedCell, to_docx, CellStyle +using SummaryTables: WriteDocx +using SummaryTables: SortingError +const W = WriteDocx +using Test + +using DataFrames +using Statistics +using ReferenceTests +using tectonic_jll +using Typst_jll + +# Wrapper type to dispatch to the right `show` implementations. +struct AsMIME{M} + object +end +Base.show(io::IO, m::AsMIME{M}) where M = show(io, M(), m.object) +function Base.show(io::IO, m::AsMIME{M}) where M <: MIME"text/latex" + print(io, raw""" + \documentclass{article} + \usepackage{threeparttable} + \usepackage{multirow} + \usepackage{booktabs} + \begin{document} + """ + ) + show(io, M(), m.object) + print(io, raw"\end{document}") +end +as_html(object) = AsMIME{MIME"text/html"}(object) +as_latex(object) = AsMIME{MIME"text/latex"}(object) +as_docx(object) = nothing +as_typst(object) = AsMIME{MIME"text/typst"}(object) + +function run_reftest(table, path, func) + path_full = joinpath(@__DIR__, path * extension(func)) + if func === as_docx + # TODO: Real reference tests once WriteDocx has stabilized more + @test_nowarn mktempdir() do dir + tablenode = to_docx(table) + doc = W.Document( + W.Body([ + W.Section([tablenode]) + ]), + W.Styles([]) + ) + W.save(joinpath(dir, "test.docx"), doc) + end + else + @test_reference path_full func(table) + if func === as_latex + latex_render_test(path_full) + end + if func === as_typst + typst_render_test(path_full) + end + end +end + +function latex_render_test(filepath) + mktempdir() do path + texpath = joinpath(path, "input.tex") + pdfpath = joinpath(path, "input.pdf") + cp(filepath, texpath) + tectonic_jll.tectonic() do bin + run(`$bin $texpath`) + end + @test isfile(pdfpath) + end +end + +function typst_render_test(filepath) + mktempdir() do path + typ_path = joinpath(path, "input.typ") + pdfpath = joinpath(path, "input.pdf") + cp(filepath, typ_path) + Typst_jll.typst() do bin + run(`$bin compile $typ_path`) + end + @test isfile(pdfpath) + end +end + +extension(f::typeof(as_html)) = ".txt" +extension(f::typeof(as_latex)) = ".latex.txt" +extension(f::typeof(as_docx)) = ".docx" +extension(f::typeof(as_typst)) = ".typ.txt" + +# This can be removed for `@test_throws` once CI only uses Julia 1.8 and up +macro test_throws_message(message::String, exp) + quote + threw_exception = false + try + $(esc(exp)) + catch e + threw_exception = true + @test occursin($message, e.msg) # Currently only works for ErrorException + end + @test threw_exception + end +end + +@testset "SummaryTables" begin + df = DataFrame( + value1 = 1:8, + value2 = ["a", "b", "c", "a", "b", "c", "a", "b"], + group1 = repeat(["a", "b"], inner = 4), + group3 = repeat(repeat(["c", "d"], inner = 2), 2), + group2 = repeat(["e", "f"], 4), + ) + + df2 = DataFrame( + dose = repeat(["1 mg", "50 mg", "5 mg", "10 mg"], 3), + id = repeat(["5", "50", "8", "10", "1", "80"], inner = 2), + value = [1, 2, 3, 4, 2, 3, 4, 5, 5, 2, 1, 4], + ) + + unsortable_df = let + parameters = repeat([ + Concat("T", Subscript("max")), + Concat("C", Superscript("max")), + Multiline("One Line", "Another Line") + ], inner = 4) + + _df = DataFrame(; + parameters, + value = eachindex(parameters), + group = repeat(1:4, 3), + group2 = repeat(1:2, 6), + ) + + sort!(_df, [:group2, :group]) + end + + @testset for func in [as_html, as_latex, as_docx, as_typst] + reftest(t, path) = @testset "$path" run_reftest(t, path, func) + + @testset "table_one" begin + @test_throws MethodError table_one(df) + + t = table_one(df, [:value1]) + reftest(t, "references/table_one/one_row") + + t = table_one(df, [:value1 => "Value 1"]) + reftest(t, "references/table_one/one_row_renamed") + + t = table_one(df, [:value1, :value2]) + reftest(t, "references/table_one/two_rows") + + t = table_one(df, [:value1, :value2], groupby = [:group1]) + reftest(t, "references/table_one/two_rows_one_group") + + t = table_one(df, [:value1, :value2], groupby = [:group1, :group2]) + reftest(t, "references/table_one/two_rows_two_groups") + + t = table_one(df, [:value1], groupby = [:group1, :group2], show_pvalues = true) + reftest(t, "references/table_one/one_row_two_groups_pvalues") + + t = table_one(df, [:value1], groupby = [:group1], show_pvalues = true, show_tests = true, show_confints = true) + reftest(t, "references/table_one/one_row_one_group_pvalues_tests_confints") + + function summarizer(col) + m = mean(col) + s = std(col) + (m => "Mean", s => "SD") + end + + t = table_one(df, [:value1 => [mean, std => "SD"], :value1 => summarizer]) + reftest(t, "references/table_one/vector_and_function_arguments") + + t = table_one(df2, :value, groupby = :dose) + reftest(t, "references/table_one/natural_sort_order") + + @test_throws SortingError t = table_one(unsortable_df, [:value], groupby = :parameters) + t = table_one(unsortable_df, [:value], groupby = :parameters, sort = false) + reftest(t, "references/table_one/sort_false") + + t = table_one( + (; + empty = Union{Float64,Missing}[missing, missing, missing, 1, 2, 3], + group = [1, 1, 1, 2, 2, 2] + ), + [:empty], + groupby = :group + ) + reftest(t, "references/table_one/all_missing_group") + + data = (; x = [1, 2, 3, 4, 5, 6], y = ["A", "A", "B", "B", "B", "A"], z = ["C", "C", "C", "D", "D", "D"]) + t = table_one(data, :x, groupby = [:y, :z], sort = false) + reftest(t, "references/table_one/nested_spans_bad_sort") + + data = (; + category = ["a", "b", "c", "b", missing, "b", "c", "c"], + group = [1, 1, 1, 1, 2, 2, 2, 2] + ) + t = table_one(data, [:category], groupby = :group) + reftest(t, "references/table_one/category_with_missing") + end + + + @testset "listingtable" begin + @test_throws MethodError listingtable(df) + + @test_throws SummaryTables.TooManyRowsError listingtable(df, :value1) + @test_throws SummaryTables.TooManyRowsError listingtable(df, :value2) + @test_throws SummaryTables.TooManyRowsError listingtable(df, :value1, rows = [:group1]) + + t = listingtable(df, :value1, rows = [:group1, :group2, :group3]) + reftest(t, "references/listingtable/rows_only") + + t = listingtable(df, :value1, cols = [:group1, :group2, :group3]) + reftest(t, "references/listingtable/cols_only") + + t = listingtable(df, :value1, rows = [:group1, :group2], cols = [:group3]) + reftest(t, "references/listingtable/two_rows_one_col") + + t = listingtable(df, :value1, rows = [:group1], cols = [:group2, :group3]) + reftest(t, "references/listingtable/one_row_two_cols") + + t = listingtable(df, :value1, + rows = [:group1, :group2], + cols = [:group3], + summarize_rows = [mean] + ) + reftest(t, "references/listingtable/summarize_end_rows") + + t = listingtable(df, :value1, + rows = [:group1, :group2], + cols = [:group3], + summarize_rows = [mean, std] + ) + reftest(t, "references/listingtable/summarize_end_rows_two_funcs") + + t = listingtable(df, :value1, + rows = [:group1, :group2], + cols = [:group3], + summarize_rows = :group2 => [mean] + ) + reftest(t, "references/listingtable/summarize_last_group_rows") + + t = listingtable(df, :value1, + rows = [:group1, :group2], + cols = [:group3], + summarize_rows = :group1 => [mean] + ) + reftest(t, "references/listingtable/summarize_first_group_rows") + + t = listingtable(df, :value1, + cols = [:group1, :group2], + rows = [:group3], + summarize_cols = [mean, std] + ) + reftest(t, "references/listingtable/summarize_end_cols_two_funcs") + + t = listingtable(df, :value1, + cols = [:group1, :group2], + rows = [:group3], + summarize_cols = :group2 => [mean] + ) + reftest(t, "references/listingtable/summarize_last_group_cols") + + t = listingtable(df, :value1, + cols = [:group1, :group2], + rows = [:group3], + summarize_cols = :group1 => [mean] + ) + reftest(t, "references/listingtable/summarize_first_group_cols") + + t = listingtable(df, :value1 => "Value 1", + rows = [:group1 => "Group 1", :group2 => "Group 2"], + cols = [:group3 => "Group 3"], + summarize_rows = [mean => "Mean", minimum => "Minimum"] + ) + reftest(t, "references/listingtable/renaming") + + t = listingtable(df, :value1 => "Value 1", + rows = [:group1], + cols = [:group2, :group3], + variable_header = false, + ) + reftest(t, "references/listingtable/no_variable_header") + + t = listingtable(df2, :value, rows = [:id, :dose]) + reftest(t, "references/listingtable/natural_sort_order") + + t = listingtable(df2, :value, rows = [:id, :dose], summarize_rows = [mean, mean], summarize_cols = [mean, mean]) + reftest(t, "references/listingtable/two_same_summarizers") + + @test_throws SortingError t = listingtable(unsortable_df, :value, rows = :parameters, cols = [:group2, :group]) + t = listingtable(unsortable_df, :value, cols = :parameters, rows = [:group2, :group], sort = false) + reftest(t, "references/listingtable/sort_false") + + pt = listingtable(df, :value1, Pagination(rows = 1); + rows = [:group1, :group2], cols = :group3) + for (i, page) in enumerate(pt.pages) + reftest(page.table, "references/listingtable/pagination_rows=1_$i") + end + + pt = listingtable(df, :value1, Pagination(rows = 2); + rows = [:group1, :group2], cols = :group3) + for (i, page) in enumerate(pt.pages) + reftest(page.table, "references/listingtable/pagination_rows=2_$i") + end + + pt = listingtable(df, :value1, Pagination(cols = 1); + cols = [:group1, :group2], rows = :group3) + for (i, page) in enumerate(pt.pages) + reftest(page.table, "references/listingtable/pagination_cols=1_$i") + end + + pt = listingtable(df, :value1, Pagination(cols = 2); + cols = [:group1, :group2], rows = :group3) + for (i, page) in enumerate(pt.pages) + reftest(page.table, "references/listingtable/pagination_cols=2_$i") + end + + pt = listingtable(df, :value1, Pagination(rows = 1, cols = 2); + cols = [:group1, :group2], rows = :group3) + for (i, page) in enumerate(pt.pages) + reftest(page.table, "references/listingtable/pagination_rows=1_cols=2_$i") + end + + if func === as_html + reftest(pt, "references/paginated_table_interactive") + end + + pt = listingtable(df, :value1, Pagination(rows = 1); + rows = [:group1, :group2], cols = :group3, summarize_rows = [mean, std]) + @test length(pt.pages) == 1 + for (i, page) in enumerate(pt.pages) + reftest(page.table, "references/listingtable/pagination_rows=2_summarized_$i") + end + + pt = listingtable(df, :value1, Pagination(rows = 1); + rows = [:group1, :group2], cols = :group3, summarize_rows = :group2 => [mean, std]) + @test length(pt.pages) == 4 + for (i, page) in enumerate(pt.pages) + reftest(page.table, "references/listingtable/pagination_rows=2_summarized_grouplevel_2_$i") + end + + pt = listingtable(df, :value1, Pagination(rows = 1); + rows = [:group1, :group2], cols = :group3, summarize_rows = :group1 => [mean, std]) + @test length(pt.pages) == 2 + for (i, page) in enumerate(pt.pages) + reftest(t, "references/listingtable/pagination_rows=2_summarized_grouplevel_1_$i") + end + end + + @testset "summarytable" begin + @test_throws ArgumentError("No summary analyses defined.") t = summarytable(df, :value1) + + t = summarytable(df, :value1, summary = [mean]) + reftest(t, "references/summarytable/no_group_one_summary") + + t = summarytable(df, :value1, summary = [mean, std => "SD"]) + reftest(t, "references/summarytable/no_group_two_summaries") + + t = summarytable(df, :value1, rows = [:group1 => "Group 1"], summary = [mean]) + reftest(t, "references/summarytable/one_rowgroup_one_summary") + + t = summarytable(df, :value1, rows = [:group1 => "Group 1"], summary = [mean, std]) + reftest(t, "references/summarytable/one_rowgroup_two_summaries") + + t = summarytable(df, :value1, rows = [:group1 => "Group 1"], cols = [:group2 => "Group 2"], summary = [mean]) + reftest(t, "references/summarytable/one_rowgroup_one_colgroup_one_summary") + + t = summarytable(df, :value1, rows = [:group1 => "Group 1"], cols = [:group2 => "Group 2"], summary = [mean, std]) + reftest(t, "references/summarytable/one_rowgroup_one_colgroup_two_summaries") + + t = summarytable(df, :value1, rows = [:group1 => "Group 1", :group2], cols = [:group3 => "Group 3"], summary = [mean, std]) + reftest(t, "references/summarytable/two_rowgroups_one_colgroup_two_summaries") + + t = summarytable(df, :value1, rows = [:group1 => "Group 1", :group2], cols = [:group3 => "Group 3"], summary = [mean, std], variable_header = false) + reftest(t, "references/summarytable/two_rowgroups_one_colgroup_two_summaries_no_header") + + t = summarytable(df, :value1, summary = [mean, mean]) + reftest(t, "references/summarytable/two_same_summaries") + + t = summarytable(df2, :value, rows = [:id, :dose], summary = [mean]) + reftest(t, "references/summarytable/natural_sort_order") + + @test_throws SortingError t = summarytable(unsortable_df, :value, rows = :parameters, cols = [:group2, :group], summary = [mean]) + t = summarytable(unsortable_df, :value, cols = :parameters, rows = [:group2, :group], summary = [mean], sort = false) + reftest(t, "references/summarytable/sort_false") + end + + @testset "annotations" begin + t = Table( + [ + SpannedCell(1, 1, Annotated("A", "Note 1")), + SpannedCell(1, 2, Annotated("B", "Note 2")), + SpannedCell(2, 1, Annotated("C", "Note 3")), + SpannedCell(2, 2, Annotated("D", "Note 1")), + ], + nothing, + nothing, + ) + reftest(t, "references/annotations/automatic_annotations") + t = Table( + [ + SpannedCell(1, 1, Annotated("A", "Note 1", label = "X")), + SpannedCell(1, 2, Annotated("B", "Note 2", label = "Y")), + SpannedCell(2, 1, Annotated("C", "Note 3")), + SpannedCell(2, 2, Annotated("D", "Note 4")), + ], + nothing, + nothing, + ) + reftest(t, "references/annotations/manual_annotations") + + t = Table( + [ + SpannedCell(1, 1, Annotated("A", "Note 1", label = "A")), + SpannedCell(1, 2, Annotated("A", "Note 1", label = "B")), + ], + nothing, + nothing, + ) + if func !== as_docx # TODO needs logic rework for this backend + @test_throws_message "Found the same annotation" show(devnull, func(t)) + end + + t = Table( + [ + SpannedCell(1, 1, Annotated("A", "Note 1", label = "A")), + SpannedCell(1, 2, Annotated("A", "Note 2", label = "A")), + ], + nothing, + nothing, + ) + if func !== as_docx # TODO needs logic rework for this backend + @test_throws_message "Found the same label" show(devnull, func(t)) + end + + t = Table( + [ + SpannedCell(1, 1, Annotated(0.1235513245, "Note 1", label = "A")), + ], + nothing, + nothing, + ) + reftest(t, "references/annotations/annotated_float") + end + + @testset "manual footnotes" begin + t = Table( + [ + SpannedCell(1, 1, "Cell 1"), + SpannedCell(1, 2, "Cell 2"), + ], + nothing, + nothing, + footnotes = ["First footnote.", "Second footnote."] + ) + reftest(t, "references/manual_footnotes/footnotes") + + t = Table( + [ + SpannedCell(1, 1, Annotated("Cell 1", "Note 1")), + SpannedCell(1, 2, "Cell 2"), + ], + nothing, + nothing, + footnotes = ["First footnote.", "Second footnote."] + ) + reftest(t, "references/manual_footnotes/footnotes_and_annotated") + end + + @testset "Replace" begin + t = Table( + [ + SpannedCell(1, 1, missing), + SpannedCell(1, 2, missing), + SpannedCell(2, 1, 1), + SpannedCell(2, 2, 2), + ], + nothing, + nothing, + postprocess = [ReplaceMissing()] + ) + reftest(t, "references/replace/replacemissing_default") + + t = Table( + [ + SpannedCell(1, 1, missing), + SpannedCell(1, 2, nothing), + SpannedCell(2, 1, 1), + SpannedCell(2, 2, 2), + ], + nothing, + nothing, + postprocess = [ReplaceMissing(with = "???")] + ) + reftest(t, "references/replace/replacemissing_custom") + + t = Table( + [ + SpannedCell(1, 1, missing), + SpannedCell(1, 2, nothing), + SpannedCell(2, 1, 1), + SpannedCell(2, 2, 2), + ], + nothing, + nothing, + postprocess = [Replace(x -> x isa Int, "an Int was here")] + ) + reftest(t, "references/replace/replace_predicate_value") + + t = Table( + [ + SpannedCell(1, 1, missing), + SpannedCell(1, 2, nothing), + SpannedCell(2, 1, 1), + SpannedCell(2, 2, 2), + ], + nothing, + nothing, + postprocess = [Replace(x -> x isa Int, x -> x + 10)] + ) + reftest(t, "references/replace/replace_predicate_function") + end + + @testset "Global rounding" begin + cells = [ + SpannedCell(1, 1, sqrt(2)), + SpannedCell(1, 2, 12352131.000001), + SpannedCell(2, 1, sqrt(11251231251243123)), + SpannedCell(2, 2, sqrt(0.00000123124)), + SpannedCell(3, 1, Concat(1.23456, " & ", 0.0012345)), + SpannedCell(3, 2, Multiline(1.23456, 0.0012345)), + ] + t = Table( + cells, + nothing, + nothing, + ) + reftest(t, "references/global_rounding/default") + + t = Table( + cells, + nothing, + nothing, + round_mode = nothing, + ) + reftest(t, "references/global_rounding/no_rounding") + + for round_mode in [:auto, :sigdigits, :digits] + for trailing_zeros in [true, false] + for round_digits in [1, 3] + t = Table( + cells, + nothing, + nothing; + round_mode, + trailing_zeros, + round_digits + ) + reftest(t, "references/global_rounding/$(round_mode)_$(trailing_zeros)_$(round_digits)") + end + end + end + end + + @testset "Character escaping" begin + cells = [ + SpannedCell(1, 1, "& % \$ # _ { } ~ ^ \\ < > \" ' ") + ] + t = Table( + cells, + nothing, + nothing, + ) + reftest(t, "references/character_escaping/problematic_characters") + end + + @testset "Styles" begin + cells = [ + SpannedCell(1, 1, "Row 1"), + SpannedCell(2, 1, "Row 2"), + SpannedCell(3, 1, "Row 3"), + SpannedCell(1:3, 2, "top", CellStyle(valign = :top)), + SpannedCell(1:3, 3, "center", CellStyle(valign = :center)), + SpannedCell(1:3, 4, "bottom", CellStyle(valign = :bottom)), + ] + t = Table( + cells, + nothing, + nothing, + ) + reftest(t, "references/styles/valign") + end + + @testset "Row and column gaps" begin + if func !== as_docx # TODO needs logic rework for this backend + t = Table([SpannedCell(1, 1, "Row 1")], rowgaps = [1 => 5.0]) + @test_throws_message "No row gaps allowed for a table with one row" show(devnull, func(t)) + t = Table([SpannedCell(1, 1, "Column 1")], colgaps = [1 => 5.0]) + @test_throws_message "No column gaps allowed for a table with one column" show(devnull, func(t)) + t = Table([SpannedCell(1, 1, "Row 1"), SpannedCell(2, 1, "Row 2")], rowgaps = [1 => 5.0, 2 => 5.0]) + @test_throws_message "A row gap index of 2 is invalid for a table with 2 rows" show(devnull, func(t)) + t = Table([SpannedCell(1, 1, "Column 1"), SpannedCell(1, 2, "Column 2")], colgaps = [1 => 5.0, 2 => 5.0]) + @test_throws_message "A column gap index of 2 is invalid for a table with 2 columns" show(devnull, func(t)) + t = Table([SpannedCell(1, 1, "Row 1"), SpannedCell(2, 1, "Row 2")], rowgaps = [0 => 5.0]) + @test_throws_message "A row gap index of 0 is invalid, must be at least 1" show(devnull, func(t)) + t = Table([SpannedCell(1, 1, "Column 1"), SpannedCell(1, 2, "Column 2")], colgaps = [0 => 5.0]) + @test_throws_message "A column gap index of 0 is invalid, must be at least 1" show(devnull, func(t)) + end + t = Table([SpannedCell(i, j, "$i, $j") for i in 1:4 for j in 1:4], rowgaps = [1 => 4.0, 2 => 8.0], colgaps = [2 => 4.0, 3 => 8.0]) + reftest(t, "references/row_and_column_gaps/singlecell") + t = Table([SpannedCell(2:4, 1, "Spanned rows"), SpannedCell(1, 2:4, "Spanned columns")], rowgaps = [1 => 4.0], colgaps = [2 => 4.0]) + reftest(t, "references/row_and_column_gaps/spanned_cells") + end + end +end + + +@testset "auto rounding" begin + @test SummaryTables.auto_round( 1234567, target_digits = 4) == 1.235e6 + @test SummaryTables.auto_round( 123456.7, target_digits = 4) == 123457 + @test SummaryTables.auto_round( 12345.67, target_digits = 4) == 12346 + @test SummaryTables.auto_round( 1234.567, target_digits = 4) == 1235 + @test SummaryTables.auto_round( 123.4567, target_digits = 4) == 123.5 + @test SummaryTables.auto_round( 12.34567, target_digits = 4) == 12.35 + @test SummaryTables.auto_round( 1.234567, target_digits = 4) == 1.235 + @test SummaryTables.auto_round( 0.1234567, target_digits = 4) == 0.1235 + @test SummaryTables.auto_round( 0.01234567, target_digits = 4) == 0.01235 + @test SummaryTables.auto_round( 0.001234567, target_digits = 4) == 0.001235 + @test SummaryTables.auto_round( 0.0001234567, target_digits = 4) == 0.0001235 + @test SummaryTables.auto_round( 0.00001234567, target_digits = 4) == 1.235e-5 + @test SummaryTables.auto_round( 0.000001234567, target_digits = 4) == 1.235e-6 + @test SummaryTables.auto_round(0.0000001234567, target_digits = 4) == 1.235e-7 + + @test SummaryTables.auto_round(0.1, target_digits = 4) == 0.1 + @test SummaryTables.auto_round(0.0, target_digits = 4) == 0 + @test SummaryTables.auto_round(1.0, target_digits = 4) == 1 +end + +@testset "Formatted float strings" begin + RF = SummaryTables.RoundedFloat + str(rf) = sprint(io -> SummaryTables._showas(io, MIME"text"(), rf)) + + x = 0.006789 + + @test str(RF(x, 3, :auto, true)) == "0.00679" + @test str(RF(x, 3, :sigdigits, true)) == "0.00679" + @test str(RF(x, 3, :digits, true)) == "0.007" + + @test str(RF(x, 2, :auto, true)) == "0.0068" + @test str(RF(x, 2, :sigdigits, true)) == "0.0068" + @test str(RF(x, 2, :digits, true)) == "0.01" + + x = 0.120 + + @test str(RF(x, 3, :auto, true)) == "0.12" + @test str(RF(x, 3, :sigdigits, true)) == "0.12" + @test str(RF(x, 3, :digits, true)) == "0.120" + + @test str(RF(x, 3, :auto, false)) == "0.12" + @test str(RF(x, 3, :sigdigits, false)) == "0.12" + @test str(RF(x, 3, :digits, false)) == "0.12" + + x = 1.0 + + @test str(RF(x, 3, :auto, true)) == "1.0" + @test str(RF(x, 3, :sigdigits, true)) == "1.0" + @test str(RF(x, 3, :digits, true)) == "1.000" + + @test str(RF(x, 3, :auto, false)) == "1" + @test str(RF(x, 3, :sigdigits, false)) == "1" + @test str(RF(x, 3, :digits, false)) == "1" + + x = 12345678.910 + + @test str(RF(x, 3, :auto, true)) == "1.23e7" + @test str(RF(x, 3, :sigdigits, true)) == "1.23e7" + @test str(RF(x, 3, :digits, true)) == "12345678.910" + + @test str(RF(x, 3, :auto, false)) == "1.23e7" + @test str(RF(x, 3, :sigdigits, false)) == "1.23e7" + @test str(RF(x, 3, :digits, false)) == "12345678.91" +end