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 数据清洗的完整流程

好了,前面讲了去重和异常值处理。现在我把整个数据清洗的流程串起来,给你画个图:

数据清洗完整流程 步骤1:数据采集 从CRM/ERP导出 步骤2:数据合并 以代理商ID关联 步骤3:数据去重 精确+模糊匹配 步骤4:异常值检测 3σ/箱线图/业务规则 步骤5:异常值处理 删除/填充/标记 步骤6:数据验证 业务人员确认 输出 发现问题?返回步骤3或4重新处理 整个流程可能需要迭代2-3次,才能得到干净的数据

这个流程看起来简单,但实际操作中,你可能需要反复迭代。比如去重之后,发现有些异常值其实是重复数据导致的,那就得回去重新去重。

我建议你每次清洗完数据,都做一次“数据质量报告”。报告里包含:

  • 原始数据量
  • 去重后数据量
  • 异常值数量及处理方式
  • 最终可用数据量

这样做有两个好处:一是你自己心里有数,二是跟业务方沟通的时候有依据。

避坑指南:我曾经在一个项目里,数据清洗做了五轮。第一轮去重,发现重复率高达30%。第二轮异常值处理,发现有一半的代理商交易金额是空的。第三轮去问业务方,才知道是系统迁移的时候数据丢了。你说要是我不做清洗,直接用原始数据跑分级,那结果得多离谱?

4.5 数据清洗的“黄金法则”

最后,分享几条我总结的黄金法则:

  1. 先备份,再动手。 任何时候都不要直接在原始数据上操作。我习惯先复制一份,命名为“原始数据_备份_日期”。
  2. 规则要可追溯。 每一条清洗规则,都要记录下来。比如“交易金额为负数的,统一替换为0”。这样出了问题,你能找到原因。
  3. 让业务方参与。 数据清洗不是纯技术活。很多异常值,只有业务人员才知道是怎么回事。别自己闷头干。
  4. 自动化脚本。 如果你每个月都要做一次数据清洗,那就写个脚本。别每次都手动操作,太容易出错了。

嗯,数据采集与清洗这部分,就讲到这里。说白了,这就是个“脏活累活”,但也是整个代理商分级体系的地基。地基不稳,上面盖的楼再漂亮也没用。

最后提醒一句:数据清洗没有“完成”的时候。只要系统还在跑,数据就在变。所以,把清洗流程做成自动化的、可重复的,才是长久之计。


公众号:蓝海资料掘金营,微信deep3321