Skip to content
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

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

Closed
fleimgruber opened this issue Feb 23, 2018 · 2 comments

Comments

Projects
None yet
3 participants
@fleimgruber
Copy link
Contributor

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

This comment has been minimized.

Copy link
Contributor

commented Feb 23, 2018

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.
"""

@fleimgruber

This comment has been minimized.

Copy link
Contributor Author

commented Feb 23, 2018

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

Include MultiIndex slice in non-reducing slices
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

Include MultiIndex slice in non-reducing slices
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

Include MultiIndex slice in non-reducing slices
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

Include MultiIndex slice in non-reducing slices
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

Include MultiIndex slice in non-reducing slices
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

Include MultiIndex slice in non-reducing slices
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

Include MultiIndex slice in non-reducing slices
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

Include MultiIndex slice in non-reducing slices
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
You can’t perform that action at this time.