Is there a built-in way in pandas dataframes to include next/previous rows of slice if requested index is between integer index labels?

Viewed 57

Example dataframe:

    value 
ind
1   2.3
3   4.4
5   3.3
7   6.6

and then I slice with:

df.loc[2:4]

Default output is going to be just:

3   4.4

But I want the upper and lower values included:

1   2.3
3   4.4
5   3.3

Is there a built-in way without some extra logic?

3 Answers

I am not sure if there's an in-built way for this, but you can get the upper and lower values you want specifying your boundaries and including them in your slice:

>>> r = 1
>>> df.loc[2 - r : 4 + r]

Out[75]: 
     value
ind       
1      2.3
3      4.4
5      3.3

So by adjusting the r you can specify how many next or previous rows to include

EDIT: Based on your comments below, try using iloc and not loc:

Assume this df:

>>> df

Out[108]: 
     value
ind       
1      2.3
3      4.4
5      3.3
7      6.6
11     7.4
21     6.7
33     7.8
34     2.3
35     6.9


# Boundary
>>> r = 2

# Using loc
>>> df.loc[2 - r : 4 + r]

Out[111]: 
     value
ind       
1      2.3
3      4.4
5      3.3

# Using iloc
>>> df.iloc[2 - r : 4 + r]

Out[112]: 
     value
ind       
1      2.3
3      4.4
5      3.3
7      6.6
11     7.4
21     6.7

If you don't care about the index, you could use shift()

s = slice(2,4)
df_ = pd.concat([df['value'].shift(1).loc[s], df['value'].loc[s], df['value'].shift(-1).loc[s]])
# print(df_)

ind
3    2.3
3    4.4
3    3.3
Name: value, dtype: float64

Short answer: No

The .loc[] method looks for the first index value equal to or larger the start value of the slice, and the reverse for the stop value of the slice.

If you need that extra logic (evaluate if the start value has an exact match, if not, get the next best lower value, and equivalent for the stop value), you have to do that using your own code (at least not pandas built-in, there might be a solution for that in the net already)

Working stub (no exception handling etc, assumes df is ordered by integer index, and fails if hitting first or last row):

def loc2(df, start, stop):
    r = df.loc[start:stop]
    if r.index[0]>start:
        start=df.index.to_list()[df.index.to_list().index(r.index[0])-1]
    if r.index[-1]<stop:
        stop=df.index.to_list()[df.index.to_list().index(r.index[-1])+1]
    return df.loc[start:stop]
Related