Skip to content

Commit 42d8bf1

Browse files
authored
Merge pull request #30 from dbatten5/col-index
Col index
2 parents 7f339a5 + 67d9ca1 commit 42d8bf1

File tree

3 files changed

+168
-21
lines changed

3 files changed

+168
-21
lines changed

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "arraytex"
3-
version = "0.0.6"
3+
version = "0.0.7"
44
description = "ArrayTeX"
55
authors = ["Dom Batten <[email protected]>"]
66
license = "MIT"
@@ -106,7 +106,6 @@ line-length = 80
106106
select = [
107107
'B',
108108
'B9',
109-
'C',
110109
'D',
111110
'E',
112111
'F',

src/arraytex/api.py

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ def to_tabular(
5858
num_format: Optional[str] = None,
5959
scientific_notation: bool = False,
6060
col_align: Union[List[str], str] = "c",
61-
cols: Optional[List[str]] = None,
61+
col_names: Optional[List[str]] = None,
62+
col_index: Optional[List[str]] = None,
6263
to_clp: bool = False,
6364
) -> str:
6465
"""Convert a numpy.NDArray to LaTeX tabular environment.
@@ -72,7 +73,8 @@ def to_tabular(
7273
single character is provided then it will be broadcast to all columns. If a list
7374
is provided then each item will be assigned to each column, list size and
7475
number of columns must match
75-
cols: an optional list of column names, otherwise generic names will be assigned
76+
col_names: an optional list of column names, otherwise generic names will be assigned
77+
col_index: an optional list of column indices, i.e. row identifiers
7678
to_clp: copy the output to the system clipboard
7779
7880
Returns:
@@ -94,29 +96,57 @@ def to_tabular(
9496
else:
9597
raise TooManyDimensionsError
9698

97-
if isinstance(col_align, list) and len(col_align) != n_cols:
99+
if not col_index:
100+
if isinstance(col_align, list) and len(col_align) != n_cols:
101+
raise DimensionMismatchError(
102+
f"Number of `col_align` items ({len(col_align)}) "
103+
+ f"doesn't match number of columns ({n_cols})"
104+
)
105+
106+
if col_names and len(col_names) != n_cols:
107+
raise DimensionMismatchError(
108+
f"Number of `col_names` items ({len(col_names)}) "
109+
+ f"doesn't match number of columns ({n_cols})"
110+
)
111+
112+
if (
113+
col_index
114+
and col_names
115+
and isinstance(col_align, list)
116+
and len(col_names) != len(col_align)
117+
):
98118
raise DimensionMismatchError(
99119
f"Number of `col_align` items ({len(col_align)}) "
100-
+ f"doesn't match number of columns ({n_cols})"
120+
+ f"doesn't match number of columns ({len(col_names)})"
101121
)
102122

103123
if isinstance(col_align, str):
104124
col_align = [col_align for _ in range(n_cols)]
105125

106-
if cols and len(cols) != n_cols:
107-
raise DimensionMismatchError(
108-
f"Number of `cols` items ({len(cols)}) "
109-
+ f"doesn't match number of columns ({n_cols})"
110-
)
111-
112-
if not cols:
113-
cols = [f"Col {i + 1}" for i in range(n_cols)]
126+
if not col_names:
127+
col_names = [f"Col {i + 1}" for i in range(n_cols)]
114128

115129
lines = _parse_lines(arr, num_format, scientific_notation)
116130

131+
if col_index:
132+
if len(col_index) != len(lines):
133+
raise DimensionMismatchError(
134+
f"Number of `col_index` items ({len(col_index)}) "
135+
+ f"doesn't match number of rows ({len(lines)})"
136+
)
137+
138+
if len(col_align) == n_cols:
139+
col_align.insert(0, "l")
140+
141+
if len(col_names) == n_cols:
142+
col_names.insert(0, "Index")
143+
144+
for idx, line in enumerate(lines):
145+
lines[idx] = f"{col_index[idx]} & " + line.strip()
146+
117147
rv = [f"\\begin{{tabular}}{{{' '.join(col_align)}}}"]
118148
rv += [r"\toprule"]
119-
rv += [" & ".join(cols) + r" \\"]
149+
rv += [" & ".join(col_names) + r" \\"]
120150
rv += [r"\midrule"]
121151
rv += [line.strip() + r" \\" for line in lines]
122152
rv += [r"\bottomrule"]

tests/test_api.py

Lines changed: 124 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,15 +179,15 @@ def test_mismatch_col_align(self) -> None:
179179
"Number of `col_align` items (2) doesn't match number of columns (3)"
180180
)
181181

182-
def test_mismatch_cols(self) -> None:
183-
"""Error is thrown if wrong number of cols items."""
182+
def test_mismatch_col_names(self) -> None:
183+
"""Error is thrown if wrong number of col_names items."""
184184
mat = np.arange(6).reshape(2, 3)
185185

186186
with pytest.raises(DimensionMismatchError) as exc:
187-
to_tabular(mat, cols=["1", "2"])
187+
to_tabular(mat, col_names=["1", "2"])
188188

189189
assert str(exc.value) == (
190-
"Number of `cols` items (2) doesn't match number of columns (3)"
190+
"Number of `col_names` items (2) doesn't match number of columns (3)"
191191
)
192192

193193
def test_default(self) -> None:
@@ -208,11 +208,11 @@ def test_default(self) -> None:
208208
\end{tabular}"""
209209
)
210210

