Advanced Table Features
Learn about multi-column cells, complex layouts, and advanced table techniques.
MultiColumn
Span multiple columns with MultiColumn:
from texer import Tabular, Row, MultiColumn, evaluate
table = Tabular(
columns="lccc",
rows=[
Row(
"Category",
MultiColumn(3, "c", "Measurements"), # Spans 3 columns, centered
),
Row("", "A", "B", "C"), # Sub-headers
Row("Test 1", "1.2", "2.3", "3.4"),
Row("Test 2", "4.5", "5.6", "6.7"),
],
toprule=True,
bottomrule=True,
)
print(evaluate(table, {}))
Output structure:
Category | Measurements (spans 3 columns)
---------|--------------------------------
| A | B | C
Test 1 | 1.2 | 2.3 | 3.4
Test 2 | 4.5 | 5.6 | 6.7
MultiColumn Syntax
MultiColumn(
num_cols, # Number of columns to span
alignment, # 'l', 'c', or 'r'
content, # Cell content (can be Ref, Format, etc.)
)
Examples:
MultiColumn(2, "c", "Centered across 2 columns")
MultiColumn(3, "l", "Left-aligned across 3 columns")
MultiColumn(1, "r", "Right-aligned single column")
Headers with MultiColumn
Create complex multi-level headers:
from texer import Tabular, Row, MultiColumn, Cell, evaluate
table = Tabular(
columns="lcccccc",
rows=[
# First level: Major groupings
Row(
"",
MultiColumn(3, "c", Cell("Group A", bold=True)),
MultiColumn(3, "c", Cell("Group B", bold=True)),
),
# Second level: Sub-headers
Row("", "X", "Y", "Z", "X", "Y", "Z"),
# Data rows
Row("Sample 1", "1", "2", "3", "4", "5", "6"),
Row("Sample 2", "7", "8", "9", "10", "11", "12"),
],
toprule=True,
midrule=False, # Manual control
bottomrule=True,
)
Partial Rules with cmidrule
The cmidrule helper function generates \cmidrule commands from the booktabs package for partial horizontal rules:
from texer import Tabular, Row, MultiColumn, Raw, cmidrule, evaluate
table = Tabular(
columns="lccc",
rows=[
Row("", MultiColumn(3, "c", "Metrics")),
Raw(cmidrule(2, 4)), # Rule under columns 2-4 only
Row("", "A", "B", "C"),
Row("Test 1", "1", "2", "3"),
Row("Test 2", "4", "5", "6"),
],
toprule=True,
bottomrule=True,
)
cmidrule Options
from texer import cmidrule
# Basic usage
cmidrule(1, 3) # \cmidrule{1-3}
# With trimming (reduces rule width at edges)
cmidrule(2, 4, trim_left=True, trim_right=True) # \cmidrule(lr){2-4}
# Custom trim widths
cmidrule(1, 2, trim_left="0.5em") # \cmidrule(l{0.5em}){1-2}
cmidrule(1, 2, trim_right="1em") # \cmidrule(r{1em}){1-2}
cmidrule(1, 2, trim_left="0.5em", trim_right="0.5em") # \cmidrule(l{0.5em}r{0.5em}){1-2}
Multiple cmidrules
Pass a list of (start, end) tuples to generate multiple rules at once:
from texer import cmidrule
# Multiple ranges
cmidrule([(2, 4), (5, 7)]) # \cmidrule{2-4} \cmidrule{5-7}
# With automatic trimming between adjacent rules
cmidrule([(2, 4), (5, 7)], trim_between=True)
# Output: \cmidrule(r){2-4} \cmidrule(l){5-7}
# Three or more ranges - middle ones get both trims
cmidrule([(1, 2), (3, 4), (5, 6)], trim_between=True)
# Output: \cmidrule(r){1-2} \cmidrule(lr){3-4} \cmidrule(l){5-6}
Use in a table with grouped columns:
from texer import Tabular, Row, MultiColumn, Raw, cmidrule
table = Tabular(
columns="lcccccc",
rows=[
Row(
"",
MultiColumn(3, "c", "Group A"),
MultiColumn(3, "c", "Group B"),
),
Raw(cmidrule([(2, 4), (5, 7)], trim_between=True)),
Row("", "X", "Y", "Z", "X", "Y", "Z"),
Row("Sample", "1", "2", "3", "4", "5", "6"),
],
toprule=True,
bottomrule=True,
)
trim_between
The trim_between=True option automatically adds trim_right to all but the last rule and trim_left to all but the first rule, creating visual gaps between adjacent rules.
Row Spacing Control
Control vertical spacing with the end parameter on Row:
from texer import Tabular, Row, Raw, cmidrule
table = Tabular(
columns="lcc",
rows=[
Row("Header 1", "Header 2", "Header 3"),
Raw(cmidrule(1, 3)),
Row("Group A", "", "", end=r"\\[6pt]"), # Extra space after this row
Row(" Item 1", "10", "20"),
Row(" Item 2", "30", "40", end=r"\\[6pt]"),
Row("Group B", "", ""),
Row(" Item 3", "50", "60"),
],
toprule=True,
bottomrule=True,
)
Row end Options
from texer import Row
# Default: standard line ending
Row("A", "B", "C") # A & B & C \\
# Extra vertical space
Row("A", "B", "C", end=r"\\[4pt]") # A & B & C \\[4pt]
Row("A", "B", "C", end=r"\\[1ex]") # A & B & C \\[1ex]
# No line ending (useful in special cases)
Row("A", "B", "C", end="") # A & B & C
Common spacing values:
- \\[2pt] - small extra space
- \\[4pt] - medium extra space
- \\[6pt] - large extra space
- \\[1ex] - height of an "x" in current font
- \\[1em] - width of an "M" in current font
Custom Rules with MultiColumn
Insert rules between sections:
from texer import Tabular, Row, MultiColumn, Raw, evaluate
table = Tabular(
columns="lccc",
rows=[
Row("", MultiColumn(3, "c", Cell("Header", bold=True))),
Raw(r"\midrule"),
Row("", "A", "B", "C"),
Raw(r"\midrule"),
Row("Data 1", "1", "2", "3"),
Row("Data 2", "4", "5", "6"),
Raw(r"\midrule"),
Row("Total", "5", "7", "9"),
],
toprule=True,
midrule=False, # We're adding rules manually
bottomrule=True,
)
Dynamic MultiColumn
Use with Ref and Iter:
from texer import Tabular, Row, MultiColumn, Ref, Iter, evaluate
table = Tabular(
columns="lcccc",
rows=[
Row(
"Category",
MultiColumn(Ref("num_metrics"), "c", Ref("group_title")),
),
# ... more rows
],
)
data = {
"num_metrics": 4,
"group_title": "Performance Metrics",
}
Alignment Override
Use MultiColumn to override column alignment for a single cell:
# Default columns="lcc" (left, center, center)
Row(
"Item",
MultiColumn(1, "r", "999.99"), # Right-align this number
"Normal",
)
This is useful when you want a specific cell to have different alignment than its column.
Complex Example: Grouped Data
from texer import Table, Tabular, Row, MultiColumn, Cell, Ref, Iter, Format, Raw, evaluate
table = Table(
Tabular(
columns="lcccccc",
rows=[
# Main header
Row(
Cell("Experiment", bold=True),
MultiColumn(3, "c", Cell("Trial 1", bold=True)),
MultiColumn(3, "c", Cell("Trial 2", bold=True)),
),
# Sub-headers
Row("", "Mean", "Std", "N", "Mean", "Std", "N"),
Raw(r"\midrule"),
# Data rows with Iter
*Iter(
Ref("experiments"),
template=Row(
Cell(Ref("name"), italic=True),
Format(Ref("trial1.mean"), ".2f"),
Format(Ref("trial1.std"), ".2f"),
Ref("trial1.n"),
Format(Ref("trial2.mean"), ".2f"),
Format(Ref("trial2.std"), ".2f"),
Ref("trial2.n"),
)
),
],
toprule=True,
midrule=False,
bottomrule=True,
),
caption="Experimental Results Summary",
label="tab:results",
)
data = {
"experiments": [
{
"name": "Exp A",
"trial1": {"mean": 10.5, "std": 1.2, "n": 30},
"trial2": {"mean": 11.2, "std": 1.5, "n": 30},
},
{
"name": "Exp B",
"trial1": {"mean": 8.3, "std": 0.9, "n": 25},
"trial2": {"mean": 9.1, "std": 1.1, "n": 25},
},
]
}
print(evaluate(table, data))
Nested Tabular Environments
For complex layouts, you can nest tabular environments:
from texer import Raw
# Outer table
outer_table = Tabular(
columns="lc",
rows=[
Row("Name", "Data"),
Row(
"Nested",
Raw(r"\begin{tabular}{cc} A & B \\ C & D \end{tabular}"),
),
],
)
Use Sparingly
Nested tables can be hard to maintain. Consider if a simpler design works.
Paragraph Columns
Use p{width} for paragraph-style columns with wrapping:
Tabular(
columns="lp{5cm}c",
rows=[
Row(
"ID",
"This is a long description that will automatically wrap within the 5cm column width",
"Status",
),
],
toprule=True,
bottomrule=True,
)
Other paragraph column types:
p{width}: Top-aligned paragraphm{width}: Middle-aligned paragraph (requiresarraypackage)b{width}: Bottom-aligned paragraph (requiresarraypackage)
Vertical Rules
Add vertical lines in the column specification:
Booktabs Style
The booktabs package (which provides \toprule, \midrule, etc.) recommends against vertical rules for professional tables.
Custom Column Types
Define custom column types with LaTeX:
from texer import Raw
# In your LaTeX preamble:
# \newcolumntype{R}[1]{>{\raggedleft\arraybackslash}p{#1}}
table = Tabular(
columns="lRc", # Using custom 'R' column type
rows=[...],
)
Wide Tables
For tables wider than the text width:
# Option 1: Adjust column widths
Tabular(
columns="p{2cm}p{3cm}p{2cm}",
rows=[...],
)
# Option 2: Use landscape (in LaTeX preamble)
from texer import Raw
# Wrap in landscape environment
Raw(r"\begin{landscape}")
# ... table here ...
Raw(r"\end{landscape}")
Long Tables (Multi-page)
For tables spanning multiple pages, use the longtable package:
from texer import Raw
# Note: This is a raw LaTeX example
# texer doesn't have built-in longtable support yet
latex = r"""
\begin{longtable}{lcc}
\toprule
Header 1 & Header 2 & Header 3 \\
\midrule
\endfirsthead
\multicolumn{3}{c}{\textit{(continued)}} \\
\toprule
Header 1 & Header 2 & Header 3 \\
\midrule
\endhead
\bottomrule
\endfoot
% Data rows
Row 1 & Data & Data \\
Row 2 & Data & Data \\
% ... many more rows ...
\end{longtable}
"""
Conditional Rows
Add rows conditionally:
from texer import Cond, Raw
rows = [
Row("Always shown", "value1", "value2"),
# Conditional row
*([Row("Optional", "val3", "val4")] if condition else []),
Row("Also always shown", "value5", "value6"),
]
Or with specs:
rows=Iter(
Ref("items"),
template=Cond(
Ref("include"),
Row(Ref("name"), Ref("value")),
Raw(""), # Empty for excluded items
)
)
Summary Rows
Add summary/total rows:
from texer import Tabular, Row, Cell, Ref, Iter, Format, Raw, Call
table = Tabular(
columns="lcc",
header=Row("Item", "Quantity", "Price"),
rows=[
*Iter(
Ref("items"),
template=Row(
Ref("name"),
Ref("qty"),
Format(Ref("price"), ".2f"),
)
),
Raw(r"\midrule"),
Row(
Cell("Total", bold=True),
Call("sum", Iter(Ref("items"), template=Ref("qty"))),
Cell(Format(Call("sum", Iter(Ref("items"), template=Ref("price"))), ".2f"), bold=True),
),
],
toprule=True,
bottomrule=True,
)
Tips for Complex Tables
- Start simple: Build incrementally
- Test frequently: Verify LaTeX compilation at each step
- Use comments: Document complex structures
- Modularize: Break large tables into reusable parts
- Consider alternatives: Sometimes multiple simple tables are better than one complex table
Common Patterns
Grouped Rows
rows=[
Cell("Group A", bold=True),
Row(" Item 1", "data", "data"),
Row(" Item 2", "data", "data"),
Raw(r"\midrule"),
Cell("Group B", bold=True),
Row(" Item 3", "data", "data"),
Row(" Item 4", "data", "data"),
]
Alternating Row Colors
# Requires \usepackage{xcolor,colortbl}
rows=[
Row("Item 1", "data"),
Raw(r"\rowcolor{gray!20}"),
Row("Item 2", "data"),
Row("Item 3", "data"),
Raw(r"\rowcolor{gray!20}"),
Row("Item 4", "data"),
]
Next Steps
- Basic Tables - Table fundamentals
- Data-Driven Tables - Dynamic table generation
- Cell Formatting - Bold, italic, colors