Qyouu
多重索引/高级索引注该员额的结构如下:执行部分中提出的问题将一个接一个地加以解决。对于每个问题,将演示一个或多个用于解决该问题并获得预期结果的方法。注对于有兴趣了解更多功能、实现细节和其他相关信息的读者,将包括S(与此类似)。这些注释是通过浏览文档和发现各种晦涩的特性,并根据我自己(无可否认是有限的)经验编写的。所有代码示例都在熊猫v0.23.4,python3.7..如果某些事情不清楚,或者实际上不正确,或者如果您没有找到适用于您的用例的解决方案,请随时建议编辑,在评论中要求澄清,或在适用的情况下打开一个新的问题。下面是一些常见习语的介绍(从此以后称为四个习语),我们将经常重访。DataFrame.loc-标签选择的一般解决方案(+)pd.IndexSlice对于涉及片的更复杂的应用程序)DataFrame.xs-从系列/DataFrame中提取特定的截面。DataFrame.query-动态指定切片和/或过滤操作(即,作为动态计算的表达式。比其他情况更适用于某些情况。亦见这部分文档用于查询多个索引。使用MultiIndex.get_level_values(通常与Index.isin,特别是当使用多个值进行筛选时)。在某些情况下,这也是相当有用的。从这四个成语的角度来考察各种切分和过滤问题,将有助于更好地理解在特定情况下可以应用什么。非常重要的是要明白,并不是所有的成语在每一种情况下都能发挥同样好的作用(如果有的话)。如果一个成语没有被列为下面问题的潜在解决方案,那就意味着成语不能有效地应用于这个问题。问题1如何选择在“一级”中具有“a”的行? col
one two
a t 0
u 1
v 2
w 3你可以用loc,作为适用于大多数情况的一般目的解决办法:df.loc[['a']]在这一点上,如果你TypeError: Expected tuple, got str这意味着你使用的是更老版本的熊猫。考虑升级!否则,使用df.loc[('a', slice(None)), :].或者,您可以使用xs在这里,因为我们要提取一个横截面。注意levels和axis参数(这里可以假定合理的默认值)。df.xs('a', level=0, axis=0, drop_level=False)# df.xs('a', drop_level=False)在这里,drop_level=False需要用参数来防止xs从结果中删除“一级”(我们切片的级别)。这里的另一个选择是使用query:df.query("one == 'a'")如果索引没有名称,则需要将查询字符串更改为"ilevel_0 == 'a'".最后,使用get_level_values:df[df.index.get_level_values('one') == 'a']# If your levels are unnamed, or if you need to select by position (not label),# df[df.index.get_level_values(0) == 'a']此外,我怎样才能降低输出中的“一”级呢? col
two
t 0u 1v 2w 3这可能是容易使用df.loc['a'] # Notice the single string argument instead the list.或,df.xs('a', level=0, axis=0, drop_level=True)# df.xs('a')注意,我们可以省略drop_level论点(假定为True(默认情况下)。注您可能会注意到,过滤后的DataFrame可能仍然具有所有级别,即使它们在打印出DataFrame时没有显示出来。例如,v = df.loc[['a']]print(v)
col
one two
a t 0
u 1
v 2
w 3print(v.index)MultiIndex(levels=[['a', 'b', 'c', 'd'], ['t', 'u', 'v', 'w']],
labels=[[0, 0, 0, 0], [0, 1, 2, 3]],
names=['one', 'two'])您可以使用MultiIndex.remove_unused_levels:v.index = v.index.remove_unused_levels()print(v.index)MultiIndex(levels=[['a'], ['t', 'u', 'v', 'w']],
labels=[[0, 0, 0, 0], [0, 1, 2, 3]],
names=['one', 'two'])问题1b如何将值为“t”的所有行分割为“二级”? col
one two
a t 0b t 4
t 8d t 12直觉上,你会想要一些slice():df.loc[(slice(None), 't'), :]它只是起作用了!™,但是它很笨重。我们可以使用pd.IndexSliceAPI在这里。idx = pd.IndexSlicedf.loc[idx[:, 't'], :]这个要干净得多。注为什么尾翼:纵横交错?这是因为loc可用于沿两个轴(axis=0或axis=1)。如果不明确说明要在哪个轴上进行切片,操作就变得模糊不清。看到那个红色的大盒子切片文件.如果你想消除任何含糊不清的地方,loc接受axis参数:df.loc(axis=0)[pd.IndexSlice[:, 't']]没有axis参数(即,只需执行df.loc[pd.IndexSlice[:, 't']]),切片被假定在列上,并且KeyError将在这种情况下提出。这被记录在切片机..然而,为了这篇文章的目的,我们将明确指定所有的轴。带着xs,是的df.xs('t', axis=0, level=1, drop_level=False)带着query,是的df.query("two == 't'")# Or, if the first level has no name, # df.query("ilevel_1 == 't'")最后,用get_level_values,你可以df[df.index.get_level_values('two') == 't']# Or, to perform selection by position/integer,# df[df.index.get_level_values(1) == 't']结果都一样。问题2如何选择与“一级”中的“b”和“d”项对应的行? col
one two
b t 4
u 5
v 6
w 7
t 8d w 11
t 12
u 13
v 14
w 15使用LOC,通过指定一个列表以类似的方式完成这一任务。df.loc[['b', 'd']]要解决上述选择“b”和“d”的问题,还可以使用query:items = ['b', 'd']df.query("one in @items")# df.query("one == @items", parser='pandas')# df.query("one in ['b', 'd']")# df.query("one == ['b', 'd']", parser='pandas')注是的,默认的解析器是'pandas'但是,强调这个语法并不是传统的python,这一点很重要。Pandas解析器生成一个与表达式略有不同的解析树。这样做是为了使某些操作更直观地指定。欲知更多信息,请阅读我在熊猫动态表达评价的pd.val().还有,和get_level_values + Index.isin:df[df.index.get_level_values("one").isin(['b', 'd'])]问题2b如何得到“二级”中与“t”和“w”对应的所有值? col
one two
a t 0
w 3b t 4
w 7
t 8d w 11
t 12
w 15带着loc,这是可能的只与.pd.IndexSlice.df.loc[pd.IndexSlice[:, ['t', 'w']], :]第一冒号:在……里面pd.IndexSlice[:, ['t', 'w']]意思是分出第一个层次。随着被查询级别深度的增加,您将需要指定更多的切片,每个级别都要切片一个。您不需要指定更多级别。超越然而,正在被切割的那个。带着query,这是items = ['t', 'w']df.query("two in @items")# df.query("two == @items", parser='pandas') # df.query("two in ['t', 'w']")# df.query("two == ['t', 'w']", parser='pandas')带着get_level_values和Index.isin(与上文类似):df[df.index.get_level_values('two').isin(['t', 'w'])]问题3如何检索横截面,即具有索引的特定值的单个行df?具体而言,如何检索('c', 'u'),由 col
one two
c u 9使用loc通过指定键元组:df.loc[('c', 'u'), :]或,df.loc[pd.IndexSlice[('c', 'u')]]注此时,您可能会遇到PerformanceWarning看起来是这样的:PerformanceWarning: indexing past lexsort depth may impact performance.这只意味着索引没有排序。熊猫取决于被排序的索引(在这种情况下,按字典顺序,因为我们处理的是字符串值),以便进行最佳搜索和检索。一个快速的解决方法是预先使用以下方法对DataFrame进行排序DataFrame.sort_index..如果您计划同时执行多个这样的查询,那么从性能的角度来看,这是特别可取的:df_sort = df.sort_index()df_sort.loc[('c', 'u')]您也可以使用MultiIndex.is_lexsorted()若要检查索引是否已排序,请执行以下操作。此函数返回True或False因此。您可以调用此函数来确定是否需要额外的排序步骤。带着xs,这再次简单地传递一个元组作为第一个参数,所有其他参数都设置为相应的默认值:df.xs(('c', 'u'))带着query,事情变得有点笨重:df.query("one == 'c' and two == 'u'")你现在可以看到,这将是相对难以概括的。但是对于这个特殊的问题还是可以的。访问跨越多个层次,get_level_values仍然可以使用,但不建议:m1 = (df.index.get_level_values('one') == 'c')m2 = (df.index.get_level_values('two') == 'u')df[m1 & m2]问题4如何选择与('c', 'u'),和('a', 'w')? col
one two
c u 9a w 3带着loc,这仍然很简单:df.loc[[('c', 'u'), ('a', 'w')]]# df.loc[pd.IndexSlice[[('c', 'u'), ('a', 'w')]]]带着query,您需要通过遍历横截面和级别来动态生成查询字符串:cses = [('c', 'u'), ('a', 'w')]levels = ['one', 'two']# This is a useful check to make in advance.assert all(len(levels) == len(cs) for cs in cses) query = '(' + ') or ('.join([
' and '.join([f"({l} == {repr(c)})" for l, c in zip(levels, cs)])
for cs in cses]) + ')'print(query)# ((one == 'c') and (two == 'u')) or ((one == 'a') and (two == 'w'))df.query(query)100%不推荐!但这是有可能的。问题5如何检索与“一级”中的“a”或“二级”中的“t”对应的所有行? col
one two
a t 0
u 1
v 2
w 3b t 4
t 8d t 12这实际上是很难做到的。loc同时确保正确性和仍然保持代码清晰。df.loc[pd.IndexSlice['a', 't']]是不正确的,它被解释为df.loc[pd.IndexSlice[('a', 't')]](即选择横截面)。你可能会想到一个解决方案pd.concat若要单独处理每个标签:pd.concat([
df.loc[['a'],:], df.loc[pd.IndexSlice[:, 't'],:]])
col
one two
a t 0
u 1
v 2
w 3
t 0 # Does this look right to you? No, it isn't!b t 4
t 8d t 12但是你会注意到其中一行是重复的。这是因为该行既满足了切片条件,又出现了两次。你需要做的是v = pd.concat([
df.loc[['a'],:], df.loc[pd.IndexSlice[:, 't'],:]])v[~v.index.duplicated()]但是,如果您的DataFrame固有地包含重复的索引(您想要的),那么这将不会保留它们。极为谨慎地使用.带着query,这是愚蠢的简单:df.query("one == 'a' or two == 't'")带着get_level_values,这仍然很简单,但没有那么优雅:m1 = (df.index.get_level_values('one') == 'c')m2 = (df.index.get_level_values('two') == 'u')df[m1 | m2]问题6如何分割特定的截面?对于“a”和“b”,我想选择所有具有子级别“u”和“v”的行,对于“d”,我想选择具有子级别“w”的行。 col
one two
a u 1
v 2b u 5
v 6d w 11
w 15这是我添加的一个特例,以帮助理解这四种成语的适用性-这是这样一种情况,在这种情况下,它们都不能有效地工作,因为切片是非常具体而言,并不遵循任何真实的模式。通常,像这样的切片问题需要显式地将键列表传递给loc..这样做的一种方法是:keys = [('a', 'u'), ('a', 'v'), ('b', 'u'), ('b', 'v'), ('d', 'w')]df.loc[keys, :]如果您想保存一些类型,您将认识到对“a”、“b”及其子级别进行切片是有模式的,因此我们可以将切片任务分为两部分,concat结果:pd.concat([
df.loc[(('a', 'b'), ('u', 'v')), :],
df.loc[('d', 'w'), :]
], axis=0)“a”和“b”切片规范稍微干净一些(('a', 'b'), ('u', 'v'))因为被索引的子级别对于每个级别都是相同的。问题7如何获得级别“2”中的值大于5的所有行? col
one two
b 7 4
9 5c 7 10d 6 11
8 12
8 13
6 15这可以使用query,df2.query("two > 5")和get_level_values.df2[df2.index.get_level_values('two') > 5]注与本例类似,我们可以使用这些构造基于任意条件进行筛选。一般来说,记住loc和xs专门用于基于标签的索引,而query和get_level_values有助于构建用于过滤的通用条件掩码。奖金问题如果我需要分割一个MultiIndex 列?实际上,这里的大多数解决方案也适用于列,只是做了一些小改动。考虑:np.random.seed(0)mux3 = pd.MultiIndex.from_product([
list('ABCD'), list('efgh')], names=['one','two'])df3 = pd.DataFrame(np.random.choice(10, (3, len(mux))), columns=mux3)print(df3)one A B C D
two e f g h e f g h e f g h e f g h0 5 0 3 3 7 9 3 5 2 4 7 6 8 8 1 61 7 7 8 1 5 9 8 9 4 3 0 3 5 0 2 32 8 1 3 3 3 7 0 1 9 9 0 4 7 3 2 7以下是您需要对这四个习惯用法所做的更改,以便让它们处理列。切成片loc,使用df3.loc[:, ....] # Notice how we slice across the index with `:`.或,df3.loc[:, pd.IndexSlice[...]]使用xs在适当的情况下,只需传递一个参数。axis=1.可以直接访问列级别的值。df.columns.get_level_values..然后你需要做一些类似的事情df.loc[:, {condition}]哪里{condition}表示使用columns.get_level_values.使用query,您的唯一选项是转置,查询索引,然后再次转置:df3.T.query(...).T不建议使用其他3种选项之一。