Example #1
0
def table(ax,
          data: FrameOrSeriesUnion,
          rowLabels=None,
          colLabels=None,
          **kwargs) -> Table:
    if isinstance(data, ABCSeries):
        data = data.to_frame()
    elif isinstance(data, ABCDataFrame):
        pass
    else:
        raise ValueError("Input data must be DataFrame or Series")

    if rowLabels is None:
        rowLabels = data.index

    if colLabels is None:
        colLabels = data.columns

    cellText = data.values

    table = matplotlib.table.table(ax,
                                   cellText=cellText,
                                   rowLabels=rowLabels,
                                   colLabels=colLabels,
                                   **kwargs)
    return table
Example #2
0
def reindex_weights_to_indices(
        weights: FrameOrSeriesUnion,
        indices: pd.DataFrame,
        axis: Axis = 0,
        ) -> pd.DataFrame:
    """Reshapes and reindexes weights to indices, if they do not
    already have the same shape and share the same index frequency.
    """
    axis = _handle_axis(axis)
    # Convert to a DataFrame is weight is a Series, transpose if needed
    if isinstance(weights, pd.Series):
        weights = weights.to_frame()
        if axis == 0:
            weights = weights.T

    if not weights.axes[axis].equals(indices.axes[axis]):
        return reindex_and_fill(weights, indices, 'ffill', axis)
    else:
        return weights
Example #3
0
    def __init__(
        self,
        data: FrameOrSeriesUnion,
        uuid: str | None = None,
        uuid_len: int = 5,
        table_styles: CSSStyles | None = None,
        table_attributes: str | None = None,
        caption: str | None = None,
        cell_ids: bool = True,
    ):

        # validate ordered args
        if isinstance(data, Series):
            data = data.to_frame()
        if not isinstance(data, DataFrame):
            raise TypeError("``data`` must be a Series or DataFrame")
        if not data.index.is_unique or not data.columns.is_unique:
            raise ValueError("style is not supported for non-unique indices.")
        self.data: DataFrame = data
        self.index: Index = data.index
        self.columns: Index = data.columns
        if not isinstance(uuid_len, int) or not uuid_len >= 0:
            raise TypeError(
                "``uuid_len`` must be an integer in range [0, 32].")
        self.uuid_len = min(32, uuid_len)
        self.uuid = (uuid or uuid4().hex[:self.uuid_len]) + "_"
        self.table_styles = table_styles
        self.table_attributes = table_attributes
        self.caption = caption
        self.cell_ids = cell_ids

        # add rendering variables
        self.hidden_index: bool = False
        self.hidden_columns: Sequence[int] = []
        self.ctx: DefaultDict[tuple[int, int], CSSList] = defaultdict(list)
        self.cell_context: DefaultDict[tuple[int, int], str] = defaultdict(str)
        self._todo: list[tuple[Callable, tuple, dict]] = []
        self.tooltips: Tooltips | None = None
        def_precision = get_option("display.precision")
        self._display_funcs: DefaultDict[  # maps (row, col) -> formatting function
            tuple[int, int], Callable[[Any], str]] = defaultdict(
                lambda: partial(_default_formatter, precision=def_precision))
