经过研究,我在这个或任何其他论坛上都没有发现类似的问题。
我按其内部级别对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
我想要的是
ColumnName Value
Name Date
A 3 6
B 4 6
我能做的就是使用这个命令:
df.groupby(level=('Name')).last()
正在检索这个:
ColumnName Value
Name
A 6
B 6
或者,使用此命令:
df.groupby(level=('Name','Date')).last()
检索错误。
请记住,这是一个对性能敏感的应用程序。
想法?
编辑:同时我确实提交了feature request at GitHub
答案 0 :(得分:4)
在groupby对象上使用tail(1)
而不是last()
,可以获得所需的行为:
In [22]: df.groupby(level='Name').tail(1)
Out[22]:
ColumnName Value
Name Date
A 3 6
B 4 6
这是因为tail
就像一个过滤器'方法,保持原始索引完整(但只返回某些行,在这种情况下是每个组的最后一行)。 last
不会这样做,因为此方法会为您提供每个组中每个列的最后一个非NaN值,而不一定返回原始行。
OLD ANSWER(使用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
然后您可以将其设置为索引以获得所需的结果:
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
由于有人询问性能,因此在示例数据帧上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
但是如果你看一个更大的示例数据帧,差异已经小得多(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
这取决于当然的确切应用,但在许多情况下,这种性能差异不会很大。
答案 1 :(得分:1)
这将完成它:
def get_slice(df):
l0, l1 = df.index.levels
b0, b1 = df.index.labels
n = len(l0)
myslice = range(n)
for i in myslice:
myslice[i] = (l0[i], l1[b1[b0 == i][-1]])
return df.loc[myslice]
定时
%%timeit
get_slice(df)
1000 loops, best of 3: 458 µs per loop
答案 2 :(得分:1)
试试这个:: reset_index()
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'])
df = df.reset_index()
df2 = df.groupby(["Name"])["Name","Date","Value" ].last()
df2.set_index(['Name', 'Date'], inplace=True)
# Value
# Name Date
# A 3 6
# B 4 6