简介:面向数据分析初学者、电商运营及风控从业者,这份资源以欺诈性电子商务交易数据集为对象,完整演示了从数据清洗、探索性分析、可视化呈现到机器学习建模的闭环流程,可帮助读者理解如何从海量交易中精准识别异常行为。压缩包共4个文件,包括两个CSV交易数据集、一份Jupyter Notebook代码文件及一份HTML可视化报告,整体大小约140MB。其中CSV数据涵盖交易时间戳、金额、账户信息、支付方式等关键字段;Notebook按步骤记录了数据预处理、特征探索与模型构建的完整代码,HTML文件则可直接查看交互式图表与结论,便于对照学习交易金额分布、时间规律和欺诈特征。已有286人学习下载,适合希望快速上手数据可视化分析、并进一步尝试构建欺诈识别模型的读者。
1. 欺诈交易可视化分析,先把“异常”定义清楚
一份“欺诈性电子商务交易数据集可视化分析(数据集+代码).rar”解压后,通常是一张交易明细表加几个脚本文件,但这张表不会告诉你哪一列能直接画图、哪一个字段能直接进模型。真实的欺诈交易可视化,核心不是把代码跑通,而是先回答三个问题:正常交易长什么样、欺诈交易偏离了哪些特征、这种偏离能不能用图表稳定地暴露出来。这篇案例就从字段清洗开始,到金额、时间、频次三维度的特征加工,再到箱线图、时间序列、相关矩阵和降维图的具体参数设置,最后把可视化发现固化成可验证的业务规则。新手可以照跑命令,老手能重点看采样与特征泄露的坑。
2. 欺诈性电子商务交易数据集的结构与预处理
拿到数据集先别急着导入模型。欺诈交易数据集通常由交易流水表和标签构成,标签字段就是“是否欺诈”,但交易表里往往混着脱敏的用户ID、时间戳、金额、商户类别、支付渠道、设备指纹、IP归属地等字段。不同来源的数据集字段命名差异很大,有的把时间列叫做trans_time,有的叫datetime,所以第一件事是统一字段语义。
2.1 字段识别与类型检查:先看清每一列的业务含义
常见做法是先用 pandas 读入并查看每列的类型、非空数量和唯一值数量。下面这段代码可以直接作为起点:
import pandas as pd # 读取原始交易表,并尝试解析时间列 df = pd.read_csv("ecommerce_fraud.csv", parse_dates=["trans_time"]) # 输出列名、非空计数和数据类型 print(df.info()) # 查看分类字段的基数,基数过高多半是脱敏ID for col in df.select_dtypes(include=["object"]).columns: print(col, df[col].nunique())parse_dates参数会在读取时把指定列转为datetime64,比读进来之后再pd.to_datetime更容易发现格式问题。df.select_dtypes(include=["object"])用于挑出文本列,对脱敏数据集来说,文本列通常只有两类:一类是用户ID、交易ID这类高基数主键,另一类是渠道、设备类型这类低基数枚举。主键列后续要作为分组键而不是特征,枚举列才考虑做编码或交叉分析。
拿到字段清单后,建议先人工维护一张字段字典,记录业务含义、类型、是否可用于建模。例如下面这种形式:
| 字段名 | 类型 | 业务含义 | 可视化/建模用途 |
|---|---|---|---|
| trans_id | string | 交易流水号 | 主键,不进入特征 |
| user_id | string | 脱敏用户标识 | 分组统计频率 |
| trans_time | datetime | 交易发生时间 | 提取小时/星期/节假日 |
| trans_amount | float | 交易金额 | 金额分布与离群点 |
| merchant_type | string | 商户行业类别 | 类别对比与热力图 |
| pay_channel | string | 支付渠道 | 渠道偏好分析 |
| is_fraud | int | 是否欺诈 | 标签,用于着色与标注 |
这张表的价值在于让后续所有绘图和特征工程都围绕同一组业务语义展开,不至于画完图才发现user_id被当成数值特征用了。
提示:如果数据集里没有时间列,欺诈交易可视化会少掉最重要的一维。至少要有交易序号或累计时间,否则只能做截面分析。
2.2 缺失值、重复值和标签分布处理
欺诈检测数据集通常正负样本极不平衡,“是否欺诈”里 1 的比例可能只有 0.5% 到 2%,所以预处理阶段就要先量化这种不平衡,后面所有可视化颜色映射和采样策略都依赖这个比例。看分布用value_counts(normalize=True),不要只看绝对数量。
# 缺失值情况 print(df.isnull().sum()) # 重复交易按交易ID剔除 df = df.drop_duplicates(subset="trans_id", keep="first") # 标签分布与占比 label_dist = df["is_fraud"].value_counts().rename({0: "正常", 1: "欺诈"}) print(label_dist) print("欺诈占比:%.4f%%" % (df["is_fraud"].mean() * 100))drop_duplicates的keep="first"表示保留第一条重复记录,这里的前提是trans_id全局唯一;如果数据源没有交易ID,需要用user_id + trans_time + trans_amount多列组合判断重复。缺失值处理要看列的业务属性:时间戳缺失直接丢弃这条记录,金额缺失可以视为异常,但更稳妥的做法是剔除,因为欺诈检测里“金额为空”本身也可能是一个特征。
标签分布打印出来后,如果欺诈占比超过 10%,要怀疑采样方式是否已经做过向下采样,这会影响后面可视化的绝对频次判断。可视化时建议同时保留原始占比和抽样后的占比两个口径。
2.3 时间字段解析与交易窗口特征
欺诈行为有很强的时间模式,比如凌晨的娱乐类商户交易、同一用户短时间内连续多笔小额交易。所以在进入可视化之前,先把时间字段拆出小时、星期、是否节假日等周期特征,再按用户维度聚合出频率特征。
# 从交易时间提取周期特征 df["hour"] = df["trans_time"].dt.hour df["weekday"] = df["trans_time"].dt.weekday # 周一为0 df["is_weekend"] = df["weekday"] >= 5 # 按用户统计过去24小时内的交易次数 df_sorted = df.sort_values("trans_time") df["prev_txn_count"] = df_sorted.groupby("user_id")["trans_id"].cumcount() # 聚合用户历史频率与金额统计 user_stats = df.groupby("user_id").agg( user_mean=("trans_amount", "mean"), user_max=("trans_amount", "max"), user_count=("trans_id", "count") ) df = df.merge(user_stats, left_on="user_id", right_index=True, how="left")cumcount在分组内按时间排序后给每笔交易编号,直接得到“这是该用户今天的第几笔交易”。注意步骤是先排序再分组,否则累计顺序不反映时间先后。groupby聚合出的user_mean、user_max、user_count是后续金额与频率组合特征的基础,也是散点图里很自然的 X 轴或 Y 轴。
这里的预处理并没有做复杂的标准化,因为可视化阶段更需要保留原始单位;等到建模阶段再对连续特征做标准化,避免图像上的数轴被缩放搞乱。
3. 特征工程:为可视化分析准备三个关键维度
很多数据分析案例把特征工程和可视化分成两步:先做一堆特征,再画图。实际上,对于欺诈交易这种标签稀疏的数据集,可视化的作用恰恰是反过来指导特征工程——先画图看正常与欺诈在哪个维度上分得开,再把那个维度固化成特征。这一章讲三个最常见的可作图维度:金额与频率、时间与渠道、类别不平衡下的标注策略。
3.1 金额与频率的组合特征:识别“小额试探”与“大额快刷”
欺诈交易在金额上有两种典型形态:一是小额高频试探,用来验证盗用的卡是否还能支付;二是单笔大额快刷,在风控拦截前迅速交易。仅看单笔金额无法区分这两类,必须把金额和频率组合起来。
import numpy as np # 金额对数变换,压缩长尾分布 df["amount_log"] = np.log1p(df["trans_amount"]) # 每笔交易相对该用户均值金额的偏离倍数 df["amount_deviation"] = df["trans_amount"] / (df["user_mean"] + 1e-6) # 高频率连续交易标记:1小时内同一用户交易次数大于等于3 one_hour = pd.to_timedelta("1H") df_sorted = df.sort_values(["user_id", "trans_time"]) df["quick_seq"] = df_sorted.groupby("user_id")["trans_time"].diff().lt(one_hour).astype(int)np.log1p对金额做对数加一变换,能压缩极端大额对箱线图的影响,但要注意画图时坐标轴标签需要从原始金额换算回去。amount_deviation表示单笔金额偏离用户历史均值的倍数,欺诈交易往往比正常交易偏离更大,这比绝对值更具跨用户可比性。diff().lt(one_hour)判断当前交易与上一笔交易的时间差是否小于 1 小时,得到一个 0/1 序列,用来表示“是否处于快速连续交易状态”。
做这个特征时有两个容易踩的坑:一是user_mean包含了当前这笔交易自身,正常用户偶尔的一笔大额也会拉高均值,导致偏离倍数被稀释;严谨做法是先用shift(1)排除当前行再统计。二是快速连续交易标记只区分了是否,没有区分连续几笔,实际业务里第 3 笔和第 10 笔的风险完全不同,可视化时可以进一步按cumsum累加连续次数。
下面是金额与频率维度的几个关键特征及其可视化用途:
| 特征名 | 计算方式 | 可视化用途 |
|---|---|---|
| amount_log | log(1 + 金额) | 箱线图、散点图X/Y轴 |
| amount_deviation | 金额 / 用户均值 | 离群点突出显示 |
| quick_seq | 1小时内是否有连续交易 | 热力图的行/列分组 |
| prev_txn_count | 该用户历史累计交易数 | 散点图X轴,观察频率边界 |
3.2 时间规律与渠道交叉:这是欺诈占比的放大镜
单一时间维度画出的图往往是一根趋势线,把欺诈单独拉出来之后,形状可能跟正常交易高度重合。真正有区分度的是时间与渠道或商户类型交叉后的条件分布,例如凌晨的珠宝类交易、新设备登入后的第一笔交易。数据集中如果有设备标识或IP区域,通常直接作为字符串用,但可视化时更适合先做交叉聚合。
# 按“小时+支付渠道”透视欺诈发生率 pivot = df.pivot_table( index="hour", columns="pay_channel", values="is_fraud", aggfunc="mean" ) # 按“星期+商户类型”统计欺诈笔数 cross = pd.crosstab(df["weekday"], df["merchant_type"], values=df["is_fraud"], aggfunc="count")pivot_table的values="is_fraud"可以直接求每个小时在各个支付渠道上的欺诈占比,颜色深浅就能区分风险时段和渠道组合。crosstab适合统计频次,矩阵里的每个格子代表一类星期-商户组合的交易量,后续在单元格上叠加欺诈占比做双重编码,比单独两张图有效得多。
欺诈交易的可视化分析不是只看欺诈组本身,还要看正常交易的基座。比如凌晨 3 点欺诈占比高,但可能这个时段正常交易量也小,占比高是小样本下的波动。所以交叉表里最好同时记录“组内总笔数”,只给欺诈占比不给样本量,很容易被小类别误导。
下表是交叉分析时建议固化的三种聚合口径:
| 聚合目标 | 透视函数 | 关键参数 | 注意点 |
|---|---|---|---|
| 渠道欺诈占比 | pivot_table | aggfunc="mean" | 需要同时算count |
| 时段交易量 | crosstab | aggfunc="count" | 观察基数波动 |
| 双维度欺诈频次 | pivot_table | values="is_fraud", aggfunc="sum" | 用于热力图的颜色值 |
3.3 类别不平衡处理:采样方式会直接影响可视化结论
常见的处理方式是随机欠采样正常样本,让正常与欺诈比例为 1:1 或 2:1 再做建模。这个操作对机器学习模型很有效,但不能直接用于可视化全局分布图。欠采样之后的“欺诈占比”已经被人工放大,画出的密度曲线会让人觉得欺诈在总交易中占比很高,这跟业务事实不一致。
更推荐的做法是:全局分布图用原始数据画,只把欺诈点用不同颜色强调;建模前的训练数据单独做采样,采样后的数据只用来做模型相关的散点图。如果需要用欠采样后的数据观察两类样本的特征边界,图上要明确标注“采样后样本”。
# 仅用于训练集的欠采样,不影响全量可视化 fraud_df = df[df["is_fraud"] == 1] normal_df = df[df["is_fraud"] == 0].sample(n=len(fraud_df), random_state=42) balanced_df = pd.concat([fraud_df, normal_df], axis=0).sample(frac=1, random_state=42) # 从全量数据中单独抽样一份给交互式可视化,避免浏览器卡顿 sample_df = df.sample(n=20000, random_state=42, replace=False)random_state固定后,同一份数据每次抽样结果一致,调试图形时不会因为随机波动怀疑参数。交互式可视化如果直接画几十万点,浏览器会非常卡,所以先抽样 2 万点看全局趋势,确定模式后再回原始数据做精确统计。这里要特别注意,sample(n=20000)如果原始欺诈样本不足 2 万,正常样本会被大量带入,欺诈点占比变小但绝对数量不变,需要在图上标注抽样比例。
4. 可视化实战:用箱线图、热力图和降维图把欺诈画出来
这一章直接进入绘图,也是整个 python 数据分析与可视化案例里最有信息量的部分。选择图表类型的原则是:单变量分布用箱线图和直方图,双变量关系用散点图和折线图,多变量相关性用热力图,高维结构用降维投影。所有图都要带上is_fraud的颜色映射。
4.1 交易金额分布:箱线图与分位数噪声过滤
先画原始金额的箱线图,大概率金额长尾分布会让图形变成一根扁平的盒子,欺诈点全部挤在顶部。两种解决办法:一是对金额做对数变换,二是先截断到 95% 分位数再画。
import matplotlib.pyplot as plt import seaborn as sns plt.figure(figsize=(10, 5)) plt.subplot(1, 2, 1) sns.boxplot(data=df, x="is_fraud", y="amount_log", palette=["#7fcdbb", "#f03b20"]) plt.xlabel("是否欺诈(0=正常,1=欺诈)") plt.ylabel("log(交易金额+1)") plt.subplot(1, 2, 2) cutoff = df["trans_amount"].quantile(0.95) sns.violinplot(data=df[df["trans_amount"] <= cutoff], x="is_fraud", y="trans_amount", palette=["#7fcdbb", "#f03b20"]) plt.xlabel("是否欺诈(0=正常,1=欺诈)") plt.ylabel("交易金额(截断95%分位)") plt.tight_layout() plt.show()箱线图的中位数和四分位距能直观对比两组分布的集中趋势。palette参数里用了两种对比色,正常用青绿,欺诈用红,这是为了色弱用户也能区分,不只是为了好看。violinplot比箱线图多展示核密度形状,截断 95% 分位数保证图形主体不被极端值压扁,但截断线最好在标题或说明里写清楚。
实际看图的顺序是:先看欺诈组的中位数是否偏离正常组,再看欺诈组的上须和离群点集中在哪个区间。如果两组分布完全重叠,说明金额单维度对欺诈没有辨识力,需要进入时间或频率维度。
4.2 时间序列聚合:识别欺诈的波段规律
欺诈交易往往在某些时段聚集,比如活动开始后的数分钟内被批量试探。把时间列按小时和日期聚合,画出欺诈笔数和欺诈占比两条线,能快速定位风险窗口。
# 按小时统计欺诈笔数和占比 hourly = df.groupby("hour").agg( txn_count=("trans_id", "count"), fraud_count=("is_fraud", "sum") ) hourly["fraud_rate"] = hourly["fraud_count"] / hourly["txn_count"] fig, ax1 = plt.subplots(figsize=(10, 4)) ax1.bar(hourly.index, hourly["txn_count"], alpha=0.3, label="总交易笔数") ax1.set_xlabel("小时") ax1.set_ylabel("总笔数") ax2 = ax1.twinx() ax2.plot(hourly.index, hourly["fraud_rate"], color="#d7191c", marker="o", label="欺诈占比") ax2.set_ylabel("欺诈占比") plt.title("各小时交易量与欺诈占比") plt.show()这里用twinx()创建共享 X 轴的第二个 Y 轴,让柱状图和折线图的量纲互不干扰。需要特别注意的是,欺诈占比在小样本时段会剧烈波动,比如凌晨 4 点总笔数只有几十笔,欺诈占比可能高达 10%,这时不能直接下结论,要结合左侧柱状图的总笔数一起看。更稳健的做法是计算“每万笔交易欺诈数”而不是百分比。
如果数据集时间跨度大,还可以按“天”做同样的聚合,观察欺诈是否与促销日或系统发布时间相关。这个图表也能反馈给业务团队,用于排班和风控策略调整。
4.3 相关矩阵与特征筛选
数值特征之间的相关性能帮助判断哪些变量应该同时出现在散点图里,但欺诈数据集里因为类别不平衡,标签与单个特征的线性相关通常非常低,所以热力图只做辅助筛选,不做最终结论。
# 选择需要检查相关性的数值列 feature_cols = ["trans_amount", "amount_log", "hour", "weekday", "user_mean", "user_count", "amount_deviation", "quick_seq", "is_fraud"] corr = df[feature_cols].corr() plt.figure(figsize=(8, 6)) sns.heatmap(corr, annot=True, fmt=".2f", cmap="RdBu_r", center=0, xticklabels=feature_cols, yticklabels=feature_cols) plt.title("特征相关矩阵") plt.show()sns.heatmap的center=0让零相关显示为白色,正负相关分别用暖色和冷色,annot=True直接把数值打在格子里。看这张图的重点是找与is_fraud相关度较高或与业务理解冲突的列,比如user_count和quick_seq如果相关系数过高,它们本质上在表达同一信息,后续建模时可以只保留一个特征。
相关矩阵不能发现非线性关系。有时候欺诈样本只在“金额偏离倍数大于 3 且 hour 大于 22”这个矩形区域内聚集,这种模式画二维散点图才看得出来。
4.4 交互式散点图与降维投影
静态图适合写进报告,但排查具体异常交易时,交互式散点图效率更高。用plotly.express.scatter可以快速生成一个带悬停信息的二维散点图。
import plotly.express as px fig = px.scatter( sample_df, x="user_count", y="amount_log", color="is_fraud", color_continuous_scale=["#7fcdbb", "#f03b20"], size="trans_amount", hover_data=["hour", "merchant_type", "pay_channel"], opacity=0.5 ) fig.update_layout( xaxis_title="用户历史交易笔数", yaxis_title="log(交易金额+1)" ) fig.show()size用来映射交易金额,点越大表示金额越高,hover_data是鼠标悬停时额外显示的字段。要注意color在 plotly 里默认是连续色条,即便is_fraud只有 0 和 1,也会显示出渐变色;更清晰的做法是先把is_fraud转成字符串类型,让 color 变成离散分类。
高维特征可以用 PCA 降维后再散点,但 PCA 只保留方差大的方向,不一定区分欺诈。如果降维图上两组完全重叠,不代表特征没有区分度,可能是主成分方向不对;这时可以试 t-SNE,但n_components=2的 t-SNE 结果受perplexity影响很大,欺诈样本很少时要把perplexity调小,比如 15 到 30 之间,否则每个簇里都是稀疏的点。
5. 可视化结果反哺模型:把图表里的规律固化成业务规则
可视化分析不是为了画图而画图,最终要落地成规则或模型特征。一个常见做法是画完散点图后,在图上框出欺诈集中的区域,再把区域边界转成规则条件。例如可视化发现欺诈点大量聚集在“凌晨 0 点到 4 点、金额对数大于 5、用户历史交易笔数小于 5”的区域,那就可以把这三条写成一条业务规则。
# 根据可视化圈定的区域生成规则 rule_mask = ( (df["hour"].isin([0, 1, 2, 3, 4])) & (df["amount_log"] > 5) & (df["user_count"] < 5) ) # 规则命中情况 rule_df = pd.DataFrame({"is_fraud": df["is_fraud"], "rule_hit": rule_mask}) rule_metrics = rule_df.groupby("rule_hit")["is_fraud"].agg( total="count", fraud="sum" ) rule_metrics["precision"] = rule_metrics["fraud"] / rule_metrics["total"] print(rule_metrics)上面这段代码直接在原始数据上回测规则,输出的precision表示命中规则的交易里有多大比例是真实欺诈。规则召回率可以用“命中规则的欺诈数 / 总欺诈数”来计算,一般以精确率和召回率的平衡点决定临界值。这种从“图上框一块区域”到“规则回测”的过程,是可视化分析区别于普通报表的关键。
进阶技巧是做一个可复用的可视化探查模板:把特征列、目标列、时间列作为参数,写成一个函数,之后换一份数据只要改几行配置就能输出同一套图。函数内部注意两个细节:一是所有图表标题要带上样本量和欺诈占比,避免看图的人误会绝对数量;二是交互式图形导出为 HTML 时,把规则边界用add_shape画成矩形或虚线,业务人员可以直接看到命中区域。
如果后续要做机器学习模型,这些可视化发现可以直接作为特征筛选依据。用shap验证模型时,如果某个特征的重要性排序与可视化里观察到的分布分离度不一致,优先检查是否存在特征泄露,比如交易 ID 被当作数值读入,或时间字段的预测目标被错误包含在了特征中。建议把这个可视化探查模板连同规则回测脚本沉淀到项目目录的viz_report/下,下次拿到新数据集先跑一遍可视化函数,再决定要不要做特征工程。
本文还有配套的精品资源,点击获取