☰
Pandas合并与分组聚合详解:从concat、merge到groupby+agg实战
2026/10/21 14:35:49 网站建设 项目流程

数据分析的硬骨头,其实就这两块

很多人学 Pandas,前面读写数据、清洗数据都挺顺,一到了"合并"和"分组聚合"就开始卡壳。原因很简单:这两块内容不像dropna()、fillna()那样单打独斗,它们需要你脑子里同时装着一套"表与表之间的关系"和"数据从宽到长的变化逻辑"。尤其是分组聚合,它几乎是所有日常报表、统计分析的底座——不管你是做电商订单分析、用户行为留存、还是财务对账,归根到底都是在做"先分组,再聚合"这件事。

这篇文章我打算把 Pandas 里最常用的两个操作讲透:DataFrame 的合并(concat、merge)和分组聚合(groupby+agg),并且带着真实场景走一遍完整流程。不只是告诉你函数怎么用,我会把每个操作背后的适用场景、参数选择的原因、以及我实际踩过的坑也都写出来。适合谁看?已经学过 Pandas 基础操作、想进阶到"能用数据回答业务问题"阶段的人,以及做数据分析时总觉得自己代码写得很啰嗦、想用更优雅方式搞定问题的人。

1. 合并操作的两种思路:堆叠与连接

1.1 concat:纵向堆叠与横向拼接

concat的全名叫pd.concat(),它做的事情非常简单粗暴:把两个或多个 DataFrame 直接拼在一起。拼的方向由axis参数决定,axis=0是纵向堆叠(默认),axis=1是横向拼接。

纵向堆叠最常见的场景是:你手里有几张结构完全一样的表,比如一月份销售数据、二月份销售数据、三月份销售数据,需要合并成一张季度表。这时候用pd.concat([df1, df2, df3])即可,前提是这几张表的列名必须保持一致,否则 Pandas 会按列名对齐,缺失位置填 NaN。

横向拼接相对少用,但也偶尔会碰到:比如某个 DataFrame 存了用户的基础信息,另一个存了用户当月的消费汇总,索引都是用户ID,这时候axis=1可以快速把两张表拼在一起。

实际操作中最容易翻车的点是索引。concat默认保留原索引,如果原表里有重复索引,拼接后会出现大量重复的索引值,这时候后续做loc选取或groupby可能被干扰。建议在拼接后执行df.reset_index(drop=True),重新生成一份干净的自增整数索引。

import pandas as pd q1 = pd.DataFrame({"月份": ["1月", "2月", "3月"], "销售额": [12000, 15000, 17000]}) q2 = pd.DataFrame({"月份": ["4月", "5月", "6月"], "销售额": [19000, 21000, 23000]}) # 纵向堆叠 half_year = pd.concat([q1, q2], axis=0, ignore_index=True) print(half_year)

上面代码里我加了ignore_index=True,这样拼接后索引会重置为 0 到 5,省去手动 reset 的麻烦。如果你忘了加,也能事后补救,但养成秒加这个参数的习惯会省不少事。

1.2 merge:SQL 风格的连接操作

merge才是真正意义上的"合并",它解决的是"两张表通过某个共同字段关联起来"的问题,对应 SQL 里的JOIN。核心参数有四个:how、on、left_on/right_on、suffixes。

how的可选值包括inner(内连接,只保留两边都匹配上的行)、left(左连接,以左表为基准)、right(右连接,以右表为基准)、outer(外连接,取并集)。四个连接方式对应业务上不同的需求,实际分析里left连接用得最多,因为它最保险——不会因为右表有额外记录导致左表行数膨胀,也不会丢失左表主体记录。

on参数指定两个表共同拥有的连接键。如果两张表的字段名不同,比如左表叫user_id,右表叫uid,就必须用left_on和right_on分开指定。连接键重复是很危险的:如果右表的连接键有重复值,连接后行数会翻倍,这在统计里叫"多对多连接",使用前务必确认连接键的唯一性。

orders = pd.DataFrame({ "订单号": ["A001", "A002", "A003"], "用户ID": ["U1", "U2", "U3"], "金额": [199, 299, 499] }) users = pd.DataFrame({ "用户ID": ["U1", "U2", "U4"], "城市": ["北京", "上海", "广州"] }) # 左连接:以订单表为主,补上用户城市 merged = orders.merge(users, on="用户ID", how="left") print(merged)

