熊猫切片不包括末端

Posted

技术标签:

【中文标题】熊猫切片不包括末端【英文标题】:Pandas slicing excluding the end 【发布时间】:2018-01-13 09:25:28 【问题描述】:

当使用 loc 对数据帧进行切片时,

df.loc[开始:结束]

开始和结束都包括在内。使用 loc 时有没有简单的方法来排除结尾?

【问题讨论】:

为什么不df.loc[start:end-1] @WonjinKim 大概是因为它们的索引不是整数。而@OP,原则上您可能应该只使用包含区间作为包含区间 - 即,如果您不需要端点,请更改您的控制流,以便您将 end 作为您的最后一个索引传递 想要 在我的例子中,索引是日期时间对象。所以 end-1 是不允许的。此外 end - datetime.timedelta(days=1) 也不起作用,因为索引不包含每一天。 【参考方案1】:

loc 包括开始和结束,一个不太理想的解决方法是获取索引位置并使用iloc 对数据框进行切片(假设您没有重复的索引):

df=pd.DataFrame('A':[1,2,3,4], index = ['a','b','c','d'])

df.iloc[df.index.get_loc('a'):df.index.get_loc('c')]

#   A
#a  1
#b  2

df.loc['a':'c']

#   A
#a  1
#b  2
#c  3

【讨论】:

是的,这与我所做的很接近。当索引变得很长时,我担心性能问题。我想没有办法解决这个问题。 我对性能没有把握。但是与索引相关的查询通常是优化且快速的。【参考方案2】:

我能想到的最简单的是df.loc[start:end].iloc[:-1]

砍掉最后一个。

【讨论】:

谢谢,所以iloc 不会包括最后一个? 假设end 确实是您索引的一部分,则此方法有效。使用日期时间可能会适得其反。【参考方案3】:

没有一个答案涉及end 不属于索引的情况。 更通用的解决方案是简单地将索引与startend 进行比较,这样您就可以强制它们中的任何一个都包含独占。

df[(df.index >= start) & (df.index < end)]

例如:

>>> import pandas as pd
>>> import numpy as np

>>> df = pd.DataFrame(
    
        "x": np.arange(48),
        "y": np.arange(48) * 2,
    ,
    index=pd.date_range("2020-01-01 00:00:00", freq="1H", periods=48)
)

>>> start = "2020-01-01 14:00"
>>> end = "2020-01-01 19:30" # this is not in the index

>>> df[(df.index >= start) & (df.index < end)]

                    x   y
2020-01-01 14:00:00 14  28
2020-01-01 15:00:00 15  30
2020-01-01 16:00:00 16  32
2020-01-01 17:00:00 17  34
2020-01-01 18:00:00 18  36
2020-01-01 19:00:00 19  38

【讨论】:

请谨慎使用此解决方案。即使它是正确的,它也需要更长的时间。在我的例子中,它不是 1 秒的操作,而是每次调用索引的 20 秒。 @dmitry502 是正确的 - 感谢您的支持。显然需要权衡取舍。【参考方案4】:

要分割DatetimeIndex,你可以试试这个。它将在您的结束时间之前最多一纳秒抓取所有内容。这将排除结束时间(假设您没有使用 ns 精度),但不一定是最后一次。

df.loc[start:(end - pd.Timedelta('1ns'))]

【讨论】:

虽然这是非常基本的,但这个解决方案有一些优点。 (a)很明显发生了什么,(b)你不必担心范围的结尾不在索引中(因为这个原因,砍掉最后一个元素是一个糟糕的主意),并且(c)它应该不要像this answer 那样受到性能的影响。【参考方案5】:

pd.RangeIndex 可用于使用带有排他停止符的.loc 对索引进行切片如果索引具有整数 dtype。这是一个简单的助手:

class _eidx:
    def __getitem__(self, s: slice) -> pd.RangeIndex:
        return pd.RangeIndex(s.start, s.stop, s.step)
eidx = _eidx()

例子:

df = pd.DataFrame("x": range(10), "y": range(10, 20))

print(df.loc[eidx[3:5]])
   x   y
3  3  13
4  4  14

更简单的方法是使用 python range:

print(df.loc[range(3, 5)])
   x   y
3  3  13
4  4  14

【讨论】:

【参考方案6】:

似乎没有任何真正巧妙的方法可以做到这一点,但我更喜欢富有表现力的解决方案(我想做什么很清楚吗?)。

出于这个原因,我喜欢this solution,尽管它有点基本而且有点笨拙。

一个更健壮、更有表现力和我认为同样想法的高性能版本是首先创建包含切片,然后过滤结果以排除端点:

df.loc[start:end][lambda _: _.index != end]

这个解决方案相当快(我设置了s = start; e = end)并使用名为ts的系列完成了它:

In [1]: %timeit ts[s:e]
135 µs ± 1.07 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [2]: %timeit ts[(ts.index >= s) & (ts.index < e)]
45.1 ms ± 142 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [3]: %timeit ts[s:e][lambda s: s.index != e]
299 µs ± 1.75 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

通过允许中间变量可以使其更具可读性:

inclusive = df.loc[start:end]
exclusive = inclusive[inclusive.index != end]

【讨论】:

以上是关于熊猫切片不包括末端的主要内容,如果未能解决你的问题,请参考以下文章

切片熊猫数据框以获取不连续的列

熊猫切片多索引数据框

将值从一个数据帧切片复制到另一个:使用“IndexSlice”的多索引熊猫数据帧的切片是不是总是一致地排序?

大熊猫中的切片和副本有啥区别? [复制]

使用切片布尔索引的熊猫子集

熊猫如何切片多索引数据框?