def _marshall_styler(proto: ArrowProto, styler: Styler, default_uuid: str) -> None: """Marshall pandas.Styler into an Arrow proto. Parameters ---------- proto : proto.Arrow Output. The protobuf for Streamlit Arrow proto. styler : pandas.Styler Helps style a DataFrame or Series according to the data with HTML and CSS. default_uuid : str If pandas.Styler uuid is not provided, this value will be used. """ # pandas.Styler uuid should be set before _compute is called. _marshall_uuid(proto, styler, default_uuid) # We're using protected members of pandas.Styler to get styles, # which is not ideal and could break if the interface changes. styler._compute() # In Pandas 1.3.0, styler._translate() signature was changed. # 2 arguments were added: sparse_index and sparse_columns. # The functionality that they provide is not yet supported. if type_util.is_pandas_version_less_than("1.3.0"): pandas_styles = styler._translate() else: pandas_styles = styler._translate(False, False) _marshall_caption(proto, styler) _marshall_styles(proto, styler, pandas_styles) _marshall_display_values(proto, styler.data, pandas_styles)
def _marshall_styler(proto, styler, default_uuid): """Marshall pandas.Styler styling data into an ArrowTable proto. Parameters ---------- proto : proto.ArrowTable Output. The protobuf for a Streamlit ArrowTable proto. styler : pandas.Styler Styler holding styling data for the dataframe. default_uuid : str If Styler custom uuid is not provided, this value will be used. """ # NB: UUID should be set before _compute is called. _marshall_uuid(proto, styler, default_uuid) # NB: We're using protected members of Styler to get styles, # which is non-ideal and could break if Styler's interface changes. styler._compute() # In Pandas 1.3.0, styler._translate() signature was changed. # 2 arguments were added: sparse_index and sparse_columns. # The functionality that they provide is not yet supported. if type_util.is_pandas_version_less_than("1.3.0"): pandas_styles = styler._translate() else: pandas_styles = styler._translate(False, False) _marshall_caption(proto, styler) _marshall_styles(proto, styler, pandas_styles) _marshall_display_values(proto, styler.data, pandas_styles)
def _pandas_style_to_css(style_type, style, uuid, separator=""): """Convert pandas.Styler translated styles entry to CSS. Parameters ---------- style : dict pandas.Styler translated styles entry. uuid: str pandas.Styler UUID. separator: str A string separator used between table and cell selectors. """ declarations = [] for css_property, css_value in style["props"]: declaration = css_property.strip() + ": " + css_value.strip() declarations.append(declaration) table_selector = "#T_" + str(uuid) # In pandas < 1.1.0 # translated_style["cellstyle"] has the following shape: # [ # { # "props": [["color", " black"], ["background-color", "orange"], ["", ""]], # "selector": "row0_col0" # } # ... # ] # # In pandas >= 1.1.0 # translated_style["cellstyle"] has the following shape: # [ # { # "props": [("color", " black"), ("background-color", "orange"), ("", "")], # "selectors": ["row0_col0"] # } # ... # ] if style_type == "table_styles" or ( style_type == "cell_style" and type_util.is_pandas_version_less_than("1.1.0")): cell_selectors = [style["selector"]] else: cell_selectors = style["selectors"] selectors = [] for cell_selector in cell_selectors: selectors.append(table_selector + separator + cell_selector) selector = ", ".join(selectors) declaration_block = "; ".join(declarations) rule_set = selector + " { " + declaration_block + " }" return rule_set
def _get_css_styles(translated_style): """Parses pandas.Styler style dictionary into a {(row, col): [CSSStyle]} dictionary """ # In pandas < 1.1.0 # translated_style["cellstyle"] has the following shape: # [ # { # "props": [["color", " black"], ["background-color", "orange"], ["", ""]], # "selector": "row0_col0" # } # ... # ] # # In pandas >= 1.1.0 # translated_style["cellstyle"] has the following shape: # [ # { # "props": [("color", " black"), ("background-color", "orange"), ("", "")], # "selectors": ["row0_col0"] # } # ... # ] cell_selector_regex = re.compile(r"row(\d+)_col(\d+)") css_styles = {} for cell_style in translated_style["cellstyle"]: if type_util.is_pandas_version_less_than("1.1.0"): cell_selectors = [cell_style["selector"]] else: cell_selectors = cell_style["selectors"] for cell_selector in cell_selectors: match = cell_selector_regex.match(cell_selector) if not match: raise RuntimeError('Failed to parse cellstyle selector "%s"' % cell_selector) row = int(match.group(1)) col = int(match.group(2)) css_declarations = [] props = cell_style["props"] for prop in props: if not isinstance(prop, (tuple, list)) or len(prop) != 2: raise RuntimeError('Unexpected cellstyle props "%s"' % prop) name = str(prop[0]).strip() value = str(prop[1]).strip() if name and value: css_declarations.append( CSSStyle(property=name, value=value)) css_styles[(row, col)] = css_declarations return css_styles
def _marshall_styles(proto_table_style: TableStyleProto, df: DataFrame, styler: Optional[Styler] = None) -> None: """Adds pandas.Styler styling data to a proto.DataFrame Parameters ---------- proto_table_style : proto.TableStyle df : pandas.DataFrame styler : pandas.Styler holding styling data for the data frame, or None if there's no style data to marshall """ # NB: we're using protected members of Styler to get this data, # which is non-ideal and could break if Styler's interface changes. if styler is not None: styler._compute() # In Pandas 1.3.0, styler._translate() signature was changed. # 2 arguments were added: sparse_index and sparse_columns. # The functionality that they provide is not yet supported. if type_util.is_pandas_version_less_than("1.3.0"): translated_style = styler._translate() else: translated_style = styler._translate(False, False) css_styles = _get_css_styles(translated_style) display_values = _get_custom_display_values(translated_style) else: # If we have no Styler, we just make an empty CellStyle for each cell css_styles = {} display_values = {} nrows, ncols = df.shape for col in range(ncols): proto_col = proto_table_style.cols.add() for row in range(nrows): proto_cell_style = proto_col.styles.add() for css in css_styles.get((row, col), []): proto_css = proto_cell_style.css.add() proto_css.property = css.property proto_css.value = css.value display_value = display_values.get((row, col), None) if display_value is not None: proto_cell_style.display_value = display_value proto_cell_style.has_display_value = True
from unittest.mock import patch import numpy as np import pandas as pd import pyarrow as pa from streamlit.type_util import ( bytes_to_data_frame, is_pandas_version_less_than, pyarrow_table_to_bytes, ) from tests import testutil import streamlit as st # In Pandas 1.3.0, Styler functionality was moved under StylerRenderer. if is_pandas_version_less_than("1.3.0"): from pandas.io.formats.style import Styler else: from pandas.io.formats.style_render import StylerRenderer as Styler def mock_data_frame(): return pd.DataFrame( index=[[0, 1], ["i1", "i2"]], columns=[[2, 3, 4], ["c1", "c2", "c3"]], data=np.arange(0, 6, 1).reshape(2, 3), ) class ArrowTest(testutil.DeltaGeneratorTestCase): """Test ability to marshall arrow protos."""