Skip to content

Commit e872067

Browse files
authored
BUG: Preserve name in Index.astype (#32036)
1 parent 1a57596 commit e872067

File tree

7 files changed

+73
-33
lines changed

7 files changed

+73
-33
lines changed

doc/source/whatsnew/v1.1.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ Reshaping
414414
- Bug in :func:`crosstab` when inputs are two Series and have tuple names, the output will keep dummy MultiIndex as columns. (:issue:`18321`)
415415
- :meth:`DataFrame.pivot` can now take lists for ``index`` and ``columns`` arguments (:issue:`21425`)
416416
- Bug in :func:`concat` where the resulting indices are not copied when ``copy=True`` (:issue:`29879`)
417+
- Bug where :meth:`Index.astype` would lose the name attribute when converting from ``Float64Index`` to ``Int64Index``, or when casting to an ``ExtensionArray`` dtype (:issue:`32013`)
417418
- :meth:`Series.append` will now raise a ``TypeError`` when passed a DataFrame or a sequence containing Dataframe (:issue:`31413`)
418419
- :meth:`DataFrame.replace` and :meth:`Series.replace` will raise a ``TypeError`` if ``to_replace`` is not an expected type. Previously the ``replace`` would fail silently (:issue:`18634`)
419420
- Bug on inplace operation of a Series that was adding a column to the DataFrame from where it was originally dropped from (using inplace=True) (:issue:`30484`)

pandas/core/indexes/base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ def astype(self, dtype, copy=True):
670670
return CategoricalIndex(self.values, name=self.name, dtype=dtype, copy=copy)
671671

672672
elif is_extension_array_dtype(dtype):
673-
return Index(np.asarray(self), dtype=dtype, copy=copy)
673+
return Index(np.asarray(self), name=self.name, dtype=dtype, copy=copy)
674674

675675
try:
676676
casted = self.values.astype(dtype, copy=copy)

pandas/core/indexes/numeric.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ def astype(self, dtype, copy=True):
369369
# TODO(jreback); this can change once we have an EA Index type
370370
# GH 13149
371371
arr = astype_nansafe(self._values, dtype=dtype)
372-
return Int64Index(arr)
372+
return Int64Index(arr, name=self.name)
373373
return super().astype(dtype, copy=copy)
374374

375375
# ----------------------------------------------------------------

pandas/tests/indexes/datetimes/test_astype.py

+24-16
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,32 @@
2222
class TestDatetimeIndex:
2323
def test_astype(self):
2424
# GH 13149, GH 13209
25-
idx = DatetimeIndex(["2016-05-16", "NaT", NaT, np.NaN])
25+
idx = DatetimeIndex(["2016-05-16", "NaT", NaT, np.NaN], name="idx")
2626

2727
result = idx.astype(object)
28-
expected = Index([Timestamp("2016-05-16")] + [NaT] * 3, dtype=object)
28+
expected = Index(
29+
[Timestamp("2016-05-16")] + [NaT] * 3, dtype=object, name="idx"
30+
)
2931
tm.assert_index_equal(result, expected)
3032

3133
result = idx.astype(int)
3234
expected = Int64Index(
33-
[1463356800000000000] + [-9223372036854775808] * 3, dtype=np.int64
35+
[1463356800000000000] + [-9223372036854775808] * 3,
36+
dtype=np.int64,
37+
name="idx",
3438
)
3539
tm.assert_index_equal(result, expected)
3640

37-
rng = date_range("1/1/2000", periods=10)
41+
rng = date_range("1/1/2000", periods=10, name="idx")
3842
result = rng.astype("i8")
39-
tm.assert_index_equal(result, Index(rng.asi8))
43+
tm.assert_index_equal(result, Index(rng.asi8, name="idx"))
4044
tm.assert_numpy_array_equal(result.values, rng.asi8)
4145

4246
def test_astype_uint(self):
43-
arr = date_range("2000", periods=2)
47+
arr = date_range("2000", periods=2, name="idx")
4448
expected = pd.UInt64Index(
45-
np.array([946684800000000000, 946771200000000000], dtype="uint64")
49+
np.array([946684800000000000, 946771200000000000], dtype="uint64"),
50+
name="idx",
4651
)
4752

4853
tm.assert_index_equal(arr.astype("uint64"), expected)
@@ -148,7 +153,7 @@ def test_astype_str(self):
148153

149154
def test_astype_datetime64(self):
150155
# GH 13149, GH 13209
151-
idx = DatetimeIndex(["2016-05-16", "NaT", NaT, np.NaN])
156+
idx = DatetimeIndex(["2016-05-16", "NaT", NaT, np.NaN], name="idx")
152157

153158
result = idx.astype("datetime64[ns]")
154159
tm.assert_index_equal(result, idx)
@@ -158,10 +163,12 @@ def test_astype_datetime64(self):
158163
tm.assert_index_equal(result, idx)
159164
assert result is idx
160165

161-
idx_tz = DatetimeIndex(["2016-05-16", "NaT", NaT, np.NaN], tz="EST")
166+
idx_tz = DatetimeIndex(["2016-05-16", "NaT", NaT, np.NaN], tz="EST", name="idx")
162167
result = idx_tz.astype("datetime64[ns]")
163168
expected = DatetimeIndex(
164-
["2016-05-16 05:00:00", "NaT", "NaT", "NaT"], dtype="datetime64[ns]"
169+
["2016-05-16 05:00:00", "NaT", "NaT", "NaT"],
170+
dtype="datetime64[ns]",
171+
name="idx",
165172
)
166173
tm.assert_index_equal(result, expected)
167174

@@ -273,8 +280,8 @@ def _check_rng(rng):
273280
def test_integer_index_astype_datetime(self, tz, dtype):
274281
# GH 20997, 20964, 24559
275282
val = [pd.Timestamp("2018-01-01", tz=tz).value]
276-
result = pd.Index(val).astype(dtype)
277-
expected = pd.DatetimeIndex(["2018-01-01"], tz=tz)
283+
result = pd.Index(val, name="idx").astype(dtype)
284+
expected = pd.DatetimeIndex(["2018-01-01"], tz=tz, name="idx")
278285
tm.assert_index_equal(result, expected)
279286

280287
def test_dti_astype_period(self):
@@ -292,10 +299,11 @@ def test_dti_astype_period(self):
292299
class TestAstype:
293300
@pytest.mark.parametrize("tz", [None, "US/Central"])
294301
def test_astype_category(self, tz):
295-
obj = pd.date_range("2000", periods=2, tz=tz)
302+
obj = pd.date_range("2000", periods=2, tz=tz, name="idx")
296303
result = obj.astype("category")
297304
expected = pd.CategoricalIndex(
298-
[pd.Timestamp("2000-01-01", tz=tz), pd.Timestamp("2000-01-02", tz=tz)]
305+
[pd.Timestamp("2000-01-01", tz=tz), pd.Timestamp("2000-01-02", tz=tz)],
306+
name="idx",
299307
)
300308
tm.assert_index_equal(result, expected)
301309

@@ -305,9 +313,9 @@ def test_astype_category(self, tz):
305313

306314
@pytest.mark.parametrize("tz", [None, "US/Central"])
307315
def test_astype_array_fallback(self, tz):
308-
obj = pd.date_range("2000", periods=2, tz=tz)
316+
obj = pd.date_range("2000", periods=2, tz=tz, name="idx")
309317
result = obj.astype(bool)
310-
expected = pd.Index(np.array([True, True]))
318+
expected = pd.Index(np.array([True, True]), name="idx")
311319
tm.assert_index_equal(result, expected)
312320

313321
result = obj._data.astype(bool)

pandas/tests/indexes/period/test_astype.py

+14-11
Original file line numberDiff line numberDiff line change
@@ -27,31 +27,34 @@ def test_astype_raises(self, dtype):
2727

2828
def test_astype_conversion(self):
2929
# GH#13149, GH#13209
30-
idx = PeriodIndex(["2016-05-16", "NaT", NaT, np.NaN], freq="D")
30+
idx = PeriodIndex(["2016-05-16", "NaT", NaT, np.NaN], freq="D", name="idx")
3131

3232
result = idx.astype(object)
3333
expected = Index(
3434
[Period("2016-05-16", freq="D")] + [Period(NaT, freq="D")] * 3,
3535
dtype="object",
36+
name="idx",
3637
)
3738
tm.assert_index_equal(result, expected)
3839

3940
result = idx.astype(np.int64)
40-
expected = Int64Index([16937] + [-9223372036854775808] * 3, dtype=np.int64)
41+
expected = Int64Index(
42+
[16937] + [-9223372036854775808] * 3, dtype=np.int64, name="idx"
43+
)
4144
tm.assert_index_equal(result, expected)
4245

4346
result = idx.astype(str)
44-
expected = Index(str(x) for x in idx)
47+
expected = Index([str(x) for x in idx], name="idx")
4548
tm.assert_index_equal(result, expected)
4649

47-
idx = period_range("1990", "2009", freq="A")
50+
idx = period_range("1990", "2009", freq="A", name="idx")
4851
result = idx.astype("i8")
49-
tm.assert_index_equal(result, Index(idx.asi8))
52+
tm.assert_index_equal(result, Index(idx.asi8, name="idx"))
5053
tm.assert_numpy_array_equal(result.values, idx.asi8)
5154

5255
def test_astype_uint(self):
53-
arr = period_range("2000", periods=2)
54-
expected = UInt64Index(np.array([10957, 10958], dtype="uint64"))
56+
arr = period_range("2000", periods=2, name="idx")
57+
expected = UInt64Index(np.array([10957, 10958], dtype="uint64"), name="idx")
5558
tm.assert_index_equal(arr.astype("uint64"), expected)
5659
tm.assert_index_equal(arr.astype("uint32"), expected)
5760

@@ -116,10 +119,10 @@ def test_astype_object2(self):
116119
assert result_list[2] is NaT
117120

118121
def test_astype_category(self):
119-
obj = period_range("2000", periods=2)
122+
obj = period_range("2000", periods=2, name="idx")
120123
result = obj.astype("category")
121124
expected = CategoricalIndex(
122-
[Period("2000-01-01", freq="D"), Period("2000-01-02", freq="D")]
125+
[Period("2000-01-01", freq="D"), Period("2000-01-02", freq="D")], name="idx"
123126
)
124127
tm.assert_index_equal(result, expected)
125128

@@ -128,9 +131,9 @@ def test_astype_category(self):
128131
tm.assert_categorical_equal(result, expected)
129132

130133
def test_astype_array_fallback(self):
131-
obj = period_range("2000", periods=2)
134+
obj = period_range("2000", periods=2, name="idx")
132135
result = obj.astype(bool)
133-
expected = Index(np.array([True, True]))
136+
expected = Index(np.array([True, True]), name="idx")
134137
tm.assert_index_equal(result, expected)
135138

136139
result = obj._data.astype(bool)

pandas/tests/indexes/test_common.py

+26
Original file line numberDiff line numberDiff line change
@@ -369,3 +369,29 @@ def test_has_duplicates(self, indices):
369369
idx = holder([indices[0]] * 5)
370370
assert idx.is_unique is False
371371
assert idx.has_duplicates is True
372+
373+
@pytest.mark.parametrize(
374+
"dtype",
375+
["int64", "uint64", "float64", "category", "datetime64[ns]", "timedelta64[ns]"],
376+
)
377+
@pytest.mark.parametrize("copy", [True, False])
378+
def test_astype_preserves_name(self, indices, dtype, copy):
379+
# https://github.com/pandas-dev/pandas/issues/32013
380+
if isinstance(indices, MultiIndex):
381+
indices.names = ["idx" + str(i) for i in range(indices.nlevels)]
382+
else:
383+
indices.name = "idx"
384+
385+
try:
386+
# Some of these conversions cannot succeed so we use a try / except
387+
if copy:
388+
result = indices.copy(dtype=dtype)
389+
else:
390+
result = indices.astype(dtype)
391+
except (ValueError, TypeError, NotImplementedError, SystemError):
392+
return
393+
394+
if isinstance(indices, MultiIndex):
395+
assert result.names == indices.names
396+
else:
397+
assert result.name == indices.name

pandas/tests/indexes/timedeltas/test_astype.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,22 @@ def test_astype_object_with_nat(self):
4747

4848
def test_astype(self):
4949
# GH 13149, GH 13209
50-
idx = TimedeltaIndex([1e14, "NaT", NaT, np.NaN])
50+
idx = TimedeltaIndex([1e14, "NaT", NaT, np.NaN], name="idx")
5151

5252
result = idx.astype(object)
53-
expected = Index([Timedelta("1 days 03:46:40")] + [NaT] * 3, dtype=object)
53+
expected = Index(
54+
[Timedelta("1 days 03:46:40")] + [NaT] * 3, dtype=object, name="idx"
55+
)
5456
tm.assert_index_equal(result, expected)
5557

5658
result = idx.astype(int)
5759
expected = Int64Index(
58-
[100000000000000] + [-9223372036854775808] * 3, dtype=np.int64
60+
[100000000000000] + [-9223372036854775808] * 3, dtype=np.int64, name="idx"
5961
)
6062
tm.assert_index_equal(result, expected)
6163

6264
result = idx.astype(str)
63-
expected = Index(str(x) for x in idx)
65+
expected = Index([str(x) for x in idx], name="idx")
6466
tm.assert_index_equal(result, expected)
6567

6668
rng = timedelta_range("1 days", periods=10)

0 commit comments

Comments
 (0)