这段代码的运行结果中,A003 订单因为用户 U3 在用户表里不存在,城市字段会是 NaN。这就是左连接的典型场景——订单数据是全的,用户资料可能不全,但你不希望订单记录被删掉。

1.3 连接键的陷阱:重复、缺失与类型不一致

连接键看着简单,实际上是合并操作的最大坑位。我处理过一个很典型的 case:两张表的用户ID一个是整数类型(int64),另一个是字符串类型(object),merge直接报错或者匹配不到任何记录。Pandas 不会自动帮你做类型转换,你需要手动统一:

df1["用户ID"] = df1["用户ID"].astype(str) df2["用户ID"] = df2["用户ID"].astype(str)

另外,连接键里如果有重复值,一定要注意行数变化。当右表连接键重复时,左表的对应行会被复制多份。如果你只想知道"某个用户有没有下单",而右表每个用户下了 5 单,连接后左表同样一行会变 5 行,后面做去重或统计时容易懵。

2. 分组聚合:从明细数据到统计视图

2.1 groupby 到底在做什么

理解groupby的核心,是把它想成一个"拆-算-合"三步走的过程。拆:按某个或某几个字段把大表拆成多个小组;算:对每个小组独立执行聚合计算(求和、平均、计数等);合:把每个小组的计算结果合并成一张汇总表。整个过程听起来简单,但写代码的时候容易犯迷糊,因为 Pandas 的语法把这几个步骤压成了一行。

df.groupby("城市")["销售额"].sum()

这一行拆开来看:groupby("城市")是按城市分组;["销售额"]是告诉 Pandas 只关心销售额这一列;.sum()是对每个城市的销售额求和。返回的结果是一个 Series,城市名作为索引。

如果想同时对多列做不同的聚合计算,用.agg()方法。它接收一个字典,键是列名,值是对应的聚合函数名或函数对象:

result = df.groupby("城市").agg({ "销售额": ["sum", "mean"], "订单量": "count", "利润": "max" })

这时候返回的是多层次列索引的 DataFrame,第一层是原始列名,第二层是聚合函数名。拿到这种结果后,通常需要把列索引降下来:result.columns = ["_".join(col).strip("_") for col in result.columns],或者用result.reset_index()把分组字段变回普通列。

2.2 常用聚合函数与自定义聚合

Pandas 内置的聚合函数数量不少,日常用到的主要有这些:

函数作用适用场景
sum()求和销售额、订单金额汇总
mean()平均值客单价、平均库存
median()中位数对异常值敏感的数据
count()非空计数订单数、活跃用户数
nunique()去重计数独立的用户数、品牌数
max()/min()最大/最小值最高单笔金额、最早下单时间
std()/var()标准差/方差波动性分析

count和nunique的区别经常有人混淆。count统计的是这一列非空值的数量,如果某条记录该字段是 NaN 就不会被算进去;nunique统计的是去重后的数量。举例来说,统计"订单表里有多少个用户"要用df["用户ID"].nunique(),统计"订单表里有多少条成交订单"要用df["订单号"].count()。

除了内置函数,.agg()也支持传入自定义函数。自定义函数接收一个 Series(或者 DataFrame,取决于调用方式),返回一个标量值:

def price_range(s): return s.max() - s.min() result = df.groupby("城市")["销售额"].agg(["sum", price_range])

注意用自定义函数时,聚合结果列名就是函数名,如果想要更易读的名字,可以用 lambda 配合自定义函数封装,或者聚合后重命名列。

2.3 多级分组:分组键的排列组合

实际业务里很少只有一个分组维度,更多是"按城市+按品类"这种交叉分析。groupby直接接收一个列表,就可以做多级分组:

result = df.groupby(["城市", "品类"]).agg({"销售额": "sum"}).reset_index()

返回结果的行顺序是由外层分组键(城市)主排序的,然后才是内层(品类)。如果希望显示上更清晰,可以加sort=True参数做组内排序。多级分组之后如果不想 reset_index,得到的是一个 MultiIndex 索引的 DataFrame,很多新手在这里犯迷糊——明明数据都对,但透视表转不过来。我的建议是:只要后续还要做数据处理,就得reset_index();只有最终展示时,才考虑保留 MultiIndex 方便看层级关系。

多级分组还有一个隐藏用法,就是分组键本身可以是多列的组合,且不一定是原始列名——你可以在分组前先构造新列(比如把时间字段裁剪成"年月"),再按新列分组。这个技巧在时间序列分析里用得尤其多。

3. 实战案例:订单数据的合并分析与分组统计

3.1 场景定义与数据准备

我模拟一个偏实战的场景来把上面的知识串起来。假设你手上有一份电商平台的订单明细表orders.csv,字段包括:订单号、用户ID、下单时间、城市、品类、金额、利润。另外还有一份用户等级表users.csv,字段包括:用户ID、注册时间、会员等级。需求是:计算每个城市、每个会员等级的总销售额和总利润,并找出每个城市销售额最高的品类。

先用代码生成模拟数据,方便你跟着跑:

import pandas as pd import numpy as np np.random.seed(42) city_list = ["北京", "上海", "广州", "深圳", "杭州"] cat_list = ["数码", "服饰", "食品", "家居", "美妆"] level_list = ["普通会员", "白银会员", "黄金会员", "铂金会员"] orders = pd.DataFrame({ "订单号": [f"O{i:05d}" for i in range(1000)], "用户ID": np.random.choice([f"U{i}" for i in range(300)], size=1000), "城市": np.random.choice(city_list, size=1000), "品类": np.random.choice(cat_list, size=1000), "金额": np.random.randint(50, 1000, size=1000), "利润": np.random.randint(5, 200, size=1000), }) users = pd.DataFrame({ "用户ID": [f"U{i}" for i in range(300)], "会员等级": np.random.choice(level_list, size=300), })

这里orders表有 1000 行,订单的用户来自 300 个用户;users表是用户基础信息。两张表通过用户ID关联。

3.2 合并订单表与用户表

先进行左连接,补齐每个订单对应的会员等级:

df = orders.merge(users, on="用户ID", how="left") print(df.shape) # 检查行数是否还是1000 print(df["会员等级"].isna().sum()) # 查看缺失情况

因为users表包含了所有订单里出现的用户,理论上缺失值为 0。但如果你把users表换成从某个渠道导出的部分用户,缺失值就出现了。我在项目里遇到过一次,某个数据源的用户ID是历史遗留下来的旧格式,订单表里是新的统一格式,merge 之后几千行订单匹配不上用户等级。这种问题排查起来很头疼,因为你看到的是大量 NaN,第一反应是脏数据,实际上是对接键出了问题。

所以做 merge 时,建议养成一个习惯:先验证连接键的类型和样例值,再用isna()统计连接后的缺失情况。如果左连接后关键字段的缺失率特别高,大概率不是数据质量问题,而是连接键本身没对齐。

3.3 分城市统计销售额与利润

现在我们有了包含会员等级的完整订单表,接下来按城市进行分组聚合:

city_stats = df.groupby("城市").agg({ "金额": ["sum", "mean", "count"], "利润": "sum" }).reset_index() # 展开多层列索引 city_stats.columns = ["_".join(c).strip("_") for c in city_stats.columns] print(city_stats)

运行后你会得到一个 5 行 × 5 列的统计表,包含每个城市的订单总金额、平均订单金额、订单数量和总利润。这种表拿出来可以直接用 Excel 打开,或者接入后续的可视化流程。

这里我用了"_".join(c).strip("_")把多层列索引展平。为什么需要这一步?因为 Pandas 生成的多层列索引在后续处理中非常别扭,比如你想改成中文列名、按某列排序、或者直接传给绘图库,多级索引都会造成不必要的麻烦。展平成普通字符串列名是约定俗成的做法。

3.4 城市 + 会员等级交叉分析

继续推进,我们现在需要看每个城市内部不同会员等级的消费贡献。这个分析的价值很直接:判断哪些城市的高等级会员贡献占比更大,从而指导运营资源投放。

cross_stats = df.groupby(["城市", "会员等级"]).agg({ "金额": ["sum", "count"], "利润": "sum" }).reset_index() cross_stats.columns = ["_".join(c).strip("_") for c in cross_stats.columns] # 按城市分组,内部按金额排序 cross_stats_sorted = cross_stats.sort_values(["城市", "金额_sum"], ascending=[True, False]) print(cross_stats_sorted.head(10))

多级分组之后先展平列索引,再按组内排序,是最顺畅的流程。你会在结果里清晰地看到,比如 "北京" 这个城市里哪个会员等级贡献最大,哪个等级虽然订单数量不多但利润很高。