211-
def test_given_cols(self) -> None:
211+
def test_given_col_names(self) -> None:
212212
"""User can supply col names."""
213213
mat = np.arange(1, 5).reshape(2, 2)
214214

215-
out = to_tabular(mat, cols=["1", "b"])
215+
out = to_tabular(mat, col_names=["1", "b"])
216216

217217
assert (
218218
out
@@ -277,3 +277,121 @@ def test_0_d(self) -> None:
277277
\bottomrule
278278
\end{tabular}"""
279279
)
280+
281+
class TestColIndex:
282+
"""Tests for the `col_index` support."""
283+
284+
def test_default(self) -> None:
285+
"""`col_index` forms the row names."""
286+
col_index = ["Row 1", "Row 2"]
287+
mat = np.arange(1, 5).reshape(2, 2)
288+
289+
out = to_tabular(mat, col_index=col_index)
290+
291+
assert (
292+
out
293+
== r"""\begin{tabular}{l c c}
294+
\toprule
295+
Index & Col 1 & Col 2 \\
296+
\midrule
297+
Row 1 & 1 & 2 \\
298+
Row 2 & 3 & 4 \\
299+
\bottomrule
300+
\end{tabular}"""
301+
)
302+
303+
def test_bad_dimensions(self) -> None:
304+
"""An error is raised if wrong dimension of `col_index`."""
305+
col_index = ["Row 1", "Row 2"]
306+
mat = np.arange(1, 4).reshape(3, 1)
307+
308+
with pytest.raises(DimensionMismatchError) as exc:
309+
to_tabular(mat, col_index=col_index)
310+
311+
assert str(exc.value) == (
312+
"Number of `col_index` items (2) doesn't match number of rows (3)"
313+
)
314+
315+
def test_given_col_names(self) -> None:
316+
"""A given index name as part of `col_names` is used."""
317+
col_index = ["Row 1", "Row 2"]
318+
col_names = ["My Index", "Col 1", "Col 2"]
319+
mat = np.arange(1, 5).reshape(2, 2)
320+
321+
out = to_tabular(mat, col_index=col_index, col_names=col_names)
322+
323+
assert (
324+
out
325+
== r"""\begin{tabular}{l c c}
326+
\toprule
327+
My Index & Col 1 & Col 2 \\
328+
\midrule
329+
Row 1 & 1 & 2 \\
330+
Row 2 & 3 & 4 \\
331+
\bottomrule
332+
\end{tabular}"""
333+
)
334+
335+
def test_given_col_align(self) -> None:
336+
"""A given col align char can be used for the col index."""
337+
col_index = ["Row 1", "Row 2"]
338+
mat = np.arange(1, 5).reshape(2, 2)
339+
340+
out = to_tabular(mat, col_index=col_index, col_align=["r", "c", "c"])
341+
342+
assert (
343+
out
344+
== r"""\begin{tabular}{r c c}
345+
\toprule
346+
Index & Col 1 & Col 2 \\
347+
\midrule
348+
Row 1 & 1 & 2 \\
349+
Row 2 & 3 & 4 \\
350+
\bottomrule
351+
\end{tabular}"""
352+
)
353+
354+
def test_given_col_name_and_align(self) -> None:
355+
"""A given col index name and align can be used for the index."""
356+
col_index = ["Row 1", "Row 2"]
357+
col_names = ["My Index", "Col 1", "Col 2"]
358+
col_align = ["r", "c", "c"]
359+
mat = np.arange(1, 5).reshape(2, 2)
360+
361+
out = to_tabular(
362+
mat,
363+
col_align=col_align,
364+
col_names=col_names,
365+
col_index=col_index,
366+
)
367+
368+
assert (
369+
out
370+
== r"""\begin{tabular}{r c c}
371+
\toprule
372+
My Index & Col 1 & Col 2 \\
373+
\midrule
374+
Row 1 & 1 & 2 \\
375+
Row 2 & 3 & 4 \\
376+
\bottomrule
377+
\end{tabular}"""
378+
)
379+
380+
def test_col_align_bad_dimensions(self) -> None:
381+
"""Bad dimensions of `col_align` is caught."""
382+
col_index = ["Row 1", "Row 2"]
383+
col_names = ["My Index", "Col 1", "Col 2"]
384+
col_align = ["r", "c"]
385+
mat = np.arange(1, 5).reshape(2, 2)
386+
387+
with pytest.raises(DimensionMismatchError) as exc:
388+
to_tabular(
389+
mat,
390+
col_align=col_align,
391+
col_names=col_names,
392+
col_index=col_index,
393+
)
394+
395+
assert str(exc.value) == (
396+
"Number of `col_align` items (2) doesn't match number of columns (3)"
397+
)

0 commit comments

Comments
 (0)