Pandas MultiIndex groupby 保留索引级别

Ped*_*raz 5 python performance pandas

经过研究,我在这个论坛或任何其他论坛上都没有发现类似的问题。

我正在按其内部级别对 MultiIndex 数据帧进行分组。问题是,在按分组分组后,我仍然想知道在这个内部索引上哪些是“选择的值”。

所以我有这样的东西

df = pd.DataFrame([['A', 1, 3],
                   ['A', 2, 4],
                   ['A', 3, 6],
                   ['B', 1, 9],
                   ['B', 2, 10],
                   ['B', 4, 6]],
                  columns=pd.Index(['Name', 'Date', 'Value'], name='ColumnName')
                 ).set_index(['Name', 'Date'])

ColumnName         Value
Name    Date
A        1           3
         2           4
         3           6 
B        1           9
         2           10
         4           6
Run Code Online (Sandbox Code Playgroud)

我想要的是

ColumnName         Value
Name    Date
A        3           6
B        4           6
Run Code Online (Sandbox Code Playgroud)

我能够做的是使用这个命令:

df.groupby(level=('Name')).last()
Run Code Online (Sandbox Code Playgroud)

正在检索这个:

ColumnName         Value
Name    
A                    6
B                    6
Run Code Online (Sandbox Code Playgroud)

或者,通过使用此命令:

df.groupby(level=('Name','Date')).last()
Run Code Online (Sandbox Code Playgroud)

检索错误。

请记住,这是一个性能敏感的应用程序。

想法?

编辑:同时我确实在 GitHub 上提交了一个功能请求

jor*_*ris 6

通过在 groupby 对象上使用tail(1)而不是last(),您可以获得所需的行为:

In [22]: df.groupby(level='Name').tail(1)
Out[22]:
ColumnName  Value
Name Date
A    3          6
B    4          6
Run Code Online (Sandbox Code Playgroud)

这是因为它的tail作用类似于“过滤器”方法,保持原始索引不变(但只返回某些行,在这种情况下是每组的最后一行)。last不这样做,因为此方法将为您提供每个组中每一列的最后一个非 NaN 值,不一定返回原始行。


旧答案(使用last):您可以groupby通过将要保留在 groupby 中的索引级别作为列来简单地使用来实现此目的:

In [44]: df.reset_index(level='Date').groupby(level=0).last()
Out[44]:
ColumnName  Date  Value
Name
A              3      6
B              4      6
Run Code Online (Sandbox Code Playgroud)

之后您可以将其设置回索引以获得所需的结果:

In [46]: df.reset_index(level='Date').groupby(level=0).last().set_index('Date', append=True)
Out[46]:
ColumnName  Value
Name Date
A    3          6
B    4          6
Run Code Online (Sandbox Code Playgroud)

由于询问了性能,因此 groupby 解决方案在示例数据帧上速度较慢:

In [96]: %timeit get_slice(df)
1000 loops, best of 3: 879 µs per loop

In [97]: %timeit df.reset_index(level='Date').groupby(level='Name').last().set_index('Date', append=True)
100 loops, best of 3: 3.75 ms per loop

In [220]: %timeit df.groupby(level='Name').tail(1)
1000 loops, best of 3: 1.04 ms per loop
Run Code Online (Sandbox Code Playgroud)

但是,如果您查看更大的示例数据帧,差异已经小得多(并且该last方法甚至更快):

In [83]: df1 = pd.DataFrame(
             {'Value':np.random.randint(100, size=len(string.letters)*100)}, 
             index=pd.MultiIndex.from_product([list(string.letters), range(100)],
                                              names=['Name', 'Date']))

In [84]: df1
Out[84]:
           Value
Name Date
a    0        13
     1         9
     2        11
     3        16
...          ...
Z    96       15
     97       20
     98       40
     99       91

[5200 rows x 1 columns]

In [85]: %timeit get_slice(df1)
100 loops, best of 3: 3.24 ms per loop

In [86]: %timeit df1.reset_index(level='Date').groupby(level='Name').last().set_index('Date', append=True)
100 loops, best of 3: 4.69 ms per loop

In [218]: %timeit df1.groupby(level='Name').tail(1)
1000 loops, best of 3: 1.66 ms per loop
Run Code Online (Sandbox Code Playgroud)

这当然取决于确切的应用程序,但在许多情况下,这种性能差异不会很大。