Skip to content

BUG: Styler.map_index() breaks command-query separation by mutating AND returning the Styler #59360

Open
@wjandrea

Description

@wjandrea

I noticed this by messing up the callback signature, but when I fixed the problem, the error kept happening.

Example setup:

df = pd.DataFrame([[1, 2], [3, 4]], columns=["A", "B"])
st = df.style

I meant to write this (makes the index values red):

st.applymap_index(lambda _: 'color: #ff0000', axis='index')

But accidentally wrote this (returning a list):

st.applymap_index(lambda _: ['color: #000000'], axis='index')

This raises ValueError: too many values to unpack (expected 2)

After fixing the callback and re-running the Jupyter cell, the issue kept appearing, which didn't make any sense to me.

I found the problem by looking at the source code:

def map_index(
self,
func: Callable,
axis: AxisInt | str = 0,
level: Level | list[Level] | None = None,
**kwargs,
) -> Styler:
self._todo.append(
(
lambda instance: getattr(instance, "_apply_index"),
(func, axis, level, "map"),
kwargs,
)
)
return self

This modifies the Styler object's _todo attribute and returns the Styler, which is confusing and breaks the command-query separation that Python code normally follows (e.g. list.sort() doesn't return the sorted list; use sorted(list) instead).

I believe you could fix this by making a copy of the Styler object, modifying its _todo, and returning the copy.


  • I have checked that this issue has not already been reported.
    • No bugs even mention .map_index()
  • I have confirmed this bug exists on the latest version of pandas.
    • It exists on 2.0.3, where I noticed it, and the code is the same on main, just under a different name (applymap_index instead of map_index)
  • I have confirmed this bug exists on the main branch of pandas.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Closing CandidateMay be closeable, needs more eyeballsStylerconditional formatting using DataFrame.style

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions