4. 数据加载:使用 pandas 读取 CSV / Excel 格式的保单与理赔数据
数据加载是赔付率监控与尖峰识别的基础环节。在实际生产环境中,保单数据与理赔数据通常以 CSV 或 Excel 格式存储在数据仓库或本地文件系统中。本节将演示如何使用 pandas 库高效、稳定地加载这两种格式的数据,并处理常见的加载异常。
4.1 数据源结构概览
在开始编码之前,需要明确两个核心数据集的字段结构。下表列出了典型的保单主表与理赔明细表的关键字段:
| 数据集 | 字段名 | 类型 | 说明 |
|---|---|---|---|
| 保单主表 | policy_id |
字符串 | 保单唯一标识 |
underwrite_date |
日期 | 承保日期 | |
premium |
浮点数 | 保费金额 | |
product_line |
字符串 | 产品线(如车险、健康险) | |
| 理赔明细表 | claim_id |
字符串 | 理赔案件唯一标识 |
policy_id |
字符串 | 关联保单号 | |
claim_date |
日期 | 出险日期 | |
claim_amount |
浮点数 | 赔付金额 |
4.2 使用 pandas 读取 CSV 文件
CSV 是精算分析中最常见的文本格式。使用 pd.read_csv() 时,需要关注编码、分隔符、日期解析和数据类型优化。以下代码演示了标准加载流程:
import pandas as pd
# 读取保单数据
df_policy = pd.read_csv(
'policy_data.csv',
encoding='utf-8',
sep=',',
parse_dates=['underwrite_date'],
dtype={
'policy_id': 'string',
'premium': 'float64',
'product_line': 'category'
}
)
# 读取理赔数据
df_claim = pd.read_csv(
'claim_data.csv',
encoding='utf-8',
sep=',',
parse_dates=['claim_date'],
dtype={
'claim_id': 'string',
'policy_id': 'string',
'claim_amount': 'float64'
}
)
print(f"保单数据加载完成:{df_policy.shape[0]} 条记录")
print(f"理赔数据加载完成:{df_claim.shape[0]} 条记录")
精算实践提示: 对于百万级以上的保单数据,建议在
read_csv 中指定 dtype 字典,避免 pandas 自动推断类型带来的内存膨胀。将 product_line 设为 category 类型可显著降低内存占用。
4.3 使用 pandas 读取 Excel 文件
Excel 文件在业务部门中广泛使用,但加载时需注意多工作表、合并单元格和空行问题。推荐使用 pd.read_excel() 配合 sheet_name 参数:
# 读取 Excel 中的指定工作表
df_policy_excel = pd.read_excel(
'policy_data.xlsx',
sheet_name='保单信息',
header=0,
usecols='A:F',
dtype={
'policy_id': 'string',
'premium': 'float64'
}
)
# 读取理赔工作表,跳过前 2 行说明行
df_claim_excel = pd.read_excel(
'claim_data.xlsx',
sheet_name='理赔明细',
skiprows=2,
parse_dates=['claim_date']
)
print(f"Excel 保单数据加载完成:{df_policy_excel.shape[0]} 条记录")
print(f"Excel 理赔数据加载完成:{df_claim_excel.shape[0]} 条记录")
常见陷阱: Excel 文件中可能存在合并单元格,读取后会产生 NaN 值。建议在加载后使用
df.fillna(method='ffill') 进行前向填充,或要求业务方在导出前取消合并。
4.4 数据加载后的快速验证
加载完成后,必须执行一组快速验证,确保数据完整性。以下为推荐的验证清单:
- 记录数核对: 使用
df.shape确认行数与源文件一致。 - 缺失值检查: 使用
df.isnull().sum()定位关键字段的缺失情况。 - 日期范围确认: 使用
df['claim_date'].min()和.max()检查时间跨度是否合理。 - 唯一性校验: 使用
df['policy_id'].nunique()检查保单主键是否重复。
# 快速验证示例
print("缺失值统计:")
print(df_policy.isnull().sum())
print("\n理赔日期范围:")
print(f"最早:{df_claim['claim_date'].min()}")
print(f"最晚:{df_claim['claim_date'].max()}")
print("\n保单唯一性:")
print(f"唯一保单数:{df_policy['policy_id'].nunique()}")
print(f"总记录数:{df_policy.shape[0]}")
4.5 数据加载流程架构
下图展示了从原始文件到内存 DataFrame 的完整加载流程,包含异常处理与验证环节:
4.6 常见加载异常与处理策略
在实际项目中,数据加载阶段常遇到以下三类异常。建议在代码中统一捕获并记录日志:
| 异常类型 | 典型原因 | 处理策略 |
|---|---|---|
UnicodeDecodeError |
文件编码非 UTF-8(如 GBK) | 使用 encoding='gbk' 或 encoding='ISO-8859-1' 回退 |
ParserError |
CSV 中存在不规则引号或分隔符 | 设置 quoting=csv.QUOTE_NONE 或 error_bad_lines=False |
ValueError |
日期列包含无法解析的格式 | 使用 errors='coerce' 将无效日期转为 NaT |
import logging
logging.basicConfig(level=logging.INFO)
def safe_read_csv(filepath, **kwargs):
"""带异常处理的 CSV 加载函数"""
try:
df = pd.read_csv(filepath, **kwargs)
logging.info(f"成功加载 {filepath},共 {df.shape[0]} 行")
return df
except UnicodeDecodeError:
logging.warning(f"UTF-8 解码失败,尝试 GBK 编码: {filepath}")
kwargs['encoding'] = 'gbk'
return pd.read_csv(filepath, **kwargs)
except Exception as e:
logging.error(f"加载失败: {filepath},错误: {str(e)}")
raise
最佳实践: 在精算生产环境中,建议将数据加载封装为独立的函数模块,统一管理编码、日志和异常回退策略。这能大幅减少因数据源格式变化导致的管道中断。