pandas:具有多索引的布尔索引

时间:2014-09-14 19:58:18

标签: python pandas

这里有很多类似标题的问题,但我找不到解决这个问题的问题。

我有来自许多不同来源的数据帧,我想逐个过滤。当布尔序列与过滤的数据帧大小相同时,使用布尔索引很有效,但是当序列的大小与过滤后的数据帧的更高级别索引相同时,则不行。

简而言之,我们说我有这个数据框:

In [4]: df = pd.DataFrame({'a':[1,1,1,2,2,2,3,3,3], 
                           'b':[1,2,3,1,2,3,1,2,3], 
                           'c':range(9)}).set_index(['a', 'b'])
Out[4]: 
     c
a b   
1 1  0
  2  1
  3  2
2 1  3
  2  4
  3  5
3 1  6
  2  7
  3  8

这一系列:

In [5]: filt = pd.Series({1:True, 2:False, 3:True})
Out[6]: 
1     True
2    False
3     True
dtype: bool

我想要的输出是:

     c
a b   
1 1  0
  2  1
  3  2
3 1  6
  2  7
  3  8

我不是在寻找不使用filt系列的解决方案,例如:

df[df.index.get_level_values('a') != 2]
df[df.index.get_level_values('a').isin([1,3])]

我想知道我是否可以按原样使用输入filt系列,因为我会在c上使用过滤器:

filt = df.c < 7
df[filt]

6 个答案:

答案 0 :(得分:2)

如果将索引'a'转换回列,则可以按如下方式执行:

>>> df = pd.DataFrame({'a':[1,1,1,2,2,2,3,3,3], 
                       'b':[1,2,3,1,2,3,1,2,3], 
                       'c':range(9)})
>>> filt = pd.Series({1:True, 2:False, 3:True})
>>> df[filt[df['a']].values]
   a  b  c
0  1  1  0
1  1  2  1
2  1  3  2
6  3  1  6
7  3  2  7
8  3  3  8

修改即可。 正如@joris所建议的那样,这也适用于索引。以下是您的示例数据的代码:

>>> df[filt[df.index.get_level_values('a')].values]
     c
a b   
1 1  0
  2  1
  3  2
3 1  6
  2  7
  3  8

答案 1 :(得分:2)

如果布尔系列未与您要为其编制索引的数据帧对齐,则可以先将其与align明确对齐:

In [25]: df_aligned, filt_aligned = df.align(filt.to_frame(), level=0, axis=0)

In [26]: filt_aligned
Out[26]:
         0
a b
1 1   True
  2   True
  3   True
2 1  False
  2  False
  3  False
3 1   True
  2   True
  3   True

然后你可以用它编制索引:

In [27]: df[filt_aligned[0]]
Out[27]:
     c
a b
1 1  0
  2  1
  3  2
3 1  6
  2  7
  3  8

注意:对齐不适用于系列,因此对齐调用中的to_frame,因此上面的[0]可以取回系列。

答案 2 :(得分:1)

您可以使用pd.IndexSlicer

>>> df.loc[pd.IndexSlice[filt[filt].index.values, :], :]
     c
a b   
1 1  0
  2  1
  3  2
3 1  6
  2  7
  3  8

其中filt[filt].index.values仅是[1, 3]。换句话说

>>> df.loc[pd.IndexSlice[[1, 3], :]]
     c
a b   
1 1  0
  2  1
  3  2
3 1  6
  2  7
  3  8

因此,如果您对过滤器的结构进行一些不同的设计,则表达式会更短。 Emanuele Paolini的解决方案df[filt[df.index.get_level_values('a')].values]的优势在于,您可以更好地控制索引编制。

关于多索引切片的主题将更深入地介绍here

完整的代码

import pandas as pd
import numpy as np
df = pd.DataFrame({'a':[1,1,1,2,2,2,3,3,3], 'b':[1,2,3,1,2,3,1,2,3], 'c':range(9)}).set_index(['a', 'b'])
filt = pd.Series({1:True, 2:False, 3:True})

print(df.loc[pd.IndexSlice[[1, 3], :]])
print(df.loc[(df.index.levels[0].values[filt], slice(None)), :])
print(df.loc[pd.IndexSlice[filt[filt].index.values, :], :])

答案 3 :(得分:0)

我遇到了完全相同的问题。我发现了这个问题,并尝试了解决方案,但没有一个是足够有效的。我的数据框是:A = 700k rows x 14 colsB = 100M rows x 3 colsB有一个MultiIndex,其中第一个(高)级别等于A的索引。让C成为A10k行的切片。我的任务是从B获取尽可能快的高级索引与C索引匹配的行。在运行时选择CAB是静态的。

我从这里尝试了解决方案:get_level_values需要很多秒,df.align甚至没有完成授予MemoryError(并且还需要几秒钟)。

对我有用的解决方案(在运行时~300msec内)如下:

  1. 对于来自index的每个iA,找到{{1}中的第一个和最后一个(非包含)位置索引其中包含B作为MultiIndex的第一级。将这些对存储在i中。这是一次提前完成的。 示例代码:

    A
  2. 在运行时,从def construct_position_indexes(A, B): indexes = defaultdict(list) prev_index = 0 for i, cur_index in enumerate(B.index.get_level_values(0)): if cur_index != prev_index: indexes[cur_index].append(i) if prev_index: indexes[prev_index].append(i) prev_index = cur_index indexes[cur_index].append(i+1) index_df = pd.DataFrame(indexes.values(), index=indexes.keys(), columns=['start_index', 'end_index'], dtype=int) A = A.join(index_df) # they become floats, so we fix that A['start_index'] = A.start_index.fillna(0).astype(int) A['end_index'] = A.end_index.fillna(0).astype(int) return A 获取位置边界,并构建要在C中搜索的所有位置索引的列表,并将它们传递给B

    B.take()
  3. 我希望它不会太复杂。本质上,我们的想法是def get_slice(B, C): all_indexes = [] for start_index, end_index in zip( C.start_index.values, C.end_index.values): all_indexes.extend(range(start_index, end_index)) return B.take(all_indexes) 中的每一行都存储A中行的相应(位置)索引的范围,以便在运行时我们可以快速构造所有位置索引的列表以查询{{ 1}} by。

    这是一个玩具示例:

    B

答案 4 :(得分:0)

简单地:

df.where(
    filt.rename_axis('a').rename('c').to_frame()
).dropna().astype(int)

说明:

  • .rename_axis('a')索引重命名为a(我们要过滤的索引)
  • .rename('c')重命名为c(存储值的列)
  • .to_frame()将此系列转换为 DataFrame ,以与df
  • 兼容
  • df.where(...)过滤行,留下缺失值(NaN),其中过滤器为False
  • .drop_na()删除缺少值的行(在我们的情况下为a == 2
  • .astype(int)float转换回int(不确定为什么float开头)

顺便说一下,df.where(...)df[...]在这里的表现类似,所以请选择。

答案 5 :(得分:0)

更具可读性(我喜欢)的解决方案是重新索引布尔系列(数据帧)以匹配多索引 df 的索引:

df.loc[filt.reindex(df.index, level='a')]