Pandas中groupby+agg的两种写法区别小结
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">一、一个非常真实的使用场景</a></li><li><a href="#_label1">二、第一种写法:很多人最常用,但最容易踩坑</a></li><li><a href="#_label2">三、第二种写法:Pandas 官方推荐(强烈建议)</a></li><li><a href="#_label3">四、两种写法的核心区别对比</a></li><li><a href="#_label4">五、真实项目中的差距会越来越大</a></li></ul></div><p>在使用 Pandas 做数据统计时,<code>groupby + agg</code> 是绕不开的操作。</p><p>但很多人(包括我自己)在实际项目中都会遇到一个问题:</p>
<blockquote><p>为什么明明只是做个统计,结果 DataFrame 却变成了 MultiIndex,<br />后面 merge、导 Excel、画图全都开始报错?</p></blockquote>
<p>追根溯源,问题往往出在:<br />👉 <code>groupby + agg</code> 的写法选错了</p>
<p>本文结合真实工程经验,详细讲清楚 Pandas 中 agg 的两种常见写法,以及为什么在工程场景下强烈推荐其中一种。</p>
<p class="maodian"><a name="_label0"></a></p><h2>一、一个非常真实的使用场景</h2>
<p>假设你在做检测评测统计,有如下数据:</p>
<table><thead><tr><th>class</th><th>part1</th><th>TP</th><th>FP</th><th>FN</th><th>GT_count</th></tr></thead><tbody><tr><td>cow</td><td>v01</td><td>5</td><td>1</td><td>0</td><td>6</td></tr><tr><td>cow</td><td>v01</td><td>4</td><td>0</td><td>1</td><td>5</td></tr><tr><td>dog</td><td>v02</td><td>3</td><td>2</td><td>1</td><td>4</td></tr></tbody></table>
<p>目标是:</p>
<blockquote><p>按 class + part1 统计 TP / FP / FN / GT 数量</p></blockquote>
<p class="maodian"><a name="_label1"></a></p><h2>二、第一种写法:很多人最常用,但最容易踩坑</h2>
<div class="jb51code"><pre class="brush:py;">groupby_df = df.groupby(['class', 'part1']).agg(
{'TP': 'sum', 'FP': 'sum', 'FN': 'sum', 'GT_count': 'sum'}
)
</pre></div>
<p><strong>这段代码有问题吗?<br />没有。</strong></p>
<p>但它返回的结果结构是:</p>
<ul><li>class 和 part1 变成了 MultiIndex</li><li>统计结果才是列</li></ul>
<p>也就是说,你拿到的是一张 <strong>多级索引 DataFrame。</strong></p>
<p>MultiIndex 在工程中会带来什么问题?</p>
<p>以下问题你大概率都会遇到:</p>
<ul><li>导出 Excel 前必须 reset_index()</li><li>和其他表 merge 时经常报错</li><li>新同事很难快速理解数据结构</li><li>后面再算指标时代码变得很别扭</li></ul>
<p>👉 分析阶段还能接受,工程阶段非常痛苦</p>
<p class="maodian"><a name="_label2"></a></p><h2>三、第二种写法:Pandas 官方推荐(强烈建议)</h2>
<div class="jb51code"><pre class="brush:py;">groupby_df = (
df.groupby(['class', 'part1'], as_index=False)
.agg(
TP=('TP', 'sum'),
FP=('FP', 'sum'),
FN=('FN', 'sum'),
GT=('GT_count', 'sum'),
)
)
</pre></div>
<p>返回结果是什么样?</p>
<blockquote><p>class | part1 | TP | FP | FN | GT</p></blockquote>
<ul><li>分组字段是普通列</li><li>没有 MultiIndex</li><li>看起来就是一张“标准统计表”</li></ul>
<p class="maodian"><a name="_label3"></a></p><h2>四、两种写法的核心区别对比</h2>
<table><thead><tr><th>对比点</th><th>第一种写法</th><th>第二种写法</th></tr></thead><tbody><tr><td>分组字段位置</td><td>Index</td><td>普通列</td></tr><tr><td>索引类型</td><td>MultiIndex</td><td>普通 Index</td></tr><tr><td>是否需要 reset_index</td><td>是</td><td>否</td></tr><tr><td>列名是否可控</td><td>否</td><td>是</td></tr><tr><td>扩展复杂统计</td><td>不优雅</td><td>非常自然</td></tr><tr><td>工程实用性</td><td>较低</td><td>很高</td></tr></tbody></table>
<p class="maodian"><a name="_label4"></a></p><h2>五、真实项目中的差距会越来越大</h2>
<p>当统计需求升级,比如你还想算:</p>
<ul><li>平均置信度</li><li>唯一 track 数</li></ul>
<p>第一种写法会越来越臃肿:</p>
<div class="jb51code"><pre class="brush:py;">df.groupby(['class', 'part1']).agg({
'TP': 'sum',
'FP': 'sum',
'FN': 'sum',
'score': 'mean',
'track_id': 'nunique'
})
</pre></div>
<p>而第二种写法依然清晰:</p>
<div class="jb51code"><pre class="brush:py;">df.groupby(['class', 'part1'], as_index=False).agg(
TP=('TP', 'sum'),
FP=('FP', 'sum'),
FN=('FN', 'sum'),
avg_score=('score', 'mean'),
track_cnt=('track_id', 'nunique'),
)
</pre></div>
頁:
[1]