Example #4
0
def _add_margins(
    table: FrameOrSeriesUnion,
    data,
    values,
    rows,
    cols,
    aggfunc,
    observed=None,
    margins_name: str = "All",
    fill_value=None,
):
    if not isinstance(margins_name, str):
        raise ValueError("margins_name argument must be a string")

    msg = f'Conflicting name "{margins_name}" in margins'
    for level in table.index.names:
        if margins_name in table.index.get_level_values(level):
            raise ValueError(msg)

    grand_margin = _compute_grand_margin(data, values, aggfunc, margins_name)

    if table.ndim == 2:
        # i.e. DataFrame
        for level in table.columns.names[1:]:
            if margins_name in table.columns.get_level_values(level):
                raise ValueError(msg)

    key: str | tuple[str, ...]
    if len(rows) > 1:
        key = (margins_name, ) + ("", ) * (len(rows) - 1)
    else:
        key = margins_name

    if not values and isinstance(table, ABCSeries):
        # If there are no values and the table is a series, then there is only
        # one column in the data. Compute grand margin and return it.
        return table.append(Series({key: grand_margin[margins_name]}))

    elif values:
        marginal_result_set = _generate_marginal_results(
            table, data, values, rows, cols, aggfunc, observed, margins_name)
        if not isinstance(marginal_result_set, tuple):
            return marginal_result_set
        result, margin_keys, row_margin = marginal_result_set
    else:
        # no values, and table is a DataFrame
        assert isinstance(table, ABCDataFrame)
        marginal_result_set = _generate_marginal_results_without_values(
            table, data, rows, cols, aggfunc, observed, margins_name)
        if not isinstance(marginal_result_set, tuple):
            return marginal_result_set
        result, margin_keys, row_margin = marginal_result_set

    row_margin = row_margin.reindex(result.columns, fill_value=fill_value)
    # populate grand margin
    for k in margin_keys:
        if isinstance(k, str):
            row_margin[k] = grand_margin[k]
        else:
            row_margin[k] = grand_margin[k[0]]

    from pandas import DataFrame

    margin_dummy = DataFrame(row_margin, columns=[key]).T

    row_names = result.index.names
    # check the result column and leave floats
    for dtype in set(result.dtypes):
        cols = result.select_dtypes([dtype]).columns
        margin_dummy[cols] = margin_dummy[cols].apply(maybe_downcast_to_dtype,
                                                      args=(dtype, ))
    result = result.append(margin_dummy)
    result.index.names = row_names

    return result
Example #5
0
def aggregate(
    indices: pd.DataFrame,
    weights: FrameOrSeriesUnion,
    method: str = 'mean',
    axis: Axis = 1,
) -> pd.Series:
    """
    Aggregate unchained indices with weights using the given method.

    Supports mean aggregation by default, where the weight shares are
    calculated from the weights, and the sum product of indices and
    weight shares calculated to get the mean.

    Also supports geometric mean aggregation, which takes the exponent
    of the sum product of the natural log of the indices and the weight
    shares.

    If the weights are not the same shape as the indices, then they
    will be reshaped by the function. This allows the user to pass in
    a DataFrame of weights, with only the time periods where the
    weights are updated, as weights are usually fixed over different
    periods.

    Parameters
    ----------
    indices: DataFrame
        The indices or price relatives to aggregate.
    weights: DataFrame or Series
        The weights to be aggregated with the indices. Can be a Series
        when there is only one base period in the dataset.
    method: {'mean', 'geomean'}, str defaults to mean
        The aggregation method.
    axis : {0 or ‘index’, 1 or ‘columns’}, default 1
        Axis along which the function is applied:
            * 0 or ‘index’: apply function to each column.
            * 1 or ‘columns’: apply function to each row.

    Returns
    -------
    Series:
        The aggregated index.
    """
    axis = _handle_axis(axis)

    methods_lib = {
        'mean': mean_aggregate,
        'geomean': geo_mean_aggregate,
    }
    agg_method = methods_lib.get(method)

    # Make sure that the indices and weights have the same time series
    # axis before aggregating.
    weights = reindex_weights_to_indices(weights, indices, flip(axis))

    # Ensure zero, NA and inf indices have zero weight so weight shares
    # calculation reflects the indices being excluded.
    zero_weights_mask = indices.isin([0, np.nan, np.inf])
    masked_weights = weights.mask(zero_weights_mask, 0)

    # Except where all indices are zero, NA and inf.
    slice_ = axis_slice(zero_weights_mask.all(axis), flip(axis))
    masked_weights.loc[slice_] = np.nan

    weight_shares = get_weight_shares(masked_weights, axis)
    return agg_method(indices, weight_shares, axis)