Skip to content

Commit 436228b

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

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

+1540
-1289
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

+7-18
Original file line numberDiff line numberDiff line change
@@ -104,24 +104,13 @@ of multi-axis indexing.
104104

105105
See more at :ref:`Selection by Position <indexing.integer>`
106106

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-
118107
See more at :ref:`Advanced Indexing <advanced>` and :ref:`Advanced
119108
Hierarchical <advanced.advanced_hierarchical>`.
120109

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

123112
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
113+
notation (using ``.loc`` as an example, but applies to ``.iloc`` as
125114
well). Any of the axes accessors may be the null slice ``:``. Axes left out of
126115
the specification are assumed to be ``:``. (e.g. ``p.loc['a']`` is equiv to
127116
``p.loc['a', :, :]``)
@@ -193,7 +182,7 @@ columns.
193182

194183
.. warning::
195184

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

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

@@ -526,7 +515,7 @@ Selection By Callable
526515

527516
.. versionadded:: 0.18.1
528517

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

532521
.. ipython:: python
@@ -641,7 +630,7 @@ Setting With Enlargement
641630

642631
.. versionadded:: 0.13
643632

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

646635
In the ``Series`` case this is effectively an appending operation
647636

@@ -906,7 +895,7 @@ without creating a copy:
906895

907896
Furthermore, ``where`` aligns the input boolean condition (ndarray or DataFrame),
908897
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)
898+
partial setting via ``.loc`` (but on the contents rather than the axis labels)
910899

911900
.. ipython:: python
912901
@@ -1716,7 +1705,7 @@ A chained assignment can also crop up in setting in a mixed dtype frame.
17161705

17171706
.. note::
17181707

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

17211710
This is the correct access method
17221711

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 ``.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)