Skip to content

DataFrame.style.applymap: subset kwarg fails with MultiIndexSlice #19861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
fleimgruber opened this issue Feb 23, 2018 · 2 comments · Fixed by #19881
Closed

DataFrame.style.applymap: subset kwarg fails with MultiIndexSlice #19861

fleimgruber opened this issue Feb 23, 2018 · 2 comments · Fixed by #19881
Labels
Bug IO HTML read_html, to_html, Styler.apply, Styler.applymap
Milestone

Comments

@fleimgruber
Copy link
Contributor

fleimgruber commented Feb 23, 2018

Code Sample, a copy-pastable example if possible

import pandas as pd


def color_negative_red(val):
    """
    Takes a scalar and returns a string with
    the css property `'color: red'` for negative
    strings, black otherwise.
    """
    color = 'red' if val < 0 else 'black'
    return 'color: %s' % color


dic = {('a', 'd'): [-1.12, 2.11],
       ('a', 'c'): [2.78, -2.88],
       ('b', 'c'): [-3.99, 3.77],
       ('b', 'd'): [4.21, -1.22],
}

idx = pd.IndexSlice
df = pd.DataFrame(dic, index=[0, 1])

html = (
        df.style
        # .applymap(color_negative_red)
        .applymap(color_negative_red, subset=idx[:, idx['b', 'd']])
        .render()
    )

Problem description

The subset keyword argument does not work with a MultiIndex IndexSlice. The relevant documentation says "For row and column slicing, any valid indexer to .loc will work" (source).

Actual Output

Traceback (most recent call last):
  File "<stdin>", line 5, in <module>
  File "c:\Users\user\AppData\Local\Continuum\Miniconda3\envs\lg\lib\site-packages\pandas\io\formats\style.py", line 434, in render
    self._compute()
  File "c:\Users\user\AppData\Local\Continuum\Miniconda3\envs\lg\lib\site-packages\pandas\io\formats\style.py", line 502, in _compute
    r = func(self)(*args, **kwargs)
  File "c:\Users\user\AppData\Local\Continuum\Miniconda3\envs\lg\lib\site-packages\pandas\io\formats\style.py", line 591, in _applymap
    result = self.data.loc[subset].applymap(func)
  File "c:\Users\user\AppData\Local\Continuum\Miniconda3\envs\lg\lib\site-packages\pandas\core\generic.py", line 3081, in __getattr__
    return object.__getattribute__(self, name)
AttributeError: 'Series' object has no attribute 'applymap'

Expected Output

color_negative_red applied to the cell(s) corresponding to the MultiIndex IndexSlice.

Output of pd.show_versions()

[paste the output of pd.show_versions() here below this line]

INSTALLED VERSIONS

commit: None
python: 3.6.3.final.0
python-bits: 64
OS: Windows
OS-release: 7
machine: AMD64
processor: Intel64 Family 6 Model 78 Stepping 3, GenuineIntel
byteorder: little
LC_ALL: None
LANG: None
LOCALE: None.None
pandas: 0.22.0
pytest: 3.3.2
pip: 9.0.1
setuptools: 38.4.0
Cython: None
numpy: 1.14.0
scipy: 1.0.0
pyarrow: 0.8.0
xarray: None
IPython: None
sphinx: 1.6.6
patsy: None
dateutil: 2.6.1
pytz: 2017.3
blosc: None
bottleneck: None
tables: None
numexpr: None
feather: 0.4.0
matplotlib: None
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml: None
bs4: None
html5lib: None
sqlalchemy: None
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: None

@TomAugspurger
Copy link
Contributor

Can you try changing your indexer to subset=idx[:, [idx['b', 'd']]? note the extra brackets around the column slice.

We could probably detect _non_reducing_slice to account for a MI in the columns. Want to take a look?

pandas/pandas/core/indexing.py

Lines 2156 to 2162 in 01e99de

def _non_reducing_slice(slice_):
"""
Ensurse that a slice doesn't reduce to a Series or Scalar.
Any user-paseed `subset` should have this called on it
to make sure we're always working with DataFrames.
"""

@TomAugspurger TomAugspurger added Bug IO HTML read_html, to_html, Styler.apply, Styler.applymap Difficulty Intermediate labels Feb 23, 2018
@fleimgruber
Copy link
Contributor Author

Thanks for the prompt response.

Can you try changing your indexer to subset=idx[:, [idx['b', 'd']]]? note the extra brackets around the column slice.

Yes, this works and will serve for now.

We could probably detect _non_reducing_slice to account for a MI in the columns. Want to take a look?

I can have a look at it, yes.

fleimgruber added a commit to fleimgruber/pandas that referenced this issue Feb 24, 2018
Changes behaviour of user-passed IndexSlice to return DataFrame instead of
reducing to Series.  MultiIndex slices are tuples so this explicitly checks type
and guards with parentheses.

Fixes pandas-dev#19861
fleimgruber added a commit to fleimgruber/pandas that referenced this issue Feb 24, 2018
Changes behaviour of user-passed IndexSlice to return DataFrame instead of
reducing to Series.  MultiIndex slices are tuples so this explicitly checks type
and guards with parentheses.

Fixes pandas-dev#19861
fleimgruber added a commit to fleimgruber/pandas that referenced this issue Feb 24, 2018
Changes behaviour of user-passed IndexSlice to return DataFrame instead of
reducing to Series.  MultiIndex slices are tuples so this explicitly checks type
and guards with parentheses.

Fixes pandas-dev#19861
@jreback jreback added this to the 0.23.0 milestone Feb 24, 2018
@jreback jreback modified the milestones: 0.23.0, Next Major Release Apr 14, 2018
fleimgruber added a commit to fleimgruber/pandas that referenced this issue Nov 1, 2018
Changes behaviour of user-passed IndexSlice to return DataFrame instead of
reducing to Series.  MultiIndex slices are tuples so this explicitly checks type
and guards with parentheses.

Fixes pandas-dev#19861
fleimgruber added a commit to fleimgruber/pandas that referenced this issue Nov 21, 2018
Changes behaviour of user-passed IndexSlice to return DataFrame instead of
reducing to Series.  MultiIndex slices are tuples so this explicitly checks type
and guards with parentheses.

Fixes pandas-dev#19861
fleimgruber added a commit to fleimgruber/pandas that referenced this issue Nov 21, 2018
Changes behaviour of user-passed IndexSlice to return DataFrame instead of
reducing to Series.  MultiIndex slices are tuples so this explicitly checks type
and guards with parentheses.

Fixes pandas-dev#19861
fleimgruber added a commit to fleimgruber/pandas that referenced this issue Dec 27, 2018
Changes behaviour of user-passed IndexSlice to return DataFrame instead of
reducing to Series.  MultiIndex slices are tuples so this explicitly checks type
and guards with parentheses.

Fixes pandas-dev#19861
@jreback jreback modified the milestones: Contributions Welcome, 0.24.0 Dec 27, 2018
fleimgruber added a commit to fleimgruber/pandas that referenced this issue Jan 2, 2019
Changes behaviour of user-passed IndexSlice to return DataFrame instead of
reducing to Series.  MultiIndex slices are tuples so this explicitly checks type
and guards with parentheses.

Fixes pandas-dev#19861
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug IO HTML read_html, to_html, Styler.apply, Styler.applymap
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants