Source code for lena.structures.elements
import copy
import lena.context
import lena.core
import lena.flow
import lena.variables
from .histogram import histogram
from .hist_functions import hist_to_graph
[docs]class HistToGraph():
"""Transform a :class:`.histogram` to a :class:`.graph`."""
def __init__(self, make_value, get_coordinate="left",
field_names=("x", "y"), scale=None):
"""*make_value* is a :class:`.Variable`
that creates graph value from the bin value.
*get_coordinate* defines the coordinate of the graph point.
By default, it is the left bin edge. Other allowed values are
"right" and "middle".
*field_names* set field names of resulting graphs.
*scale* sets scales of resulting graphs. If it is ``True``,
the scale is computed from the histogram.
See :func:`hist_to_graph` for details and examples.
Incorrect values for *make_value* or *get_coordinate* raise,
respectively, :exc:`.LenaTypeError` or :exc:`.LenaValueError`.
"""
if isinstance(make_value, lena.variables.Variable):
self._make_value = make_value
else:
raise lena.core.LenaTypeError(
"make_value must be a Variable, "
"{} given".format(make_value)
)
# todo? functions for coordinates should be allowed
# -- see comment in hist_to_graph
# todo: do we need a run method, or should it be just __call__?
# -- see comment in the method! It should.
# todo: allow passing a scale(histogram, context?) function
# to create initial scales of graphs
if get_coordinate not in ["left", "right", "middle"]:
raise lena.core.LenaValueError(
'get_coordinate must be one of "left", "right" or "middle"; '
'"{}" provided'.format(get_coordinate)
)
self._get_coordinate = get_coordinate
self._field_names = field_names
self._scale = scale
[docs] def run(self, flow):
"""Iterate the *flow* and transform histograms to graphs.
*context.value* is updated with *make_value* context.
If histogram bins contained context
(which is assumed to be the same for all bins),
*make_value* context is composed with that.
Not histograms or histograms with
*context.histogram.to_graph* set to ``False``
pass unchanged.
"""
from lena.flow import get_data_context
from lena.flow import get_context
# don't know differences between these two ways of imports
get_example_bin = lena.structures.get_example_bin
update_nested = lena.context.update_nested
# why can't it be a Call element, which just returns
# unchanged values for non-histograms?
# It could be used in a FillCompute sequence then,
# but probably might have some design flaws.
# -- in fact, Run elements can be used as FillInto.
#
# Yes: FillCompute element would expect a uniform flow.
# Odd values (not histograms) should be filtered
# in the beginning, and not get to this element at all.
# A separate fill_into (or better __call__) method would be fine.
for val in flow:
hist, context = get_data_context(val)
if (not isinstance(hist, histogram) or
not lena.context.get_recursively(context,
"histogram.to_graph", True)
):
yield val
continue
bin_context = get_context(get_example_bin(hist))
# bin context is ignored in hist_to_graph,
# so we can safely skip its copying
self._make_value._update_context(
bin_context, copy.deepcopy(self._make_value.var_context)
)
lena.context.update_nested("value", context, bin_context)
graph = hist_to_graph(
hist,
make_value=self._make_value.getter,
field_names=self._field_names,
get_coordinate=self._get_coordinate,
scale=self._scale
)
yield (graph, context)