4. 数据采集与清洗:如何从CRM/ERP提取数据、数据去重与异常值处理
做代理商分级,最头疼的是什么?
不是模型不会搭,也不是策略定不好。而是——数据根本没法用。
我见过太多团队,花了两周搭了一套完美的分级模型,结果一跑数据,全是脏数据。CRM里代理商名称重复了三遍,ERP里的交易金额有的填了负数,有的直接空着。你说这怎么玩?
所以这一章,咱们就聊聊数据采集与清洗。说白了,就是怎么把CRM/ERP里的“垃圾”变成“黄金”。
4.1 数据采集:从CRM/ERP里“捞”数据
先说说数据从哪来。大部分公司的代理商数据,主要存在两个系统里:CRM(客户关系管理)和ERP(企业资源计划)。
CRM管的是“人”——代理商的基本信息、联系人、跟进记录、合同状态。ERP管的是“钱”——订单、发货、回款、库存。
我个人习惯,先把这两个系统的数据导出来,然后做一次“联表”。
核心思路:以代理商ID为唯一键,把CRM和ERP的数据拼在一起。
举个例子,CRM里有个字段叫“代理商名称”,ERP里也有个字段叫“客户名称”。这两个字段其实指的是同一个东西。但系统不同,命名规则可能不一样。我建议你统一用“代理商ID”作为关联字段,别用名称。为什么?因为名称太容易写错了。
我在项目中遇到过一件事:某代理商在CRM里叫“北京华信科技有限公司”,在ERP里叫“北京华信科技”。就差了“有限”两个字,系统就认为这是两家公司。你说冤不冤?
4.2 数据去重:别让同一个代理商出现两次
数据导出来后,第一件事就是去重。
去重听起来简单,但实际操作起来坑很多。你以为用“代理商名称”去重就行了?太天真了。
常见的重复情况有三种:
- 完全重复:两行数据一模一样。这个最简单,直接删掉一行就行。
- 名称相似但不同:比如“上海鼎盛贸易有限公司”和“上海鼎盛贸易”。这种需要人工判断,或者用模糊匹配算法。
- ID不同但实际是同一家:比如代理商换了营业执照,ID变了,但实际还是同一拨人。这种最麻烦,需要业务人员介入。
我一般用Python的pandas库来做去重。给你看一段我常用的代码:
import pandas as pd
# 读取数据
df_crm = pd.read_excel('crm_data.xlsx')
df_erp = pd.read_excel('erp_data.xlsx')
# 合并数据
df = pd.merge(df_crm, df_erp, on='agent_id', how='outer')
# 去重:基于代理商ID和名称
df = df.drop_duplicates(subset=['agent_id', 'agent_name'], keep='first')
# 模糊匹配:名称相似度大于0.9的视为重复
from fuzzywuzzy import fuzz
def dedup_by_name(df, threshold=90):
duplicates = []
for i in range(len(df)):
for j in range(i+1, len(df)):
score = fuzz.ratio(df.iloc[i]['agent_name'], df.iloc[j]['agent_name'])
if score >= threshold:
duplicates.append((i, j, score))
return duplicates
# 输出疑似重复的记录
dup_pairs = dedup_by_name(df)
print(f"发现 {len(dup_pairs)} 对疑似重复记录")
小技巧:去重的时候,别急着删数据。先把疑似重复的记录标记出来,让业务人员确认后再处理。我曾经因为手快删了一批“重复”数据,结果发现人家其实是两家不同的分公司……嗯,从那以后我再也不敢直接删了。
4.3 异常值处理:把“离谱”的数据揪出来
数据去重完了,接下来就是异常值处理。
什么叫异常值?就是那些明显不合理的数据。比如:
- 交易金额为负数
- 代理商成立日期是2099年
- 年销售额超过100亿(但这家公司一共就3个人)
这些数据如果不处理,会严重干扰分级结果。你想想看,一个异常值可能把一个C级代理商拉到A级,那整个分级体系就崩了。
我常用的异常值检测方法有三种:
| 方法 | 适用场景 | 说明 |
|---|---|---|
| 3σ原则 | 数据近似正态分布 | 超过均值±3倍标准差的值视为异常 |
| 箱线图法 | 数据分布未知 | 超过Q1-1.5IQR或Q3+1.5IQR的值视为异常 |
| 业务规则法 | 有明确的业务约束 | 比如“交易金额不能为负数” |
我个人最常用的是箱线图法。为什么?因为它不要求数据是正态分布,适用范围更广。
给你看一段处理异常值的代码:
# 箱线图法检测异常值
def detect_outliers_iqr(df, column):
Q1 = df[column].quantile(0.25)
Q3 = df[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]
return outliers
# 检测交易金额的异常值
outliers_amount = detect_outliers_iqr(df, 'total_amount')
print(f"交易金额异常值数量:{len(outliers_amount)}")
# 处理方式:用中位数填充
median_amount = df['total_amount'].median()
df.loc[outliers_amount.index, 'total_amount'] = median_amount
注意:异常值不一定要删除。有时候异常值本身就是有价值的信息。比如某个代理商突然有一笔超大额交易,可能是他签了一个大客户。这种情况下,你应该标记出来,而不是直接替换掉。
4.4 数据清洗的完整流程
好了,前面讲了去重和异常值处理。现在我把整个数据清洗的流程串起来,给你画个图:
这个流程看起来简单,但实际操作中,你可能需要反复迭代。比如去重之后,发现有些异常值其实是重复数据导致的,那就得回去重新去重。
我建议你每次清洗完数据,都做一次“数据质量报告”。报告里包含:
- 原始数据量
- 去重后数据量
- 异常值数量及处理方式
- 最终可用数据量
这样做有两个好处:一是你自己心里有数,二是跟业务方沟通的时候有依据。
避坑指南:我曾经在一个项目里,数据清洗做了五轮。第一轮去重,发现重复率高达30%。第二轮异常值处理,发现有一半的代理商交易金额是空的。第三轮去问业务方,才知道是系统迁移的时候数据丢了。你说要是我不做清洗,直接用原始数据跑分级,那结果得多离谱?
4.5 数据清洗的“黄金法则”
最后,分享几条我总结的黄金法则:
- 先备份,再动手。 任何时候都不要直接在原始数据上操作。我习惯先复制一份,命名为“原始数据_备份_日期”。
- 规则要可追溯。 每一条清洗规则,都要记录下来。比如“交易金额为负数的,统一替换为0”。这样出了问题,你能找到原因。
- 让业务方参与。 数据清洗不是纯技术活。很多异常值,只有业务人员才知道是怎么回事。别自己闷头干。
- 自动化脚本。 如果你每个月都要做一次数据清洗,那就写个脚本。别每次都手动操作,太容易出错了。
嗯,数据采集与清洗这部分,就讲到这里。说白了,这就是个“脏活累活”,但也是整个代理商分级体系的地基。地基不稳,上面盖的楼再漂亮也没用。
最后提醒一句:数据清洗没有“完成”的时候。只要系统还在跑,数据就在变。所以,把清洗流程做成自动化的、可重复的,才是长久之计。
公众号:蓝海资料掘金营,微信deep3321