Python 如何在pandas中有条件地分割数据帧

Python 如何在pandas中有条件地分割数据帧,python,pandas,dataframe,Python,Pandas,Dataframe,考虑如下构造的数据帧: df = pandas.DataFrame({'a':['one','two','three']}) 然后我可以找到包含两个的数据帧的特定行,如: df[df.a == 'two'] 但到目前为止,我发现将数据帧子集到此行的唯一方法如下: df[:df[df.a == 'two'].index[0]] 但这很难看,所以: 是否有一种更合适的方法来完成此子集设置 具体来说,我感兴趣的是如何在行索引之间分割数据帧,其中给定的列匹配一些任意文本字符串(在本例中为“两”)。

考虑如下构造的数据帧:

df = pandas.DataFrame({'a':['one','two','three']})
然后我可以找到包含两个
的数据帧的特定行,如:

df[df.a == 'two']
但到目前为止,我发现将
数据帧子集到此行的唯一方法如下:

df[:df[df.a == 'two'].index[0]]
但这很难看,所以:

是否有一种更合适的方法来完成此子集设置

具体来说,我感兴趣的是如何在行索引之间分割数据帧,其中给定的列匹配一些任意文本字符串(在本例中为“两”)。对于这种特殊情况,它相当于
df[:2]
。但是,一般来说,基于列值为切片的开始和/或结束查找索引的能力似乎是合理的

最后一个例子,也许会有所帮助;我希望能够做到以下几点:

df[df.a=='1':df.a=='3']


要获取包含数据帧第1行和第2行的切片,相当于df[0:3]

您需要标识特定开始值和停止值的索引,并获取匹配行加上中间的所有行。一种方法是找到索引并建立一个范围,但您已经说过您不喜欢这种方法。这是一个使用布尔逻辑的通用解决方案,应该适合您

首先,让我们做一个更有趣的例子:

import pandas as pd
df = pd.DataFrame({'a':['one','two','three', 'four', 'five']})
假设
start=“two”
stop=“four”
。也就是说,您希望获得以下输出数据帧:

       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()
和布尔异或运算符(
^
)来实现:

(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
不是所需列中的值。

如果临时使用列“a”作为索引,则locate方法(loc)会完全按照您的要求执行

df = pd.DataFrame({'a':['one','two','three', 'four', 'five']})
start = 'two'
stop = 'four'
df = df.set_index('a').loc[start:stop].reset_index()

我不明白你在问什么。
df[df.a=='two']
能给你想要的吗?这块面包从哪里来的?或者您正在寻找
df[df.a='two'].重置索引(drop=True)
?感谢您对索引的深入处理。一般来说,我对熊猫还不熟悉,仍然在处理你所说的一些内容,但这确实很有帮助。我忍不住想知道为什么您实现的这个“获取行”功能没有内置到用于切片的DataFrame API中。。。我的意思是,考虑到数据帧的索引可以是一个条件,也可以是一个片段,添加对根据两个条件定义片段的支持似乎是一个明显的扩展。无论如何,谢谢你富有洞察力的回答。@Travis很可能有一种更简单的方法/内置的,但我以前从未见过。(熊猫是一个大图书馆,我不是专家)。无论如何,我使用基于索引的方法添加了一个更新。你可能会发现这更简单,“loc”,我认为是locate的缩写,与最初的问题完全一样。因此df.loc[“2”,“4”]给出了从标签“2”到标签“4”(包括)的所有记录。复杂的是,loc只对索引有效,而不对其他列有效。因此,将_索引设置为列a,然后再将其设置回原来的位置。