def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0, subset=None): """ Color the background in a gradient according to the data in each column (optionally row). Requires matplotlib. Parameters ---------- cmap: str or colormap matplotlib colormap low, high: float compress the range by these values. axis: int or str 1 or 'columns' for columnwise, 0 or 'index' for rowwise subset: IndexSlice a valid slice for ``data`` to limit the style application to Returns ------- self : Styler Notes ----- Tune ``low`` and ``high`` to keep the text legible by not using the entire range of the color map. These extend the range of the data by ``low * (x.max() - x.min())`` and ``high * (x.max() - x.min())`` before normalizing. """ subset = _maybe_numeric_slice(self.data, subset) subset = _non_reducing_slice(subset) self.apply(self._background_gradient, cmap=cmap, subset=subset, axis=axis, low=low, high=high) return self
def _apply(self, func, axis=0, subset=None, **kwargs): subset = slice(None) if subset is None else subset subset = _non_reducing_slice(subset) data = self.data.loc[subset] if axis is not None: result = data.apply(func, axis=axis, result_type='expand', **kwargs) result.columns = data.columns else: result = func(data, **kwargs) if not isinstance(result, pd.DataFrame): raise TypeError( "Function {func!r} must return a DataFrame when " "passed to `Styler.apply` with axis=None" .format(func=func)) if not (result.index.equals(data.index) and result.columns.equals(data.columns)): msg = ('Result of {func!r} must have identical index and ' 'columns as the input'.format(func=func)) raise ValueError(msg) result_shape = result.shape expected_shape = self.data.loc[subset].shape if result_shape != expected_shape: msg = ("Function {func!r} returned the wrong shape.\n" "Result has shape: {res}\n" "Expected shape: {expect}".format(func=func, res=result.shape, expect=expected_shape)) raise ValueError(msg) self._update_ctx(result) return self
def bar(self, subset=None, axis=0, color='#d65f5f', width=100): """ Color the background ``color`` proptional to the values in each column. Excludes non-numeric data by default. .. versionadded:: 0.17.1 Parameters ---------- subset: IndexSlice, default None a valid slice for ``data`` to limit the style application to axis: int color: str width: float A number between 0 or 100. The largest value will cover ``width`` percent of the cell's width Returns ------- self : Styler """ subset = _maybe_numeric_slice(self.data, subset) subset = _non_reducing_slice(subset) self.apply(self._bar, subset=subset, axis=axis, color=color, width=width) return self
def test_list_slice(self): # like dataframe getitem slices = [['A'], Series(['A']), np.array(['A'])] df = DataFrame({'A': [1, 2], 'B': [3, 4]}, index=['A', 'B']) expected = pd.IndexSlice[:, ['A']] for subset in slices: result = _non_reducing_slice(subset) tm.assert_frame_equal(df.loc[result], df.loc[expected])
def _applymap(self, func, subset=None, **kwargs): func = partial(func, **kwargs) # applymap doesn't take kwargs? if subset is None: subset = pd.IndexSlice[:] subset = _non_reducing_slice(subset) result = self.data.loc[subset].applymap(func) self._update_ctx(result) return self
def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0, subset=None, text_color_threshold=0.408): """ Color the background in a gradient according to the data in each column (optionally row). Requires matplotlib. Parameters ---------- cmap : str or colormap matplotlib colormap low, high : float compress the range by these values. axis : {0 or 'index', 1 or 'columns', None}, default 0 apply to each column (``axis=0`` or ``'index'``), to each row (``axis=1`` or ``'columns'``), or to the entire DataFrame at once with ``axis=None``. subset : IndexSlice a valid slice for ``data`` to limit the style application to. text_color_threshold : float or int luminance threshold for determining text color. Facilitates text visibility across varying background colors. From 0 to 1. 0 = all text is dark colored, 1 = all text is light colored. .. versionadded:: 0.24.0 Returns ------- self : Styler Raises ------ ValueError If ``text_color_threshold`` is not a value from 0 to 1. Notes ----- Set ``text_color_threshold`` or tune ``low`` and ``high`` to keep the text legible by not using the entire range of the color map. The range of the data is extended by ``low * (x.max() - x.min())`` and ``high * (x.max() - x.min())`` before normalizing. """ subset = _maybe_numeric_slice(self.data, subset) subset = _non_reducing_slice(subset) self.apply(self._background_gradient, cmap=cmap, subset=subset, axis=axis, low=low, high=high, text_color_threshold=text_color_threshold) return self
def _apply_formatters(self): """Apply all added formatting.""" for subset, function in self.formatters: if subset is None: subset = self.data.index else: subset = _non_reducing_slice(subset) self.data.loc[subset] = self.data.loc[subset].applymap(function) return self
def _apply(self, func, axis=0, subset=None, **kwargs): subset = slice(None) if subset is None else subset subset = _non_reducing_slice(subset) if axis is not None: result = self.data.loc[subset].apply(func, axis=axis, **kwargs) else: # like tee result = func(self.data.loc[subset], **kwargs) self._update_ctx(result) return self
def _highlight_handler(self, subset=None, color='yellow', axis=None, max_=True): subset = _non_reducing_slice(_maybe_numeric_slice(self.data, subset)) self.apply(self._highlight_extrema, color=color, axis=axis, subset=subset, max_=max_) return self
def test_non_reducing_slice_on_multiindex(self): # GH 19861 dic = { ("a", "d"): [1, 4], ("a", "c"): [2, 3], ("b", "c"): [3, 2], ("b", "d"): [4, 1], } df = pd.DataFrame(dic, index=[0, 1]) idx = pd.IndexSlice slice_ = idx[:, idx["b", "d"]] tslice_ = _non_reducing_slice(slice_) result = df.loc[tslice_] expected = pd.DataFrame({("b", "d"): [4, 1]}) tm.assert_frame_equal(result, expected)
def test_non_reducing_slice_on_multiindex(self): # GH 19861 dic = { ('a', 'd'): [1, 4], ('a', 'c'): [2, 3], ('b', 'c'): [3, 2], ('b', 'd'): [4, 1] } df = pd.DataFrame(dic, index=[0, 1]) idx = pd.IndexSlice slice_ = idx[:, idx['b', 'd']] tslice_ = _non_reducing_slice(slice_) result = df.loc[tslice_] expected = pd.DataFrame({('b', 'd'): [4, 1]}) tm.assert_frame_equal(result, expected)
def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0, subset=None): """ Color the background in a gradient according to the data in each column (optionally row). Requires matplotlib. .. versionadded:: 0.17.1 Parameters ---------- cmap: str or colormap matplotlib colormap low, high: float compress the range by these values. axis: int or str 1 or 'columns' for columnwise, 0 or 'index' for rowwise subset: IndexSlice a valid slice for ``data`` to limit the style application to Returns ------- self : Styler Notes ----- Tune ``low`` and ``high`` to keep the text legible by not using the entire range of the color map. These extend the range of the data by ``low * (x.max() - x.min())`` and ``high * (x.max() - x.min())`` before normalizing. """ subset = _maybe_numeric_slice(self.data, subset) subset = _non_reducing_slice(subset) self.apply(self._background_gradient, cmap=cmap, subset=subset, axis=axis, low=low, high=high) return self
def hide_columns(self, subset): """ Hide columns from rendering. .. versionadded:: 0.23.0 Parameters ---------- subset: IndexSlice An argument to ``DataFrame.loc`` that identifies which columns are hidden. Returns ------- self : Styler """ subset = _non_reducing_slice(subset) hidden_df = self.data.loc[subset] self.hidden_columns = self.columns.get_indexer_for(hidden_df.columns) return self
def test_non_reducing_slice(self): df = DataFrame([[0, 1], [2, 3]]) slices = [ # pd.IndexSlice[:, :], pd.IndexSlice[:, 1], pd.IndexSlice[1, :], pd.IndexSlice[[1], [1]], pd.IndexSlice[1, [1]], pd.IndexSlice[[1], 1], pd.IndexSlice[1], pd.IndexSlice[1, 1], slice(None, None, None), [0, 1], np.array([0, 1]), Series([0, 1]) ] for slice_ in slices: tslice_ = _non_reducing_slice(slice_) assert isinstance(df.loc[tslice_], DataFrame)
def test_non_reducing_slice(self): df = pd.DataFrame([[0, 1], [2, 3]]) slices = [ # pd.IndexSlice[:, :], pd.IndexSlice[:, 1], pd.IndexSlice[1, :], pd.IndexSlice[[1], [1]], pd.IndexSlice[1, [1]], pd.IndexSlice[[1], 1], pd.IndexSlice[1], pd.IndexSlice[1, 1], slice(None, None, None), [0, 1], np.array([0, 1]), pd.Series([0, 1]) ] for slice_ in slices: tslice_ = _non_reducing_slice(slice_) assert isinstance(df.loc[tslice_], DataFrame)
def _apply(self, func, axis=0, subset=None, **kwargs): subset = slice(None) if subset is None else subset subset = _non_reducing_slice(subset) data = self.data.loc[subset] if axis is not None: result = data.apply(func, axis=axis, result_type="expand", **kwargs) result.columns = data.columns else: result = func(data, **kwargs) if not isinstance(result, pd.DataFrame): raise TypeError( "Function {func!r} must return a DataFrame when " "passed to `Styler.apply` with axis=None".format(func=func) ) if not ( result.index.equals(data.index) and result.columns.equals(data.columns) ): msg = ( "Result of {func!r} must have identical index and " "columns as the input".format(func=func) ) raise ValueError(msg) result_shape = result.shape expected_shape = self.data.loc[subset].shape if result_shape != expected_shape: msg = ( "Function {func!r} returned the wrong shape.\n" "Result has shape: {res}\n" "Expected shape: {expect}".format( func=func, res=result.shape, expect=expected_shape ) ) raise ValueError(msg) self._update_ctx(result) return self
def background_gradient( self, cmap="PuBu", low=0, high=0, axis=0, subset=None, text_color_threshold=0.408, vmin: Optional[float] = None, vmax: Optional[float] = None, ): """ Color the background in a gradient style. The background font is determined according to the data in each column (optionally row). Requires matplotlib. Parameters ---------- cmap : str or colormap Matplotlib colormap. low : float Compress the range by the low. high : float Compress the range by the high. axis : {0 or 'index', 1 or 'columns', None}, default 0 Apply to each column (``axis=0`` or ``'index'``), to each row (``axis=1`` or ``'columns'``), or to the entire DataFrame at once with ``axis=None``. subset : IndexSlice A valid slice for ``data`` to limit the style application to. text_color_threshold : float or int Luminance threshold for determining text font. Facilitates text visibility across varying background colors. From 0 to 1. 0 = all text is dark colored, 1 = all text is light colored. .. versionadded:: 0.24.0 vmin : float, optional Minimum data value that corresponds to colormap minimum value. When None (default): the minimum value of the data will be used. .. versionadded:: 1.0.0 vmax : float, optional Maximum data value that corresponds to colormap maximum value. When None (default): the maximum value of the data will be used. .. versionadded:: 1.0.0 Returns ------- self : Styler Raises ------ ValueError If ``text_color_threshold`` is not a value from 0 to 1. Notes ----- Set ``text_color_threshold`` or tune ``low`` and ``high`` to keep the text legible by not using the entire range of the font map. The range of the data is extended by ``low * (x.max() - x.min())`` and ``high * (x.max() - x.min())`` before normalizing. """ subset = _maybe_numeric_slice(self.data, subset) subset = _non_reducing_slice(subset) self.apply( self._background_gradient, cmap=cmap, subset=subset, axis=axis, low=low, high=high, text_color_threshold=text_color_threshold, vmin=vmin, vmax=vmax, ) return self
def test_non_reducing_slice(self, slc): df = DataFrame([[0, 1], [2, 3]]) tslice_ = _non_reducing_slice(slc) assert isinstance(df.loc[tslice_], DataFrame)
def format(self, formatter, subset=None): """ Format the text display value of cells. .. versionadded:: 0.18.0 Parameters ---------- formatter: str, callable, or dict subset: IndexSlice An argument to ``DataFrame.loc`` that restricts which elements ``formatter`` is applied to. Returns ------- self : Styler Notes ----- ``formatter`` is either an ``a`` or a dict ``{column name: a}`` where ``a`` is one of - str: this will be wrapped in: ``a.format(x)`` - callable: called with the value of an individual cell The default display value for numeric values is the "general" (``g``) format with ``pd.options.display.precision`` precision. Examples -------- >>> df = pd.DataFrame(np.random.randn(4, 2), columns=['a', 'b']) >>> df.style.format("{:.2%}") >>> df['c'] = ['a', 'b', 'c', 'd'] >>> df.style.format({'c': str.upper}) """ if subset is None: row_locs = range(len(self.data)) col_locs = range(len(self.data.columns)) else: subset = _non_reducing_slice(subset) if len(subset) == 1: subset = subset, self.data.columns sub_df = self.data.loc[subset] row_locs = self.data.index.get_indexer_for(sub_df.index) col_locs = self.data.columns.get_indexer_for(sub_df.columns) if isinstance(formatter, MutableMapping): for col, col_formatter in formatter.items(): # formatter must be callable, so '{}' are converted to lambdas col_formatter = _maybe_wrap_formatter(col_formatter) col_num = self.data.columns.get_indexer_for([col])[0] for row_num in row_locs: self._display_funcs[(row_num, col_num)] = col_formatter else: # single scalar to format all cells with locs = product(*(row_locs, col_locs)) for i, j in locs: formatter = _maybe_wrap_formatter(formatter) self._display_funcs[(i, j)] = formatter return self
def bar(self, subset=None, axis=0, color='#d65f5f', width=100, align='left'): """ Color the background ``color`` proportional to the values in each column. Excludes non-numeric data by default. Parameters ---------- subset: IndexSlice, default None a valid slice for ``data`` to limit the style application to axis: int color: str or 2-tuple/list If a str is passed, the color is the same for both negative and positive numbers. If 2-tuple/list is used, the first element is the color_negative and the second is the color_positive (eg: ['#d65f5f', '#5fba7d']) width: float A number between 0 or 100. The largest value will cover ``width`` percent of the cell's width align : {'left', 'zero',' mid'}, default 'left' - 'left' : the min value starts at the left of the cell - 'zero' : a value of zero is located at the center of the cell - 'mid' : the center of the cell is at (max-min)/2, or if values are all negative (positive) the zero is aligned at the right (left) of the cell .. versionadded:: 0.20.0 Returns ------- self : Styler """ subset = _maybe_numeric_slice(self.data, subset) subset = _non_reducing_slice(subset) base = 'width: 10em; height: 80%;' if not(is_list_like(color)): color = [color, color] elif len(color) == 1: color = [color[0], color[0]] elif len(color) > 2: msg = ("Must pass `color` as string or a list-like" " of length 2: [`color_negative`, `color_positive`]\n" "(eg: color=['#d65f5f', '#5fba7d'])") raise ValueError(msg) if align == 'left': self.apply(self._bar_left, subset=subset, axis=axis, color=color, width=width, base=base) elif align == 'zero': self.apply(self._bar_center_zero, subset=subset, axis=axis, color=color, width=width, base=base) elif align == 'mid': self.apply(self._bar_center_mid, subset=subset, axis=axis, color=color, width=width, base=base) else: msg = ("`align` must be one of {'left', 'zero',' mid'}") raise ValueError(msg) return self
def bar(self, subset=None, axis=0, color='#d65f5f', width=100, align='left', vmin=None, vmax=None): """ Draw bar chart in the cell backgrounds. Parameters ---------- subset : IndexSlice, optional A valid slice for `data` to limit the style application to. axis : int, str or None, default 0 Apply to each column (`axis=0` or `'index'`) or to each row (`axis=1` or `'columns'`) or to the entire DataFrame at once with `axis=None`. color : str or 2-tuple/list If a str is passed, the color is the same for both negative and positive numbers. If 2-tuple/list is used, the first element is the color_negative and the second is the color_positive (eg: ['#d65f5f', '#5fba7d']). width : float, default 100 A number between 0 or 100. The largest value will cover `width` percent of the cell's width. align : {'left', 'zero',' mid'}, default 'left' How to align the bars with the cells. - 'left' : the min value starts at the left of the cell. - 'zero' : a value of zero is located at the center of the cell. - 'mid' : the center of the cell is at (max-min)/2, or if values are all negative (positive) the zero is aligned at the right (left) of the cell. .. versionadded:: 0.20.0 vmin : float, optional Minimum bar value, defining the left hand limit of the bar drawing range, lower values are clipped to `vmin`. When None (default): the minimum value of the data will be used. .. versionadded:: 0.24.0 vmax : float, optional Maximum bar value, defining the right hand limit of the bar drawing range, higher values are clipped to `vmax`. When None (default): the maximum value of the data will be used. .. versionadded:: 0.24.0 Returns ------- self : Styler """ if align not in ('left', 'zero', 'mid'): raise ValueError("`align` must be one of {'left', 'zero',' mid'}") if not (is_list_like(color)): color = [color, color] elif len(color) == 1: color = [color[0], color[0]] elif len(color) > 2: raise ValueError("`color` must be string or a list-like" " of length 2: [`color_neg`, `color_pos`]" " (eg: color=['#d65f5f', '#5fba7d'])") subset = _maybe_numeric_slice(self.data, subset) subset = _non_reducing_slice(subset) self.apply(self._bar, subset=subset, axis=axis, align=align, colors=color, width=width, vmin=vmin, vmax=vmax) return self
def bar(self, subset=None, axis=0, color='#d65f5f', width=100, align='left'): """ Color the background ``color`` proptional to the values in each column. Excludes non-numeric data by default. .. versionadded:: 0.17.1 Parameters ---------- subset: IndexSlice, default None a valid slice for ``data`` to limit the style application to axis: int color: str or 2-tuple/list If a str is passed, the color is the same for both negative and positive numbers. If 2-tuple/list is used, the first element is the color_negative and the second is the color_positive (eg: ['#d65f5f', '#5fba7d']) width: float A number between 0 or 100. The largest value will cover ``width`` percent of the cell's width align : {'left', 'zero',' mid'}, default 'left' - 'left' : the min value starts at the left of the cell - 'zero' : a value of zero is located at the center of the cell - 'mid' : the center of the cell is at (max-min)/2, or if values are all negative (positive) the zero is aligned at the right (left) of the cell .. versionadded:: 0.20.0 Returns ------- self : Styler """ subset = _maybe_numeric_slice(self.data, subset) subset = _non_reducing_slice(subset) base = 'width: 10em; height: 80%;' if not (is_list_like(color)): color = [color, color] elif len(color) == 1: color = [color[0], color[0]] elif len(color) > 2: msg = ("Must pass `color` as string or a list-like" " of length 2: [`color_negative`, `color_positive`]\n" "(eg: color=['#d65f5f', '#5fba7d'])") raise ValueError(msg) if align == 'left': self.apply(self._bar_left, subset=subset, axis=axis, color=color, width=width, base=base) elif align == 'zero': self.apply(self._bar_center_zero, subset=subset, axis=axis, color=color, width=width, base=base) elif align == 'mid': self.apply(self._bar_center_mid, subset=subset, axis=axis, color=color, width=width, base=base) else: msg = ("`align` must be one of {'left', 'zero',' mid'}") raise ValueError(msg) return self
def format(self, formatter, subset=None): """ Format the text display value of cells. .. versionadded:: 0.18.0 Parameters ---------- formatter: str, callable, or dict subset: IndexSlice An argument to ``DataFrame.loc`` that restricts which elements ``formatter`` is applied to. Returns ------- self : Styler Notes ----- ``formatter`` is either an ``a`` or a dict ``{column name: a}`` where ``a`` is one of - str: this will be wrapped in: ``a.format(x)`` - callable: called with the value of an individual cell The default display value for numeric values is the "general" (``g``) format with ``pd.options.display.precision`` precision. Examples -------- >>> df = pd.DataFrame(np.random.randn(4, 2), columns=['a', 'b']) >>> df.style.format("{:.2%}") >>> df['c'] = ['a', 'b', 'c', 'd'] >>> df.style.format({'C': str.upper}) """ if subset is None: row_locs = range(len(self.data)) col_locs = range(len(self.data.columns)) else: subset = _non_reducing_slice(subset) if len(subset) == 1: subset = subset, self.data.columns sub_df = self.data.loc[subset] row_locs = self.data.index.get_indexer_for(sub_df.index) col_locs = self.data.columns.get_indexer_for(sub_df.columns) if isinstance(formatter, MutableMapping): for col, col_formatter in formatter.items(): # formatter must be callable, so '{}' are converted to lambdas col_formatter = _maybe_wrap_formatter(col_formatter) col_num = self.data.columns.get_indexer_for([col])[0] for row_num in row_locs: self._display_funcs[(row_num, col_num)] = col_formatter else: # single scalar to format all cells with locs = product(*(row_locs, col_locs)) for i, j in locs: formatter = _maybe_wrap_formatter(formatter) self._display_funcs[(i, j)] = formatter return self