Skip to content

Commit 527c2c6

Browse files
chinskiyjreback
chinskiy
authored andcommitted
BUG: ensure Series.name is hashable
closes pandas-dev#12610 closes pandas-dev#12612
1 parent 247fe07 commit 527c2c6

File tree

7 files changed

+41
-7
lines changed

7 files changed

+41
-7
lines changed

doc/source/whatsnew/v0.18.1.txt

+1
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,4 @@ Bug Fixes
167167

168168

169169
- Bug in ``pivot_table`` when ``margins=True`` and ``dropna=True`` where nulls still contributed to margin count (:issue:`12577`)
170+
- Bug in ``Series.name`` when ``name`` attribute can be a hashable type (:issue:`12610`)

pandas/core/generic.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class NDFrame(PandasObject):
8181
copy : boolean, default False
8282
"""
8383
_internal_names = ['_data', '_cacher', '_item_cache', '_cache', 'is_copy',
84-
'_subtyp', '_index', '_default_kind',
84+
'_subtyp', '_name', '_index', '_default_kind',
8585
'_default_fill_value', '_metadata', '__array_struct__',
8686
'__array_interface__']
8787
_internal_names_set = set(_internal_names)

pandas/core/series.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ def __init__(self, data=None, index=None, dtype=None, name=None,
232232

233233
generic.NDFrame.__init__(self, data, fastpath=True)
234234

235-
object.__setattr__(self, 'name', name)
235+
self.name = name
236236
self._set_axis(0, index, fastpath=True)
237237

238238
@classmethod
@@ -301,6 +301,16 @@ def _update_inplace(self, result, **kwargs):
301301
# we want to call the generic version and not the IndexOpsMixin
302302
return generic.NDFrame._update_inplace(self, result, **kwargs)
303303

304+
@property
305+
def name(self):
306+
return self._name
307+
308+
@name.setter
309+
def name(self, value):
310+
if value is not None and not com.is_hashable(value):
311+
raise TypeError('Series.name must be a hashable type')
312+
object.__setattr__(self, '_name', value)
313+
304314
# ndarray compatibility
305315
@property
306316
def dtype(self):

pandas/tests/series/test_alter_axes.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# coding=utf-8
22
# pylint: disable-msg=E1101,W0612
33

4+
from datetime import datetime
5+
46
import numpy as np
57
import pandas as pd
68

@@ -64,20 +66,29 @@ def test_rename_by_series(self):
6466

6567
def test_rename_set_name(self):
6668
s = Series(range(4), index=list('abcd'))
67-
for name in ['foo', ['foo'], ('foo',)]:
69+
for name in ['foo', 123, 123., datetime(2001, 11, 11), ('foo',)]:
6870
result = s.rename(name)
6971
self.assertEqual(result.name, name)
7072
self.assert_numpy_array_equal(result.index.values, s.index.values)
7173
self.assertTrue(s.name is None)
7274

7375
def test_rename_set_name_inplace(self):
7476
s = Series(range(3), index=list('abc'))
75-
for name in ['foo', ['foo'], ('foo',)]:
77+
for name in ['foo', 123, 123., datetime(2001, 11, 11), ('foo',)]:
7678
s.rename(name, inplace=True)
7779
self.assertEqual(s.name, name)
7880
self.assert_numpy_array_equal(s.index.values,
7981
np.array(['a', 'b', 'c']))
8082

83+
def test_set_name_attribute(self):
84+
s = Series([1, 2, 3])
85+
s2 = Series([1, 2, 3], name='bar')
86+
for name in [7, 7., 'name', datetime(2001, 1, 1), (1,), u"\u05D0"]:
87+
s.name = name
88+
self.assertEqual(s.name, name)
89+
s2.name = name
90+
self.assertEqual(s2.name, name)
91+
8192
def test_set_name(self):
8293
s = Series([1, 2, 3])
8394
s2 = s._set_name('foo')

pandas/tests/series/test_constructors.py

+11
Original file line numberDiff line numberDiff line change
@@ -725,3 +725,14 @@ def f():
725725
self.assertEqual(s.dtype, 'timedelta64[ns]')
726726
s = Series([pd.NaT, np.nan, '1 Day'])
727727
self.assertEqual(s.dtype, 'timedelta64[ns]')
728+
729+
def test_constructor_name_hashable(self):
730+
for n in [777, 777., 'name', datetime(2001, 11, 11), (1, ), u"\u05D0"]:
731+
for data in [[1, 2, 3], np.ones(3), {'a': 0, 'b': 1}]:
732+
s = Series(data, name=n)
733+
self.assertEqual(s.name, n)
734+
735+
def test_constructor_name_unhashable(self):
736+
for n in [['name_list'], np.ones(2), {1: 2}]:
737+
for data in [['name_list'], np.ones(2), {1: 2}]:
738+
self.assertRaises(TypeError, Series, data, name=n)

pandas/tests/series/test_io.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,9 @@ def test_timeseries_periodindex(self):
146146
self.assertEqual(new_ts.index.freq, 'M')
147147

148148
def test_pickle_preserve_name(self):
149-
unpickled = self._pickle_roundtrip_name(self.ts)
150-
self.assertEqual(unpickled.name, self.ts.name)
149+
for n in [777, 777., 'name', datetime(2001, 11, 11), (1, 2)]:
150+
unpickled = self._pickle_roundtrip_name(tm.makeTimeSeries(name=n))
151+
self.assertEqual(unpickled.name, n)
151152

152153
def _pickle_roundtrip_name(self, obj):
153154

pandas/tests/series/test_repr.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def test_repr(self):
9797
rep_str = repr(ser)
9898
self.assertIn("Name: 0", rep_str)
9999

100-
ser = Series(["a\n\r\tb"], name=["a\n\r\td"], index=["a\n\r\tf"])
100+
ser = Series(["a\n\r\tb"], name="a\n\r\td", index=["a\n\r\tf"])
101101
self.assertFalse("\t" in repr(ser))
102102
self.assertFalse("\r" in repr(ser))
103103
self.assertFalse("a\n" in repr(ser))

0 commit comments

Comments
 (0)