Tabulator on pandas groupby, describe, stack failing

Hello

I have a pandas dataframe in tidy format. Each column is a variable. I would like to show the statistic summary in a table for a selected amount of columns.
This is how I do this:

component.table().groupby(by="Part_no")[component.Steps].describe().stack()

And the result looks like this in a jupyter notebook (only part of the result is shown):
afbeelding

But when I feed this dataframe into a tabulator widget,

pn.widgets.Tabulator(component.table().groupby(by="Variant")[column_list].describe().stack())

the code executing gives me a value error:

ValueError: failed to validate DataTabulator(id=‘21068’, …).indexes: expected an element of List(String), got seq with invalid items [None]

Am I doing something wrong?

I guess you should convert groupby to pd.Dataframe
Before inserting it in tabulator

pn.widgets.Tabulator(pd.Dataframe(component.table().groupby(by=“Variant”)[column_list].describe().stack()))

That’s also what I tried. But it give the same ValueError.

This is the traceback:

ValueError                                Traceback (most recent call last)
~\Anaconda3\envs\jupyterlab-debugger\lib\site-packages\IPython\core\formatters.py in __call__(self, obj, include, exclude)
    968 
    969             if method is not None:
--> 970                 return method(include=include, exclude=exclude)
    971             return None
    972         else:

~\Anaconda3\envs\jupyterlab-debugger\lib\site-packages\panel\viewable.py in _repr_mimebundle_(self, include, exclude)
    614         doc = _Document()
    615         comm = state._comm_manager.get_server_comm()
--> 616         model = self._render_model(doc, comm)
    617         ref = model.ref['id']
    618         manager = CommManager(comm_id=comm.id, plot_id=ref)

~\Anaconda3\envs\jupyterlab-debugger\lib\site-packages\panel\viewable.py in _render_model(self, doc, comm)
    453         if comm is None:
    454             comm = state._comm_manager.get_server_comm()
--> 455         model = self.get_root(doc, comm)
    456 
    457         if config.embed:

~\Anaconda3\envs\jupyterlab-debugger\lib\site-packages\panel\viewable.py in get_root(self, doc, comm, preprocess)
    510         """
    511         doc = init_doc(doc)
--> 512         root = self._get_model(doc, comm=comm)
    513         if preprocess:
    514             self._preprocess(root)

~\Anaconda3\envs\jupyterlab-debugger\lib\site-packages\panel\widgets\tables.py in _get_model(self, doc, root, parent, comm)
   1051         if comm:
   1052             with set_resource_mode('inline'):
-> 1053                 model = super()._get_model(doc, root, parent, comm)
   1054         else:
   1055             model = super()._get_model(doc, root, parent, comm)

~\Anaconda3\envs\jupyterlab-debugger\lib\site-packages\panel\widgets\tables.py in _get_model(self, doc, root, parent, comm)
    185         source.selected.indices = self.selection
    186         properties = self._get_properties(source)
--> 187         model = self._widget_type(**properties)
    188         if root is None:
    189             root = model

~\Anaconda3\envs\jupyterlab-debugger\lib\site-packages\bokeh\model\model.py in __init__(self, **kwargs)
    126         kwargs.pop("id", None)
    127 
--> 128         super().__init__(**kwargs)
    129         default_theme.apply_to_model(self)
    130 

~\Anaconda3\envs\jupyterlab-debugger\lib\site-packages\bokeh\core\has_props.py in __init__(self, **properties)
    204 
    205         for name, value in properties.items():
--> 206             setattr(self, name, value)
    207 
    208         self._initialized = True

~\Anaconda3\envs\jupyterlab-debugger\lib\site-packages\bokeh\core\has_props.py in __setattr__(self, name, value)
    228         properties = self.properties(_with_props=True)
    229         if name in properties:
--> 230             return super().__setattr__(name, value)
    231 
    232         descriptor = getattr(self.__class__, name, None)

~\Anaconda3\envs\jupyterlab-debugger\lib\site-packages\bokeh\core\property\descriptors.py in __set__(self, obj, value, setter)
    281             raise RuntimeError(f"{class_name}.{self.name} is a readonly property")
    282 
--> 283         value = self.property.prepare_value(obj, self.name, value)
    284         old = self._get(obj)
    285         self._set(obj, old, value, setter=setter)

~\Anaconda3\envs\jupyterlab-debugger\lib\site-packages\bokeh\core\property\bases.py in prepare_value(self, owner, name, value, hint)
    363         else:
    364             obj_repr = owner if isinstance(owner, HasProps) else owner.__name__
--> 365             raise ValueError(f"failed to validate {obj_repr}.{name}: {error}")
    366 
    367         if isinstance(owner, HasProps):

ValueError: failed to validate DataTabulator(id='1743', ...).indexes: expected an element of List(String), got seq with invalid items [None]

I think you should flatten you dataframe. Maybe with something like this: df.columns = df.columns.get_level_values(0)

But without a minimal, reproducible example (MRE) it is hard to determine what exactly the problem is.

1 Like

This stack creates a MultiIndex DataFrame with None as name. This can be fixed by setting the name to an empty string:

import numpy as np
import panel as pn
from pandas.util.testing import makeDataFrame
pn.extension('tabulator')

# Create DataFrame
df0 = makeDataFrame()
df0["part"] = np.random.randint(1, 5, df0.shape[0])
df = df0.groupby(by="part").describe().stack()

# The fix
df.index.set_names(["part", ""], inplace=True)

# Tabulator
pn.widgets.Tabulator(df)
1 Like

Thanks @Hoxbro