第3周 Pandas实战1(冷链物流数据清洗)详细学习指南
一、知识点讲解
本部分围绕冷链物流数据清洗核心需求,拆解DataFrame高级操作、缺失值处理、重复值/异常值过滤、数据合并四大知识点,结合业务场景说明用法与注意事项。
(一)DataFrame高级操作
作用说明:对冷链物流数据(如运输轨迹、温湿度数据)进行筛选、列操作、统计分析,为后续清洗和合并打基础,核心涵盖条件筛选、列新增/修改、分组统计。
1. 条件筛选(loc/iloc)
作用:根据冷链业务条件(如温湿度范围、运输时间)筛选目标数据,精准提取所需订单或轨迹记录。
| 参数 | 说明 | 常用可选值及说明 | 传参示例 |
|---|---|---|---|
| loc行参数 | 按标签索引筛选行,支持布尔条件、切片、列表 | 布尔数组(如df['温度']>0)、标签切片(如'2026-01-01':'2026-01-07')、标签列表(如[0,2,5]) | df.loc[df['温度'] > -18, :] |
| loc列参数 | 按列名筛选列,支持单个列名、列表、切片 | 单个列名(如'订单号')、列名列表(如['订单号','温度','时间'])、列名切片(如'订单号':'湿度') | df.loc[:, ['订单号','温度','运输时间']] |
| iloc行/列参数 | 按位置索引筛选,仅支持整数、整数列表、切片 | 整数(如0)、整数列表(如[0,1,3])、整数切片(如0:100) | df.iloc[0:100, 0:3](取前100行前3列) |
典型用法示例:筛选冷链数据中温度在-18℃~-22℃(冷冻标准)的记录
import pandas as pd
# 构造模拟冷链数据
data = {'订单号': ['OD001', 'OD002', 'OD003', 'OD004'],
'温度': [-19, -23, -18, -20],
'湿度': [60, 58, 62, 59],
'运输时间': ['2026-01-01 08:00', '2026-01-01 09:30', '2026-01-01 10:15', '2026-01-01 11:00']}
df = pd.DataFrame(data)
# 筛选温度符合冷冻标准的记录
valid_temp_df = df.loc[(df['温度'] >= -22) & (df['温度'] <= -18), :]
print(valid_temp_df)常见陷阱提醒:① 多条件筛选需用&(且)/|(或)连接,不可用and/or;② 条件需加括号分组,否则会因运算优先级报错;③ loc按标签筛选时,行标签若为整数,仍需按标签逻辑而非位置逻辑。
2. 分组统计(groupby)
作用:按订单号、运输批次等维度分组,统计平均温度、湿度极值等指标,满足冷链订单温度统计需求。
| 参数 | 说明 | 常用可选值及说明 | 传参示例 |
|---|---|---|---|
| by | 分组依据,可指定列名、列名列表 | 单个列名(如'订单号')、列名列表(如['订单号','运输路线']) | df.groupby(by='订单号') |
| as_index | 是否将分组列作为索引 | True(默认,分组列成为索引)、False(分组列作为普通列) | df.groupby('订单号', as_index=False) |
| dropna | 是否排除分组列中含缺失值的组 | True(默认,排除)、False(保留) | df.groupby('订单号', dropna=False) |
典型用法示例:按订单号分组,统计每笔订单的平均温度、最高湿度
# 基于上述模拟数据,新增1条OD001的记录
new_data = {'订单号': ['OD001'], '温度': [-21], '湿度': [61], '运输时间': ['2026-01-01 12:00']}
new_df = pd.DataFrame(new_data)
df = pd.concat([df, new_df], ignore_index=True)
# 分组统计
order_stats = df.groupby('订单号', as_index=False).agg({
'温度': 'mean', # 平均温度
'湿度': 'max' # 最高湿度
}).rename(columns={'温度': '平均温度', '湿度': '最高湿度'})
print(order_stats)常见陷阱提醒:① 分组后需搭配agg(聚合)、apply(自定义函数)使用,否则仅返回groupby对象而非结果;② 聚合多个列时,需传入字典指定每列的聚合方式,避免默认聚合导致指标混乱。
(二)缺失值处理
作用说明:针对冷链数据中可能存在的温湿度缺失、运输时间缺失等问题,按业务规范处理,确保数据可用性(如冷冻食品温湿度缺失需谨慎填充或删除)。核心方法:isnull/notnull(检测)、dropna(删除)、fillna(填充)。
1. 缺失值检测(isnull/notnull)
作用:识别数据中的缺失值,为后续处理提供依据,冷链场景中需重点检测温湿度、订单号等关键字段。
| 方法 | 作用 | 常用参数 | 示例 |
|---|---|---|---|
| isnull() | 返回布尔值DataFrame,缺失值对应True | 无核心参数,可搭配sum()统计每列缺失数 | df.isnull().sum()(统计每列缺失值数量) |
| notnull() | 返回布尔值DataFrame,非缺失值对应True | 无核心参数,可搭配any()判断行是否有非缺失值 | df.notnull().any(axis=1)(判断每行是否有非缺失值) |
2. 缺失值删除(dropna)
作用:删除含缺失值的行/列,冷链场景中温湿度、订单号等关键字段缺失时,通常删除对应行(符合业务规范,避免无效数据干扰)。
| 参数 | 说明 | 常用可选值及说明 | 传参示例 |
|---|---|---|---|
| axis | 删除维度 | 0(默认,删除行)、1(删除列) | df.dropna(axis=0) |
| subset | 指定检测缺失值的列 | 列名列表(如['温度','湿度']) | df.dropna(subset=['温度','湿度']) |
| how | 删除条件 | any(默认,行/列有1个缺失值即删除)、all(行/列全为缺失值才删除) | df.dropna(how='all') |
3. 缺失值填充(fillna)
作用:用指定值或逻辑填充缺失值,冷链场景中可用于非关键字段(如运输备注),温湿度等关键字段慎用(需符合业务规范)。
| 参数 | 说明 | 常用可选值及说明 | 传参示例 |
|---|---|---|---|
| value | 填充的固定值 | 数字(如-18)、字符串(如'未知')、字典(按列指定值) | df.fillna(value={'湿度': 60}) |
| method | 填充逻辑(仅对数值型列有效) | ffill(前向填充,用前一行值)、bfill(后向填充,用后一行值) | df['温度'].fillna(method='ffill') |
| limit | 最大填充次数 | 整数(如1,仅填充连续1个缺失值) | df['温度'].fillna(method='ffill', limit=1) |
典型用法示例:处理冷链数据缺失值(关键字段删除,非关键字段填充)
# 构造含缺失值的冷链数据
data_with_na = {'订单号': ['OD001', 'OD002', None, 'OD004'],
'温度': [-19, None, -18, -20],
'湿度': [60, 58, None, 59],
'运输备注': [None, '正常', '延迟', None]}
df_na = pd.DataFrame(data_with_na)
# 1. 检测缺失值
print("各列缺失值数量:")
print(df_na.isnull().sum())
# 2. 处理关键字段(订单号、温度、湿度):删除缺失值
df_clean_na = df_na.dropna(subset=['订单号', '温度', '湿度'])
# 3. 处理非关键字段(运输备注):填充为'无备注'
df_clean_na['运输备注'] = df_clean_na['运输备注'].fillna(value='无备注')
print("\n处理后的数据:")
print(df_clean_na)常见陷阱提醒:① 填充温湿度等关键字段时,需结合冷链业务标准(如冷冻食品默认温度-18℃),不可随意填充;② 前向/后向填充仅适用于时序数据(如按运输时间排序后),否则会导致数据失真。
(三)重复值/异常值过滤
作用说明:剔除重复数据(如重复录入的温湿度记录)和异常数据(如超出冷链安全范围的温度),保证数据准确性。
1. 重复值过滤(duplicated/drop_duplicates)
作用:检测并删除重复记录,冷链场景中需排除同一时间、同一订单的重复温湿度记录。
| 方法 | 核心参数 | 参数说明 | 示例 |
|---|---|---|---|
| duplicated() | subset | 指定判断重复的列,默认所有列 | df.duplicated(subset=['订单号','运输时间']) |
| drop_duplicates() | keep | 保留重复项的方式:first(默认,保留第一条)、last(保留最后一条)、False(删除所有重复项) | df.drop_duplicates(subset=['订单号','运输时间'], keep='first') |
2. 异常值过滤
作用:剔除超出业务合理范围的数据,冷链场景中核心针对温湿度(如冷冻食品温度<-25℃或>-15℃为异常)、运输时长等字段。常用方法:条件筛选、3σ原则(适用于正态分布数据)。
典型用法示例:过滤重复值和异常温度数据
# 构造含重复值和异常值的冷链数据
data_abnormal = {'订单号': ['OD001', 'OD001', 'OD002', 'OD003'],
'温度': [-19, -19, -30, -10], # -30(过低)、-10(过高)为异常
'湿度': [60, 60, 58, 62],
'运输时间': ['2026-01-01 08:00', '2026-01-01 08:00', '2026-01-01 09:30', '2026-01-01 10:15']}
df_abnormal = pd.DataFrame(data_abnormal)
# 1. 过滤重复值(按订单号+运输时间去重,保留第一条)
df_no_dup = df_abnormal.drop_duplicates(subset=['订单号','运输时间'], keep='first')
# 2. 过滤异常温度(冷冻标准:-22℃~-18℃)
df_normal = df_no_dup.loc[(df_no_dup['温度'] >= -22) & (df_no_dup['温度'] <= -18), :]
print("去重后数据:")
print(df_no_dup)
print("\n过滤异常温度后数据:")
print(df_normal)常见陷阱提醒:① 异常值范围需结合具体冷链业务确定(如冷藏食品温度范围0~4℃,与冷冻不同);② 去重时需明确判断重复的核心字段组合,避免误删有效数据。
(四)数据合并(merge)
作用说明:将多个DataFrame按关键字段(如订单号)合并,实现冷链运输轨迹与温湿度表的关联,为后续统计分析提供完整数据。
| 参数 | 说明 | 常用可选值及说明 | 传参示例 |
|---|---|---|---|
| left/right | 待合并的两个DataFrame | Pandas DataFrame对象 | pd.merge(left=df_track, right=df_temp) |
| on | 合并关键字段,需在两个DataFrame中均存在 | 列名(如'订单号') | pd.merge(df_track, df_temp, on='订单号') |
| left_on/right_on | 左右DataFrame的合并字段名不同时使用 | 列名(如left_on='订单编号', right_on='订单号') | pd.merge(df1, df2, left_on='订单编号', right_on='订单号') |
| how | 合并方式 | inner(默认,仅保留双方都有的记录)、left(保留左表所有记录)、right(保留右表所有记录)、outer(保留所有记录) | pd.merge(df_track, df_temp, on='订单号', how='inner') |
| suffixes | 合并后重名列的后缀 | 元组(如('_轨迹','_温湿度')) | pd.merge(df1, df2, on='订单号', suffixes=('_A','_B')) |
典型用法示例:合并运输轨迹表与温湿度表
# 构造运输轨迹表
df_track = pd.DataFrame({
'订单号': ['OD001', 'OD002', 'OD003'],
'出发地': ['济南', '济南', '青岛'],
'目的地': ['北京', '上海', '广州'],
'运输时长': [8, 12, 15]
})
# 构造温湿度表
df_temp = pd.DataFrame({
'订单号': ['OD001', 'OD002', 'OD004'],
'温度': [-19, -20, -18],
'湿度': [60, 59, 61]
})
# 内连接合并(仅保留双方都有的订单)
df_merge = pd.merge(left=df_track, right=df_temp, on='订单号', how='inner')
print("合并后数据:")
print(df_merge)常见陷阱提醒:① 合并前需确认关键字段格式一致(如订单号均为字符串,无空格差异);② 避免多对多合并,否则会产生数据冗余,需提前处理重复记录。
二、实战场景
本场景模拟济南某冷链企业数据处理需求,实现运输轨迹与温湿度表合并、数据清洗、订单温度统计全流程,输出标准格式数据。
实战代码(含详细注释)
import pandas as pd
# ---------------------- 1. 数据准备(模拟冷链企业真实数据)----------------------
# 运输轨迹数据(含订单基本信息、运输路线)
track_data = {
'订单号': ['JN-LN-001', 'JN-LN-002', 'JN-LN-003', 'JN-LN-004', 'JN-LN-002'], # 含重复订单
'出发地': ['济南', '济南', '济南', '青岛', '济南'],
'目的地': ['北京', '上海', '广州', '济南', '上海'],
'运输时间': ['2026-01-01 08:00', '2026-01-02 09:30', None, '2026-01-03 10:15', '2026-01-02 09:30'], # 含缺失值
'运输车辆': ['鲁A12345', '鲁A67890', '鲁A23456', '鲁B11223', '鲁A67890']
}
df_track = pd.DataFrame(track_data)
# 温湿度数据(含异常值、缺失值)
temp_hum_data = {
'订单号': ['JN-LN-001', 'JN-LN-002', 'JN-LN-003', 'JN-LN-004', 'JN-LN-005'],
'温度': [-19, -25, -18, None, -10], # -25(过低)、-10(过高)为异常,含缺失值
'湿度': [60, 58, None, 59, 62], # 含缺失值
'记录时间': ['2026-01-01 09:00', '2026-01-02 10:30', '2026-01-02 11:15', '2026-01-03 11:15', '2026-01-04 08:00']
}
df_temp = pd.DataFrame(temp_hum_data)
# ---------------------- 2. 数据清洗(按冷链业务规范处理)----------------------
# 2.1 处理运输轨迹表
# 去重:按订单号+运输时间去重(删除重复录入的轨迹记录)
df_track_clean = df_track.drop_duplicates(subset=['订单号', '运输时间'], keep='first')
# 删除关键字段(运输时间)缺失的记录
df_track_clean = df_track_clean.dropna(subset=['运输时间'])
# 2.2 处理温湿度表
# 删除温度、湿度缺失的记录(冷链核心字段,缺失无法使用)
df_temp_clean = df_temp.dropna(subset=['温度', '湿度'])
# 过滤异常温度(冷冻食品标准:-22℃~-18℃)
df_temp_clean = df_temp_clean.loc[(df_temp_clean['温度'] >= -22) & (df_temp_clean['温度'] <= -18), :]
# ---------------------- 3. 数据合并(关联轨迹与温湿度数据)----------------------
# 内连接合并(仅保留双方都有、且已清洗后的订单数据,避免冗余)
df_merged = pd.merge(
left=df_track_clean,
right=df_temp_clean,
on='订单号',
how='inner',
suffixes=('_轨迹', '_温湿度') # 若有重名列,添加后缀区分
)
# ---------------------- 4. 订单温度统计(计算每笔订单平均温度)----------------------
order_temp_stats = df_merged.groupby('订单号', as_index=False).agg({
'温度': 'mean',
'出发地': 'first', # 取第一个出发地(同一订单一致)
'目的地': 'first', # 取第一个目的地
'运输时长': 'first'
}).rename(columns={'温度': '订单平均温度'})
# 格式化输出:保留2位小数,调整列顺序
order_temp_stats['订单平均温度'] = order_temp_stats['订单平均温度'].round(2)
output_cols = ['订单号', '出发地', '目的地', '运输时长', '订单平均温度']
df_output = order_temp_stats[output_cols]
print("最终输出标准格式数据:")
print(df_output)实战技巧总结
- 数据清洗优先级:先去重、再处理缺失值、最后过滤异常值,避免无效操作(如先过滤异常值再去重,导致重复工作)。
- 合并技巧:冷链数据合并优先用内连接(inner),仅保留双方都有的有效订单,减少冗余数据;合并前务必清洗数据,避免脏数据导致合并后数据混乱。
- 参数核心用法:① dropna的subset参数精准控制需检测的关键字段;② merge的on参数需确保关键字段无格式差异;③ groupby的as_index=False让分组列作为普通列,便于后续格式化。
- 业务适配:异常温度范围、缺失值处理方式需严格遵循冷链业务规范,不可脱离业务随意处理(如冷冻与冷藏温度标准不同,需针对性调整条件)。
三、练习题
练习题1:冷链订单运输时长异常过滤与统计
题目:现有济南冷链企业订单数据,包含订单号、运输距离(km)、运输时长(h)、温度,其中运输时长存在异常值(合理范围:运输距离/60 ≤ 时长 ≤ 运输距离/40,假设冷链车时速40-60km/h)。请清洗数据(过滤异常时长、删除温度缺失值),并统计各目的地的平均运输时长、订单数量。
实现思路:① 检测并删除温度缺失值;② 按运输距离计算合理时长范围,过滤异常时长记录;③ 按目的地分组,统计平均时长和订单数。
代码:
import pandas as pd
# 模拟数据
data = {
'订单号': ['JN-LN-001', 'JN-LN-002', 'JN-LN-003', 'JN-LN-004', 'JN-LN-005'],
'运输距离': [400, 600, 300, 500, 450],
'运输时长': [8, 18, 5, 9, 15], # 18(600km,超60km/h时速)、15(450km,超60km/h)为异常
'温度': [-19, -20, None, -18, -21],
'目的地': ['北京', '上海', '北京', '广州', '上海']
}
df = pd.DataFrame(data)
# 1. 删除温度缺失值
df_clean = df.dropna(subset=['温度'])
# 2. 计算合理运输时长范围,过滤异常值
df_clean['最小合理时长'] = df_clean['运输距离'] / 60
df_clean['最大合理时长'] = df_clean['运输距离'] / 40
df_normal = df_clean.loc[(df_clean['运输时长'] >= df_clean['最小合理时长']) &
(df_clean['运输时长'] <= df_clean['最大合理时长']), :]
# 3. 按目的地分组统计
dest_stats = df_normal.groupby('目的地', as_index=False).agg({
'运输时长': 'mean',
'订单号': 'count'
}).rename(columns={'运输时长': '平均运输时长', '订单号': '订单数量'})
# 格式化输出
dest_stats['平均运输时长'] = dest_stats['平均运输时长'].round(2)
print("各目的地统计结果:")
print(dest_stats)练习题2:多表合并与订单温湿度极值统计
题目:现有三张表:订单基础表(订单号、客户名称、货物类型)、温湿度表(订单号、记录时间、温度、湿度)、运输轨迹表(订单号、出发地、目的地)。请合并三张表,清洗数据(删除重复记录、过滤温度异常值-22℃~-18℃),统计每笔订单的温度最大值、最小值及对应的记录时间。
实现思路:① 合并三张表(按订单号内连接);② 去重(按订单号+记录时间);③ 过滤温度异常值;④ 按订单号分组,用agg结合自定义逻辑获取温湿度极值及对应时间。
代码:
import pandas as pd
# 模拟三张表数据
df_order = pd.DataFrame({
'订单号': ['OD001', 'OD002', 'OD003'],
'客户名称': ['甲公司', '乙公司', '丙公司'],
'货物类型': ['冷冻肉类', '冷冻海鲜', '冷冻果蔬']
})
df_temp = pd.DataFrame({
'订单号': ['OD001', 'OD001', 'OD002', 'OD002', 'OD003'],
'记录时间': ['2026-01-01 09:00', '2026-01-01 10:00', '2026-01-02 10:30', '2026-01-02 11:30', '2026-01-03 09:00'],
'温度': [-19, -20, -23, -18, -21],
'湿度': [60, 59, 58, 61, 62]
})
df_track = pd.DataFrame({
'订单号': ['OD001', 'OD002', 'OD003'],
'出发地': ['济南', '济南', '青岛'],
'目的地': ['北京', '上海', '广州']
})
# 1. 合并三张表(先合并订单表与温湿度表,再合并轨迹表)
df_merge1 = pd.merge(df_order, df_temp, on='订单号', how='inner')
df_merge_all = pd.merge(df_merge1, df_track, on='订单号', how='inner')
# 2. 去重(按订单号+记录时间,避免重复记录)
df_no_dup = df_merge_all.drop_duplicates(subset=['订单号', '记录时间'], keep='first')
# 3. 过滤温度异常值(-22℃~-18℃)
df_normal = df_no_dup.loc[(df_no_dup['温度'] >= -22) & (df_no_dup['温度'] <= -18), :]
# 4. 按订单号分组,统计温度极值及对应时间
def get_time_by_temp(group, func):
"""根据函数(max/min)获取对应温度的记录时间"""
temp_val = func(group['温度'])
return group.loc[group['温度'] == temp_val, '记录时间'].iloc[0]
order_temp_extremes = df_normal.groupby('订单号', as_index=False).agg({
'温度': ['max', 'min'],
'记录时间': [lambda x: get_time_by_temp(df_normal[df_normal['订单号']==x.name], max),
lambda x: get_time_by_temp(df_normal[df_normal['订单号']==x.name], min)],
'客户名称': 'first',
'货物类型': 'first'
})
# 扁平化列名,格式化输出
order_temp_extremes.columns = ['订单号', '最高温度', '最低温度', '最高温度时间', '最低温度时间', '客户名称', '货物类型']
print("订单温湿度极值统计结果:")
print(order_temp_extremes)练习题3:缺失值填充与订单完成率统计
题目:现有济南冷链企业订单数据,包含订单号、下单时间、运输完成时间、温度、湿度。其中运输完成时间部分缺失(按业务规则,缺失视为未完成),湿度部分缺失(可用同订单其他记录的平均湿度填充)。请处理数据,统计已完成订单的温度合格率(温度-22℃~-18℃为合格)及整体订单完成率。
实现思路:① 填充湿度缺失值(按订单号分组,用平均湿度填充);② 区分已完成(完成时间非空)和未完成订单;③ 计算订单完成率(已完成订单数/总订单数);④ 计算已完成订单的温度合格率(合格订单数/已完成订单数)。
代码:
import pandas as pd
# 模拟数据
data = {
'订单号': ['OD001', 'OD001', 'OD002', 'OD003', 'OD003', 'OD004'],
'下单时间': ['2026-01-01 08:00', '2026-01-01 08:00', '2026-01-02 09:00', '2026-01-02 10:00', '2026-01-02 10:00', '2026-01-03 08:00'],
'运输完成时间': ['2026-01-01 16:00', '2026-01-01 16:00', None, '2026-01-02 22:00', '2026-01-02 22:00', None],
'温度': [-19, -20, -18, -23, -21, -17],
'湿度': [60, None, 58, None, 62, 61]
}
df = pd.DataFrame(data)
# 1. 去重(按订单号+下单时间)
df_no_dup = df.drop_duplicates(subset=['订单号', '下单时间'], keep='first')
# 2. 填充湿度缺失值(按订单号分组,用平均湿度填充)
df_no_dup['湿度'] = df_no_dup.groupby('订单号')['湿度'].transform(lambda x: x.fillna(x.mean()).round(1))
# 3. 区分已完成和未完成订单
df_completed = df_no_dup.dropna(subset=['运输完成时间']) # 已完成
df_uncompleted = df_no_dup[df_no_dup['运输完成时间'].isnull()] # 未完成
# 4. 计算订单完成率
total_orders = len(df_no_dup['订单号'].unique())
completed_orders = len(df_completed['订单号'].unique())
completion_rate = (completed_orders / total_orders) * 100
# 5. 计算已完成订单的温度合格率
df_completed['温度合格'] = (df_completed['温度'] >= -22) & (df_completed['温度'] <= -18)
qualified_orders = len(df_completed[df_completed['温度合格']]['订单号'].unique())
qualified_rate = (qualified_orders / completed_orders) * 100 if completed_orders > 0 else 0
# 输出结果
print(f"整体订单完成率:{completion_rate:.1f}%")
print(f"已完成订单温度合格率:{qualified_rate:.1f}%")
print("\n处理后的数据:")
print(df_no_dup[['订单号', '下单时间', '运输完成时间', '温度', '湿度']])(注:文档部分内容可能由 AI 生成)