
简介面向数据分析初学者、电商运营及风控从业者这份资源以欺诈性电子商务交易数据集为对象完整演示了从数据清洗、探索性分析、可视化呈现到机器学习建模的闭环流程可帮助读者理解如何从海量交易中精准识别异常行为。压缩包共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_idstring交易流水号主键不进入特征user_idstring脱敏用户标识分组统计频率trans_timedatetime交易发生时间提取小时/星期/节假日trans_amountfloat交易金额金额分布与离群点merchant_typestring商户行业类别类别对比与热力图pay_channelstring支付渠道渠道偏好分析is_fraudint是否欺诈标签用于着色与标注这张表的价值在于让后续所有绘图和特征工程都围绕同一组业务语义展开不至于画完图才发现user_id被当成数值特征用了。提示如果数据集里没有时间列欺诈交易可视化会少掉最重要的一维。至少要有交易序号或累计时间否则只能做截面分析。2.2 缺失值、重复值和标签分布处理欺诈检测数据集通常正负样本极不平衡“是否欺诈”里 1 的比例可能只有 0.5% 到 2%所以预处理阶段就要先量化这种不平衡后面所有可视化颜色映射和采样策略都依赖这个比例。看分布用value_counts(normalizeTrue)不要只看绝对数量。# 缺失值情况 print(df.isnull().sum()) # 重复交易按交易ID剔除 df df.drop_duplicates(subsettrans_id, keepfirst) # 标签分布与占比 label_dist df[is_fraud].value_counts().rename({0: 正常, 1: 欺诈}) print(label_dist) print(欺诈占比%.4f%% % (df[is_fraud].mean() * 100))drop_duplicates的keepfirst表示保留第一条重复记录这里的前提是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_onuser_id, right_indexTrue, howleft)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_loglog(1 金额)箱线图、散点图X/Y轴amount_deviation金额 / 用户均值离群点突出显示quick_seq1小时内是否有连续交易热力图的行/列分组prev_txn_count该用户历史累计交易数散点图X轴观察频率边界3.2 时间规律与渠道交叉这是欺诈占比的放大镜单一时间维度画出的图往往是一根趋势线把欺诈单独拉出来之后形状可能跟正常交易高度重合。真正有区分度的是时间与渠道或商户类型交叉后的条件分布例如凌晨的珠宝类交易、新设备登入后的第一笔交易。数据集中如果有设备标识或IP区域通常直接作为字符串用但可视化时更适合先做交叉聚合。# 按“小时支付渠道”透视欺诈发生率 pivot df.pivot_table( indexhour, columnspay_channel, valuesis_fraud, aggfuncmean ) # 按“星期商户类型”统计欺诈笔数 cross pd.crosstab(df[weekday], df[merchant_type], valuesdf[is_fraud], aggfunccount)pivot_table的valuesis_fraud可以直接求每个小时在各个支付渠道上的欺诈占比颜色深浅就能区分风险时段和渠道组合。crosstab适合统计频次矩阵里的每个格子代表一类星期-商户组合的交易量后续在单元格上叠加欺诈占比做双重编码比单独两张图有效得多。欺诈交易的可视化分析不是只看欺诈组本身还要看正常交易的基座。比如凌晨 3 点欺诈占比高但可能这个时段正常交易量也小占比高是小样本下的波动。所以交叉表里最好同时记录“组内总笔数”只给欺诈占比不给样本量很容易被小类别误导。下表是交叉分析时建议固化的三种聚合口径聚合目标透视函数关键参数注意点渠道欺诈占比pivot_tableaggfuncmean需要同时算count时段交易量crosstabaggfunccount观察基数波动双维度欺诈频次pivot_tablevaluesis_fraud, aggfuncsum用于热力图的颜色值3.3 类别不平衡处理采样方式会直接影响可视化结论常见的处理方式是随机欠采样正常样本让正常与欺诈比例为 1:1 或 2:1 再做建模。这个操作对机器学习模型很有效但不能直接用于可视化全局分布图。欠采样之后的“欺诈占比”已经被人工放大画出的密度曲线会让人觉得欺诈在总交易中占比很高这跟业务事实不一致。更推荐的做法是全局分布图用原始数据画只把欺诈点用不同颜色强调建模前的训练数据单独做采样采样后的数据只用来做模型相关的散点图。如果需要用欠采样后的数据观察两类样本的特征边界图上要明确标注“采样后样本”。# 仅用于训练集的欠采样不影响全量可视化 fraud_df df[df[is_fraud] 1] normal_df df[df[is_fraud] 0].sample(nlen(fraud_df), random_state42) balanced_df pd.concat([fraud_df, normal_df], axis0).sample(frac1, random_state42) # 从全量数据中单独抽样一份给交互式可视化避免浏览器卡顿 sample_df df.sample(n20000, random_state42, replaceFalse)random_state固定后同一份数据每次抽样结果一致调试图形时不会因为随机波动怀疑参数。交互式可视化如果直接画几十万点浏览器会非常卡所以先抽样 2 万点看全局趋势确定模式后再回原始数据做精确统计。这里要特别注意sample(n20000)如果原始欺诈样本不足 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(datadf, xis_fraud, yamount_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(datadf[df[trans_amount] cutoff], xis_fraud, ytrans_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], alpha0.3, label总交易笔数) ax1.set_xlabel(小时) ax1.set_ylabel(总笔数) ax2 ax1.twinx() ax2.plot(hourly.index, hourly[fraud_rate], color#d7191c, markero, 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, annotTrue, fmt.2f, cmapRdBu_r, center0, xticklabelsfeature_cols, yticklabelsfeature_cols) plt.title(特征相关矩阵) plt.show()sns.heatmap的center0让零相关显示为白色正负相关分别用暖色和冷色annotTrue直接把数值打在格子里。看这张图的重点是找与is_fraud相关度较高或与业务理解冲突的列比如user_count和quick_seq如果相关系数过高它们本质上在表达同一信息后续建模时可以只保留一个特征。相关矩阵不能发现非线性关系。有时候欺诈样本只在“金额偏离倍数大于 3 且 hour 大于 22”这个矩形区域内聚集这种模式画二维散点图才看得出来。4.4 交互式散点图与降维投影静态图适合写进报告但排查具体异常交易时交互式散点图效率更高。用plotly.express.scatter可以快速生成一个带悬停信息的二维散点图。import plotly.express as px fig px.scatter( sample_df, xuser_count, yamount_log, coloris_fraud, color_continuous_scale[#7fcdbb, #f03b20], sizetrans_amount, hover_data[hour, merchant_type, pay_channel], opacity0.5 ) fig.update_layout( xaxis_title用户历史交易笔数, yaxis_titlelog(交易金额1) ) fig.show()size用来映射交易金额点越大表示金额越高hover_data是鼠标悬停时额外显示的字段。要注意color在 plotly 里默认是连续色条即便is_fraud只有 0 和 1也会显示出渐变色更清晰的做法是先把is_fraud转成字符串类型让 color 变成离散分类。高维特征可以用 PCA 降维后再散点但 PCA 只保留方差大的方向不一定区分欺诈。如果降维图上两组完全重叠不代表特征没有区分度可能是主成分方向不对这时可以试 t-SNE但n_components2的 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( totalcount, fraudsum ) rule_metrics[precision] rule_metrics[fraud] / rule_metrics[total] print(rule_metrics)上面这段代码直接在原始数据上回测规则输出的precision表示命中规则的交易里有多大比例是真实欺诈。规则召回率可以用“命中规则的欺诈数 / 总欺诈数”来计算一般以精确率和召回率的平衡点决定临界值。这种从“图上框一块区域”到“规则回测”的过程是可视化分析区别于普通报表的关键。进阶技巧是做一个可复用的可视化探查模板把特征列、目标列、时间列作为参数写成一个函数之后换一份数据只要改几行配置就能输出同一套图。函数内部注意两个细节一是所有图表标题要带上样本量和欺诈占比避免看图的人误会绝对数量二是交互式图形导出为 HTML 时把规则边界用add_shape画成矩形或虚线业务人员可以直接看到命中区域。如果后续要做机器学习模型这些可视化发现可以直接作为特征筛选依据。用shap验证模型时如果某个特征的重要性排序与可视化里观察到的分布分离度不一致优先检查是否存在特征泄露比如交易 ID 被当作数值读入或时间字段的预测目标被错误包含在了特征中。建议把这个可视化探查模板连同规则回测脚本沉淀到项目目录的viz_report/下下次拿到新数据集先跑一遍可视化函数再决定要不要做特征工程。本文还有配套的精品资源点击获取