熊猫切片不包括末端
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
不属于索引的情况。
更通用的解决方案是简单地将索引与start
和end
进行比较,这样您就可以强制它们中的任何一个都包含独占。
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]
【讨论】:
以上是关于熊猫切片不包括末端的主要内容,如果未能解决你的问题,请参考以下文章