Skip to content

Commit c462504

Browse files
committed
DEPR: deprecate .ix in favor of .loc/.iloc
closes pandas-dev#14218
1 parent 0fe491d commit c462504

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+1590
-1290
lines changed

doc/source/advanced.rst

+6-17
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ of tuples:
230230
Advanced indexing with hierarchical index
231231
-----------------------------------------
232232

233-
Syntactically integrating ``MultiIndex`` in advanced indexing with ``.loc/.ix`` is a
233+
Syntactically integrating ``MultiIndex`` in advanced indexing with ``.loc`` is a
234234
bit challenging, but we've made every effort to do so. for example the
235235
following works as you would expect:
236236

@@ -258,7 +258,7 @@ Passing a list of labels or tuples works similar to reindexing:
258258

259259
.. ipython:: python
260260
261-
df.ix[[('bar', 'two'), ('qux', 'one')]]
261+
df.loc[[('bar', 'two'), ('qux', 'one')]]
262262
263263
.. _advanced.mi_slicers:
264264

@@ -604,7 +604,7 @@ intended to work on boolean indices and may return unexpected results.
604604
605605
ser = pd.Series(np.random.randn(10))
606606
ser.take([False, False, True, True])
607-
ser.ix[[0, 1]]
607+
ser.iloc[[0, 1]]
608608
609609
Finally, as a small note on performance, because the ``take`` method handles
610610
a narrower range of inputs, it can offer performance that is a good deal
@@ -620,7 +620,7 @@ faster than fancy indexing.
620620
timeit arr.take(indexer, axis=0)
621621

622622
ser = pd.Series(arr[:, 0])
623-
timeit ser.ix[indexer]
623+
timeit ser.iloc[indexer]
624624
timeit ser.take(indexer)
625625

626626
.. _indexing.index_types:
@@ -661,7 +661,7 @@ Setting the index, will create create a ``CategoricalIndex``
661661
df2 = df.set_index('B')
662662
df2.index
663663
664-
Indexing with ``__getitem__/.iloc/.loc/.ix`` works similarly to an ``Index`` with duplicates.
664+
Indexing with ``__getitem__/.iloc/.loc`` works similarly to an ``Index`` with duplicates.
665665
The indexers MUST be in the category or the operation will raise.
666666

667667
.. ipython:: python
@@ -759,14 +759,12 @@ same.
759759
sf = pd.Series(range(5), index=indexf)
760760
sf
761761
762-
Scalar selection for ``[],.ix,.loc`` will always be label based. An integer will match an equal float index (e.g. ``3`` is equivalent to ``3.0``)
762+
Scalar selection for ``[],.loc`` will always be label based. An integer will match an equal float index (e.g. ``3`` is equivalent to ``3.0``)
763763
764764
.. ipython:: python
765765
766766
sf[3]
767767
sf[3.0]
768-
sf.ix[3]
769-
sf.ix[3.0]
770768
sf.loc[3]
771769
sf.loc[3.0]
772770
@@ -783,7 +781,6 @@ Slicing is ALWAYS on the values of the index, for ``[],ix,loc`` and ALWAYS posit
783781
.. ipython:: python
784782
785783
sf[2:4]
786-
sf.ix[2:4]
787784
sf.loc[2:4]
788785
sf.iloc[2:4]
789786
@@ -813,14 +810,6 @@ In non-float indexes, slicing using floats will raise a ``TypeError``
813810
In [3]: pd.Series(range(5)).iloc[3.0]
814811
TypeError: cannot do positional indexing on <class 'pandas.indexes.range.RangeIndex'> with these indexers [3.0] of <type 'float'>
815812
816-
Further the treatment of ``.ix`` with a float indexer on a non-float index, will be label based, and thus coerce the index.
817-
818-
.. ipython:: python
819-
820-
s2 = pd.Series([1, 2, 3], index=list('abc'))
821-
s2
822-
s2.ix[1.0] = 10
823-
s2
824813
825814
Here is a typical use-case for using this type of indexing. Imagine that you have a somewhat
826815
irregular timedelta-like indexing scheme, but the data is recorded as floats. This could for

doc/source/indexing.rst

+51-18
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ See the :ref:`MultiIndex / Advanced Indexing <advanced>` for ``MultiIndex`` and
6161

6262
See the :ref:`cookbook<cookbook.selection>` for some advanced strategies
6363

64+
.. _indexing.choice:
65+
6466
Different Choices for Indexing
6567
------------------------------
6668

@@ -104,24 +106,13 @@ of multi-axis indexing.
104106

105107
See more at :ref:`Selection by Position <indexing.integer>`
106108

