Skip to content

Commit a60d1bd

Browse files
simonjayhawkinsjreback
authored andcommitted
DEPR: remove Panel-specific parts of core.indexing (#25567)
1 parent 8154efb commit a60d1bd

File tree

3 files changed

+123
-82
lines changed

3 files changed

+123
-82
lines changed

pandas/_libs/indexing.pyx

+4
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,8 @@ cdef class _NDFrameIndexerBase:
1717
ndim = self._ndim
1818
if ndim is None:
1919
ndim = self._ndim = self.obj.ndim
20+
if ndim > 2:
21+
msg = ("NDFrameIndexer does not support NDFrame objects with"
22+
" ndim > 2")
23+
raise ValueError(msg)
2024
return ndim

pandas/core/indexing.py

+6-82
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from pandas.core.dtypes.common import (
1212
ensure_platform_int, is_float, is_integer, is_integer_dtype, is_iterator,
1313
is_list_like, is_numeric_dtype, is_scalar, is_sequence, is_sparse)
14-
from pandas.core.dtypes.generic import ABCDataFrame, ABCPanel, ABCSeries
14+
from pandas.core.dtypes.generic import ABCDataFrame, ABCSeries
1515
from pandas.core.dtypes.missing import _infer_fill_value, isna
1616

1717
import pandas.core.common as com
@@ -450,10 +450,6 @@ def _setitem_with_indexer(self, indexer, value):
450450
self.obj._maybe_update_cacher(clear=True)
451451
return self.obj
452452

453-
# set using setitem (Panel and > dims)
454-
elif self.ndim >= 3:
455-
return self.obj.__setitem__(indexer, value)
456-
457453
# set
458454
item_labels = self.obj._get_axis(info_axis)
459455

@@ -642,9 +638,6 @@ def can_do_equal_len():
642638
elif isinstance(value, ABCDataFrame):
643639
value = self._align_frame(indexer, value)
644640

645-
if isinstance(value, ABCPanel):
646-
value = self._align_panel(indexer, value)
647-
648641
# check for chained assignment
649642
self.obj._check_is_chained_assignment_possible()
650643

@@ -690,7 +683,6 @@ def ravel(i):
690683
sum_aligners = sum(aligners)
691684
single_aligner = sum_aligners == 1
692685
is_frame = self.obj.ndim == 2
693-
is_panel = self.obj.ndim >= 3
694686
obj = self.obj
695687

696688
# are we a single alignable value on a non-primary
@@ -702,11 +694,6 @@ def ravel(i):
702694
if is_frame:
703695
single_aligner = single_aligner and aligners[0]
704696

705-
# panel
706-
elif is_panel:
707-
single_aligner = (single_aligner and
708-
(aligners[1] or aligners[2]))
709-
710697
# we have a frame, with multiple indexers on both axes; and a
711698
# series, so need to broadcast (see GH5206)
712699
if (sum_aligners == self.ndim and
@@ -738,38 +725,14 @@ def ravel(i):
738725
return ser.reindex(new_ix)._values
739726

740727
# 2 dims
741-
elif single_aligner and is_frame:
728+
elif single_aligner:
742729

743730
# reindex along index
744731
ax = self.obj.axes[1]
745732
if ser.index.equals(ax) or not len(ax):
746733
return ser._values.copy()
747734
return ser.reindex(ax)._values
748735

749-
# >2 dims
750-
elif single_aligner:
751-
752-
broadcast = []
753-
for n, labels in enumerate(self.obj._get_plane_axes(i)):
754-
755-
# reindex along the matching dimensions
756-
if len(labels & ser.index):
757-
ser = ser.reindex(labels)
758-
else:
759-
broadcast.append((n, len(labels)))
760-
761-
# broadcast along other dims
762-
ser = ser._values.copy()
763-
for (axis, l) in broadcast:
764-
shape = [-1] * (len(broadcast) + 1)
765-
shape[axis] = l
766-
ser = np.tile(ser, l).reshape(shape)
767-
768-
if self.obj.ndim == 3:
769-
ser = ser.T
770-
771-
return ser
772-
773736
elif is_scalar(indexer):
774737
ax = self.obj._get_axis(1)
775738

@@ -782,7 +745,6 @@ def ravel(i):
782745

783746
def _align_frame(self, indexer, df):
784747
is_frame = self.obj.ndim == 2
785-
is_panel = self.obj.ndim >= 3
786748

787749
if isinstance(indexer, tuple):
788750

@@ -802,21 +764,6 @@ def _align_frame(self, indexer, df):
802764
else:
803765
sindexers.append(i)
804766

805-
# panel
806-
if is_panel:
807-
808-
# need to conform to the convention
809-
# as we are not selecting on the items axis
810-
# and we have a single indexer
811-
# GH 7763
812-
if len(sindexers) == 1 and sindexers[0] != 0:
813-
df = df.T
814-
815-
if idx is None:
816-
idx = df.index
817-
if cols is None:
818-
cols = df.columns
819-
820767
if idx is not None and cols is not None:
821768

822769
if df.index.equals(idx) and df.columns.equals(cols):
@@ -843,24 +790,8 @@ def _align_frame(self, indexer, df):
843790
val = df.reindex(index=ax)._values
844791
return val
845792

846-
elif is_scalar(indexer) and is_panel:
847-
idx = self.obj.axes[1]
848-
cols = self.obj.axes[2]
849-
850-
# by definition we are indexing on the 0th axis
851-
# a passed in dataframe which is actually a transpose
852-
# of what is needed
853-
if idx.equals(df.index) and cols.equals(df.columns):
854-
return df.copy()._values
855-
856-
return df.reindex(idx, columns=cols)._values
857-
858793
raise ValueError('Incompatible indexer with DataFrame')
859794

860-
def _align_panel(self, indexer, df):
861-
raise NotImplementedError("cannot set using an indexer with a Panel "
862-
"yet!")
863-
864795
def _getitem_tuple(self, tup):
865796
try:
866797
return self._getitem_lowerdim(tup)
@@ -1059,13 +990,6 @@ def _getitem_nested_tuple(self, tup):
1059990
# has the dim of the obj changed?
1060991
# GH 7199
1061992
if obj.ndim < current_ndim:
1062-
1063-
# GH 7516
1064-
# if had a 3 dim and are going to a 2d
1065-
# axes are reversed on a DataFrame
1066-
if i >= 1 and current_ndim == 3 and obj.ndim == 2:
1067-
obj = obj.T
1068-
1069993
axis -= 1
1070994

1071995
return obj
@@ -1562,8 +1486,8 @@ class _LocIndexer(_LocationIndexer):
15621486
15631487
- A boolean array of the same length as the axis being sliced,
15641488
e.g. ``[True, False, True]``.
1565-
- A ``callable`` function with one argument (the calling Series, DataFrame
1566-
or Panel) and that returns valid output for indexing (one of the above)
1489+
- A ``callable`` function with one argument (the calling Series or
1490+
DataFrame) and that returns valid output for indexing (one of the above)
15671491
15681492
See more at :ref:`Selection by Label <indexing.label>`
15691493
@@ -1931,8 +1855,8 @@ class _iLocIndexer(_LocationIndexer):
19311855
- A list or array of integers, e.g. ``[4, 3, 0]``.
19321856
- A slice object with ints, e.g. ``1:7``.
19331857
- A boolean array.
1934-
- A ``callable`` function with one argument (the calling Series, DataFrame
1935-
or Panel) and that returns valid output for indexing (one of the above).
1858+
- A ``callable`` function with one argument (the calling Series or
1859+
DataFrame) and that returns valid output for indexing (one of the above).
19361860
This is useful in method chains, when you don't have a reference to the
19371861
calling object, but would like to base your selection on some value.
19381862

pandas/tests/indexing/test_indexing.py

+113
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111

1212
import pandas as pd
1313
from pandas import DataFrame, Index, NaT, Series
14+
from pandas.core.generic import NDFrame
1415
from pandas.core.indexing import (
1516
_maybe_numeric_slice, _non_reducing_slice, validate_indices)
1617
from pandas.tests.indexing.common import Base, _mklbl
1718
import pandas.util.testing as tm
1819

20+
ignore_ix = pytest.mark.filterwarnings("ignore:\\n.ix:FutureWarning")
21+
1922
# ------------------------------------------------------------------------
2023
# Indexing test cases
2124

@@ -53,6 +56,93 @@ def test_setitem_ndarray_1d(self):
5356
with pytest.raises(ValueError):
5457
df[2:5] = np.arange(1, 4) * 1j
5558

59+
@pytest.mark.parametrize('index', tm.all_index_generator(5),
60+
ids=lambda x: type(x).__name__)
61+
@pytest.mark.parametrize('obj', [
62+
lambda i: Series(np.arange(len(i)), index=i),
63+
lambda i: DataFrame(
64+
np.random.randn(len(i), len(i)), index=i, columns=i)
65+
], ids=['Series', 'DataFrame'])
66+
@pytest.mark.parametrize('idxr, idxr_id', [
67+
(lambda x: x, 'getitem'),
68+
(lambda x: x.loc, 'loc'),
69+
(lambda x: x.iloc, 'iloc'),
70+
pytest.param(lambda x: x.ix, 'ix', marks=ignore_ix)
71+
])
72+
def test_getitem_ndarray_3d(self, index, obj, idxr, idxr_id):
73+
# GH 25567
74+
obj = obj(index)
75+
idxr = idxr(obj)
76+
nd3 = np.random.randint(5, size=(2, 2, 2))
77+
78+
msg = (r"Buffer has wrong number of dimensions \(expected 1,"
79+
r" got 3\)|"
80+
"The truth value of an array with more than one element is"
81+
" ambiguous|"
82+
"Cannot index with multidimensional key|"
83+
r"Wrong number of dimensions. values.ndim != ndim \[3 != 1\]|"
84+
"unhashable type: 'numpy.ndarray'" # TypeError
85+
)
86+
87+
if (isinstance(obj, Series) and idxr_id == 'getitem'
88+
and index.inferred_type in [
89+
'string', 'datetime64', 'period', 'timedelta64',
90+
'boolean', 'categorical']):
91+
idxr[nd3]
92+
else:
93+
if (isinstance(obj, DataFrame) and idxr_id == 'getitem'
94+
and index.inferred_type == 'boolean'):
95+
error = TypeError
96+
else:
97+
error = ValueError
98+
99+
with pytest.raises(error, match=msg):
100+
idxr[nd3]
101+
102+
@pytest.mark.parametrize('index', tm.all_index_generator(5),
103+
ids=lambda x: type(x).__name__)
104+
@pytest.mark.parametrize('obj', [
105+
lambda i: Series(np.arange(len(i)), index=i),
106+
lambda i: DataFrame(
107+
np.random.randn(len(i), len(i)), index=i, columns=i)
108+
], ids=['Series', 'DataFrame'])
109+
@pytest.mark.parametrize('idxr, idxr_id', [
110+
(lambda x: x, 'setitem'),
111+
(lambda x: x.loc, 'loc'),
112+
(lambda x: x.iloc, 'iloc'),
113+
pytest.param(lambda x: x.ix, 'ix', marks=ignore_ix)
114+
])
115+
def test_setitem_ndarray_3d(self, index, obj, idxr, idxr_id):
116+
# GH 25567
117+
obj = obj(index)
118+
idxr = idxr(obj)
119+
nd3 = np.random.randint(5, size=(2, 2, 2))
120+
121+
msg = (r"Buffer has wrong number of dimensions \(expected 1,"
122+
r" got 3\)|"
123+
"The truth value of an array with more than one element is"
124+
" ambiguous|"
125+
"Only 1-dimensional input arrays are supported|"
126+
"'pandas._libs.interval.IntervalTree' object has no attribute"
127+
" 'set_value'|" # AttributeError
128+
"unhashable type: 'numpy.ndarray'|" # TypeError
129+
r"^\[\[\[" # pandas.core.indexing.IndexingError
130+
)
131+
132+
if ((idxr_id == 'iloc')
133+
or ((isinstance(obj, Series) and idxr_id == 'setitem'
134+
and index.inferred_type in [
135+
'floating', 'string', 'datetime64', 'period', 'timedelta64',
136+
'boolean', 'categorical']))
137+
or (idxr_id == 'ix' and index.inferred_type in [
138+
'string', 'datetime64', 'period', 'boolean'])):
139+
idxr[nd3] = 0
140+
else:
141+
with pytest.raises(
142+
(ValueError, AttributeError, TypeError,
143+
pd.core.indexing.IndexingError), match=msg):
144+
idxr[nd3] = 0
145+
56146
def test_inf_upcast(self):
57147
# GH 16957
58148
# We should be able to use np.inf as a key
@@ -1015,3 +1105,26 @@ def test_extension_array_cross_section_converts():
10151105

10161106
result = df.iloc[0]
10171107
tm.assert_series_equal(result, expected)
1108+
1109+
1110+
@pytest.mark.parametrize('idxr, error, error_message', [
1111+
(lambda x: x,
1112+
AttributeError,
1113+
"'numpy.ndarray' object has no attribute 'get'"),
1114+
(lambda x: x.loc,
1115+
AttributeError,
1116+
"type object 'NDFrame' has no attribute '_AXIS_ALIASES'"),
1117+
(lambda x: x.iloc,
1118+
AttributeError,
1119+
"type object 'NDFrame' has no attribute '_AXIS_ALIASES'"),
1120+
pytest.param(
1121+
lambda x: x.ix,
1122+
ValueError,
1123+
"NDFrameIndexer does not support NDFrame objects with ndim > 2",
1124+
marks=ignore_ix)
1125+
])
1126+
def test_ndframe_indexing_raises(idxr, error, error_message):
1127+
# GH 25567
1128+
frame = NDFrame(np.random.randint(5, size=(2, 2, 2)))
1129+
with pytest.raises(error, match=error_message):
1130+
idxr(frame)[0]

0 commit comments

Comments
 (0)