Skip to content

Commit 32689ba

Browse files
committed
Place html table formatting in util.py
Allows it to be used by other tabular outputting routines. Also put the "limit" functionality into a single util function
1 parent 47b0fa6 commit 32689ba

File tree

3 files changed

+70
-108
lines changed

3 files changed

+70
-108
lines changed

python/tests/test_util.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,12 +489,12 @@ def test_unicode_table():
489489
)
490490

491491

492-
def test_unicode_table_alignments():
492+
def test_unicode_table_column_alignments():
493493
assert (
494494
util.unicode_table(
495495
[["5", "6", "7", "8"], ["90", "10", "11", "12"]],
496496
header=["1", "2", "3", "4"],
497-
alignments="<>><",
497+
column_alignments="<>><",
498498
)
499499
== textwrap.dedent(
500500
"""

python/tskit/tables.py

Lines changed: 17 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import collections.abc
2828
import dataclasses
2929
import datetime
30-
import itertools
3130
import json
3231
import numbers
3332
import warnings
@@ -657,41 +656,10 @@ def __str__(self):
657656
return util.unicode_table(rows, header=headers, row_separator=False)
658657

659658
def _repr_html_(self):
660-
"""
661-
Called by jupyter notebooks to render tables
662-
"""
663659
headers, rows = self._text_header_and_rows(
664660
limit=tskit._print_options["max_lines"]
665661
)
666-
headers = "".join(f"<th>{header}</th>" for header in headers)
667-
rows = (
668-
f'<td style="text-align: center;" colspan="{len(headers)}"><em>{row[11:]}'
669-
f" rows skipped (tskit.set_print_options)</em></td>"
670-
if "__skipped__" in row
671-
else "".join(f"<td>{cell}</td>" for cell in row)
672-
for row in rows
673-
)
674-
rows = "".join(f"<tr>{row}</tr>\n" for row in rows)
675-
return f"""
676-
<div>
677-
<style scoped="">
678-
.tskit-table tbody tr th:only-of-type {{vertical-align: middle;}}
679-
.tskit-table tbody tr th {{vertical-align: top;}}
680-
.tskit-table tbody td {{text-align: right;padding: 0.5em 0.5em;}}
681-
.tskit-table tbody th {{padding: 0.5em 0.5em;}}
682-
</style>
683-
<table border="1" class="tskit-table">
684-
<thead>
685-
<tr>
686-
{headers}
687-
</tr>
688-
</thead>
689-
<tbody>
690-
{rows}
691-
</tbody>
692-
</table>
693-
</div>
694-
"""
662+
return util.html_table(rows, header=headers)
695663

696664
def _columns_all_integer(self, *colnames):
697665
# For displaying floating point values without loads of decimal places
@@ -852,15 +820,8 @@ def __init__(self, max_rows_increment=0, ll_table=None):
852820
def _text_header_and_rows(self, limit=None):
853821
headers = ("id", "flags", "location", "parents", "metadata")
854822
rows = []
855-
if limit is None or self.num_rows <= limit:
856-
indexes = range(self.num_rows)
857-
else:
858-
indexes = itertools.chain(
859-
range(limit // 2),
860-
[-1],
861-
range(self.num_rows - (limit - (limit // 2)), self.num_rows),
862-
)
863-
for j in indexes:
823+
row_indexes = util.truncate_rows(self.num_rows, limit)
824+
for j in row_indexes:
864825
if j == -1:
865826
rows.append(f"__skipped__{self.num_rows-limit}")
866827
else:
@@ -1105,16 +1066,9 @@ def __init__(self, max_rows_increment=0, ll_table=None):
11051066
def _text_header_and_rows(self, limit=None):
11061067
headers = ("id", "flags", "population", "individual", "time", "metadata")
11071068
rows = []
1108-
if limit is None or self.num_rows <= limit:
1109-
indexes = range(self.num_rows)
1110-
else:
1111-
indexes = itertools.chain(
1112-
range(limit // 2),
1113-
[-1],
1114-
range(self.num_rows - (limit - (limit // 2)), self.num_rows),
1115-
)
1069+
row_indexes = util.truncate_rows(self.num_rows, limit)
11161070
decimal_places_times = 0 if self._columns_all_integer("time") else 8
1117-
for j in indexes:
1071+
for j in row_indexes:
11181072
row = self[j]
11191073
if j == -1:
11201074
rows.append(f"__skipped__{self.num_rows-limit}")
@@ -1306,16 +1260,9 @@ def __init__(self, max_rows_increment=0, ll_table=None):
13061260
def _text_header_and_rows(self, limit=None):
13071261
headers = ("id", "left", "right", "parent", "child", "metadata")
13081262
rows = []
1309-
if limit is None or self.num_rows <= limit:
1310-
indexes = range(self.num_rows)
1311-
else:
1312-
indexes = itertools.chain(
1313-
range(limit // 2),
1314-
[-1],
1315-
range(self.num_rows - (limit - (limit // 2)), self.num_rows),
1316-
)
1263+
row_indexes = util.truncate_rows(self.num_rows, limit)
13171264
decimal_places = 0 if self._columns_all_integer("left", "right") else 8
1318-
for j in indexes:
1265+
for j in row_indexes:
13191266
if j == -1:
13201267
rows.append(f"__skipped__{self.num_rows-limit}")
13211268
else:
@@ -1528,17 +1475,10 @@ def __init__(self, max_rows_increment=0, ll_table=None):
15281475
def _text_header_and_rows(self, limit=None):
15291476
headers = ("id", "left", "right", "node", "source", "dest", "time", "metadata")
15301477
rows = []
1531-
if limit is None or self.num_rows <= limit:
1532-
indexes = range(self.num_rows)
1533-
else:
1534-
indexes = itertools.chain(
1535-
range(limit // 2),
1536-
[-1],
1537-
range(self.num_rows - (limit - (limit // 2)), self.num_rows),
1538-
)
1478+
row_indexes = util.truncate_rows(self.num_rows, limit)
15391479
decimal_places_coords = 0 if self._columns_all_integer("left", "right") else 8
15401480
decimal_places_times = 0 if self._columns_all_integer("time") else 8
1541-
for j in indexes:
1481+
for j in row_indexes:
15421482
if j == -1:
15431483
rows.append(f"__skipped__{self.num_rows-limit}")
15441484
else:
@@ -1748,16 +1688,9 @@ def __init__(self, max_rows_increment=0, ll_table=None):
17481688
def _text_header_and_rows(self, limit=None):
17491689
headers = ("id", "position", "ancestral_state", "metadata")
17501690
rows = []
1751-
if limit is None or self.num_rows <= limit:
1752-
indexes = range(self.num_rows)
1753-
else:
1754-
indexes = itertools.chain(
1755-
range(limit // 2),
1756-
[-1],
1757-
range(self.num_rows - (limit - (limit // 2)), self.num_rows),
1758-
)
1691+
row_indexes = util.truncate_rows(self.num_rows, limit)
17591692
decimal_places = 0 if self._columns_all_integer("position") else 8
1760-
for j in indexes:
1693+
for j in row_indexes:
17611694
if j == -1:
17621695
rows.append(f"__skipped__{self.num_rows-limit}")
17631696
else:
@@ -1971,17 +1904,10 @@ def __init__(self, max_rows_increment=0, ll_table=None):
19711904
def _text_header_and_rows(self, limit=None):
19721905
headers = ("id", "site", "node", "time", "derived_state", "parent", "metadata")
19731906
rows = []
1974-
if limit is None or self.num_rows <= limit:
1975-
indexes = range(self.num_rows)
1976-
else:
1977-
indexes = itertools.chain(
1978-
range(limit // 2),
1979-
[-1],
1980-
range(self.num_rows - (limit - (limit // 2)), self.num_rows),
1981-
)
1907+
row_indexes = util.truncate_rows(self.num_rows, limit)
19821908
# Currently mutations do not have discretised times: this for consistency
19831909
decimal_places_times = 0 if self._columns_all_integer("time") else 8
1984-
for j in indexes:
1910+
for j in row_indexes:
19851911
if j == -1:
19861912
rows.append(f"__skipped__{self.num_rows-limit}")
19871913
else:
@@ -2232,15 +2158,8 @@ def add_row(self, metadata=None):
22322158
def _text_header_and_rows(self, limit=None):
22332159
headers = ("id", "metadata")
22342160
rows = []
2235-
if limit is None or self.num_rows <= limit:
2236-
indexes = range(self.num_rows)
2237-
else:
2238-
indexes = itertools.chain(
2239-
range(limit // 2),
2240-
[-1],
2241-
range(self.num_rows - (limit - (limit // 2)), self.num_rows),
2242-
)
2243-
for j in indexes:
2161+
row_indexes = util.truncate_rows(self.num_rows, limit)
2162+
for j in row_indexes:
22442163
if j == -1:
22452164
rows.append(f"__skipped__{self.num_rows-limit}")
22462165
else:
@@ -2490,15 +2409,8 @@ def append_columns(
24902409
def _text_header_and_rows(self, limit=None):
24912410
headers = ("id", "timestamp", "record")
24922411
rows = []
2493-
if limit is None or self.num_rows <= limit:
2494-
indexes = range(self.num_rows)
2495-
else:
2496-
indexes = itertools.chain(
2497-
range(limit // 2),
2498-
[-1],
2499-
range(self.num_rows - (limit - (limit // 2)), self.num_rows),
2500-
)
2501-
for j in indexes:
2412+
row_indexes = util.truncate_rows(self.num_rows, limit)
2413+
for j in row_indexes:
25022414
if j == -1:
25032415
rows.append(f"__skipped__{self.num_rows-limit}")
25042416
else:

python/tskit/util.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
Module responsible for various utility functions used in other modules.
2424
"""
2525
import dataclasses
26+
import itertools
2627
import json
2728
import numbers
2829
import os
@@ -370,7 +371,7 @@ def render_metadata(md, length=40):
370371

371372

372373
def unicode_table(
373-
rows, title=None, header=None, row_separator=True, column_alignments=None
374+
rows, *, title=None, header=None, row_separator=True, column_alignments=None
374375
):
375376
"""
376377
Convert a table (list of lists) of strings to a unicode table. If a row contains
@@ -445,6 +446,41 @@ def unicode_table(
445446
return "".join(out)
446447

447448

449+
def html_table(rows, *, header):
450+
"""
451+
Called by jupyter notebooks to render tables
452+
"""
453+
headers = "".join(f"<th>{h}</th>" for h in header)
454+
rows = (
455+
f'<td style="text-align: center;" colspan="{len(headers)}"><em>{row[11:]}'
456+
f" rows skipped (tskit.set_print_options)</em></td>"
457+
if "__skipped__" in row
458+
else "".join(f"<td>{cell}</td>" for cell in row)
459+
for row in rows
460+
)
461+
rows = "".join(f"<tr>{row}</tr>\n" for row in rows)
462+
return f"""
463+
<div>
464+
<style scoped="">
465+
.tskit-table tbody tr th:only-of-type {{vertical-align: middle;}}
466+
.tskit-table tbody tr th {{vertical-align: top;}}
467+
.tskit-table tbody td {{text-align: right;padding: 0.5em 0.5em;}}
468+
.tskit-table tbody th {{padding: 0.5em 0.5em;}}
469+
</style>
470+
<table border="1" class="tskit-table">
471+
<thead>
472+
<tr>
473+
{headers}
474+
</tr>
475+
</thead>
476+
<tbody>
477+
{rows}
478+
</tbody>
479+
</table>
480+
</div>
481+
"""
482+
483+
448484
def tree_sequence_html(ts):
449485
table_rows = "".join(
450486
f"""
@@ -686,6 +722,20 @@ def set_print_options(*, max_lines=40):
686722
tskit._print_options = {"max_lines": max_lines}
687723

688724

725+
def truncate_rows(num_rows, limit=None):
726+
"""
727+
Return a list of indexes into a set of rows, but is limit is set, truncate the
728+
number of rows and place a `-1` instead of the intermediate indexes
729+
"""
730+
if limit is None or num_rows <= limit:
731+
return range(num_rows)
732+
return itertools.chain(
733+
range(limit // 2),
734+
[-1],
735+
range(num_rows - (limit - (limit // 2)), num_rows),
736+
)
737+
738+
689739
def random_nucleotides(length: numbers.Number, *, seed: Union[int, None] = None) -> str:
690740
"""
691741
Returns a random string of nucleotides of the specified length. Characters

0 commit comments

Comments
 (0)