Skip to content

Commit bbfc79f

Browse files
author
Christopher C. Aycock
committed
BUG: Index.copy() honors 'name' parameter (#14302)
1 parent 99b5876 commit bbfc79f

File tree

5 files changed

+83
-17
lines changed

5 files changed

+83
-17
lines changed

doc/source/whatsnew/v0.19.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1578,3 +1578,4 @@ Bug Fixes
15781578
- Bugs in ``stack``, ``get_dummies``, ``make_axis_dummies`` which don't preserve categorical dtypes in (multi)indexes (:issue:`13854`)
15791579
- ``PeridIndex`` can now accept ``list`` and ``array`` which contains ``pd.NaT`` (:issue:`13430`)
15801580
- Bug in ``df.groupby`` where ``.median()`` returns arbitrary values if grouped dataframe contains empty bins (:issue:`13629`)
1581+
- Bug in ``Index.copy()`` where ``name`` parameter was ignored (:issue:`14302`)

pandas/indexes/base.py

+24-10
Original file line numberDiff line numberDiff line change
@@ -620,26 +620,40 @@ def _coerce_scalar_to_index(self, item):
620620

621621
@Appender(_index_shared_docs['copy'])
622622
def copy(self, name=None, deep=False, dtype=None, **kwargs):
623-
names = kwargs.get('names')
624-
if names is not None and name is not None:
625-
raise TypeError("Can only provide one of `names` and `name`")
626623
if deep:
627-
from copy import deepcopy
628624
new_index = self._shallow_copy(self._data.copy())
629-
name = name or deepcopy(self.name)
630625
else:
631626
new_index = self._shallow_copy()
632-
name = self.name
633-
if name is not None:
634-
names = [name]
635-
if names:
636-
new_index = new_index.set_names(names)
627+
628+
names = kwargs.get('names')
629+
names = self._validate_names(name=name, names=names, deep=deep)
630+
new_index = new_index.set_names(names)
631+
637632
if dtype:
638633
new_index = new_index.astype(dtype)
639634
return new_index
640635

641636
__copy__ = copy
642637

638+
def _validate_names(self, name=None, names=None, deep=False):
639+
"""
640+
Handles the quirks of having a singular 'name' parameter for general
641+
Index and plural 'names' parameter for MultiIndex.
642+
"""
643+
from copy import deepcopy
644+
if names is not None and name is not None:
645+
raise TypeError("Can only provide one of `names` and `name`")
646+
elif names is None and name is None:
647+
return deepcopy(self.names) if deep else self.names
648+
elif names is not None:
649+
if not is_list_like(names):
650+
raise TypeError("Must pass list-like as `names`.")
651+
return names
652+
else:
653+
if not is_list_like(name):
654+
return [name]
655+
return name
656+
643657
def __unicode__(self):
644658
"""
645659
Return a string representation for this object.

pandas/indexes/multi.py

+12-7
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ def set_labels(self, labels, level=None, inplace=False,
346346
labels = property(fget=_get_labels, fset=__set_labels)
347347

348348
def copy(self, names=None, dtype=None, levels=None, labels=None,
349-
deep=False, _set_identity=False):
349+
deep=False, _set_identity=False, **kwargs):
350350
"""
351351
Make a copy of this object. Names, dtype, levels and labels can be
352352
passed and will be set on new copy.
@@ -368,15 +368,20 @@ def copy(self, names=None, dtype=None, levels=None, labels=None,
368368
``deep``, but if ``deep`` is passed it will attempt to deepcopy.
369369
This could be potentially expensive on large MultiIndex objects.
370370
"""
371+
name = kwargs.get('name')
372+
names = self._validate_names(name=name, names=names, deep=deep)
373+
371374
if deep:
372375
from copy import deepcopy
373-
levels = levels if levels is not None else deepcopy(self.levels)
374-
labels = labels if labels is not None else deepcopy(self.labels)
375-
names = names if names is not None else deepcopy(self.names)
376+
if levels is None:
377+
levels = deepcopy(self.levels)
378+
if labels is None:
379+
labels = deepcopy(self.labels)
376380
else:
377-
levels = self.levels
378-
labels = self.labels
379-
names = self.names
381+
if levels is None:
382+
levels = self.levels
383+
if labels is None:
384+
labels = self.labels
380385
return MultiIndex(levels=levels, labels=labels, names=names,
381386
sortorder=self.sortorder, verify_integrity=False,
382387
_set_identity=_set_identity)

pandas/tests/indexes/test_base.py

+24
Original file line numberDiff line numberDiff line change
@@ -1817,6 +1817,30 @@ def test_copy_name(self):
18171817
s3 = s1 * s2
18181818
self.assertEqual(s3.index.name, 'mario')
18191819

1820+
def test_copy_name2(self):
1821+
# Check that adding a "name" parameter to the copy is honored
1822+
# GH14302
1823+
idx = pd.Index([1, 2], name='MyName')
1824+
idx1 = idx.copy()
1825+
1826+
self.assertTrue(idx.equals(idx1))
1827+
self.assertEqual(idx.name, 'MyName')
1828+
self.assertEqual(idx1.name, 'MyName')
1829+
1830+
idx2 = idx.copy(name='NewName')
1831+
1832+
self.assertTrue(idx.equals(idx2))
1833+
self.assertEqual(idx.name, 'MyName')
1834+
self.assertEqual(idx2.name, 'NewName')
1835+
1836+
idx3 = idx.copy(names=['NewName'])
1837+
1838+
self.assertTrue(idx.equals(idx3))
1839+
self.assertEqual(idx.name, 'MyName')
1840+
self.assertEqual(idx.names, ['MyName'])
1841+
self.assertEqual(idx3.name, 'NewName')
1842+
self.assertEqual(idx3.names, ['NewName'])
1843+
18201844
def test_union_base(self):
18211845
idx = self.create_index()
18221846
first = idx[3:]

pandas/tests/indexes/test_multi.py

+22
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,28 @@ def test_set_value_keeps_names(self):
415415
self.assertIsNone(df.is_copy)
416416
self.assertEqual(df.index.names, ('Name', 'Number'))
417417

418+
def test_copy_names(self):
419+
# Check that adding a "names" parameter to the copy is honored
420+
# GH14302
421+
multi_idx = pd.Index([(1, 2), (3, 4)], names=['MyName1', 'MyName2'])
422+
multi_idx1 = multi_idx.copy()
423+
424+
self.assertTrue(multi_idx.equals(multi_idx1))
425+
self.assertEqual(multi_idx.names, ['MyName1', 'MyName2'])
426+
self.assertEqual(multi_idx1.names, ['MyName1', 'MyName2'])
427+
428+
multi_idx2 = multi_idx.copy(names=['NewName1', 'NewName2'])
429+
430+
self.assertTrue(multi_idx.equals(multi_idx2))
431+
self.assertEqual(multi_idx.names, ['MyName1', 'MyName2'])
432+
self.assertEqual(multi_idx2.names, ['NewName1', 'NewName2'])
433+
434+
multi_idx3 = multi_idx.copy(name=['NewName1', 'NewName2'])
435+
436+
self.assertTrue(multi_idx.equals(multi_idx3))
437+
self.assertEqual(multi_idx.names, ['MyName1', 'MyName2'])
438+
self.assertEqual(multi_idx3.names, ['NewName1', 'NewName2'])
439+
418440
def test_names(self):
419441

420442
# names are assigned in __init__

0 commit comments

Comments
 (0)