9

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

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)

这给出了: 数据

我想提取这个 DataFrame 的一个子集以供后续分析。假设我想切出jersey数字为 71 的行。我真的不喜欢使用xs来执行此操作的想法。当你做一个横截面时,xs你会丢失你选择的列。如果我运行:

data.xs(71, axis=0, level='jersey')

然后我取回正确的行,但我丢失了jersey列。

xs_slice

此外,对于我想从列中xs获取一些不同值的情况,这似乎不是一个很好的解决方案。jersey我认为一个更好的解决方案是在这里找到的:

data[[j in [71, 84] for t, j, g in data.index]]

boolean_slice_1

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

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

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

double_boolean_slice

因此第二个问题:有没有更紧凑的方法来做我刚才所做的事情?

4

4 回答 4

2

从 Pandas 0.18(可能更早)开始,您可以使用pd.IndexSlice轻松切片多索引数据帧。

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

data.loc[pd.IndexSlice[:,[71, 84],:],:] #IndexSlice on the rows

IndexSlice 需要足够的级别信息来明确,这样您就可以删除尾随的冒号:

data.loc[pd.IndexSlice[:,[71, 84]],:]

同样,您可以在列上使用 IndexSlice:

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

这为您提供了问题中的最终 DataFrame。

于 2018-02-07T18:58:11.133 回答
1

这是一种使用更多内置感觉语法的方法。但它仍然很笨拙:

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

比较方法

仍然希望有一种更清洁或更规范的方式来做到这一点。

于 2013-12-24T04:58:14.863 回答
1

注意:自 Pandas v0.20 起,ix访问器已被弃用;酌情使用locoriloc代替。

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

要获取 Ralph 的列:

data.ix[:,"Ralph"]

要为其中两个获取它,请传入一个列表:

data.ix[:,["Ralph","John"]]

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

通常,要在 MultiIndex 中进行查找,您应该传入一个元组。例如

data.[:,("Ralph","Speed")]

但是,如果您只传入一个元素,它会将其视为传入元组的第一个元素,然后是通配符。

如果您想访问不是 0 级索引的列,则变得棘手。例如,获取“速度”的所有列。然后你需要更有创意..get_level_values结合使用索引/列的方法和布尔索引:

例如,这会在行和strength列中获得球衣 71:

data.ix[data.index.get_level_values("jersey") == 71 , \
        data.columns.get_level_values("obstype") == "Strength"]
于 2013-12-24T22:03:44.497 回答
0

请注意,据我了解,select速度很慢。但这里的另一种方法是:

data.select(lambda col: col[0] in ['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)

这里最大的缺点是您必须知道索引级别编号。

于 2014-01-02T22:49:27.887 回答