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
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
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))
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
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)