Skip to content

Commit

Permalink
Add structures.iterable_to_table (100% tested).
Browse files Browse the repository at this point in the history
  • Loading branch information
ynikitenko committed Feb 17, 2022
1 parent 7f338e8 commit e113163
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 5 deletions.
2 changes: 2 additions & 0 deletions docs/source/output.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Output

MakeFilename
PDFToPNG
iterable_to_table
ToCSV
Write
Writer
Expand All @@ -30,6 +31,7 @@ Output
.. autoclass:: MakeFilename
:special-members: __call__
.. autoclass:: PDFToPNG
.. autofunction:: iterable_to_table
.. autoclass:: ToCSV
.. autofunction:: hist1d_to_csv
.. autofunction:: hist2d_to_csv
Expand Down
4 changes: 2 additions & 2 deletions lena/output/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from .latex_to_pdf import LaTeXToPDF
from .make_filename import MakeFilename
from .write import Writer, Write
from .to_csv import ToCSV, hist1d_to_csv, hist2d_to_csv
from .to_csv import ToCSV, hist1d_to_csv, hist2d_to_csv, iterable_to_table
from .write_root_tree import WriteROOTTree


Expand Down Expand Up @@ -30,6 +30,6 @@ def stub(*args, **kwargs):
'Write',
'Writer',
'WriteROOTTree',
'ToCSV', 'hist1d_to_csv', 'hist2d_to_csv',
'iterable_to_table', 'ToCSV', 'hist1d_to_csv', 'hist2d_to_csv',
'RenderLaTeX'
]
97 changes: 97 additions & 0 deletions lena/output/to_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,103 @@
# pylint: disable=invalid-name


def iterable_to_table(
iterable, format_=None, header="", header_fields=(),
row_start="", row_end="", row_separator=",",
footer=""
):
r"""Create a table from an *iterable*.
The resulting table is yielded line by line.
If the *header* or *footer* is empty, it is not yielded.
*format_* controls the output of individual cells in a row.
By default, it uses standard Python representation.
For finer control, one should provide a sequence
of formatting options for each column.
For floating values it is recommended to output
only a finite appropriate number of digits,
because this would allow the output to be immutable
between calls despite technical reasons.
Default formatting allows an arbitrary number of columns
in each cell. For tables to be well-formed, substitute
missing values in the *iterable* for some placeholder
like \"\", *None*, etc.
Each row is prepended with *row_start* and appended with *row_end*.
If it consists of several columns, they are joined by
*row_separator*.
Separators between rows can be added while iterating the result.
This function can be used to convert structures
to different formats: *csv*, *html*, *xml*, etc.
Examples:
>>> angles = [(3.1415*i/4, 180*i/4) for i in range(1, 5)]
>>> format_ = ("{:.2f}", "{:.0f}")
>>> header_fields = ("rad", "deg")
>>>
>>> csv_rows = iterable_to_table(
... angles, format_=format_,
... header="{},{}", header_fields=header_fields,
... row_separator=",",
... )
>>> print("\n".join(csv_rows))
rad,deg
0.79,45
1.57,90
2.36,135
3.14,180
>>>
>>> html_rows = iterable_to_table(
... angles, format_=format_,
... header="<table>\n" + " "*4 + "<tr><td>{}</td><td>{}</td></tr>",
... header_fields=header_fields,
... row_start=" "*4 + "<tr><td>", row_end="</td></tr>",
... row_separator="</td><td>",
... footer="</table>"
... )
>>> print("\n".join(html_rows))
<table>
<tr><td>rad</td><td>deg</td></tr>
<tr><td>0.79</td><td>45</td></tr>
<tr><td>1.57</td><td>90</td></tr>
<tr><td>2.36</td><td>135</td></tr>
<tr><td>3.14</td><td>180</td></tr>
</table>
>>>
For more complex formatting use templates
(see :class:`~.RenderLaTeX`).
"""
if header:
if header_fields:
header = header.format(*header_fields)
yield header

# one value per line may be not such a useful case
# to be treated separately.
# if isinstance(format_, str):
# format_str = format_
if format_ is not None:
format_str = row_separator.join(format_)

for row in iterable:
try:
cols = iter(row)
except TypeError:
cols = (row,)

if format_ is None:
yield row_start + row_separator.join(map(repr, cols)) + row_end
else:
yield row_start + format_str.format(*cols) + row_end

if footer:
yield footer


def hist1d_to_csv(hist, header=None, separator=',', duplicate_last_bin=True):
"""Yield CSV-formatted strings for a one-dimensional histogram."""
bins_ = hist.bins
Expand Down
4 changes: 2 additions & 2 deletions lena/structures/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ def __init__(self, points, field_names=("x", "y"), scale=None):
:attr:`dim` is the dimension of the graph,
that is of all its coordinates without errors.
If the initialization arguments are incorrect,
:exc:`~.LenaTypeError` or :exc:`~.LenaValueError` are raised.
In case of incorrect initialization arguments,
:exc:`~.LenaTypeError` or :exc:`~.LenaValueError` is raised.
"""
if not points:
raise lena.core.LenaValueError(
Expand Down
16 changes: 15 additions & 1 deletion tests/output/test_to_csv.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
from lena.output import hist1d_to_csv, hist2d_to_csv, ToCSV
from lena.output import hist1d_to_csv, hist2d_to_csv, ToCSV, iterable_to_table
from lena.structures import histogram, Histogram


def test_iterable_to_table():
# one-dimensional structure is output correctly
it0 = (1, 2, 3.5, 4)
# iterable, format_=None, header="", header_fields=(),
# row_start="", row_end="", row_separator=",",
# footer=""
assert r"\n".join(
iterable_to_table(it0, row_start="b", row_end="e", row_separator="s",
footer="f")
) == r'b1e\nb2e\nb3.5e\nb4e\nf'

it = ((1, 2), (3.5, 4))


def test_hist_to_csv():
hist = histogram(edges=[0, 1, 2], bins=[1, 2])
# test defaults
Expand Down

0 comments on commit e113163

Please sign in to comment.