在Pandas中对行和列MultiIndex使用布尔索引

8on*_*ne6 9 python multi-index pandas

问题最后是粗体.但首先,让我们设置一些数据:

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)
Run Code Online (Sandbox Code Playgroud)

这给了: 数据

我想将这个DataFrame的一个子集用于后续分析.说我想切出jersey数字为71 的行.我真的不喜欢用xs它来做这个的想法.当您通过横断面xs丢失所选的列时.如果我跑:

data.xs(71, axis=0, level='jersey')
Run Code Online (Sandbox Code Playgroud)

然后我回到正确的行,但我失去了jersey专栏.

xs_slice

此外,xs对于我希望jersey列中有一些不同值的情况,这似乎不是一个很好的解决方案.我认为一个好得多的解决方案是找到了一个在这里:

data[[j in [71, 84] for t, j, g in data.index]]
Run Code Online (Sandbox Code Playgroud)

boolean_slice_1

你甚至可以过滤球衣和球队的组合:

data[[j in [71, 84] and t in ['Dodgers', 'Mets'] for t, j, g in data.index]]
Run Code Online (Sandbox Code Playgroud)

boolean_slice_2

太好了!

所以问题是:如何选择类似的列来选择列的子集. 例如,假设我只想要表示Ralph数据的列.如果不使用我怎么能这样做xs?或者,如果我只想要列observer in ['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
Run Code Online (Sandbox Code Playgroud)

double_boolean_slice

因此第二个问题:是否有一种更紧凑的方式来做我刚刚做的事情?

8on*_*ne6 1

这是一种使用稍微更内置的语法的方法。但它仍然很笨重:

\n\n
data.loc[\n    (data.index.get_level_values(\'jersey\').isin([71, 84])\n     & data.index.get_level_values(\'team\').isin([\'Dodgers\', \'Mets\'])), \n    data.columns.get_level_values(\'observer\').isin([\'John\', \'Ralph\'])\n]\n
Run Code Online (Sandbox Code Playgroud)\n\n

所以比较一下:

\n\n
def hackedsyntax():\n    return data[[j in [71, 84] and t in [\'Dodgers\', \'Mets\'] for t, j, g in data.index]]\\\n    .T[[obs in [\'John\', \'Ralph\'] for obs, obstype in data.columns]].T\n\ndef uglybuiltinsyntax():\n    return data.loc[\n        (data.index.get_level_values(\'jersey\').isin([71, 84])\n         & data.index.get_level_values(\'team\').isin([\'Dodgers\', \'Mets\'])), \n        data.columns.get_level_values(\'observer\').isin([\'John\', \'Ralph\'])\n    ]\n\n%timeit hackedsyntax()\n%timeit uglybuiltinsyntax()\n\nhackedsyntax() - uglybuiltinsyntax()\n
Run Code Online (Sandbox Code Playgroud)\n\n

结果:

\n\n
1000 loops, best of 3: 395 \xc2\xb5s per loop\n1000 loops, best of 3: 409 \xc2\xb5s per loop\n
Run Code Online (Sandbox Code Playgroud)\n\n

方法比较

\n\n

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

\n