具有命名索引的布尔子集

时间:2019-01-06 22:45:42

标签: python python-3.x pandas boolean

我正在尝试使用布尔数组来子集数据帧。这有效:

df = pd.DataFrame(
    [
        (0, 0, 1),
        (0, 1, 2),
        (0, 3, 20),
        (1, 0, 2),
        (1, 1, 1),
        (1, 2, 30),
    ],
    columns = ['s', 'j', 'q']
)

df[df['j'] == 0]
df.loc[df['j'] == 0]

但是,以下失败:

df.set_index('s')[df['j'] == 0]
df.set_index('s').loc[df['j'] == 0]

我得到s等于0而不是j的每个实例。我求助于查询(我的条件比字面上的j == 0还要复杂,或者我将直接使用它):

df['sub'] = (df['j'] == 0)
df.query('sub')

是否可以在不创建临时变量的情况下执行此操作?非常感谢! Python 3.7和pandas 0.23.4

编辑

我正在做的问题是布尔系列和数据框具有不同的索引。以下答案详细说明了解决该问题的几种方法,但我支持这两种方法之一:

df.set_index('s')[(df['j'] == 0).values]

df.set_index('s', inplace = True)
df[df['j'] == 0]

1 个答案:

答案 0 :(得分:3)

不要在布尔操作之间重新设置索引。您的布尔系列基于原始索引来放置真值和假值,因此您不能在具有 different 索引的数据帧上重复使用该序列,因为索引随后映射到不同的行通过该新索引。

如果必须创建具有不同索引的数据框,请在执行此操作后在 上创建布尔数组,或者在具有相同索引的另一个数据框上创建。如此有效:

df.set_index('s')[df.set_index('s')['j'] == 0]
df.set_index('s').loc[df.set_index('s')['j'] == 0]

就像

df_indexed_on_s = df.set_index('s')
df_indexed_on_s[df_indexed_on_s['j'] == 0]
df_indexed_on_s.loc[df_indexed_on_s['j'] == 0]

如果必须内联执行此操作,则可能要使用可调用索引;传递给[...]索引操作的函数应该返回布尔序列,因此您也可以使用它:

df.set_index('s')[lambda sdf: sdf['j'] == 0]
df.set_index('s').loc[lambda sdf: sdf['j'] == 0]

或者您可以使用DataFrame.query()来让Pandas为您评估针对数据框以字符串形式表示的查询:

df.set_index('s').query('j == 0')

在后台,迭代附加到df.set_index('s')的索引,并将该索引中的值与df['j'] == 0系列的索引进行检查,以查看应选择的行。后一个系列仍然使用原始索引(编号为0到6的RangeIndex,因此将数字0到6映射到TrueFalse的值,而仅映射s具有值为Int64Index0的{​​{1}}索引。对于1索引具有s的行,0的结果为(df['j'] == 0)[0],因此选择了这些行,而对于True的结果为{{ 1}}。

1的布尔索引需要更多的工作,因为那里的索引是相同的基于False的Int64Index df_indexed_on_s[df_indexed_on_s['j'] == 0] 0`映射到3个单独的布尔结果,因此Pandas知道使用比索引更多的字符来选择匹配的行。