楼层: 首页/ 软件技术/ Python AI 数据科学/ Pandas:Excel 的 Python 版
3

Pandas:Excel 的 Python 版

Pandas DataFrame

Pandas 处理的是带标签的表格——就像 Excel 里的一张工作表:一行一条记录,一列一个字段。读 CSV、清洗脏数据、按某列分组、算平均值、画个图——这些事在 Excel 里要点半天,Pandas 一行代码。它是数据科学的主力工具。

两种核心结构:Series 和 DataFrame

import pandas as pd # Series:一维带标签的数组(像一列) s = pd.Series([90, 85, 78], index=["小明", "小红", "小刚"]) print(s["小明"]) # 90 # DataFrame:二维表格(像一张工作表) df = pd.DataFrame({ "姓名": ["小明", "小红", "小刚"], "年龄": [18, 20, 19], "成绩": [90, 85, 78] }) print(df) # 姓名 年龄 成绩 # 0 小明 18 90 # 1 小红 20 85 # 2 小刚 19 78

读 CSV & 查看数据

# 读 CSV(最常用) df = pd.read_csv("sales.csv") # 快速查看 df.head() # 前 5 行 df.tail() # 后 5 行 df.info() # 每列类型、非空数量 df.describe() # 数值列的统计:count/mean/std/min/max/分位数 print(df.columns) # 列名列表 print(df.shape) # (行数, 列数)

论EDA 五步检查清单

拿到新数据,按这个顺序走一遍,别上来就建模:① shape 看有多少行列;② info() 看类型对不对、有没有缺失;③ describe() 看数值分布;④ isnull().sum() 看缺失分布;⑤ 画几张分布图。很多"模型不行"其实是脏数据没看清。

选择、筛选、排序

# 选一列(返回 Series) df["成绩"] # 选多列 df[["姓名", "成绩"]] # 条件筛选:成绩 >= 80 的人 good = df[df["成绩"] >= 80] # 多条件:年龄 >= 19 且成绩 >= 80 df[(df["年龄"] >= 19) & (df["成绩"] >= 80)] # 注意:每个条件要包括号,& 是与,| 是或 # 排序 df.sort_values("成绩", ascending=False) # 按成绩降序 # 增删列 df["是否及格"] = df["成绩"] >= 60 df = df.drop(columns=["是否及格"])

apply / map:对每列每行加工

# apply:对一列(或一行)应用函数 # 成绩分等级:90+ 优,80+ 良,否则中 def grade(score): if score >= 90: return "优" elif score >= 80: return "良" else: return "中" df["等级"] = df["成绩"].apply(grade) # map:用字典映射替换(小数据快) df["性别"] = df["性别"].map({"M": "男", "F": "女"}) # 字符串访问器 .str:Series 上的字符串方法 df["邮箱"].str.lower() df["姓名"].str.startswith("张") df["电话"].str.extract(r"(\d{3})-(\d{4})") # 正则抽取

时间序列:to_datetime & resample

# 把字符串列转成真正的日期类型 df["日期"] = pd.to_datetime(df["日期"]) # 按日期过滤 df[df["日期"] >= "2026-01-01"] # 取出年、月、星期 df["年"] = df["日期"].dt.year df["月"] = df["日期"].dt.month df["星期"] = df["日期"].dt.dayofweek # 0=周一 # resample:把日数据聚合成月/周 df = df.set_index("日期") monthly = df.resample("M")["销售额"].sum() # 每月总销售额

缺失值与重复值

# 看哪些是缺失值 df.isnull().sum() # 每列有几个 NaN # 删掉有缺失的行 df_clean = df.dropna() # 用某个值填充(比如平均值) df["成绩"] = df["成绩"].fillna(df["成绩"].mean()) # 去重 df = df.drop_duplicates()

异常值检测:IQR 方法

# 箱线图原理:Q1=25分位,Q3=75分位,IQR=Q3-Q1 # 超出 [Q1-1.5*IQR, Q3+1.5*IQR] 的就是异常值 q1 = df["工资"].quantile(0.25) q3 = df["工资"].quantile(0.75) iqr = q3 - q1 lower = q1 - 1.5 * iqr upper = q3 + 1.5 * iqr # 过滤掉异常值 df = df[(df["工资"] >= lower) & (df["工资"] <= upper)]

分组聚合 groupby

Excel 里的"数据透视表",Pandas 一行搞定。

# 假设 df 有"部门"和"工资"两列 # 按部门分组,算每组的平均工资 result = df.groupby("部门")["工资"].mean() # 一次算多个统计量 stats = df.groupby("部门")["工资"].agg(["mean", "max", "min", "count"]) # 透视表 pivot = pd.pivot_table(df, values="销售额", index="月份", columns="产品", aggfunc="sum")

合并:merge / concat

# 像 SQL 的 JOIN:按某个键把两张表拼起来 users = pd.DataFrame({"uid": [1, 2, 3], "name": ["小明", "小红", "小刚"]}) orders = pd.DataFrame({"uid": [1, 1, 2], "amount": [100, 200, 50]}) merged = pd.merge(users, orders, on="uid", how="inner") print(merged) # uid name amount # 0 1 小明 100 # 1 1 小明 200 # 2 2 小红 50 # concat:上下或左右拼 all_df = pd.concat([df1, df2], axis=0) # 上下拼

导出

df.to_csv("out.csv", index=False, encoding="utf-8-sig") df.to_excel("out.xlsx", index=False) # utf-8-sig 让 Excel 打开不乱码

类别型数据 category:省内存

一列里只有有限几个值(性别、城市、血型),存成 category 比 object 省内存,groupby 也更快。

df["城市"] = df["城市"].astype("category") # 100 万行的"城市"列,object 可能占 80MB,category 只要几 MB

时间序列:DatetimeIndex / resample / rolling / shift

论时间序列四板斧

to_datetime 把字符串变时间;set_index 把时间列设成行索引;resample 按时间窗口聚合(日→周→月→年);rolling 算滑动窗口统计(移动平均);shift 把数据往前/往后挪一格,算"同比/环比"。这是金融、运营数据分析的日常。

import pandas as pd import numpy as np # 构造示例:100 天的销售额 dates = pd.date_range("2026-01-01", periods=100, freq="D") df = pd.DataFrame({ "sales": np.random.randn(100).cumsum() + 100, "visitors": np.random.randint(50, 200, size=100) }, index=dates) print(df.head()) # sales visitors # 2026-01-01 99.583795 120 # 2026-01-02 99.849708 150 # ... # 1. resample:按周(W)、按月(ME)聚合 weekly = df.resample("W").sales.sum() # 每周总销售额 monthly_mean = df.resample("ME").sales.mean() # 每月平均销售额 print(monthly_mean) # 2026-01-31 100.6 # 2026-02-28 102.3 # Freq: ME, Name: sales, dtype: float64 # 2. rolling:7 天滑动平均(移动均线) df["sales_ma7"] = df["sales"].rolling(window=7).mean() # 前 6 个值是 NaN(窗口凑不齐) # 3. shift:环比——今天比昨天涨了多少 df["sales_lag1"] = df["sales"].shift(1) # 昨天的销售额 df["pct_change"] = df["sales"].pct_change() # 环比涨幅(一行搞定) print(df[["sales", "sales_lag1", "pct_change"]].head()) # 4. 时间筛选:取某段时间 df.loc["2026-02-01":"2026-02-15"] # 2 月前 15 天 df.loc["2026-03"] # 整个 3 月 # 5. dt 访问器:从时间戳提取各种字段 df["year"] = df.index.year df["month"] = df.index.month df["weekday"] = df.index.dayofweek # 0=周一 df["quarter"] = df.index.quarter # 1~4 季度

分组聚合进阶:agg / transform / apply

