这里有很多类似标题的问题,但我找不到解决这个问题的问题。
我有来自许多不同来源的数据帧,我想逐个过滤。当布尔序列与过滤的数据帧大小相同时,使用布尔索引很有效,但是当序列的大小与过滤后的数据帧的更高级别索引相同时,则不行。
简而言之,我们说我有这个数据框:
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]
答案 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 cols
,B = 100M rows x 3 cols
。 B
有一个MultiIndex
,其中第一个(高)级别等于A
的索引。让C
成为A
行10k
行的切片。我的任务是从B
获取尽可能快的高级索引与C
索引匹配的行。在运行时选择C
。 A
和B
是静态的。
我从这里尝试了解决方案:get_level_values
需要很多秒,df.align
甚至没有完成授予MemoryError
(并且还需要几秒钟)。
对我有用的解决方案(在运行时~300msec
内)如下:
对于来自index
的每个i
值A
,找到{{1}中的第一个和最后一个(非包含)位置索引其中包含B
作为MultiIndex的第一级。将这些对存储在i
中。这是一次提前完成的。
示例代码:
A
在运行时,从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()
我希望它不会太复杂。本质上,我们的想法是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')]