3.5 找出每个城市销售额最高品类

这个需求用groupby加rank组合可以做,也可以直接用sort_values+drop_duplicates实现。我推荐后面这种写法,因为它更短、更不容易出错:

# 先计算每个城市品类的销售额汇总 city_cat = df.groupby(["城市", "品类"])["金额"].sum().reset_index() # 按城市分组,取金额最大的条目 top_cat = city_cat.sort_values(["城市", "金额"], ascending=[True, False]).groupby("城市").head(1) print(top_cat)

这个技巧的精髓在于:先排序,再分组取前几条。groupby().head(1)会在每个组内取排好序的第一行。如果你要取每个城市的前三名,把head(1)换成head(3)就行。

4. 常见问题与排查技巧实录

4.1 为什么 merge 之后行数变多了

这是新手最容易慌的情况。我见过最离谱的一次,两张表都是几百行,merge 之后直接变出几万行。原因基本都是连接键存在重复值。排查方式很简单:先对右表的连接键执行duplicated()检查,看看有没有重复;再对左表执行同样的检查。如果两边都有重复,那结果就是笛卡尔积式的多对多连接,行数会爆炸式增长。

print(users["用户ID"].duplicated().sum()) print(orders["用户ID"].duplicated().sum())

如果确认连接键在右表重复且业务上无法避免(比如订单表中一个用户可以有多条订单),你要明确自己到底想要什么口径:是"基于订单的明细连接"还是"基于用户的去重信息"。前者可以直接做 left join,后者需要先对右表去重,比如只保留用户的最新等级信息:

users_dedup = users.sort_values("注册时间").groupby("用户ID").tail(1)

4.2 groupby 之后 Series 和 DataFrame 分不清

很多初学者的困惑:df.groupby("城市")["金额"].sum()返回的是一个 Series,而df.groupby("城市")[["金额"]].sum()返回的是 DataFrame。两者差别在于["金额"]和[["金额"]]这组括号的层级。

在调试中,如果后续要和其他 DataFrame 做merge,必须用双括号取 DataFrame;如果只是简单打印或者后续转字典,Series 就够用。顺手说一下,result.to_dict()在 Series 上很好用,可以直接把分组结果变成字典,方便做映射匹配。

4.3 聚合结果缺了某一行,数据去哪了

groupby默认会丢弃分组键为 NaN 的行。如果你希望"不知道城市"的订单也保留一个分组建,需要在groupby前把 NaN 填充成特殊标记,比如df["城市"] = df["城市"].fillna("未知")。这个细节在数据质量较差的场景里非常关键,否则你辛辛苦苦清洗的数据,可能在分组这一步被静默丢弃了一些记录。

另外,groupby默认只对数值列做聚合,如果你在.agg()里传入了非数值列的计算函数(比如求字符串列的第一个值),需要用特殊方式处理——first或者自定义 lambda。我看到很多人在聚合字符串列时报错,绝大多数是因为不知道agg里可以直接传first。

4.4 性能问题:数据量大时 groupby 太慢

当 DataFrame 行数达到几百万甚至上亿时,Pandas 的groupby可能变得非常慢。这里有几个实用的提速手段:

第一,优先选择分类类型存储分组键。如果某个字段的取值种类有限(比如城市就 5 种),改成category类型:

df["城市"] = df["城市"].astype("category")

这样 groupby 的分组过程会明显加快,同时还能大幅减少内存占用。

第二,尽量避免在 groupby 之后返回超大结果集。如果只是要统计值,直接在agg里指定必要列,别把无用的高基数列带进去。

第三,如果数据真的太大,Pandas 硬扛不动,可以考虑用polars或者转spark处理。但这是后话,先学会把 Pandas 的姿势用对。

4.5 分组结果重新变成普通表格

groupby之后的分组字段默认会变成索引,很多后续操作都要求"普通列 + 普通列"的结构,所以要顺手reset_index()。但是注意,这个操作对小数据集无所谓,在大数据上会额外消耗时间。如果确认后面不需要reset_index才能进行的分组字段操作,可以省略这一步。

多级分组后行数较多时,reset_index()会把分组字段放回每一行,这时候可以顺势用rename把列名改得更规范。我习惯把所有清洗步骤放在流水线前面,最后才 reset 和重命名,保持处理流程的清爽。

5. 从能用代码到会思考数据:一些实操心得

