Code - Pandas实战1(冷链物流数据清洗)

第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待合并的两个DataFramePandas 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)

实战技巧总结

  1. 数据清洗优先级:先去重、再处理缺失值、最后过滤异常值,避免无效操作(如先过滤异常值再去重,导致重复工作)。
  2. 合并技巧:冷链数据合并优先用内连接(inner),仅保留双方都有的有效订单,减少冗余数据;合并前务必清洗数据,避免脏数据导致合并后数据混乱。
  3. 参数核心用法:① dropna的subset参数精准控制需检测的关键字段;② merge的on参数需确保关键字段无格式差异;③ groupby的as_index=False让分组列作为普通列,便于后续格式化。
  4. 业务适配:异常温度范围、缺失值处理方式需严格遵循冷链业务规范,不可脱离业务随意处理(如冷冻与冷藏温度标准不同,需针对性调整条件)。

三、练习题

练习题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 生成)

添加新评论