Python 对熊猫中的行和列多索引使用布尔索引

Python 对熊猫中的行和列多索引使用布尔索引,python,pandas,multi-index,Python,Pandas,Multi Index,问题在末尾,以粗体显示。但首先,让我们设置一些数据: import numpy as np import pandas as pd from itertools import product np.random.seed(1) team_names = ['Yankees', 'Mets', 'Dodgers'] jersey_numbers = [35, 71, 84] game_numbers = [1, 2] observer_names = ['Bill', 'John', 'Ral

问题在末尾,以粗体显示。但首先,让我们设置一些数据:

import numpy as np
import pandas as pd
from itertools import product

np.random.seed(1)

team_names = ['Yankees', 'Mets', 'Dodgers']
jersey_numbers = [35, 71, 84]
game_numbers = [1, 2]
observer_names = ['Bill', 'John', 'Ralph']
observation_types = ['Speed', 'Strength']

row_indices = list(product(team_names, jersey_numbers, game_numbers, observer_names, observation_types))
observation_values = np.random.randn(len(row_indices))

tns, jns, gns, ons, ots = zip(*row_indices)

data = pd.DataFrame({'team': tns, 'jersey': jns, 'game': gns, 'observer': ons, 'obstype': ots, 'value': observation_values})

data = data.set_index(['team', 'jersey', 'game', 'observer', 'obstype'])
data = data.unstack(['observer', 'obstype'])
data.columns = data.columns.droplevel(0)
这使得:

我想从这个数据帧中提取一个子集,用于后续分析。假设我想把
jersey
编号为71的行切掉。我真的不喜欢使用
xs
来实现这一点。当您通过
xs
进行横截面时,您将丢失所选的列。如果我跑步:

data.xs(71, axis=0, level='jersey')
然后返回正确的行,但丢失了
jersey

另外,
xs
对于我希望从
jersey
列中获得几个不同的值的情况,似乎不是一个很好的解决方案。我认为一个更好的解决方案是:

你甚至可以选择运动衫和球队的组合:

data[[j in [71, 84] and t in ['Dodgers', 'Mets'] for t, j, g in data.index]]

很好

那么问题是:我如何做类似的事情来选择列的子集。例如,假设我只想要表示拉尔夫数据的列。如果不使用
xs
,我如何才能做到这一点?或者,如果我只想在['John','Ralph']中使用带有
观察者的列,该怎么办?同样,我更喜欢一种解决方案,它可以在结果中保留所有级别的行和列索引……就像上面的布尔索引示例一样

我可以做我想做的,甚至可以将行索引和列索引中的选择组合起来。但我找到的唯一解决办法是一些真正的体操:

data[[j in [71, 84] and t in ['Dodgers', 'Mets'] for t, j, g in data.index]]\
    .T[[obs in ['John', 'Ralph'] for obs, obstype in data.columns]].T


因此,第二个问题是:有没有一种更简洁的方法来完成我刚才所做的工作?

这里有一种方法使用了稍微多一些内置的感觉语法。但它仍然像地狱一样笨重:

data.loc[
    (data.index.get_level_values('jersey').isin([71, 84])
     & data.index.get_level_values('team').isin(['Dodgers', 'Mets'])), 
    data.columns.get_level_values('observer').isin(['John', 'Ralph'])
]
因此,比较:

def hackedsyntax():
    return data[[j in [71, 84] and t in ['Dodgers', 'Mets'] for t, j, g in data.index]]\
    .T[[obs in ['John', 'Ralph'] for obs, obstype in data.columns]].T

def uglybuiltinsyntax():
    return data.loc[
        (data.index.get_level_values('jersey').isin([71, 84])
         & data.index.get_level_values('team').isin(['Dodgers', 'Mets'])), 
        data.columns.get_level_values('observer').isin(['John', 'Ralph'])
    ]

%timeit hackedsyntax()
%timeit uglybuiltinsyntax()

hackedsyntax() - uglybuiltinsyntax()
结果:

1000 loops, best of 3: 395 µs per loop
1000 loops, best of 3: 409 µs per loop


仍然希望有一种更干净或更规范的方法来做到这一点。

注意:自Pandas v0.20以来,
ix
访问器已被弃用;根据需要使用
loc
iloc

如果我正确理解了这个问题,它很简单:

要获取Ralph的列,请执行以下操作:

data.ix[:,"Ralph"]
若要为其中两人获得此信息,请输入一个列表:

data.ix[:,["Ralph","John"]]
ix运算符是幂索引运算符。请记住,第一个参数是行,然后是列(与数据[…][…]相反,后者是相反的)。冒号用作通配符,因此它返回axis=0中的所有行

通常,要在多索引中查找,应该传入一个元组。e、 g

data.[:,("Ralph","Speed")]
但如果只传入一个元素,它会将其视为传入元组的第一个元素,然后传入一个通配符

如果您想访问非0级索引的列,则需要处理一些棘手的问题。例如,获取“速度”的所有列。那你就需要更有创意了。。结合布尔索引使用索引/列的
get\u level\u value
方法:

例如,这将在行中获取强度,在列中获取强度:

data.ix[data.index.get_level_values("jersey") == 71 , \
        data.columns.get_level_values("obstype") == "Strength"]

请注意,据我所知,
select
速度很慢。但另一种方法是:

data.select(λ列:列[0]位于['John','Ralph',axis=1)

您还可以通过对行的选择将其链接:

data.select(lambda col: col[0] in ['John', 'Ralph'], axis=1) \
    .select(lambda row: row[1] in [71, 84] and row[2] > 1, axis=0)
这里最大的缺点是您必须知道索引级别编号。

从Pandas 0.18(可能更早)开始,您可以使用

对于您的特定问题,您可以使用以下选项按球队、球衣和比赛进行选择:

data.loc[pd.IndexSlice[:,[71, 84],:],:] #IndexSlice on the rows
IndexSlice需要足够的级别信息来明确,这样您就可以删除尾随的冒号:

data.loc[pd.IndexSlice[:,[71, 84]],:]
同样,您可以在列上建立索引:

data.loc[pd.IndexSlice[:,[71, 84]],pd.IndexSlice[['John', 'Ralph']]]

这为您的问题提供了最后的数据框架。

很好的方法,但您的问题是什么?@MattDMo我已将上述具体问题加粗。更一般地说:我想我已经展示了上面一些强大但语法丑陋的食谱。我希望有一种更直接的方式来完成我在那里所做的事情。具体来说,我正在寻找一种方法,该方法将基于一个或多个行索引中的值限制行,同时基于一个或多个列索引中的值限制列。非常希望有人能提出一个更自然的方法。有趣的问题。对于单元素过滤器案例,您可以传递
drop\u level=False
,以避免丢失
Jersey
列。请注意,您可以编写
data.loc[[j in[71,84]和t in['Dodgers',Mets']来代替数据.index中的t,j,g],[obs in['John',Ralph']来代替obs,obstype in data.columns]
@DSM,这也是迄今为止最快的解决方案,尽管loc在这里看起来很慢。。。看起来这可能是一个很好的功能要求。@AndyHayden:我不介意像
df.fx(rows={“jersey”:[71],“team”:[“Dodgers”,“Mets”]},columns={“observer”:[“John”,“Ralph”]})
这样的东西基本上达到了这里的要求。是的。我明确询问如何处理基于多索引中非前导项的筛选。我更新了答案以回应您的评论。但请注意,您在问题中给出的示例都是针对0级指数的。i、 e.在列轴和行轴上都可以有多个索引。在您的示例中,
Jack
是列多索引的级别0。刚刚注意到,事实上与下面的答案几乎相同。
data.loc[pd.IndexSlice[:,[71, 84]],pd.IndexSlice[['John', 'Ralph']]]