5.1 合并前先搞清楚主从关系

无论是merge还是concat,写代码之前先想清楚:哪张表是主体,哪张表是补充信息。主体表的行数不应该因为连接而改变太多,除非你刻意要做多对多的连接。我之前遇到过一个同事,报表数据莫名其妙翻倍,查了半天发现是连接方向反了,本应该用订单表 join 用户表,结果写成了用户表 join 订单表——行数变成订单量 × 用户覆盖数。

这个判断有个技巧:先想"我要分析的对象是谁"。如果要分析订单,主体就是订单表;如果要分析用户,主体就是用户表。然后连接键的选择、how参数的选择都围绕这个主从关系展开。

5.2 聚合口径:求和之前先问自己合不合理

分组聚合输出的是统计结果,这个结果是否可靠,取决于你对业务的理解。比如"平均订单金额",这里有两个隐含选择:分子是所有订单金额之和,分母是所有订单数量。但如果有些订单是退款订单,你算平均客单价的时候是否应该剔除?或者是否应该用去重用户数做分母,计算"用户平均消费贡献"?同一个表,能算出一百种统计口径。写代码前不确认口径,算出来的数字再漂亮也只是数字。

我的习惯是:每个聚合字段旁边注释一个口径说明,比如金额.sum()后面写# 不含退款订单,交易状态为已成交。代码不规范,复盘两行泪。

5.3 把常用的多表合并封装成函数

如果你在一个数据源复杂的环境里做分析,会发现"订单表 join 用户表 join 商品表"这种操作每隔几天就要写一遍。第一次写可能花了半小时,后面每次复制粘贴再改字段名,浪费的时间累积起来很恐怖。

建议把你所在业务场景里最常见的合并逻辑封装成一个函数,输入几个必要参数,输出清洗好的宽表。我给自己的项目写过类似load_store_wide_table(start_date, end_date, include_refund=False)这样的函数,参数化日期和退款口径。后面做任何分析,只需要调一个函数,从底层把数据准备好,不仅代码量大幅减少,错误率也直线下降。

5.4 数据验证:任何聚合结果都要能还原

最后一条心得,也是我吃了不少亏才总结出来的:任何 groupby 聚合结果,都应该能被原始数据还原。怎么验证?随机挑一个分组,手工筛出该分组的所有原始记录,自己加总一下,和聚合结果对比是否一致。如果一致,说明代码逻辑没问题;如果不一致,说明中间的脏数据、连接逻辑、字段选择中至少有一个地方出了问题。

# 验证:北京这个城市总销售额 validate = df[df["城市"] == "北京"]["金额"].sum() print(validate) # 和 city_stats 里的北京金额对比 print(city_stats[city_stats["城市"] == "北京"]["金额_sum"].values[0])

如果两个数字对不上,就往下游查:是不是 merge 时产生了重复行?是不是 groupby 时分组字段包含了 NaN 被丢弃?时间花在验证上,远小于花在后续报表数据错误被别人发现的难堪上。

6. 进阶技巧:分组聚合与透视表的互相转换

6.1 pivot_table 与 groupby 的异同

pivot_table和groupby在功能上有大量重叠,但输出形态不同。pivot_table的结果天然是"宽表",行是一个分组键,列是另一个分组键,交叉点是对应的聚合值——这个格式和人脑阅读的表格更接近。而groupby的结果是"长表",更贴近数据处理的流水线格式。

# 用 pivot_table 实现同样的统计 pivot = pd.pivot_table( df, index="城市", columns="会员等级", values="金额", aggfunc="sum", fill_value=0 ) print(pivot)

这段代码得到的表格,行是城市,列是会员等级,每个格子是对应组合的销售额总和,空白处自动填 0。如果你需要把这份结果画热力图或者导出成 Excel 给别人看,这种宽表格式明显更合适。反过来,如果你的下一步操作是merge或者继续分组计算,用groupby得到的长表更顺手。

6.2 宽表与长表的相互转换

数据分析中你必然会遇到宽表转长表或者长表转宽表的场景,两个核心函数分别是melt和pivot。宽表转长表用melt,它把多个列"融"成两列:一列是原列名,另一列是对应值;长表转宽表用pivot,把某一列的值"摊开"成多个新列。

# 宽表转长表 long_df = pivot.reset_index().melt( id_vars="城市", var_name="会员等级", value_name="销售额" )

