Skip to content

DataFrame rename fails with TypeError when give a ChainMap instead of a dict #23859

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
dave-kirby opened this issue Nov 22, 2018 · 6 comments · Fixed by #29306
Closed

DataFrame rename fails with TypeError when give a ChainMap instead of a dict #23859

dave-kirby opened this issue Nov 22, 2018 · 6 comments · Fixed by #29306
Labels
good first issue Needs Tests Unit test(s) needed to prevent regressions
Milestone

Comments

@dave-kirby
Copy link

dave-kirby commented Nov 22, 2018

Example code:

>>> from collections import ChainMap
>>> import pandas as pd
>>> df = pd.DataFrame({'A': range(1, 11), 'B': np.random.randn(10)})
>>> df.rename(ChainMap({'A': 'a'}, {'B':'b'}), axis='columns')
...
TypeError: 'ChainMap' object is not callable

# also fails if called with the columns parameter:
>>> df.rename(columns=ChainMap({'A': 'a'}, {'B':'b'}))

I am trying to rename columns in a DataFrame using mappings from two or more dicts combined using the collections.ChainMap class, but it fails with a TypeError.

The docs for DataFrame.rename says

mapper, index, columns : dict-like or function, optional
dict-like or functions transformations to apply to
that axis' values. Use either mapper and axis to
specify the axis to target with mapper, or index and
columns

the ChainMap class should definitely qualify as "dict-like" since it implements the Mapping protocol:

>>> from collections import Mapping
>>> issubclass(ChainMap, Mapping)
True

I am using Python 3.6.6 with Pandas 0.23.4.

@dave-kirby
Copy link
Author

dave-kirby commented Nov 22, 2018

N.B. I had a quick look at the source code and the problem is in the rename method in generic.py:

        # renamer function if passed a dict
        def _get_rename_function(mapper):
            if isinstance(mapper, (dict, ABCSeries)):

                def f(x):
                    if x in mapper:
                        return mapper[x]
                    else:
                        return x
            else:
                f = mapper

            return f

The line if isinstance(mapper, (dict, ABCSeries)): could be replaced with if isinstance(mapper, (collections.Mapping, ABCSeries)): - this would be backward compatible to python 2.6. However if that is going to be done, IMHO it should be done everywhere a dict is allowed as a parameter, otherwise I would submit a one-line PR. Alternatively the docs should be updated to say that only dicts and their subclasses are accepted, not "dict-like" objects.

@jreback
Copy link
Contributor

jreback commented Nov 22, 2018

we have an is_dict_like function for this
welcome a PR to fix here (we use this almost everywhere)

@gfyoung gfyoung added Bug Algos Non-arithmetic algos: value_counts, factorize, sorting, isin, clip, shift, diff DataFrame DataFrame data structure labels Nov 22, 2018
@dave-kirby
Copy link
Author

dave-kirby commented Nov 23, 2018

OK, I will create a PR when I have some free time.

@bourbaki
Copy link
Contributor

bourbaki commented Nov 23, 2018

@dave-kirby I could fix the issue. I have some free time tomorrow. Do you mind?

@dave-kirby
Copy link
Author

@Ma3aXaKa sorry I am using my work GitHub account so did not see your message over the weekend. If you still have some free time then go ahead.

@mroeschke
Copy link
Member

Looks to work on master. Could use a test.

In [136]: >>> from collections import ChainMap
     ...: >>> import pandas as pd
     ...: >>> df = pd.DataFrame({'A': range(1, 11), 'B': np.random.randn(10)})
     ...: >>> df.rename(ChainMap({'A': 'a'}, {'B':'b'}), axis='columns')
Out[136]:
    a         b
0   1 -0.527207
1   2 -0.261854
2   3 -0.247372
3   4  0.522214
4   5  0.415626
5   6 -0.361259
6   7 -1.349333
7   8  1.096590
8   9  0.342080
9  10  1.911814

In [137]: >>> df.rename(columns=ChainMap({'A': 'a'}, {'B':'b'}))
     ...:
Out[137]:
    a         b
0   1 -0.527207
1   2 -0.261854
2   3 -0.247372
3   4  0.522214
4   5  0.415626
5   6 -0.361259
6   7 -1.349333
7   8  1.096590
8   9  0.342080
9  10  1.911814

In [138]: pd.__version__
Out[138]: '0.26.0.dev0+684.g953757a3e'

@mroeschke mroeschke added good first issue Needs Tests Unit test(s) needed to prevent regressions and removed Algos Non-arithmetic algos: value_counts, factorize, sorting, isin, clip, shift, diff Bug DataFrame DataFrame data structure labels Oct 27, 2019
gfyoung added a commit to forking-repos/pandas that referenced this issue Oct 31, 2019
@gfyoung gfyoung added this to the 1.0 milestone Oct 31, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Needs Tests Unit test(s) needed to prevent regressions
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants