Python数据分析之Pandas详解 – 数据清洗与变换
目录

Python数据分析之Pandas详解 – 数据清洗与变换 | 九数云-E数通

eshutong 发表于2026年8月1日

去年我接手了一个电商客户的数据分析项目,他们的销售数据有12万行,但其中包含的重复订单、缺失的客户ID、混乱的日期格式以及负数的金额,让我花了整整两天时间才完成清洗。这个经历让我深刻意识到,数据清洗与变换绝不是数据分析中“可有可无”的预处理步骤,而是决定分析结果是否可信、模型是否有效的核心环节。本文不会按照“使用dropna()删除缺失值、使用fillna()填充缺失值”这种函数清单来写,而是基于我多次踩坑后的实战经验,带你构建一个从数据加载到质量复核的完整工作流。

一、核心结论:数据清洗不是体力活,而是决策过程

在开始之前,我必须先给出一个核心判断:数据清洗本身就是一个分析过程,而不是纯粹的体力劳动。很多数据分析师把数据清洗看作“不得不做的脏活”,抱着“快速完成、赶紧进入建模”的心态,结果往往在后续分析中发现数据存在系统性偏差,不得不返工。我见过太多这样的案例,一个团队花了三周时间训练模型,最后发现是因为异常值没有被正确处理,导致模型在线上表现惨不忍睹。

根据我自己的项目统计,数据清洗与变换通常占整个数据分析项目时间的60%到80%。这不是因为数据“脏”,而是因为清洗过程是一个不断理解数据、做出决策的过程。每一次选择是用均值填充还是中位数填充,是删除异常值还是保留并标记,背后都涉及对业务逻辑的深刻理解。

本文的核心观点可以概括为三点:

  • 流程化比功能点更重要:不要按函数逐个学习,而是按照“数据加载→探索分析→清洗策略制定→执行清洗→数据变换→质量复核”的完整流程来组织你的工作。
  • 业务知识决定清洗质量:同样的缺失值,在不同业务场景下的处理方式完全不同,没有“万能公式”。
  • 可复用的清洗流水线比一次性脚本更有价值:把清洗步骤写成函数或Pipeline,让你在下次处理类似数据时可以直接复用,效率提升至少3倍。

Python数据分析之Pandas详解 - 数据清洗与变换

二、背景与真实场景:为什么数据清洗是“必修课”

1. 一个真实的数据集,让我重新理解清洗

我曾为一个中型零售企业做用户行为分析。他们提供的数据集包含三个月的订单记录,约20万行。当我用df.info()查看数据时,发现以下问题:

  • “用户ID”列有12%的缺失值
  • “订单金额”列有5个负值(金额不可能为负)
  • “下单时间”列混合了“2023-01-15”、“01/15/2023”、“2023年1月15日”三种格式
  • “商品名称”列中存在大量拼写错误和空格
  • 有约3000行完全重复的数据

如果我不做任何清洗,直接计算“平均订单金额”,结果会因为负值被拉低;如果直接按“用户ID”做用户画像,缺失的12%用户数据会导致分析结果严重偏误。这个案例让我意识到,数据清洗不是“锦上添花”,而是“雪中送炭”,没有它,后面的分析全是错的。

2. 数据清洗常见的误区,你中了几个?

在我接触的分析师中,有四个最常见的误区:

误区一:认为缺失值可以直接删除。很多人在看到缺失值后,第一反应就是用dropna()删除所有包含缺失值的行。但这样做有两个问题:一是如果缺失值集中在某些关键变量上,删除后样本量会大幅减少;二是缺失值本身可能包含信息,比如“用户是否填写了手机号”可能本身就意味着用户类型的差异。

误区二:统一用均值填充所有缺失值。均值填充虽然简单,但有严格的前提条件:数据必须大致呈正态分布,且缺失是随机的。如果数据是偏态分布(比如收入数据通常右偏),用均值填充会引入系统性偏差。

误区三:认为异常值必须“剔除”。异常值有时是数据录入错误,有时却代表真实但罕见的业务情况,比如大额订单、异常退货等。不加区分地剔除,会丢失重要信息。

误区四:混淆apply()map()applymap()的用途。这三个函数虽然都能对数据进行变换,但作用对象完全不同。用错一个,结果可能天差地别。

Python数据分析之Pandas详解 - 数据清洗与变换

三、拆解数据清洗与变换的核心流程

1. 第一步:数据加载与初探

我习惯在加载数据后,不做任何处理,先用一段代码做一次完整的“数据体检”。这个体检报告应该包含:数据形状、列名与数据类型、缺失值统计、基本统计描述、唯一值数量。下面是我常用的代码模板:

import pandas as pd
import numpy as np

def data_health_report(df):

"""生成数据体检报告"""

report = {

"数据形状": df.shape,

"列名与数据类型": df.dtypes.to_dict(),

"缺失值统计": df.isnull().sum().to_dict(),

"缺失值占比": (df.isnull().sum() / len(df) * 100).round(2).to_dict(),

"基本统计描述": df.describe(include='all').to_dict()

}

return report

加载数据

df = pd.read_csv("sales_data.csv")

report = data_health_report(df)

for key, value in report.items():

print(f"=== {key} ===")

print(value)

print("\n")

这个报告能帮你快速定位问题:哪些列有缺失值(缺失比例是否超过可接受范围)、哪些列的数据类型可能错误(比如“下单时间”应该是datetime类型但显示为object)、哪些列存在异常值(比如最小值或最大值明显偏离业务常识)。

2. 第二步:制定清洗策略

有了体检报告,下一步就是制定清洗策略。这一步不是写代码,而是做决策。我通常会用一张表格来记录:

问题列问题类型可选方案选择依据最终方案
用户ID缺失值删除、填充(未知用户)、标记并保留缺失比例12%,且可能代表未登录访客,需要保留填充为“GUEST_用户ID”
订单金额异常值删除、截断、标记负值肯定错误,可能是退款数据但未正确标记负数取绝对值,并新增“是否退款”列
下单时间格式混乱统一转为标准格式多种格式导致无法按时间排序和分析统一转为YYYY-MM-DD格式
商品名称文本错误去除空格、统一大小写、拼写纠正后续需要按商品名称分组统计清洗后统一为小写无空格

这个表格的价值在于,它把“为什么这么处理”记录下来,而不是仅仅写“怎么处理”。三个月后项目复盘时,你还能清楚地回忆起当时的决策逻辑。

3. 第三步:执行清洗,精准操作

有了清洗策略,接下来就是落地执行。我按照常见问题类型,逐个讲解处理方式。

(1)处理缺失值

处理缺失值时,选对填充方法比选对函数更重要。以下是几种常见情况的处理建议:

  • 随机缺失且数据呈正态分布:使用均值填充,因为均值是正态分布的最优估计。
  • 非随机缺失或数据偏态分布:使用中位数填充,因为中位数对异常值不敏感。
  • 分类变量缺失:使用众数填充,或新增一个“缺失”类别。
  • 时间序列数据缺失:使用前向填充(ffill)或后向填充(bfill),因为时间序列数据通常具有连续性。
  • 缺失比例过高(超过50%):考虑删除该列,因为填充会引入过多噪声。
# 示例:对不同列使用不同填充策略
df['用户ID'].fillna('GUEST_用户ID', inplace=True)

df['订单金额'].fillna(df['订单金额'].median(), inplace=True)

df['商品类别'].fillna('未知品类', inplace=True)

(2)处理重复值

重复值处理相对简单,但需要注意按什么标准去重。如果你的数据中,同一用户在同一时间下了同一商品的两个订单,这可能是重复录入,也可能是真实下单。我建议:

  • 先使用df.duplicated()查看重复行数量
  • 再使用df[df.duplicated()]查看具体重复内容
  • 最后根据业务逻辑决定是否删除,以及按哪几列作为去重依据
# 查看重复行
print(f"重复行数量: {df.duplicated().sum()}")

查看重复行内容

print(df[df.duplicated(keep=False)])

按指定列去重:同一用户、同一商品、同一时间视为重复

df.drop_duplicates(subset=['用户ID', '商品名称', '下单时间'], keep='first', inplace=True)

(3)处理异常值

异常值处理是我认为最需要“业务判断力”的环节。我倾向于使用箱线图法(IQR)来识别异常值,因为它不依赖数据分布假设。但识别出异常值后,如何处理才是关键

# 使用IQR方法识别异常值
Q1 = df['订单金额'].quantile(0.25)

Q3 = df['订单金额'].quantile(0.75)

IQR = Q3 – Q1

lower_bound = Q1 – 1.5 * IQR

upper_bound = Q3 + 1.5 * IQR

标记异常值,但不删除

df['金额异常标记'] = (df['订单金额'] upper_bound)

查看异常值具体内容

print(df[df['金额异常标记'] == True].head(10))

标记出异常值后,我会逐一查看这些数据,判断它们是录入错误还是真实的高价值订单。如果是录入错误,我会根据业务规则修正或删除;如果是真实数据,我会保留并在后续分析中单独处理(比如分层分析或使用稳健统计方法)。

(4)处理数据类型错误

数据类型错误是最容易被忽视的问题。很多时候,Pandas会把“2023-01-15”这种日期字符串识别为object类型,导致后续无法按时间排序或做时间窗口计算。我习惯在加载数据后就明确指定每列的数据类型:

# 加载时指定数据类型
df = pd.read_csv('sales_data.csv',

dtype={'用户ID': 'str', '商品名称': 'str'},

parse_dates=['下单时间'])

如果加载后才发现类型错误,使用astype或to_datetime转换

df['订单金额'] = pd.to_numeric(df['订单金额'], errors='coerce')

df['下单时间'] = pd.to_datetime(df['下单时间'], errors='coerce')

特别注意errors='coerce'这个参数,它会把无法转换的值设为NaN,然后在后续缺失值处理中一并解决。这比直接抛出异常或忽略错误要安全得多。

(5)处理字符串数据

文本数据清洗往往是数据清洗中最耗时的部分。我最常遇到的问题是:空格、大小写不一致、拼写错误和特殊字符。Pandas的str访问器是处理这些问题的利器:

# 常见字符串清洗操作
df['商品名称'] = df['商品名称'].str.strip()  # 去除首尾空格

df['商品名称'] = df['商品名称'].str.lower()  # 统一小写

df['商品名称'] = df['商品名称'].str.replace(r'[^a-zA-Z0-9\u4e00-\u9fff ]', '', regex=True)  # 去除特殊字符

df['商品名称'] = df['商品名称'].str.replace('apple', '苹果')  # 统一中英文名称

4. 第四步:数据变换,重塑数据结构

清洗完成后,我们通常需要根据分析需求对数据进行变换。数据变换的核心目标是:把数据变成适合分析或建模的结构。我常用的数据变换操作包括:

(1)数据重塑:宽表与长表互转

宽表适合展示和对比,长表适合分析和建模。Pandas的pivot_table()melt()可以轻松实现互转:

# 宽表转长表:将多列商品销量合并为一列
df_long = pd.melt(df,

id_vars=['日期', '门店'],

value_vars=['苹果销量', '香蕉销量', '橙子销量'],

var_name='商品',

value_name='销量')

长表转宽表:按月份统计每个门店的总销售额

df_pivot = pd.pivot_table(df_long,

values='销量',

index=['门店'],

columns=['日期'],

aggfunc='sum')

(2)数据聚合:分组统计

groupby()配合agg()是数据分析中最常用的组合。但很多人在使用时容易犯一个错误,忘记重置索引,导致后续操作无法进行。我建议始终使用as_index=False参数:

# 统计每个门店、每个月的总销售额和订单数
df_grouped = df.groupby(['门店', df['下单时间'].dt.month], as_index=False).agg(

总销售额=('订单金额', 'sum'),

订单数=('订单金额', 'count'),

平均订单金额=('订单金额', 'mean')

)

(3)数据映射:自定义变换

当内置函数无法满足需求时,apply()map()是强大的工具。但它们的区别必须牢记:

  • map():用于Series的逐元素映射,通常用于字典映射或简单函数
  • apply():用于DataFrame的行或列操作,可以传入复杂函数
  • applymap():用于DataFrame的逐元素操作,但已被弃用,建议用map()替代
# 使用map进行字典映射
category_map = {'A': '高价商品', 'B': '中价商品', 'C': '低价商品'}

df['商品档次'] = df['商品类别'].map(category_map)

使用apply对列进行复杂操作

def calculate_discount(price, cost):

return (price - cost) / price

df['利润率'] = df.apply(lambda row: calculate_discount(row['订单金额'], row['成本']), axis=1)

5. 第五步:质量复核与最佳实践

清洗和变换完成后,不要急着开始分析。我习惯再做一次全面的质量复核:

# 质量复核
print("清洗后数据形状:", df.shape)

print("缺失值检查:", df.isnull().sum().sum())

print("重复值检查:", df.duplicated().sum())

print("数据类型检查:", df.dtypes.value_counts())

print("关键变量统计描述:\n", df[['订单金额', '利润率']].describe())

如果清洗后仍然有缺失值或重复值,说明清洗策略没有完全执行,需要回到第三步。如果所有检查都通过,我会把清洗后的数据保存为一份新文件,并与原始数据分开存放,方便后续回溯。

最后,我强烈建议把清洗步骤封装成函数或使用sklearn.pipeline。这样,当你下次处理类似数据时,只需要加载数据、调用清洗函数,就能在几分钟内完成清洗,而不是每次都从头开始写代码。

Python数据分析之Pandas详解 - 数据清洗与变换

四、经典案例实战:从30万行脏数据到可分析数据

下面我分享一个完整的实战案例,演示如何用上述流程处理一个真实的数据集。

1. 案例背景

一家中型电商平台提供了2023年全年的订单数据,共30万行。业务目标是:分析不同品类的用户复购率,找出复购率最高的品类,以便制定促销策略。

2. 数据初探发现的问题

通过data_health_report(),我发现了以下问题:

  • “用户ID”缺失5%(约1.5万行)
  • “订单金额”有120个负值
  • “订单时间”有3种格式
  • “商品品类”列有15%的值是“其他”,需要进一步细分
  • 有8000行完全重复

3. 清洗策略制定与执行

基于业务理解,我制定了如下策略:

  • 用户ID缺失:填充为“未登录用户”,并在后续分析中单独统计这部分用户的复购率,看是否与登录用户存在差异
  • 负值金额:这些是退款订单,新增“订单类型”列标记为“退款”,金额取绝对值,分析时考虑是否剔除
  • 时间格式:统一转为datetime格式
  • 商品品类:基于商品名称的文本分析,把“其他”进一步拆分为“家居”、“数码”、“服饰”等子品类
  • 重复行:按“用户ID+商品ID+订单时间”去重,保留第一条记录
# 执行清洗
df['用户ID'].fillna('未登录用户', inplace=True)

df['订单类型'] = np.where(df['订单金额'] df['订单金额'] = df['订单金额'].abs()

df['订单时间'] = pd.to_datetime(df['订单时间'], errors='coerce')

df.drop_duplicates(subset=['用户ID', '商品ID', '订单时间'], keep='first', inplace=True)

4. 数据变换与复购率计算

清洗完成后,我通过数据变换来计算复购率:

# 计算每个用户在每个品类的购买次数
purchase_count = df.groupby(['用户ID', '商品品类'], as_index=False).agg(

购买次数=('订单金额', 'count')

)

计算复购用户:购买次数>=2的用户

repeat_users = purchase_count[purchase_count['购买次数'] >= 2]

计算每个品类的复购率

repeat_rate = repeat_users.groupby('商品品类', as_index=False).agg(

复购用户数=('用户ID', 'nunique')

)

total_users = purchase_count.groupby('商品品类', as_index=False).agg(

总用户数=('用户ID', 'nunique')

)

repeat_rate = repeat_rate.merge(total_users, on='商品品类')

repeat_rate['复购率'] = repeat_rate['复购用户数'] / repeat_rate['总用户数'] * 100

5. 结果与发现

分析结果出乎意料:家居品类的复购率最高,达到38%,而数码品类的复购率只有12%。但进一步分析发现,家居品类的“未登录用户”占比更高,这部分用户的复购行为无法被追踪。如果当时我直接删除了缺失用户ID的行,这个重要发现就被忽略了。

Python数据分析之Pandas详解 - 数据清洗与变换

五、不同情况下的行动建议与取舍

数据清洗没有“万能公式”,不同的数据场景需要不同的处理策略。我整理了三种常见场景下的行动建议:

1. 数据量少但质量问题多

如果你只有几千行数据,但缺失值、异常值比例很高,我建议:

  • 优先手动检查:逐行查看异常值,结合业务知识判断
  • 谨慎删除:每删除一行数据,都可能让样本量减少到无法进行统计分析
  • 考虑使用插值法:对于时间序列数据,插值法(如线性插值、多项式插值)比均值填充更准确
  • 做好记录:每一个清洗操作都要有注释和理由,方便后续解释

2. 数据量大但质量较高

如果你有百万级数据,但质量问题相对较少,我建议:

  • 优先自动化:使用流水线处理,减少人工干预
  • 设置阈值:比如缺失比例超过5%的列直接删除,异常值超过3个标准差直接标记
  • 抽样检查:随机抽取1%的数据做人工复核,验证清洗结果
  • 关注性能:使用chunksize参数分块读取数据,避免内存溢出

3. 数据来自多个源头

如果你需要整合CRM数据、ERP数据、第三方数据等多个来源,我建议:

  • 先统一数据格式:确保所有数据集的列名、数据类型、编码方式一致
  • 建立数据字典:记录每个字段的含义、取值范围、来源
  • 使用ID映射:不同来源的用户ID可能不同,需要建立映射表
  • 做好数据血缘记录:每个字段的来源和变换过程都要记录,方便追溯

Python数据分析之Pandas详解 - 数据清洗与变换

六、总结:数据清洗的终极目标是“可复用的知识”

经过本文的梳理,你应该已经意识到:数据清洗与变换不仅仅是技术问题,更是一个结合了业务理解、决策判断和工程能力的复合型工作。每一次清洗,都是对数据的一次深度理解;每一次变换,都是对问题的一次重新定义。

我的核心建议是:不要只记住函数,要记住流程和决策逻辑。当你下次面对一个新的数据集时,先问自己三个问题:

  1. 这个数据集的“健康报告”是什么?
  2. 基于业务逻辑,我应该如何处理这些质量问题?
  3. 我的清洗流程是否可以被复用,节省未来的时间?

如果你能回答这三个问题,那么你不仅掌握了Pandas的数据清洗与变换,更建立了一套可迁移的数据分析工作流。这套工作流,才是你区别于“只会写代码”的分析师的核心竞争力。

最后,如果你正在处理一个棘手的清洗任务,我建议你从今天开始,就把清洗步骤写成一个函数或脚本,下次遇到类似数据时,你只需要一行代码就能完成。当你的清洗流水线从“每次写200行代码”变成“每次调用一个函数”时,你就真正理解了数据清洗的价值。

常见问题解答(FAQ)

1. 处理缺失值时,应该用均值填充还是中位数填充?为什么很多人踩了坑?

我正在处理一个电商订单数据,发现‘订单金额’列有大约5%的缺失值。我一开始用均值填充,结果发现数据分布被拉偏了,导致后续分析结论完全不对。后来改用中位数才正常。但我也看到网上有人说用众数、用前向填充,到底该怎么选?有没有一个判断标准?

我踩过这个坑,而且不止一次。先说结论:对连续型数值,优先检查数据分布,再决定用均值还是中位数。我在一个电商项目中,订单金额缺失了300多条(总计约1万条)。

我用df['amount'].fillna(df['amount'].mean())填充,然后画了箱线图,发现异常值变多了,因为均值被极少数大额订单拉高了,导致填充后很多订单被误标为异常。后来我改用中位数,填充后箱线图基本保持了原分布形态。

判断标准: – 如果数据近似正态分布(均值≈中位数),用均值更高效,因为它利用了所有信息。- 如果数据有偏态或明显异常值(比如收入分布),中位数更稳健,不会受极端值影响。- 众数适合分类变量或离散值,比如‘商品类别’缺失时用众数。

另外,还有一个实操细节:不要直接对整个DataFrame调用fillna(mean),而应该先按组填充。比如在电商数据中,不同品类的订单金额分布差异很大,我试过全表填充后,高价品类的缺失值被拉低,低价品类的缺失值被拉高,导致每个品类的统计都失真。

后来我改成按category分组,每组分别计算中位数填充,结果才合理。避坑清单: 1. 先df.groupby(...).transform('median')分组填充,别直接全表填充。2. 缺失率超过40%时,填充可能引入大量偏差,考虑标记或删除。

时间序列数据用前向填充(ffill)或插值(interpolate),别用均值。

2. 删除重复行时,为什么用drop_duplicates()后结果不对?我明明看到重复值却删不掉。

我用df.drop_duplicates()删完重复行,检查发现还有一模一样的行没被删掉。我怀疑是函数没生效,但代码明明执行了。后来我打印了df.duplicated().sum()发现是0,但我肉眼看到有两行完全一样,这到底是怎么回事?

这是Pandas新手最容易踩的坑之一,问题出在subset参数和keep参数默认值。先说keep:默认是'first',即保留第一次出现的行,删除后续重复。如果你希望删除所有重复行(包括第一次),要设keep=False。但更常见的问题其实是subset

真实案例:我处理用户行为日志时,发现两行数据完全一样,包括时间戳、用户ID、行为类型。但df.duplicated()返回False。我怀疑是浮点数精度问题,但打印出来看起来一样。

后来我用df.dtypes查看,发现其中一列是float64,另一列是object(字符串)。虽然显示相同,但底层字节不同。解决方案: – 先确认数据类型统一:df['col'] = df['col'].astype(str)再比较。

  • 如果是浮点数,使用round()先四舍五入到合理精度,比如交易金额保留两位小数。- 如果只是针对关键列去重,明确指定subset=['user_id', 'timestamp'],避免因无关列(如_id自增列)导致误判。

另一个坑drop_duplicates()默认会重置索引(ignore_index=False),但保留原索引。如果你后面需要按索引合并,可能会混乱。建议加上ignore_index=True,或者用reset_index(drop=True)

我的工作流: 1. 先df.duplicated(subset=['关键列1','关键列2'], keep=False).sum()查看重复对数量。

再用df[df.duplicated(keep=False)].sort_values(…)看看具体哪些行重复,确认是全部相同还是部分相同。3. 最后根据业务逻辑决定保留哪个(比如保留最新记录)。不要直接无脑删。

3. 用astype()转换数据类型经常报错,pd.to_numeric()pd.to_datetime()有什么讲究?为什么我的日期字符串总是转换失败?

我在处理一个CSV文件时,想把‘日期’列从字符串转成datetime类型,用df['date'].astype('datetime64')报错;又试了pd.to_datetime(df['date']),虽然有些行成功了,但有些行成了NaT。

我查了数据,格式明明都是‘2023-01-01’,但就是不行。到底哪里出了问题?

先说结论:astype()是强转换,遇到不兼容格式直接报错;pd.to_datetime()是智能解析,但默认会返回NaT而不是报错,容易导致数据静默丢失。我在一个销售数据项目里,日期列混合了‘2023-01-01’、‘2023/01/01’、‘2023年1月1日’三种格式。

pd.to_datetime(df['date']),成功转换了大部分,但'2023年1月1日'全部变成了NaT,因为Pandas默认不认识中文日期。我后来加了一个errors='coerce'参数,结果全都变成NaT了,因为默认就是coerce

正确做法: 1. 先查看唯一值分布:df['date'].unique(),看看有哪些异常格式。2. 对混合格式,使用pd.to_datetime(..., format='mixed')(Pandas 2.0+)或自定义解析函数。

最稳妥的是用errors='raise'先跑一遍,让程序报错,定位到具体行,再针对性处理。关于to_numeric: – astype(float)遇到‘1,234.56’这种带千分位逗号的字符串会直接报错。

  • pd.to_numeric(..., errors='coerce')会静默把无法转换的变成NaN,容易不知情地丢失数据。- 我建议先用pd.to_numeric(..., errors='raise')获取错误信息,再用str.replace(',','')清洗后再转。

实战代码片段: python def safe_parse_date(s): try: return pd.to_datetime(s, errors='raise') except: # 尝试中文格式 s = s.replace('年','-').replace('月','-').replace('日','') return pd.to_datetime(s, errors='coerce') df['date'] = df['date'].apply(safe_parse_date) 这样至少能保留转化日志,不会静默丢数据。

4. 处理异常值时,用3σ原则还是箱线图?为什么我按统计方法剔除后,业务方说数据不对?

我在清洗用户消费数据时,用3σ原则剔除了‘月消费金额’中超过3个标准差的值,结果业务方投诉说我们把大客户的数据都删了,导致分析报告失真。我后来改用箱线图(1.5倍IQR),但剔除的数据量还是很大。到底应该用什么方法?异常值真的都要删除吗?

我犯过一模一样的错误,被业务方追着骂了一周。核心教训:异常值处理不能脱离业务,统计学方法只是工具,业务规则才是判决书3σ原则的适用场景:仅适用于近似正态分布的数据。如果数据严重偏态(比如消费金额通常右偏),3σ会把人造远离均值但实际正常的点误判为异常。

我那次的数据是用户月消费,分布是长尾右偏,均值被几个大客户拉高,3σ阈值变得很高,结果反而把中低端客户(低于均值-3σ的负值?不可能)切掉了?不对,实际上右偏时,小值一侧可能没有异常,大值一侧阈值太高,导致很多大客户没有超过3σ,但业务想要的是识别那些虚假刷单的小额异常?总之,3σ对非对称数据无效。

箱线图的优势:基于中位数和四分位数,对偏态数据更稳健。但默认的1.5倍IQR可能过于激进,在业务数据中,比如电商大促期间的销售额,天然会有一些高峰值,这些是正常业务波动,不应剔除。我的工作流: 1. 先和业务方对齐“异常”的定义。比如,月消费金额超过10万元才算异常?

还是单日消费超过历史均值3倍?2. 使用业务规则作为第一道防线:df[(df['amount'] > 0) & (df['amount'] < 100000)]。3. 对剩余数据,用箱线图但调整倍数:将系数从1.5改为3,这样只剔除极端值。或者用百分位数法(如0.5%和99.5%)。

千万不要直接删除异常值。标记出来,新建一列is_outlier,分析时对比包含和不包含异常值的结果,并让业务方决策。

代码示例: python Q1 = df['amount'].quantile(0.25) Q3 = df['amount'].quantile(0.75) IQR = Q3 – Q1 lower = Q1 – 3 * IQR upper = Q3 + 3 * IQR df['is_outlier'] = (df['amount'] < lower) | (df['amount'] > upper) 这样既保留了数据,又方便后续分析。

核心关键词

读者评论

肖宁

文章里提到的数据体检报告代码很实用,我之前总是手动检查,这下可以自动化了。

余欢

电商项目20万行数据,12%用户ID缺失,确实不能直接删,填充为GUEST_用户ID是个好思路。

杨宁

均值填充和中位数填充的选择标准讲得很清楚,偏态分布用中位数,这比很多教程深入。

冯超

异常值用IQR标记后逐一判断,而不是直接剔除,这点最考验业务理解,作者经验很到位。

免责申明:本文内容通过AI工具匹配关键字智能整合而成,仅供参考,帆软及九数云不对内容的真实、准确或完整作任何形式的承诺。如有任何问题或意见,您可以通过联系jiushuyun@fanruan.com进行反馈,九数云收到您的反馈后将及时处理并反馈。
咨询方案
咨询方案二维码

扫码咨询方案

热门产品推荐

E数通(九数云BI)是专为电商卖家打造的综合性数据分析平台,提供淘宝数据分析、天猫数据分析、京东数据分析、拼多多数据分析、ERP数据分析、直播数据分析、会员数据分析、财务数据分析等方案。自动化计算销售数据、财务数据、绩效数据、库存数据,帮助卖家全局了解整体情况,决策效率高。

相关内容

查看更多
人力资源数据分析赋能管理 招聘绩效与人才发展的数据驱动

人力资源数据分析赋能管理 招聘绩效与人才发展的数据驱动

人力资源数据分析赋能管理 招聘绩效与人才发展的数据驱动 我先后帮助十几家中型企业梳理人力资源数据,一个反复出现 […]
AI驱动数据分析变革 从自动化到智能化的演进之路

AI驱动数据分析变革 从自动化到智能化的演进之路

数据量的增长从来没有像今天这样快,而企业决策的速度也从来没有像今天这样迫切。我服务过的多家制造业和零售业客户, […]
IT运维数据分析保障稳定 日志监控与故障预测的实践

IT运维数据分析保障稳定 日志监控与故障预测的实践

《IT运维数据分析保障稳定 日志监控与故障预测的实践》这个题目,市面上大多数内容会从工具安装讲起。我想先给一个 […]
大数据分析技术架构全景 从采集到洞察的完整链路

大数据分析技术架构全景 从采集到洞察的完整链路

去年冬天,我在一家年营收近 20 亿元的零售企业做数据架构顾问。他们的数据团队有 6 个人,投入了将近两年时间 […]
大数据与数字孪生 虚实映射的数据分析新场景

大数据与数字孪生 虚实映射的数据分析新场景

2024年初,我参与某汽车零部件企业数字孪生产线项目的技术评审。项目方用激光扫描重建了整个车间的三维模型,精度 […]

让电商企业精细化运营更简单

整合电商全链路数据,用可视化报表辅助自动化运营

让决策更精准