NumPy与Pandas:用专业工具处理数据
NumPy与Pandas:用专业工具处理数据
一、问题背景:Python列表算100万行数据要45分钟
接手了一个任务:分析过去一年所有Lot的膜厚数据。
数据量:100万行 × 50列。用纯Python列表处理:
```python
纯Python:循环所有行
with open("data.csv") as f:
data = [float(line.split(",")[10]) for line in f.readlines()[1:]]
avg = sum(data) / len(data) # 等了5分钟还没出结果...
```
为什么慢?Python列表里的每个元素都是独立的对象,存得分散,CPU处理的时候要一个个搬到内存里。
用NumPy处理:
```python
import numpy as np
data = np.genfromtxt("data.csv", delimiter=",", skip_header=1)
thickness = data[:, 10] # 取第11列(所有行)
avg = np.mean(thickness) # 0.01秒出结果
```
快了几万倍。因为NumPy的数据在内存里是连续排列的,CPU一次可以拉一批进缓存处理。
学完这一篇,你能做到:
1. 用numpy数组做批量计算(比列表快100倍以上)
2. 用pandas读取CSV、筛选数据、分组统计
3. 处理10万行数据像处理10行一样快
---
二、技术原理:NumPy数组 vs Python列表
2.1 先感受一下速度差
```python
import numpy as np
import time
100万个随机数
size = 1_000_000
Python列表版
py_list = list(range(size))
start = time.time()
result = [x * 2 for x in py_list]
print(f"Python列表: {time.time()-start:.3f}秒")
NumPy版
np_arr = np.arange(size)
start = time.time()
result = np_arr * 2 # 向量化,不用循环
print(f"NumPy数组: {time.time()-start:.3f}秒")
```
跑一下试试,NumPy通常快10-50倍。
2.2 基础操作
```python
import numpy as np
创建数组
thickness = np.array([1250.5, 1248.3, 1251.2, 1249.8, 1250.1])
批量统计
print(np.mean(thickness)) # 均值
print(np.std(thickness)) # 标准差
print(np.min(thickness)) # 最小值
print(np.max(thickness)) # 最大值
print(np.median(thickness)) # 中位数
筛选
data = np.array([1250, 1248, 1255, 1247, 1252])
high = data[data > 1250] # 选出>1250的元素
print(high) # [1255, 1252]
```
关键区别:`data[data > 1250]` 这种写法叫"布尔索引",NumPy一行搞定,Python列表得写for循环。
2.3 二维数组(表格)
FAB数据通常是二维的:行=样本,列=参数。
```python
模拟100批Lot,每批5个测量值
np.random.seed(42)
data = 1250 + np.random.randn(100, 5) * 3
print(data.shape) # (100, 5) —— 100行5列
每批的均值(axis=1 = 按行算)
lot_avgs = np.mean(data, axis=1)
print(f"前5批均值: {lot_avgs[:5].round(2)}")
每个位置的总均值(axis=0 = 按列算)
pos_avgs = np.mean(data, axis=0)
print(f"5个测量位置的总均值: {pos_avgs.round(2)}")
找出均值异常偏高的批次
abnormal = data[lot_avgs > 1253]
print(f"异常批次: {len(abnormal)}批")
```
为什么这样写? `axis=1` 是"沿着行的方向操作",就是每批5个值算一个均值。`axis=0` 是"沿着列的方向操作",就是100批的第一个测量点算一个均值。搞不清可以记住:axis=1算每行,axis=0算每列。
---
三、实战案例:Pandas处理真实数据
3.1 创建DataFrame
```python
import pandas as pd
import numpy as np
模拟FAB数据
data = pd.DataFrame({
'lot_id': ['FAB-001', 'FAB-002', 'FAB-003', 'FAB-004', 'FAB-005'],
'process': ['ETCH', 'ETCH', 'CVD', 'CVD', 'PHOTO'],
'thickness': [1250.5, 1248.3, 1251.2, 1240.0, 1249.8],
'yield': [96.5, 95.8, 97.2, 88.5, 96.1],
'defect_count': [5, 8, 3, 25, 6],
})
print(data.head()) # 查看前5行
```
3.2 筛选和统计
```python
筛选条件
good_lots = data[data['yield'] > 95] # 良率>95%的Lot
etch_lots = data[data['process'] == 'ETCH'] # ETCH工站的Lot
基本统计
print(data['thickness'].describe())
count 5.000000
mean 1247.960000
std 4.663525
min 1240.000000
max 1251.200000
分组统计
grouped = data.groupby('process').agg(
avg_yield=('yield', 'mean'),
avg_defect=('defect_count', 'mean'),
count=('lot_id', 'count')
)
print(grouped)
```
3.3 读取和保存CSV
```python
保存到CSV
data.to_csv('fab_data_20260115.csv', index=False)
从CSV读取
new_data = pd.read_csv('fab_data_20260115.csv')
追加列
new_data['quality'] = new_data['yield'].apply(
lambda x: '优秀' if x > 96 else ('合格' if x > 90 else '不合格')
)
print(new_data[['lot_id', 'yield', 'quality']])
```
为什么这样写? `apply(lambda x: ...)` 可以对每一行的数据进行自定义判断,比Excel的IF嵌套公式清晰多了。`groupby('process')` 相当于Excel的透视表——几行代码完成按工序汇总统计。
---
四、完整实战:自动分析良率
```python
import pandas as pd
import numpy as np
1. 加载数据(模拟)
np.random.seed(42)
n = 500 # 500批
all_data = pd.DataFrame({
'lot_id': [f'FAB-{i:04d}' for i in range(n)],
'process': np.random.choice(['ETCH', 'CVD', 'PHOTO', 'CMP'], n),
'thickness': np.random.normal(1250, 4, n).round(1),
'defects': np.random.poisson(8, n),
'yield_pct': np.random.normal(95, 3, n).round(1),
})
2. 标记良率异常
all_data['bad_lot'] = all_data['yield_pct'] < 90
3. 工序良率排行榜
pro_summary = all_data.groupby('process').agg(
avg_yield=('yield_pct', 'mean'),
min_yield=('yield_pct', 'min'),
bad_count=('bad_lot', 'sum'),
total=('lot_id', 'count')
).sort_values('avg_yield', ascending=False)
print("工序良率排行榜:")
print(pro_summary)
4. 缺陷数和良率的关系
corr = all_data['defects'].corr(all_data['yield_pct'])
print(f"\n缺陷数与良率的相关系数: {corr:.3f}")
print("(负值=缺陷越多良率越低,-1到0之间)")
5. 导出结果
pro_summary.to_csv('process_yield_summary.csv')
print("\n已导出: process_yield_summary.csv")
```
---
五、效果对比
| 操作 | Python列表 | NumPy/Pandas | 提升 |
|------|-----------|-------------|------|
| 100万数据求均值 | 2-5秒 | 0.01秒 | 200-500倍 |
| 条件筛选 | 写for循环 | `data[data>3]`一行 | 10倍 |
| 分组统计 | 50+行代码 | `groupby().mean()`一行 | 50倍 |
| 读CSV文件 | open+split+循环 | `pd.read_csv()`一行 | 100倍 |
| 内存占用 | 高(每个元素是对象) | 低(连续内存) | 少80% |
---
六、自己动手
```python
练习:分析你自己的数据
import pandas as pd
下面是一段示例数据(5个Lot,3个参数)
data = pd.DataFrame({
'lot_id': ['A001', 'A002', 'A003', 'A004', 'A005'],
'cmp_thickness': [1251, 1248, 1255, 1242, 1250],
'temperature': [85.2, 85.5, 84.8, 86.1, 85.0],
'yield': [96.3, 95.1, 97.5, 89.2, 94.8],
})
练习1:找出yield < 95的Lot
练习2:按cmp_thickness排序,看厚度和良率的关系
练习3:temperature和yield有没有关系?
✏️ 下面写你的代码
```
思考题:
1. numpy的 `axis=0` 和 `axis=1` 分别是什么意思?试试用2行2列的数据验证
2. pandas的 `data['yield']` 和 `data[['yield']]` 有什么区别?
3. 如果数据量超过内存(比如1000万行),你知道用什么工具吗?(提示:polars、dask、modin)
---
七、新手常见错误
| 错误 | 表现 | 正确做法 |
|------|------|---------|
| 忘记import numpy | NameError | 开头写 `import numpy as np` |
| 列表和数组混用 | `list * 2` 是重复不是乘法 | 用 `np.array()` 转一下 |
| axis分不清 | 结果不对 | 2D数组:axis=0按列,axis=1按行 |
| pandas空值没处理 | 统计结果都是NaN | `data.dropna()` 或 `fillna(0)` |
| 文件路径有中文 | 读取失败 | 用原始字符串 `r"路径"` |
---
> 💬 你平时处理多少行数据?用过numpy和pandas吗?评论区聊聊
> 📚 收藏+点赞,下一篇讲画图 👆