107-
- ``.ix`` supports mixed integer and label based access. It is primarily label
108-
based, but will fall back to integer positional access unless the corresponding
109-
axis is of integer type. ``.ix`` is the most general and will
110-
support any of the inputs in ``.loc`` and ``.iloc``. ``.ix`` also supports floating point
111-
label schemes. ``.ix`` is exceptionally useful when dealing with mixed positional
112-
and label based hierarchical indexes.
113-
114-
However, when an axis is integer based, ONLY
115-
label based access and not positional access is supported.
116-
Thus, in such cases, it's usually better to be explicit and use ``.iloc`` or ``.loc``.
117-
118109
See more at :ref:`Advanced Indexing <advanced>` and :ref:`Advanced
119110
Hierarchical <advanced.advanced_hierarchical>`.
120111

121-
- ``.loc``, ``.iloc``, ``.ix`` and also ``[]`` indexing can accept a ``callable`` as indexer. See more at :ref:`Selection By Callable <indexing.callable>`.
112+
- ``.loc``, ``.iloc``, and also ``[]`` indexing can accept a ``callable`` as indexer. See more at :ref:`Selection By Callable <indexing.callable>`.
122113

123114
Getting values from an object with multi-axes selection uses the following
124-
notation (using ``.loc`` as an example, but applies to ``.iloc`` and ``.ix`` as
115+
notation (using ``.loc`` as an example, but applies to ``.iloc`` as
125116
well). Any of the axes accessors may be the null slice ``:``. Axes left out of
126117
the specification are assumed to be ``:``. (e.g. ``p.loc['a']`` is equiv to
127118
``p.loc['a', :, :]``)
@@ -135,6 +126,48 @@ the specification are assumed to be ``:``. (e.g. ``p.loc['a']`` is equiv to
135126
DataFrame; ``df.loc[row_indexer,column_indexer]``
136127
Panel; ``p.loc[item_indexer,major_indexer,minor_indexer]``
137128

129+
.. _indexing.deprecate_ix:
130+
131+
IX Indexer is Deprecated
132+
------------------------
133+
134+
.. warning::
135+
136+
Startin in 0.20.0, the ``.ix`` indexer is deprecated, in favor of the more strict ``.iloc`` and ``.loc`` indexers. ``.ix`` offers a lot of magic on the inference of what the user wants to do. To wit, ``.ix`` can decide to index *positionally* OR via *labels*. This has caused quite a bit of user confusion over the years.
137+
138+
139+
The recommended methods of indexing are:
140+
141+
.. ipython:: python
142+
143+
dfd = pd.DataFrame({'A': [1, 2, 3],
144+
'B': [4, 5, 6]},
145+
index=list('abc'))
146+
147+
dfd
148+
149+
Previous Behavior, where you wish to get the 0th and the 2nd elements from the index in the 'A' column.
150+
151+
.. code-block:: ipython
152+
153+
In [3]: dfd.ix[[0, 2], 'A']
154+
Out[3]:
155+
a 1
156+
c 3
157+
Name: A, dtype: int64
158+
159+
Using ``.loc``. Here we will select the appropriate indexes from the index, then use *label* indexing.
160+
161+
.. ipython:: python
162+
163+
dfd.loc[df.index[[0, 2]], 'A']
164+
165+
Using ``.iloc``. Here we will get the location of the 'A' column, then use *positional* indexing to select things.
166+
167+
.. ipython:: python
168+
169+
dfd.iloc[[0, 2], df.columns.get_loc('A')]
170+
138171
.. _indexing.basics:
139172

140173
Basics
@@ -193,7 +226,7 @@ columns.
193226

194227
.. warning::
195228

196-
pandas aligns all AXES when setting ``Series`` and ``DataFrame`` from ``.loc``, ``.iloc`` and ``.ix``.
229+
pandas aligns all AXES when setting ``Series`` and ``DataFrame`` from ``.loc``, and ``.iloc``.
197230

198231
This will **not** modify ``df`` because the column alignment is before value assignment.
199232

@@ -526,7 +559,7 @@ Selection By Callable
526559

527560
.. versionadded:: 0.18.1
528561

529-
``.loc``, ``.iloc``, ``.ix`` and also ``[]`` indexing can accept a ``callable`` as indexer.
562+
``.loc``, ``.iloc``, and also ``[]`` indexing can accept a ``callable`` as indexer.
530563
The ``callable`` must be a function with one argument (the calling Series, DataFrame or Panel) and that returns valid output for indexing.
531564

532565
.. ipython:: python
@@ -641,7 +674,7 @@ Setting With Enlargement
641674

642675
.. versionadded:: 0.13
643676

644-
The ``.loc/.ix/[]`` operations can perform enlargement when setting a non-existant key for that axis.
677+
The ``.loc/[]`` operations can perform enlargement when setting a non-existant key for that axis.
645678

646679
In the ``Series`` case this is effectively an appending operation
647680

@@ -906,7 +939,7 @@ without creating a copy:
906939

907940
Furthermore, ``where`` aligns the input boolean condition (ndarray or DataFrame),
908941
such that partial selection with setting is possible. This is analogous to
909-
partial setting via ``.ix`` (but on the contents rather than the axis labels)
942+
partial setting via ``.loc`` (but on the contents rather than the axis labels)
910943

911944
.. ipython:: python
912945
@@ -1716,7 +1749,7 @@ A chained assignment can also crop up in setting in a mixed dtype frame.
17161749

17171750
.. note::
17181751

1719-
These setting rules apply to all of ``.loc/.iloc/.ix``
1752+
These setting rules apply to all of ``.loc/.iloc``
17201753

17211754
This is the correct access method
17221755

doc/source/whatsnew/v0.20.0.txt

+48
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ users upgrade to this version.
1010
Highlights include:
1111

1212
- Building pandas for development now requires ``cython >= 0.23`` (:issue:`14831`)
13+
- The ``.ix`` indexer has been deprecated, see :ref:`here <whatsnew.api_breaking.deprecate_ix>`
1314

1415
Check the :ref:`API Changes <whatsnew_0200.api_breaking>` and :ref:`deprecations <whatsnew_0200.deprecations>` before updating.
1516

@@ -122,6 +123,53 @@ Other enhancements
122123
Backwards incompatible API changes
123124
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
124125

126+
127+
.. _whatsnew.api_breaking.deprecate_ix
128+
129+
Deprecate .ix
130+
^^^^^^^^^^^^^
131+
132+
The ``.ix`` indexer is deprecated, in favor of the more strict ``.iloc`` and ``.loc`` indexers. ``.ix`` offers a lot of magic on the inference of what the user wants to do. To wit, ``.ix`` can decide to index *positionally* OR via *labels*. This has caused quite a bit of user confusion over the years. The full indexing documentation are :ref:`here <indexing>`. (:issue:`14218`)
133+
134+
135+
The recommended methods of indexing are:
136+
137+
- ``.loc`` if you want to *label* index
138+
- ``.iloc`` if you want to *positionally* index.
139+
140+
Using ``.ix`` will now show a deprecation warning with a mini-example of how to convert code.
141+
142+
.. ipython:: python
143+
144+
df = pd.DataFrame({'A': [1, 2, 3],
145+
'B': [4, 5, 6]},
146+
index=list('abc'))
147+
148+
df
149+
150+
Previous Behavior, where you wish to get the 0th and the 2nd elements from the index in the 'A' column.
151+
152+
.. code-block:: ipython
153+
154+
In [3]: df.ix[[0, 2], 'A']
155+
Out[3]:
156+
a 1
157+
c 3
158+
Name: A, dtype: int64
159+
160+
Using ``.loc``. Here we will select the appropriate indexes from the index, then use *label* indexing.
161+
162+
.. ipython:: python
163+
164+
df.loc[df.index[[0, 2]], 'A']
165+
166+
Using ``.iloc``. Here we will get the location of the 'A' column, then use *positional* indexing to select things.
167+
168+
.. ipython:: python
169+
170+
df.iloc[[0, 2], df.columns.get_loc('A')]
171+
172+
125173
.. _whatsnew.api_breaking.index_map
126174

127175
Map on Index types now return other Index types

pandas/core/frame.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -1961,7 +1961,7 @@ def _ixs(self, i, axis=0):
19611961
if isinstance(i, slice):
19621962
# need to return view
19631963
lab_slice = slice(label[0], label[-1])
1964-
return self.ix[:, lab_slice]
1964+
return self.loc[:, lab_slice]
19651965
else:
19661966
if isinstance(label, Index):
19671967
return self.take(i, axis=1, convert=True)
@@ -2056,7 +2056,7 @@ def _getitem_array(self, key):
20562056
indexer = key.nonzero()[0]
20572057
return self.take(indexer, axis=0, convert=False)
20582058
else:
2059-
indexer = self.ix._convert_to_indexer(key, axis=1)
2059+
indexer = self.loc._convert_to_indexer(key, axis=1)
20602060
return self.take(indexer, axis=1, convert=True)
20612061

20622062
def _getitem_multilevel(self, key):
@@ -2389,7 +2389,7 @@ def __setitem__(self, key, value):
23892389

23902390
def _setitem_slice(self, key, value):
23912391
self._check_setitem_copy()
2392-
self.ix._setitem_with_indexer(key, value)
2392+
self.loc._setitem_with_indexer(key, value)
23932393

23942394
def _setitem_array(self, key, value):
23952395
# also raises Exception if object array with NA values
@@ -2400,17 +2400,17 @@ def _setitem_array(self, key, value):
24002400
key = check_bool_indexer(self.index, key)
24012401
indexer = key.nonzero()[0]
24022402
self._check_setitem_copy()
2403-
self.ix._setitem_with_indexer(indexer, value)
2403+
self.loc._setitem_with_indexer(indexer, value)
24042404
else:
24052405
if isinstance(value, DataFrame):
24062406
if len(value.columns) != len(key):
24072407
raise ValueError('Columns must be same length as key')
24082408
for k1, k2 in zip(key, value.columns):
24092409
self[k1] = value[k2]
24102410
else:
2411-
indexer = self.ix._convert_to_indexer(key, axis=1)
2411+
indexer = self.loc._convert_to_indexer(key, axis=1)
24122412
self._check_setitem_copy()
2413-
self.ix._setitem_with_indexer((slice(None), indexer), value)
2413+
self.loc._setitem_with_indexer((slice(None), indexer), value)
24142414

24152415
def _setitem_frame(self, key, value):
24162416
# support boolean setting with DataFrame input, e.g.
@@ -4403,7 +4403,7 @@ def append(self, other, ignore_index=False, verify_integrity=False):
44034403
elif isinstance(other, list) and not isinstance(other[0], DataFrame):
44044404
other = DataFrame(other)
44054405
if (self.columns.get_indexer(other.columns) >= 0).all():
4406-
other = other.ix[:, self.columns]
4406+
other = other.loc[:, self.columns]
44074407

44084408
from pandas.tools.merge import concat
44094409
if isinstance(other, (list, tuple)):

pandas/core/generic.py

+6-11
Original file line numberDiff line numberDiff line change
@@ -1809,18 +1809,12 @@ def xs(self, key, axis=0, level=None, drop_level=True):
18091809
loc, new_ax = labels.get_loc_level(key, level=level,
18101810
drop_level=drop_level)
18111811

1812-
# convert to a label indexer if needed
1813-
if isinstance(loc, slice):
1814-
lev_num = labels._get_level_number(level)
1815-
if labels.levels[lev_num].inferred_type == 'integer':
1816-
loc = labels[loc]
1817-
18181812
# create the tuple of the indexer
18191813
indexer = [slice(None)] * self.ndim
18201814
indexer[axis] = loc
18211815
indexer = tuple(indexer)
18221816

1823-
result = self.ix[indexer]
1817+
result = self.iloc[indexer]
18241818
setattr(result, result._get_axis_name(axis), new_ax)
18251819
return result
18261820

@@ -1983,7 +1977,7 @@ def drop(self, labels, axis=0, level=None, inplace=False, errors='raise'):
19831977
slicer = [slice(None)] * self.ndim
19841978
slicer[self._get_axis_number(axis_name)] = indexer
19851979

1986-
result = self.ix[tuple(slicer)]
1980+
result = self.loc[tuple(slicer)]
19871981

19881982
if inplace:
19891983
self._update_inplace(result)
@@ -4332,8 +4326,9 @@ def first(self, offset):
43324326
if not offset.isAnchored() and hasattr(offset, '_inc'):
43334327
if end_date in self.index:
43344328
end = self.index.searchsorted(end_date, side='left')
4329+
return self.iloc[:end]
43354330

4336-
return self.ix[:end]
4331+
return self.loc[:end]
43374332

43384333
def last(self, offset):
43394334
"""
@@ -4364,7 +4359,7 @@ def last(self, offset):
43644359

43654360
start_date = start = self.index[-1] - offset
43664361
start = self.index.searchsorted(start_date, side='right')
4367-
return self.ix[start:]
4362+
return self.iloc[start:]
43684363

43694364
def rank(self, axis=0, method='average', numeric_only=None,
43704365
na_option='keep', ascending=True, pct=False):
@@ -5078,7 +5073,7 @@ def truncate(self, before=None, after=None, axis=None, copy=True):
50785073

50795074
slicer = [slice(None, None)] * self._AXIS_LEN
50805075
slicer[axis] = slice(before, after)
5081-
result = self.ix[tuple(slicer)]
5076+
result = self.loc[tuple(slicer)]
50825077

50835078
if isinstance(ax, MultiIndex):
50845079
setattr(result, self._get_axis_name(axis),

pandas/core/groupby.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -4103,7 +4103,7 @@ def _chop(self, sdata, slice_obj):
41034103
if self.axis == 0:
41044104
return sdata.iloc[slice_obj]
41054105
else:
4106-
return sdata._slice(slice_obj, axis=1) # ix[:, slice_obj]
4106+
return sdata._slice(slice_obj, axis=1) # .loc[:, slice_obj]
41074107

41084108

41094109
class NDFrameSplitter(DataSplitter):

0 commit comments

Comments
 (0)