这种转换本身不是合并或分组的内容,但它是配合分组聚合结果展示的重要技能。当你需要把一份统计好的结果再喂给绘图库做分组柱状图时,长表往往比宽表更友好;当你需要做横向对比时,宽表更直观。两者都掌握,才算真正玩转了重组数据。

6.3 分组聚合在时间序列上的应用

时间序列数据的分组聚合有一个特殊技巧:把时间字段切分成不同的时间粒度再分组。比如你有一列精确到秒的下单时间,想按"天""周""月"汇总销售额,可以用pd.Grouper或者先对时间列做dt.floor()。

df["下单日期"] = pd.to_datetime(df["下单时间"]).dt.floor("D") daily_sales = df.groupby("下单日期")["金额"].sum() monthly_sales = df.groupby(pd.Grouper(key="下单日期", freq="M"))["金额"].sum()

这里dt.floor("D")把时间向下取整到天,相当于去掉时分秒;pd.Grouper(freq="M")是按自然月分组。这个技巧在做销售日报、月报时极具实用价值。结合前面讲的城市分组,你甚至可以直接按月、按城市做二维汇总,形成一份经营分析的核心视图。

我也顺便多说一句:groupby加时间窗口之后,别忘了处理不完整月份或节假日的缺失——很多报表里某个月数据突然大幅下降,不是业务崩了,而是当月有长假或者数据源延迟未更新。这种时候先看原始数据,别急着分析。

7. 避坑手册:我踩过的那几个坑

  • 字符串和数值分别统计的坑:groupby分组后,如果用.sum()不带agg,Pandas 会尝试对所有数值列做求和,同时忽略字符串列。如果你的 DataFrame 里混着非数值列,你要么明确指定列,要么用.agg()精确控制。和看上去的"用不了"相比,更隐蔽的问题是:你以为它没算字符串列,但实际它做了别的操作,结果和预期不符。

  • reset_index 之后,列名重复:当groupby的聚合结果里有两列都叫"金额"之类的名字时,reset_index()会导致列名重复。后续用df["金额"]会返回一个 DataFrame 而不是 Series,很容易踩坑。解决办法是在展平列名时,顺手把重复列改名。

  • Grouper 的 freq 参数语义:freq="M"在旧版本里表示月末,在某些版本里会有大小写敏感问题(比如"m"是分钟)。如果你发现分组结果的时间点不对,检查一下freq的大小写。

  • 聚合函数里混入 lambda 的时间开销:自定义 lambda 不能向量化,性能比内置函数差很多。如果数据量大,能用agg(["sum", "count"])解决的,不要自作聪明写lambda x: x.sum()。

  • 多表 concat 时列数不一致会强制对齐:concat默认按列名对齐,如果你只是简单地把列顺序不同但结构一致的表拼起来,结果会出现很多 NaN。建议拼接前统一列顺序,或者拼完后再reindex列。

8. 最后的几件趁手工具

做合并和分组聚合时,我习惯准备几个小工具在代码库里。一个是检查 DataFrame 重复行的函数,一个是检查连接键唯一性的函数,还有一个是统一列名格式的函数。代码都很短,但写完之后十次有八次会复用:

def check_duplicates(df, keys): dup_count = df.duplicated(subset=keys).sum() print(f"重复行数: {dup_count}") return dup_count == 0 def normalize_columns(df): df.columns = [str(c).strip().lower().replace(" ", "_") for c in df.columns] return df

check_duplicates在多表合并之前特别有用——先扫一眼两张表连接键的重复情况,能预判 merge 之后的行数规模;normalize_columns能避免因为列名大小写差异导致的 merge 失败。这种小函数不需要重复发明轮子,但确实省下不少调试时间。

说实话,merge和groupby这两个操作,你哪怕只是把本文里的例子顺手跑一遍,把它们的使用场景刻进肌肉记忆里,就已经能在大部分数据分析任务里游刃有余了。我自己的体会是,这两个操作的代码量通常只占整个分析脚本的三成,但思考时间占了七成——思考清楚"表怎么连、数据怎么聚合、口径怎么定",后面写代码就是一马平川。

最后想提一个越早学会越好的习惯:每次做完一步合并或聚合,顺手用一个小 test 验证结果,不信就还原到原始数据抽查几个分组。等数据量大了、逻辑复杂了,你一定会感谢当初这个看起来"多此一举"的动作。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询