数据分析岗 Pandas 面试题 20 道(上):概念与读代码
写在前面前两篇讲了报错这篇换个场景面试。Pandas 面试有个特点——不考记忆考理解。没人会问你read_csv有哪些参数能查但一定会问「loc和iloc有什么区别」「为什么会有SettingWithCopyWarning」——因为这些问题能看出你是否真的想过数据是怎么存的。这篇挑出 10 道高频题每题三段面试官想听什么 → 标准答案 → 能跑的代码。代码不是装饰面试时如果能说「我现场验证一下」说服力完全不一样。答案建议背「结论 一个例子」别背长篇大论。先确认示例数据在手边importosimportmkdataifnotos.path.exists(data/sales.csv):mkdata.main()print(示例数据就绪,os.path.exists(data/sales.csv))这段会检查第 1 篇生成的data/目录在不在不在就自动补一次已经有数据时只会打印一行示例数据就绪 True不影响后面的结果。第 1 题Series 和 DataFrame 的关系想听什么你是不是把它当成「Excel 的一列和一张表」在理解。答Series 是带索引的一维数组DataFrame 是共享同一套行索引的多个 Series。取一列得到 Series取多列得到 DataFrame。importpandasaspd dfpd.DataFrame({城市:[杭州,上海],金额:[149.7,149.5]})print(取一列,type(df[城市]).__name__)print(取多列,type(df[[城市]]).__name__)print(一列本身有索引,df[城市].index.tolist())取一列 Series 取多列 DataFrame 一列本身有索引 [0, 1]第 2 题loc 和 iloc 的区别想听什么区不区分「标签」和「位置」。答loc按标签索引值、列名iloc按位置第几行第几列。切片时loc含右端点、iloc不含和 Python 列表一致。importpandasaspd dfpd.DataFrame({金额:[10,20,30,40]},index[100,200,300,400])print(loc[200]标签,df.loc[200,金额])print(iloc[1]位置,df.iloc[1,0])print(loc[100:300] 行数,len(df.loc[100:300]),含右端点)print(iloc[0:2] 行数,len(df.iloc[0:2]),不含)loc[200]标签 20 iloc[1]位置 20 loc[100:300] 行数 3 含右端点 iloc[0:2] 行数 2 不含加分点索引不是 0…n-1 时loc[0]会报 KeyError因为它找的是标签 0。第 3 题视图和副本怎么区分想听什么知不知道SettingWithCopyWarning背后的机制。答df[条件]返回的可能是视图也可能是副本pandas 无法保证所以赋值时给警告。规范写法是用loc一次完成或显式.copy()。importpandasaspd dfpd.DataFrame({城市:[杭州,上海],金额:[1.0,None]})subdf[df[城市]杭州]# 可能是视图sub[金额]sub[金额].fillna(0)# 触发警告的经典写法print(sub 已改,sub[金额].tolist())print(原表未变,df[金额].tolist())print(----)df.loc[df[城市]杭州,金额]0# 推荐写法print(loc 改完原表,df[金额].tolist())sub 已改 [1.0] 原表未变 [1.0, nan] ---- loc 改完原表 [0.0, nan]第 4 题缺失值怎么处理NaN 和 None 有什么区别想听什么有没有「按业务选策略」的意识而不是只会dropna。答None是 Python 对象NaN是浮点缺失值pandas 里数值列的缺失统一是NaN。处理分三步先量化isna().sum()、缺失率再判断关键字段 vs 可补字段最后才是删或填。importnumpyasnpimportpandasaspd dfpd.DataFrame({金额:[100.0,np.nan,300.0],城市:[杭州,None,北京]})print(缺失统计)print(df.isna().sum().to_string())print(缺失率,(df.isna().mean()*100).round(1).tolist())print(----)print(删,len(df.dropna(subset[金额])),行)print(填中位数,df[金额].fillna(df[金额].median()).tolist())print(分类填未知,df[城市].fillna(未知).tolist())缺失统计 金额 1 城市 1 缺失率 [33.3, 33.3] ---- 删 2 行 填中位数 [100.0, 200.0, 300.0] 分类填未知 [杭州, 未知, 北京]第 5 题groupby 的原理想听什么知不知道「拆分—应用—合并」这三步。答groupby先按键把行拆成若干组对每组应用函数再把结果按组键合并。理解这三步就能解释「为什么agg返回的索引是组键」「为什么transform返回等长结果」。importpandasaspd dfpd.DataFrame({城市:[杭州,杭州,上海,上海],金额:[100,200,300,400],})print(agg每组一行)print(df.groupby(城市)[金额].sum().to_string())print(----)print(transform贴回原表等长)print(df.groupby(城市)[金额].transform(sum).tolist())print(----)print(组内占比,(df[金额]/df.groupby(城市)[金额].transform(sum)).round(3).tolist())agg每组一行 城市 上海 700 杭州 300 ---- transform贴回原表等长 [300, 300, 700, 700] ---- 组内占比 [0.333, 0.667, 0.429, 0.571]第 6 题merge 的四种连接想听什么知不知道「行数为什么会变」。答inner只留两边都有的键left保留左表全部right保留右表全部outer两边都保留。行数变化来自「键重复」和「键缺失」可以用validate防翻倍、indicatorTrue查匹配。importpandasaspd leftpd.DataFrame({id:[1,2,3],金额:[10,20,30]})rightpd.DataFrame({id:[2,3,4],城市:[上海,北京,广州]})forhowin[inner,left,right,outer]:mleft.merge(right,onid,howhow)print(f{how:6s}行数{len(m)}缺失城市{m[城市].isna().sum()})inner 行数 2缺失城市 0 left 行数 3缺失城市 1 right 行数 3缺失城市 0 outer 行数 4缺失城市 1第 7 题apply、map、applymap 的区别想听什么知不知道该用哪个、以及性能差异。答map作用于 Series 的每个元素常配字典查表apply作用于 Series 或 DataFrame 的行/列能拿到整行applymap已在 pandas 2.1 废弃DataFrame 逐元素用df.map()。性能上向量化 mapapplyiterrows。importpandasaspd dfpd.DataFrame({城市:[杭州,上海],金额:[100,200]})coef{杭州:1.0,上海:1.05}print(map 查表,df[城市].map(coef).tolist())print(apply 按行,df.apply(lambdar:r[金额]*2,axis1).tolist())print(DataFrame.map替代 applymap,df[[金额]].map(lambdax:x1)[金额].tolist())map 查表 [1.0, 1.05] apply 按行 [200, 400] DataFrame.map替代 applymap [101, 201]第 8 题怎么处理重复值想听什么区不区分「完全重复」和「业务主键重复」。答duplicated()默认比所有列subset[...]可指定业务主键keep控制保留哪一条。真实场景里业务主键重复更常见——比如同一订单被录了两次但金额不同。importpandasaspd dfpd.DataFrame({订单号:[A1,A1,A2],金额:[100,100,200],})print(完全重复行,int(df.duplicated().sum()))print(按订单号重复,int(df.duplicated(subset[订单号]).sum()))print(保留最后一条,df.drop_duplicates(subset[订单号],keeplast).to_dict(records))完全重复行 1 按订单号重复 1 保留最后一条 [{订单号: A1, 金额: 100}, {订单号: A2, 金额: 200}]第 9 题怎么把宽表转成长表反过来呢想听什么知不知道melt/pivot_table或stack/unstack这一对。答长转宽用pivot_table或set_index().unstack()宽转长用melt。做透视报表用前者画图或喂模型用后者。importpandasaspd widepd.DataFrame({城市:[杭州,上海],笔记本:[100,200],鼠标:[50,60]})print(宽表)print(wide.to_string(indexFalse))longwide.melt(id_vars城市,var_name产品,value_name金额)print(melt 之后)print(long.to_string(indexFalse))print(----)print(再 pivot 回去)print(long.pivot_table(index城市,columns产品,values金额,aggfuncsum).to_string())宽表 城市 笔记本 鼠标 杭州 100 50 上海 200 60 melt 之后 城市 产品 金额 杭州 笔记本 100 上海 笔记本 200 杭州 鼠标 50 上海 鼠标 60 ---- 再 pivot 回去 产品 笔记本 鼠标 城市 上海 200 60 杭州 100 50第 10 题数据量太大内存不够怎么办想听什么有没有实战经验而不是只会read_csv。答四个层次——读的时候只读需要的列usecols并指定类型dtype内存里降级int8/float32和文本转category实在装不下就分块chunksize或让数据库先聚合长期方案换 Parquet 列式存储。importnumpyasnpimportpandasaspd dfpd.DataFrame({城市:np.random.choice([杭州,上海],1000),金额:np.random.rand(1000).round(4),})beforedf.memory_usage(deepTrue).sum()/1024df[城市]df[城市].astype(category)df[金额]df[金额].astype(float32)afterdf.memory_usage(deepTrue).sum()/1024print(f内存{before:.1f}KB -{after:.1f}KB)print(类型,df.dtypes.to_dict())内存21.6 KB - 5.0 KB 类型 {城市: CategoricalDtype(categories[上海, 杭州], orderedFalse, categories_dtypestr), 金额: dtype(float32)}面试答题的三个技巧先给结论再给例子面试官不要听定义复述要听「所以什么情况下会踩坑」主动提边界比如答loc/iloc时补一句「索引非默认值时loc[0]会报错」这是加分项不确定就现场验证print(df.head())跑一下比硬背可信。常见问题Q1被问到没准备过的 API 怎么办说清「我知道它大概是干什么的具体参数我会查文档」然后讲你会怎么查、怎么验证。承认不知道 展示检索能力比胡编好得多。Q2面试官问「你平时怎么处理脏数据」怎么答得有层次按「探查 → 量化 → 判断 → 处理 → 校验」五步答每步给一个方法info/isna().sum()/ 缺失率阈值 /fillna或dropna/duplicated().sum()。有流程感比堆方法名强。Q3要不要背代码不用背完整代码背方法名 关键参数merge(validate)、to_numeric(errorscoerce)、transform(sum)、drop_duplicates(subset)。这些是「会用」和「听说过」的分界线。小结这 10 道题的共同点是都在问「为什么」而不是「怎么用」。Series 与 DataFrame 的关系、loc/iloc、视图副本、缺失值策略、groupby 三步、merge 四种连接、apply 家族、重复值口径、宽长表互转、大数据处理——把这十个讲清楚Pandas 的基本功就到位了。下一篇是下半场10 道手写题包括每组 TopN、连续日期、宽长表转换、去重找重复全部给可运行答案。