我有一个数据框:
pe_odds[ [ 'EVENT_ID', 'SELECTION_ID', 'ODDS' ] ]
Out[67]:
EVENT_ID SELECTION_ID ODDS
0 100429300 5297529 18.00
1 100429300 5297529 20.00
2 100429300 5297529 21.00
3 100429300 5297529 22.00
4 100429300 5297529 23.00
5 100429300 5297529 24.00
6 100429300 5297529 25.00
当我使用 groupby 和 agg 时,我得到了一个多索引的结果:
pe_odds.groupby( [ 'EVENT_ID', 'SELECTION_ID' ] )[ 'ODDS' ].agg( [ np.min, np.max ] )
Out[68]:
amin amax
EVENT_ID SELECTION_ID
100428417 5490293 1.71 1.71
5881623 1.14 1.35
5922296 2.00 2.00
5956692 2.00 2.02
100428419 603721 2.44 2.90
4387436 4.30 6.20
4398859 1.23 1.35
4574687 1.35 1.46
4881396 14.50 19.00
6032606 2.94 4.20
6065580 2.70 5.80
6065582 2.42 3.65
100428421 5911426 2.22 2.52
我曾尝试使用 as_index 返回没有 multi_index 的结果:
pe_odds.groupby( [ 'EVENT_ID', 'SELECTION_ID' ], as_index=False )[ 'ODDS' ].agg( [ np.min, np.max ], as_index=False )
但它仍然给我一个多索引。
我可以使用.reset_index(),但是很慢:
pe_odds.groupby( [ 'EVENT_ID', 'SELECTION_ID' ] )[ 'ODDS' ].agg( [ np.min, np.max ] ).reset_index()
pe_odds.groupby( [ 'EVENT_ID', 'SELECTION_ID' ] )[ 'ODDS' ].agg( [ np.min, np.max ] ).reset_index()
Out[69]:
EVENT_ID SELECTION_ID amin amax
0 100428417 5490293 1.71 1.71
1 100428417 5881623 1.14 1.35
2 100428417 5922296 2.00 2.00
3 100428417 5956692 2.00 2.02
4 100428419 603721 2.44 2.90
5 100428419 4387436 4.30 6.20
如何在没有多索引的情况下使用 groupby 和/或 agg 函数的参数返回结果。而且不必求助于使用 reset_index() ?
低于电话:
>>> gr = df.groupby(['EVENT_ID', 'SELECTION_ID'], as_index=False)
>>> res = gr.agg({'ODDS':[np.min, np.max]})
>>> res
EVENT_ID SELECTION_ID ODDS
amin amax
0 100429300 5297529 18 25
1 100429300 5297559 30 38
返回具有多索引列的框架。如果您不希望列是多索引的,您可以这样做:
>>> res.columns = list(map(''.join, res.columns.values))
>>> res
EVENT_ID SELECTION_ID ODDSamin ODDSamax
0 100429300 5297529 18 25
1 100429300 5297559 30 38
也可以使用
pipe
方法、set_axis
和链接(我认为这更具可读性)删除列上的 multi_index。
(
pe_odds
.groupby(by=['EVENT_ID', 'SELECTION_ID'] )
.agg([ np.min, np.max ])
.pipe(lambda x: x.set_axis(x.columns.map('_'.join), axis=1))
)
这是没有重置索引的输出。
ODDS_amin ODDS_amax
EVENT_ID SELECTION_ID
100429300 5297529 18.0 25.0
100429300 5297559 30.0 38.0
我已经采纳了 Kim 的评论并将其优化(您根本不需要使用 .to_flat_index())到下面的代码中。我相信这是最pythonic(易于理解)和优雅的方法:
df.columns = ["_".join(col_name).rstrip('_') for col_name in df.columns]
示例用法是:
>>> df.columns = ["_".join(col_name).rstrip('_') for col_name in df.columns]
>>> df
EVENT_ID SELECTION_ID ODDS_amin ODDS_amax
0 100429300 5297529 18 25
1 100429300 5297559 30 38
这是一个适用于
columns
或 index
的通用解决方案(部分灵感来自@Manuel Bolívar 的回答):
def join_levels(df, delim='_', axis=0):
return (
df.set_axis(
df.axes[axis].map(delim.join),
axis=axis
)
)
然后使用它(例如):
res = (
pe_odds
.groupby(['EVENT_ID', 'SELECTION_ID'])
.agg([np.min, np.max])
.pipe(join_levels, axis=1)
# ...
)
为了使用名称空间组织这样的函数,我喜欢将它们收集在一个访问器中,例如(稍作修改):
@pd.api.extensions.register_dataframe_accessor("custom")
class _MyAccessor:
def __init__(self, pandas_obj: pd.DataFrame):
self._obj = pandas_obj
def join_levels(self, delim='_', axis=0):
return (
self._obj.set_axis(
self._obj.axes[axis].map(delim.join),
axis=axis
)
)
然后你可以这样做:
res = (
pe_odds
.groupby(['EVENT_ID', 'SELECTION_ID'])
.agg([np.min, np.max])
.custom.join_levels(axis=1)
# ...
)
NB:为了保持代码整洁,您可以将任何访问器放在模块(
.py
文件)的某个地方。只是import
那个模块和所有的访问器都神奇地可用。