# 假设 df 有"部门"和"工资"两列 # 1. agg:一次算多个统计量 stats = df.groupby("部门")["工资"].agg([ ("平均", "mean"), ("最高", "max"), ("最低", "min"), ("人数", "count"), ("标准差", "std") ]) print(stats) # 平均 最高 最低 人数 标准差 # 部门 # 研发 25000 35000 18000 10 5200.5 # 产品 22000 28000 16000 8 3800.2 # 不同列用不同聚合函数 df.groupby("部门").agg({ "工资": ["mean", "max"], "年龄": "mean", "入职日期": "min" # 最早入职日期 }) # 2. transform:把聚合结果"广播"回每一行(和原表同形状) # 经典用途:在组内做标准化 df["工资_组内标准化"] = df.groupby("部门")["工资"].transform("mean") # 每行都等于它所在部门的平均工资(列长度和 df 一样) # 组内 Z-score:(工资 - 部门均值) / 部门标准差 def zscore(x): return (x - x.mean()) / x.std() df["工资_组内zscore"] = df.groupby("部门")["工资"].transform(zscore) # 3. apply:对每个组应用一个自定义函数(最慢,最灵活) def top2_salary(g): return g.nlargest(2) # 每组工资最高的 2 个人 df.groupby("部门").apply(top2_salary) # 4. 过滤组:只要人数大于 5 的部门 big_dept = df.groupby("部门").filter(lambda g: g["工资"].count() > 5)

多表合并:merge / join / concat / append

import pandas as pd users = pd.DataFrame({ "uid": [1, 2, 3, 4], "name": ["小明", "小红", "小刚", "小李"] }) orders = pd.DataFrame({ "uid": [1, 1, 2, 5], "amount": [100, 200, 50, 999] }) # 1. merge:像 SQL JOIN,on 指定连接键 inner = pd.merge(users, orders, on="uid", how="inner") print(inner) # uid name amount # 0 1 小明 100 # 1 1 小明 200 # 2 2 小红 50 left = pd.merge(users, orders, on="uid", how="left") # 保留 users 全部行;没有订单的人 amount 是 NaN outer = pd.merge(users, orders, on="uid", how="outer") # 两边都保留,找不到的地方填 NaN # 连接键列名不一样时 pd.merge(users, orders, left_on="uid", right_on="user_id", how="inner") # 2. concat:上下堆(axis=0)或左右拼(axis=1) df1 = pd.DataFrame({"a": [1,2], "b": [3,4]}) df2 = pd.DataFrame({"a": [5,6], "b": [7,8]}) all_rows = pd.concat([df1, df2], axis=0, ignore_index=True) # a b # 0 1 3 # 1 2 4 # 2 5 7 # 3 6 8 # 3. join:按索引合并(不常用,一般用 merge) df1.join(df2, lsuffix="_l", rsuffix="_r")

Pandas 章节面试题

面试 · Pandas

Q1. apply / transform / agg 三者有什么区别?

查看答案

agg 返回聚合后的统计量(行数变少);transform 返回与原表相同形状(把聚合值广播回每行);apply 最灵活,可以返回任意形状,但最慢。

Q2. Pandas 怎么优化大表性能?

查看答案

① 类别列转 category;② 数值列选更小的 dtype(int32 代替 int64);③ 用 pd.read_csv(usecols=[...]) 只读需要的列;④ 用 query() / eval() 替代布尔索引;⑤ 千万行以上用 PyArrow 后端或 DuckDB。

Q3. merge 的 how 参数 inner / left / outer / right 区别?

查看答案

inner 只保留两边都匹配的行;left 保留左表全部;right 保留右表全部;outer 保留两边全部(SQL 里分别对应 INNER / LEFT OUTER / RIGHT OUTER / FULL OUTER JOIN)。

Q4. SettingWithCopyWarning 怎么修?

查看答案

用 .loc[行条件, 列名] = 值 替代链式索引 df[条件][列] = 值。链式索引可能先返回副本,改副本不影响原表。

Q5. 怎么算"环比增长率"?

查看答案

df["col"].pct_change() 一行搞定;或者 df["col"] / df["col"].shift(1) - 1。同比用 .shift(12)(月数据)或 .shift(4)(周数据)。