From 8a4d72002005d14b479e28ce0c72bacfecfc4009 Mon Sep 17 00:00:00 2001 From: Hariharan P R Date: Fri, 12 Dec 2025 13:34:45 +0000 Subject: [PATCH 1/2] DOC: clarify loc slice step behavior with example When using .loc[start:stop:step], start/stop use label semantics but step is positional within that range. Added example showing difference from explicit label selection. Closes #63311 --- doc/source/user_guide/indexing.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/source/user_guide/indexing.rst b/doc/source/user_guide/indexing.rst index ebd1791c0f4ad..47ff30dbeb348 100644 --- a/doc/source/user_guide/indexing.rst +++ b/doc/source/user_guide/indexing.rst @@ -431,6 +431,24 @@ an error will be raised. For instance, in the above example, ``s.loc[2:5]`` woul For more information about duplicate labels, see :ref:`Duplicate Labels `. +Also, when using a slice with a step, such as ``.loc[start:stop:step]``, note that +*start* and *stop* are interpreted as **labels**, while *step* is applied over +the **positional index** within that label range. This means a stepped slice +may return different labels than selecting an explicit list, even when they +appear similar. + +For example, in a ``Series`` with a non-contiguous integer index: + +.. ipython:: python + + s = pd.Series(range(10), index=[0, 5, 10, 15, 20, 25, 30, 35, 40, 45]) + s.loc[10:50:5] # (10), then skip 3 positions → 35 only + s.loc[[10, 15, 20, 25]] # explicit label selection + +The first applies *step* across **positional locations** between the start/stop +labels. The second selects each label directly. + + .. _indexing.integer: Selection by position From 99e13822e8d12b6b7a50e433795e860e9e1373a8 Mon Sep 17 00:00:00 2001 From: Hariharan P R Date: Sat, 13 Dec 2025 05:46:56 +0000 Subject: [PATCH 2/2] Add non-numeric index example for loc slice step behavior --- doc/source/user_guide/indexing.rst | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/doc/source/user_guide/indexing.rst b/doc/source/user_guide/indexing.rst index 47ff30dbeb348..83ba21e013e2e 100644 --- a/doc/source/user_guide/indexing.rst +++ b/doc/source/user_guide/indexing.rst @@ -431,23 +431,35 @@ an error will be raised. For instance, in the above example, ``s.loc[2:5]`` woul For more information about duplicate labels, see :ref:`Duplicate Labels `. -Also, when using a slice with a step, such as ``.loc[start:stop:step]``, note that +When using a slice with a step, such as ``.loc[start:stop:step]``, note that *start* and *stop* are interpreted as **labels**, while *step* is applied over the **positional index** within that label range. This means a stepped slice -may return different labels than selecting an explicit list, even when they -appear similar. +will behave differently than using the labels ``range(start, stop, step)`` when +the index is not contiguous integers. For example, in a ``Series`` with a non-contiguous integer index: .. ipython:: python - s = pd.Series(range(10), index=[0, 5, 10, 15, 20, 25, 30, 35, 40, 45]) - s.loc[10:50:5] # (10), then skip 3 positions → 35 only - s.loc[[10, 15, 20, 25]] # explicit label selection + s = pd.Series(range(10), index=[0, 5, 10, 15, 20, 25, 30, 35, 40, 45]) + s.loc[10:50:5] # (10), then skip 3 positions → 35 only + s.loc[[10, 15, 20, 25]] # explicit label selection The first applies *step* across **positional locations** between the start/stop labels. The second selects each label directly. +Similarly, with a string-based index, the behavior is identical: + +.. ipython:: python + + s = pd.Series(range(10), index=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']) + s.loc['b':'i':2] # Start at 'b' (position 1), stop at 'i' (position 8), step 2 positions → 'b', 'd', 'f', 'h' + s.loc[['b', 'd', 'f', 'h']] # explicit label selection + +In both cases, *start* and *stop* determine the label boundaries (inclusive), +while *step* skips positions within that range, regardless of the index type. + + .. _indexing.integer: