资讯动态

Pandas教学-8:map与apply的对比

发布时间:2026/10/13 9:20:14 来源:尧图企业网站定制
一句话本质区别map是查表映射apply是套用函数。两者都能逐元素处理但设计目的和参数类型不同。一、核心对比表维度Series.mapSeries.apply设计目的值映射改头换面值计算/转换套函数接收参数函数/字典/Series只能是函数或函数列表未命中字典时变成NaN直接报错见下能否整表用不行只有 SeriesDataFrame.apply(axis0/1)可以常见搭档[p1.py](file:///e:/MyTrae/Python教学/p1.py#L57) 的别名映射[p2.py](file:///e:/MyTrae/Python教学/p2.py#L61) 的及格判断二、差异点逐条说明都有实测依据1. 传字典map 可以apply 不行m{技术:技术部,研发部:技术部}s.map(m)# 可以技术→技术部研发部→技术部未命中的“未知部门”→NaNs.apply(m)# 报错AttributeError: 技术部 is not a valid function for Series object所以在 [p1.py](file:///e:/MyTrae/Python教学/p1.py#L57) 里做部门别名统一天然适合用map(字典)。但要注意它的副作用字典里没有的值会变成NaN——这正是 p1.py 用replace(DEPT_MAP)而不是map(DEPT_MAP)的原因replace未命中会原样保留。小结论map(字典)是查不到就清空replace(字典)是查不到就放过。这是个很好的课堂对比点。2. 传函数两者都能用行为相近s.map(lambdax:x.strip())# 可以s.apply(lambdax:x.strip())# 可以单看传函数二者结果基本一样。区别在于map有na_action参数可选ignore跳过缺失值不把 NaN 喂给函数。apply更宽容函数可以返回任意类型甚至返回 Series/列表此时结果的行列结构会变化进阶用法课堂上点到即可。3. 作用范围map 只能一列apply 能整表df.apply(lambdacol:col.max(),axis0)# 按列每列算一个值df.apply(lambdarow:row[语文]row[数学],axis1)# 按行跨列计算map没有行/列的概念它只认一个个元素。所以想做跨列运算必须用apply或直接向量化。4. 新版还有DataFrame.map[p2.py](file:///e:/MyTrae/Python教学/p2.py) 环境是 pandas 3.0.5我实测确认DataFrame.map(func)存在 —— 逐格处理整张表旧的DataFrame.applymap已被移除用了会报错。三、怎么选口诀你要做的事用什么按字典/Series 把值换算成另一套值Series.map(字典)对每个值调用函数做小转换map或apply都行习惯上map需要跨行/跨列计算DataFrame.apply(axis...)只是简单四则判断且数据大都别用优先向量化如np.where一句话记忆map“换标签”喂字典最顺手查不到就变NaNapply“过工序”喂函数能整行整列地干活。四、用两个文件里的真实代码对照# p1.py L57把全角“”换成半角——纯值映射用 mapdf[姓名]df[姓名].map(to_half_width).str.strip()# p2.py L61按平均分判定是否及格——规则可换函数用 applydf[是否及格]df[平均分].apply(lambdax:及格ifx60else不及格)这两行正好是映射与套用函数的代表。可以让学生试着互换把 p2 的判断改成map(lambda x: ...)会发现也能跑但把 p1 的map(to_half_width)换成apply(to_half_width)时若误传成字典就会报错——从而记住map 能吃字典、apply 不能。需要的话我可以再补一个map与replace处理未命中值的对照实验小脚本方便课堂演示那个NaN陷阱。# -*- coding: utf-8 -*- 案例8文本字段标准化 清洗场景 同一个部门有多种叫法技术部 / 技术 / 研发部 / Tech、 邮箱大小写混用、手机号带空格和横线、全角字符混排 知识点 1. str.strip() 去空格、str.upper()/lower() 统一大小写 2. unicodedata.normalize(NFKC) 全角转半角 3. replace(映射字典) 把别名统一成标准名称 4. str.replace(正则) 去掉手机号中的空格、横线 5. isin() 检查是否还有“不认识”的类别 运行后 生成 data/会员信息_脏数据.xlsx 和 data/会员信息_清洗结果.xlsx importosimportunicodedataimportpandasaspd BASE_DIRos.path.dirname(os.path.abspath(__file__))DATA_DIRos.path.join(BASE_DIR,data)os.makedirs(DATA_DIR,exist_okTrue)SRC_FILEos.path.join(DATA_DIR,会员信息_脏数据.xlsx)OUT_FILEos.path.join(DATA_DIR,会员信息_清洗结果.xlsx)# 部门别名 - 标准名称 的映射表以后新增别名只改这里DEPT_MAP{技术部:技术部,技术:技术部,研发部:技术部,tech:技术部,市场部:市场部,市场:市场部,营销部:市场部,marketing:市场部,人事部:人事部,人力资源部:人事部,hr:人事部,}defmake_sample():dfpd.DataFrame({姓名:[张三, 李四 ,Wang Wu],部门:[技术,研发部,],# 最后一个是全角英文邮箱:[ZSExample.COM,lsexample.com,wwExample.com],手机号:[138-0000-0001, 139 0000 0002,13700000003],})df.to_excel(SRC_FILE,sheet_name原始数据,indexFalse)print(已生成示例文件,SRC_FILE)defto_half_width(text):全角字符转半角 - Techreturnunicodedata.normalize(NFKC,str(text))defclean():rawpd.read_excel(SRC_FILE,sheet_name原始数据)dfraw.copy()# ---------- 1. 姓名转半角、去首尾空格 ----------df[姓名]df[姓名].map(to_half_width).str.strip()# ---------- 2. 部门去空格 - 全角转半角 - 英文小写 - 别名映射 ----------df[部门](df[部门].map(to_half_width).str.strip().str.lower().replace(DEPT_MAP))# ---------- 3. 邮箱统一转小写邮箱大小写不敏感统一后才能去重 ----------df[邮箱]df[邮箱].str.strip().str.lower()# ---------- 4. 手机号正则删除所有非数字字符空格、横线等 ----------df[手机号]df[手机号].str.replace(r\D,,regexTrue)# 校验清洗后必须是 11 位数字df[手机号合法]df[手机号].str.fullmatch(r1\d{10}).notna()# ---------- 5. 检查部门列里是否还有映射表“不认识”的值 ----------unknownset(df[部门])-set(DEPT_MAP.values())print(未识别的部门类别,unknownifunknownelse无全部标准化成功)print(\n 清洗后 )print(df.to_string(indexFalse))withpd.ExcelWriter(OUT_FILE,engineopenpyxl)aswriter:raw.to_excel(writer,sheet_name清洗前,indexFalse)df.to_excel(writer,sheet_name清洗后,indexFalse)print(\n清洗结果已写入,OUT_FILE)if__name____main__:make_sample()clean()# -*- coding: utf-8 -*- 案例1学生成绩统计分析 知识点 1. pandas 读取 Excelpd.read_excel() 2. DataFrame 基本分析求和、求平均、describe() 描述统计 3. 新增列、排序、排名 4. 将多个 DataFrame 写入同一个 Excel 的不同 SheetExcelWriter 运行后 在本文件所在目录生成 data 文件夹里面有 成绩_源数据.xlsx 和 成绩_分析结果.xlsx importosimportpandasaspd# ---------- 0. 路径准备 ----------BASE_DIRos.path.dirname(os.path.abspath(__file__))# 本脚本所在目录DATA_DIRos.path.join(BASE_DIR,data)# 数据文件夹os.makedirs(DATA_DIR,exist_okTrue)# 不存在则创建SRC_FILEos.path.join(DATA_DIR,成绩_源数据.xlsx)OUT_FILEos.path.join(DATA_DIR,成绩_分析结果.xlsx)# ---------- 1. 生成示例 Excel实际项目中这一步换成你已有的 Excel 文件 ----------defmake_sample():dfpd.DataFrame({学号:[202401,202402,202403,202404,202405,202406],姓名:[张三,李四,王五,赵六,钱七,孙八],语文:[85,76,92,60,78,88],数学:[92,88,75,71,83,95],英语:[78,82,90,55,69,84],})df.to_excel(SRC_FILE,sheet_name成绩表,indexFalse)print(已生成示例文件,SRC_FILE)# ---------- 2. 读取 Excel 到 DataFrame ----------defanalyze():dfpd.read_excel(SRC_FILE,sheet_name成绩表)print( 读取到的原始数据 )print(df)# ---------- 3. 数据分析 ----------# 3.1 每位学生总分、平均分只对语文/数学/英语三列计算subjects[语文,数学,英语]df[总分]df[subjects].sum(axis1)# axis1 表示按行求和df[平均分]df[subjects].mean(axis1).round(1)# 3.2 按总分降序排名methodmin 表示同分同名次df[名次]df[总分].rank(ascendingFalse,methodmin).astype(int)dfdf.sort_values(名次)# 按名次排序# 3.3 全班各科描述统计平均分、最高分、最低分、标准差等subject_statdf[subjects].describe().round(1)# 追加“总分/平均分”两列的统计subject_stat[总分]df[总分].describe().round(1)subject_stat[平均分]df[平均分].describe().round(1)# 3.4 及格情况平均分 60 标记为“及格”df[是否及格]df[平均分].apply(lambdax:及格ifx60else不及格)pass_ratepd.DataFrame({及格人数:[(df[是否及格]及格).sum()],不及格人数:[(df[是否及格]不及格).sum()]})# ---------- 4. 写回 Excel多个 Sheet 必须在同一个 ExcelWriter 中写入 ----------withpd.ExcelWriter(OUT_FILE,engineopenpyxl)aswriter:df.to_excel(writer,sheet_name学生排名,indexFalse)subject_stat.to_excel(writer,sheet_name各科统计)pass_rate.to_excel(writer,sheet_name及格情况,indexFalse)print(\n 分析结果按名次 )print(df.to_string(indexFalse))print(\n 各科描述统计 )print(subject_stat)print(\n分析结果已写入,OUT_FILE)if__name____main__:make_sample()analyze()

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