为了账号安全,请及时绑定邮箱和手机立即绑定

在熊猫多索引DataFrame中选择行

在熊猫多索引DataFrame中选择行

明月笑刀无情 2019-07-13 10:16:23
在熊猫多索引DataFrame中选择行目标与动机这个MultiIndexAPI多年来越来越流行,但是,并不是所有关于它的东西都是从结构、工作和相关操作的角度被完全理解的。一个重要的操作是过滤..过滤是一个常见的需求,但是用例是多种多样的。因此,某些方法和功能将比其他方法和功能更适用于某些用例。总之,这篇文章的目的是探讨一些常见的过滤问题和用例,演示解决这些问题的各种不同方法,并讨论它们的适用性。这篇文章试图解决的一些高级问题是:基于单个值/标签的切片基于一个或多个级别的多个标签的切片布尔条件和表达式的过滤哪种方法在什么情况下适用?这些问题已细分为以下6个具体问题。为了简单起见,下面设置中的示例DataFrames只有两个级别,并且没有重复的索引键。大多数问题的解决方案都可以推广到N级。这个职位不了解如何创建多索引、如何对它们执行赋值操作或任何与性能相关的讨论(这些是另一次单独的主题)。问题问题1-6将在以下设置的上下文中被问到.mux = pd.MultiIndex.from_arrays([     list('aaaabbbbbccddddd'),     list('tuvwtuvwtuvwtuvw')], names=['one', 'two'])df = pd.DataFrame({'col': np.arange(len(mux))}, mux)          col one two      a   t      0     u      1     v      2     w      3b   t      4     u      5     v      6     w      7     t      8c   u      9     v     10d   w     11     t     12     u     13     v     14     w     15问题1:选择单个项目如何选择在“一级”中具有“a”的行?         col one two      a   t      0     u      1     v      2     w      3此外,我怎样才能降低输出中的“一”级呢?     col two      t      0u      1v      2w      3问题1b如何将值为“t”的所有行分割为“二级”?         col one two      a   t      0b   t      4     t      8d   t     12问题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问题2b如何得到“二级”中与“t”和“w”对应的所有值?         col one two      a   t      0     w      3b   t      4     w      7     t      8d   w     11     t     12     w     15问题3:切割单个截面(x, y)如何检索横截面,即具有索引的特定值的单个行df?具体而言,如何检索('c', 'u'),由         col one two      c   u      9问题4:多截面切片[(a, b), (c, d), ...]如何选择与('c', 'u'),和('a', 'w')?         col one two      c   u      9a   w      3问题5:每级切片一项如何检索与“一级”中的“a”或“二级”中的“t”对应的所有行?         col one two      a   t      0     u      1     v      2     w      3b   t      4     t      8d   t     12问题6:任意切片如何分割特定的截面?对于“a”和“b”,我想选择所有具有子级别“u”和“v”的行,对于“d”,我想选择具有子级别“w”的行。         col one two      a   u      1     v      2b   u      5     v      6d   w     11     w     15
查看完整描述

1 回答

?
Qyouu

TA贡献1786条经验 获得超11个赞

多重索引/高级索引


该员额的结构如下:

  1. 执行部分中提出的问题将一个接一个地加以解决。
  2. 对于每个问题,将演示一个或多个用于解决该问题并获得预期结果的方法。

对于有兴趣了解更多功能、实现细节和其他相关信息的读者,将包括S(与此类似)。这些注释是通过浏览文档和发现各种晦涩的特性,并根据我自己(无可否认是有限的)经验编写的。

所有代码示例都在熊猫v0.23.4,python3.7..如果某些事情不清楚,或者实际上不正确,或者如果您没有找到适用于您的用例的解决方案,请随时建议编辑,在评论中要求澄清,或在适用的情况下打开一个新的问题。

下面是一些常见习语的介绍(从此以后称为四个习语),我们将经常重访。

  1. DataFrame.loc-标签选择的一般解决方案(+)pd.IndexSlice对于涉及片的更复杂的应用程序)

  2. DataFrame.xs-从系列/DataFrame中提取特定的截面。

  3. DataFrame.query-动态指定切片和/或过滤操作(即,作为动态计算的表达式。比其他情况更适用于某些情况。亦见这部分文档用于查询多个索引。

  4. 使用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在这里,因为我们要提取一个横截面。注意levelsaxis参数(这里可以假定合理的默认值)。

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=0axis=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_valuesIndex.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()若要检查索引是否已排序,请执行以下操作。此函数返回TrueFalse因此。您可以调用此函数来确定是否需要额外的排序步骤。

带着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]


与本例类似,我们可以使用这些构造基于任意条件进行筛选。一般来说,记住locxs专门用于基于标签的索引,而queryget_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

以下是您需要对这四个习惯用法所做的更改,以便让它们处理列。

  1. 切成片loc,使用

    df3.loc[:, ....] # Notice how we slice across the index with `:`.

    或,

    df3.loc[:, pd.IndexSlice[...]]
  2. 使用xs在适当的情况下,只需传递一个参数。axis=1.

  3. 可以直接访问列级别的值。df.columns.get_level_values..然后你需要做一些类似的事情

    df.loc[:, {condition}]

    哪里{condition}表示使用columns.get_level_values.

  4. 使用query,您的唯一选项是转置,查询索引,然后再次转置:

    df3.T.query(...).T

    不建议使用其他3种选项之一。


查看完整回答
反对 回复 2019-07-13
  • 1 回答
  • 0 关注
  • 195 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信