考虑一个 pandas DataFrame 的构造如下:
df = pandas.DataFrame({'a':['one','two','three']})
然后我可以找到包含两个
的数据帧的特定行,例如:
df[df.a == 'two']
但到目前为止,我发现将 DataFrame
子集化到这一行的唯一方法如下:
df[:df[df.a == 'two'].index[0]]
但这很丑陋,所以:
是否有更合适的方法来完成此子集化?
具体来说,我感兴趣的是如何在给定列与某个任意文本字符串(在本例中为“两个”)匹配的行索引之间对 DataFrame 进行切片。对于这种特殊情况,它相当于df[:2]
。然而,一般来说,能够根据列值定位切片的开头和/或结尾的索引似乎是合理的事情?
最后一个例子,也许会有帮助;我希望能够做这样的事情:
df[df.a == '一' : df.a == '三']
获取包含DataFrame第1行和第2行的切片,相当于df[0:3]
最佳答案
您想要识别特定开始值和停止值的索引并获取匹配行以及其间的所有行。一种方法是找到索引并建立一个范围,但您已经说过您不喜欢这种方法。这是使用 bool 逻辑的通用解决方案,应该适合您。
首先,让我们举一个更有趣的例子:
import pandas as pd
df = pd.DataFrame({'a':['one','two','three', 'four', 'five']})
假设start = "two"
和stop = "four"
。也就是说,您想要获得以下输出DataFrame:
a
1 two
2 three
3 four
我们可以通过以下方式找到边界行的索引:
df["a"].isin({start, stop})
#0 False
#1 True
#2 False
#3 True
#4 False
#Name: a, dtype: bool
如果索引 2 的值为 True
,我们就完成了,因为我们可以将此输出用作掩码。因此,让我们找到一种方法来创建我们需要的蒙版。
首先我们可以使用cummax()
和 bool 异或运算符(^
)来实现:
(df["a"]==start).cummax() ^ (df["a"]==stop).cummax()
#0 False
#1 True
#2 True
#3 False
#4 False
#Name: a, dtype: bool
这几乎就是我们想要的,只是我们缺少停止值索引。因此,我们只需按位或 (|
) 停止条件即可:
#0 False
#1 True
#2 True
#3 True
#4 False
#Name: a, dtype: bool
这得到了我们正在寻找的结果。因此,创建一个掩码,并索引数据帧:
mask = (df["a"]==start).cummax() ^ (df["a"]==stop).cummax() | (df["a"]==stop)
print(df[mask])
# a
#1 two
#2 three
#3 four
我们可以将这些发现扩展为一个函数,该函数还支持对一行进行索引或从一行到末尾进行索引:
def get_rows(df, col, start, stop):
if start is None:
mask = ~((df[col] == stop).cummax() ^ (df[col] == stop))
else:
mask = (df[col]==start).cummax() ^ (df[col]==stop).cummax() | (df[col]==stop)
return df[mask]
# get rows between "two" and "four" inclusive
print(get_rows(df=df, col="a", start="two", stop="four"))
# a
#1 two
#2 three
#3 four
# get rows from "two" until the end
print(get_rows(df=df, col="a", start="two", stop=None))
# a
#1 two
#2 three
#3 four
#4 five
# get rows up to "two"
print(get_rows(df=df, col="a", start=None, stop="two"))
# a
#0 one
#1 two
<小时/>
更新:
为了完整起见,这里是基于索引的解决方案。
def get_rows_indexing(df, col, start, stop):
min_ind = min(df.index[df[col]==start].tolist() or [0])
max_ind = max(df.index[df[col]==stop].tolist() or [len(df)])
return df[min_ind:max_ind+1]
这个函数本质上与其他版本的功能相同,但它可能更容易理解。此外,这更加可靠,因为其他版本依赖于 None
不是所需列中的值。
关于python - 如何有条件地切片 pandas 中的数据框,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50049823/