Source code for qf_lib.plotting.helpers.index_translator

#     Copyright 2016-present CERN – European Organization for Nuclear Research
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.

from numbers import Number
from pandas import Index
from typing import Mapping, List, Sequence, Union

from qf_lib.common.enums.orientation import Orientation
from qf_lib.plotting.charts.chart import Chart


[docs]class IndexTranslator: """ Object which automatically translates label-indexed data into number-indexed data. Parameters ---------- labels_to_locations_dict: Mapping[str, Number] Contains a mapping from labels to numbers (used as index values for plotting). Must have unique keys and unique values. """ def __init__(self, labels_to_locations_dict: Mapping[str, Number] = None): self._labels_to_locations_dict = dict() if labels_to_locations_dict is not None: self._labels_to_locations_dict.update(labels_to_locations_dict)
[docs] @classmethod def setup_ticks_and_labels(cls, chart: Chart): """ Setups ticks' locations and labels in the given chart if it used IndexTranslator. """ if chart.index_translator is not None: index_axis = chart.axes.xaxis if chart._orientation == Orientation.Vertical else chart.axes.yaxis ticks = chart.index_translator.values() index_axis.set_ticks(ticks) labels = chart.index_translator.inv_translate(index_axis.get_ticklocs()) index_axis.set_ticklabels(labels)
[docs] def translate(self, values: Union[str, Sequence[str]]) -> Sequence[Number]: """ Translates label into numeric coordinate. If the translation is done for the first time for this value it may modify the state of the translator (it may introduce a new translation). Parameters ---------- values values to be translated (e.g. ["SPY", "GOOGL"]) Returns ------- translated values value translated into numeric coordinate """ if isinstance(values, str): values = [values] result = [] for value in values: if value in self._labels_to_locations_dict: numeric_coordinate = self._labels_to_locations_dict[value] else: numeric_coordinate = self._translate_and_update_dict(value) result.append(numeric_coordinate) return Index(result)
[docs] def inv_translate(self, translated_values: Union[Number, Sequence[Number]]) -> Sequence[str]: """ Translates numeric coordinate into label. Requirement: the Translator must be familiar with mapping the label into numeric coordinate (either the translate must have been called or the labels_to_locations_dict must contain a proper entry). Parameters ---------- translated_values numeric values to be changed into labels (e.g. [0.0, 1.0, 2.0]) Returns ------- labels labels corresponding to numeric values """ if isinstance(translated_values, float): translated_values = [translated_values] inv_dict = {value: key for key, value in self._labels_to_locations_dict.items()} return Index([inv_dict.get(numeric_value, '') for numeric_value in translated_values])
def values(self) -> List[float]: return list(self._labels_to_locations_dict.values()) def _translate_and_update_dict(self, new_value: str) -> Number: first_tick_position = 1.0 numeric_coordinate = first_tick_position + len(self._labels_to_locations_dict) self._labels_to_locations_dict[new_value] = numeric_coordinate return numeric_coordinate