【促销定价】背后的算法技术 2 - 数据预处理生成

news2025/2/26 12:24:46

【促销定价】背后的算法技术 2 - 数据预处理生成

    • 01 数据探查
    • 02 数据清洗
    • 03 数据聚合
    • 04 数据补全
    • 05 小结
    • 参考文献

导读:在日常生活中,我们经常会遇见线上/线下商家推出各类打折、满减、赠品、新人价、优惠券、捆绑销售等促销活动。一次成功的促销对于消费者和商家来说是双赢的。一方面,促销活动能让消费者买到低价的商品;另一方面,促销活动也能为商家带来可观的利润。因此,对于商家来说,如何科学合理地制定促销策略是必须仔细思考的问题。


作者1:张哲铭,算法专家,某互联网大厂
作者2:向杜兵,算法专家,某制造业龙头


大家好!我们是IndustryOR团队,致力于分享业界落地的OR+AI技术。欢迎关注微信公众号/知乎【运筹匠心】 。本期我们来谈一谈《促销定价背后的算法技术》。促销活动五花八门、玩法多变,但其底层的核心商业逻辑是“价格”。因此,本期案例将选择某零售商超“促销定价”场景,共分5篇文章依次讲解:(1)业务问题拆解;(2)数据预处理生成;(3)数据挖掘分析;(4)模型算法实现-价格弹性模型;(5)模型算法实现-运筹决策模型。

本篇文章讲解(2)数据预处理生成

共分为5个部分,依次为:
01 数据探查
02 数据清洗
03 数据聚合
04 数据补全
05 小结

注:本案例数据改编自【2019年全国大学生数学建模E题】公开数据集。


01 数据探查

本案例主要用到销售流水表1/2(附件1/2)和商品信息表(附件4),表结构如下:

  • 销售流水表
字段含义
order_id订单ID
is_finished订单是否完成
sku_id商品ID
sku_name商品名称
sku_prc门店价
sku_sale_prc销售价
sku_cost_prc成本价
sku_cnt销售数量
upc_codeUPC码
create_dt创建时间
  • 商品信息表
字段含义
skuidsku名称
skunamesku名称
first_category_id一级类目id
second_category_id二级类目id
third_category_id三级类目id
first_category_name一级类目名称
second_category_name二级类目名称
third_category_name三级类目名称

1)数据加载

# 读取订单明细表、sku明细表  
order_detail_df1 = pd.read_csv('附件1.txt', sep=',', encoding='utf8')  
order_detail_df2 = pd.read_csv('附件2.txt', sep=',', encoding='utf8')  
sku_detail_df = pd.read_csv('附件4.txt', sep=',', encoding='utf8')  

注:原数据文件是excel,笔者将其转成的txt,同学在复现时需要注意一下这里

2)数据概览

图片图片图片

<<< 左右滑动见更多 >>>

我们发现:销售流水表upc_code字段部分缺失,其他字段不缺失。商品信息表无缺失字段。

3)数据细节

图片


02 数据清洗

1)合并销售流水表,并拼接商品信息表

# 合并订单明细表  
order_detail_df = pd.concat(objs=[order_detail_df1, order_detail_df2], axis=0)  
# 拼接sku明细表  
data_df = pd.merge(left=order_detail_df, right=sku_detail_df, on=['sku_id','sku_name'], how='left')  

2)合并之后探查整体数据

图片我们发现:销售流水表共有1221855条订单商品(SKU)流水信息,有类目信息的有804158条。

3)异常样本过滤

首先过滤掉类目为空的SKU,再剔除掉未完单的样本,最后剔除销量、门店价、销售价、成本价为负的样本。代码如下:

# 异常样本过滤  
data_df = data_df[~data_df['third_category_id'].isnull()] # 过滤类项目信息为空的sku  
data_df = data_df[data_df['is_finished'] == 1] # 剔除未完单样本  
data_df = data_df[(data_df['sku_cnt'] >= 0) & (data_df['sku_sale_prc'] >= 0) & \  
(data_df['sku_prc'] >= 0 )  & (data_df['sku_cost_prc'] >= 0 )] # 剔除销量、价格<=0样本  


03 数据聚合

1)将SKU流水数据合并到天,销量取和,价格取均值

# 订单聚合至sku日流水  
data_df = data_df.\  
groupby(['create_dt', 'sku_id', 'sku_name', 'first_category_id', 'second_category_id', 'third_category_id', 'first_category_name', 'second_category_name', 'third_category_name']).\  
agg({'sku_cnt':'sum', 'sku_prc':'mean', 'sku_sale_prc':'mean'}).reset_index()  

2)将日期格式转换为YYYY-MM-DD格式,以便于排序

# create_dt日期格式转换  
data_df['create_dt'] = data_df.apply(func=IOR_DP.date_style_convert, axis=1)  

3)对数据进行排序,以便观察

# 排序  
data_df = data_df.sort_values(by=['sku_id', 'create_dt'], ascending=[True,True]).reset_index(drop=True,inplace=False)  

4)避开长假,截取2018-03-01至2018-09-30的数据进行研究

# 截取非长假半年时间段  
data_df = data_df[(data_df['create_dt'] >= '2018-03-01') & (data_df['create_dt'] <= '2018-09-30')]  

5)对数据统一进行重命名

# 列原名太长,重命名  
data_df = data_df.rename(columns={'first_category_id':'cate1_id', 'first_category_name':'cate1_name', \  
                                 'third_category_id':'cate3_id', 'third_category_name':'cate3_name', \  
                                 'sku_prc':'ori_prc','sku_sale_prc':'sale_prc', 'sku_cnt':'sale_cnt', 'create_dt': 'sale_dt'})  


04 数据补全

1)由于成本价缺失严重,因此进行补全操作

根据数据集提示:零售商的毛利率大概在0.2-0.4之间。因此,可先生成毛利率,然后用门店价(原价)、销售价(折扣价)反推成本价。步骤如下:

  • 假设原价相较成本价的毛利率符合正态分布,最大值为0.4,最小值为0.2;

  • 假设折扣价相较成本价的毛利率符合正态分布,最大值为0.05,最小值为0.35;

  • 成本价 = (原价反推的成本价,折扣价反推的成本价)

注:实际业务中,价格大概率不符合正态分布,需要用真实数据。

# 原数据成本价异常(大部分缺失,未缺失的与折扣价相同),无法使用,需要重新制作  
## 一个sku历史一个成本价,暂不考虑波动,简化处理  
cost_prc_df = data_df[['sku_id', 'ori_prc', 'sale_prc']].\  
groupby(['sku_id']).agg({'ori_prc':'mean', 'sale_prc':'mean'}).reset_index()   
  
## 正态分布造原价毛利N(0.3,0.05)、折扣价毛利N(0.2,0.05)  
ori_prc_profit_ratio_array = IOR_DP.generate_norm_data(mean = 0.3, std = 0.05, x_min = 0.2, x_max = 0.4, n = len(cost_prc_df))  
sale_prc_profit_ratio_array = IOR_DP.generate_norm_data(mean = 0.2, std = 0.05, x_min = 0.05, x_max = 0.35, n = len(cost_prc_df))  
  
## 制作成本价,保证要比折扣价低,不能赔本卖  
cost_prc_df['ori_prc_profit_ratio'] = ori_prc_profit_ratio_array  
cost_prc_df['sale_prc_profit_ratio'] = sale_prc_profit_ratio_array  
cost_prc_df['ori_prc_cost_prc'] = cost_prc_df['ori_prc'] / (cost_prc_df['ori_prc_profit_ratio'] + 1) # 原价反推的成本价  
cost_prc_df['sale_prc_cost_prc'] = cost_prc_df['sale_prc'] / (cost_prc_df['sale_prc_profit_ratio'] + 1) # 折扣价反推的成本价  
cost_prc_df['cost_prc'] = cost_prc_df.apply(func=lambda x: min(x['ori_prc_cost_prc'], x['sale_prc_cost_prc']), axis=1) # 取小者  
data_df = pd.merge(left=data_df, right=cost_prc_df[['sku_id', 'cost_prc']], on=['sku_id'], how='left') # 拼接回主表  

2)最后修改特征字段类型并输出至csv

# 修改列类型、小数点后位数、展示顺序  
data_df['sku_id'] = data_df['sku_id'].astype('int')  
data_df['cate1_id'] = data_df['cate1_id'].astype('int')  
data_df['cate2_id'] = data_df['cate2_id'].astype('int')  
data_df['cate3_id'] = data_df['cate3_id'].astype('int')  
data_df['sale_cnt'] = data_df['sale_cnt'].astype('int')  
data_df['ori_prc'] = data_df['ori_prc'].astype('float').round(2)  
data_df['sale_prc'] = data_df['sale_prc'].astype('float').round(2)  
data_df['cost_prc'] = data_df['cost_prc'].astype('float').round(2)  
data_df = data_df[['sku_id', 'sku_name', 'ori_prc', 'sale_prc', 'cost_prc', 'sale_cnt', 'cate1_id', 'cate2_id', 'cate3_id', 'cate1_name', 'cate2_name', 'cate3_name', 'sale_dt']]  
# 输出  
data_df.to_csv('promotional_pricing_data1.csv', sep=',', encoding='utf8', index=False)  


05 小结

第一篇(业务问题拆解):我们把一个实际的促销定价问题拆解成了一系列的数学问题。

本篇(数据预处理生成):我们选择了一份公开的促销定价数据集,将其加工成了可分析求解的数据。

下一篇(数据挖掘分析):我们将介绍如何对数据进行全方位的挖掘和分析。敬请期待~~~


参考文献

  1. Hua J, Yan L, Xu H,et al. Markdowns in E-Commerce Fresh Retail: A Counterfactual Prediction and Multi-Period Optimization Approach[J]. arxiv, 2021.(https://arxiv.org/pdf/2105.08313.pdf)

  2. Kui Zhao, Junhao Hua, Ling Yan, et al. A Unified Framework for Marketing Budget Allocation[J]. arxiv, 20.(https://arxiv.org/pdf/1902.01128.pdf)

  3. 用相关系数进行Kmeans聚类,利用利润率、打折率、销售额、毛利润得到商品价格弹性标签,建立价格折扣力度模型(https://blog.csdn.net/weixin_45934622/article/details/114382037)

  4. 2019全国大学生数学建模竞赛讲评:“薄利多销”分析(https://dxs.moe.gov.cn/zx/a/hd_sxjm_sxjmstjp_2019sxjmstjp/210604/1699445.shtml)

  5. 策略算法工程师之路-基于线性规划的简单价格优化模型(https://zhuanlan.zhihu.com/p/145192690)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1382937.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么类型的企业需要工单系统?适用场景与优势分析

在现代商业中&#xff0c;必须高效地管理与跟踪大量任务和工作流程。对一些企业而言&#xff0c;处理很多订单是一项具有挑战性的任务。此订单可能来自客户需求、内部问题反馈或各种业务流程中的任务分配。可是&#xff0c;如果没有好的解决方法&#xff0c;这类订单可能会致使…

用LM Studio:2分钟在本地免费部署大语言模型,替代ChatGPT

你想在本地使用类似ChatGPT 的大语言模型么&#xff1f;LM Studio 可以帮你2分钟实现ChatGPT的功能&#xff0c;而且可以切换很多不同类型的大语言模型&#xff0c;同时支持在Windows和MAC上的PC端部署。 LM Studio是一款面向开发者的友好工具&#xff0c;特别适合那些想要探索…

Costco攻入山姆大本营

01 Costco深圳店开业火爆 “我今天不去Costco&#xff0c;早上还没开业&#xff0c;路上就已经堵车了&#xff0c;看来今天人很多&#xff0c;过几天再去”&#xff0c;原本计划在Costco开业当天去逛逛的张芸&#xff08;化名&#xff09;无奈只能放弃。 家住在Costco深圳店旁…

vue2+elementui的el-table固定列会遮住横向滚动条以及错位

解决方案 我是最右侧固定列&#xff0c;所以下面的class名称是.el-table__fixed-right, 如果有左侧固定请自行替换为el-table__fixed 防止固定列表格高度错位 .el-table__fixed-body-wrapper {.el-table__body {padding-bottom: 15px; // 这个得自己调试看多少合适}} }如果还…

C#,史密斯数(Smith Number)的计算方法与源代码

一、关于史密斯数的传说 1、关于理海大学Lehigh University 理海大学&#xff08;Lehigh University&#xff09;&#xff0c;位于宾夕法尼亚州&#xff08;Pennsylvania&#xff09;伯利恒&#xff08;Bethlehem&#xff09;&#xff0c;由富有爱国情怀与民族精神的实业家艾萨…

Java基础之虚拟机

1、前言 本篇基于网络整理&#xff0c;和自己编辑。在不断的完善补充哦。 2、什么是虚拟机&#xff1f; Java 虚拟机&#xff0c;是一个可以执行 Java 字节码的虚拟机进程。Java 源文件被编译成能被 Java 虚拟机执行的字节码文件( .class )。 Java 被设计成允许应用程序可以运…

Required request parameter ‘ids‘ for method parameter type List is not present]

在写delete方法的时候&#xff0c;出现了bug 于是将RequestParam换成PathVariable 两者有何区别&#xff1f; pathVariable和RequestParam的区别 在Spring MVC框架中&#xff0c;PathVariable 和 RequestParam 是两种用于从HTTP请求中获取参数的注解&#xff0c;它们的区别…

企业网盘助力数字化教育资源库建设

教育行业数字化是适应社会发展的必然选择&#xff0c;是教育行业的未来重要发展趋势。万事开头难&#xff0c;如何在数字化时代升级转型是教育行业团队正在面临的挑战。Zoho Workdrive企业网盘深耕智慧文件管理服务&#xff0c;为教育行业量身打造集中文件管理库&#xff0c;推…

test-04-test case generate 测试用例生成 tcases A model-based test case generator

拓展阅读 junit5 系列 基于 junit5 实现 junitperf 源码分析 Auto generate mock data for java test.(便于 Java 测试自动生成对象信息) Junit performance rely on junit5 and jdk8.(java 性能测试框架。性能测试。压测。测试报告生成。) 自动生成测试用例 Tcases&#xf…

Elasticsearch 地理空间搜索 - 远超 OpenSearch

作者&#xff1a;来自 Elastic Nathan_Reese 2021 年&#xff0c;OpenSearch 和 OpenSearch Dashboards 开始作为 Elasticsearch 和 Kibana 的分支。 尽管 OpenSearch 和 OpenSearch Dashboards 具有相似的血统&#xff0c;但它们不提供相同的功能。 在分叉时&#xff0c;只能克…

决策树:理解机器学习中的关键算法

决策树&#xff1a;理解机器学习中的关键算法 决策树是一种流行而强大的机器学习算法&#xff0c;它从数据中学习并模拟决策过程&#xff0c;以便对新的未知数据做出预测。由于其直观性和易理解性&#xff0c;决策树成为了分类和回归任务中的首选算法之一。在本文中&#xff0…

JVM实战(14)——Young GC调优

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 学习必须往深处挖&…

浅析爱泼斯坦事件 —— 弱电控制强电原理

据网络文字与视频资料&#xff0c;爱泼斯坦事件是犹太精英阶层&#xff0c;为了掌控美国国家机器为犹太利益集团服务&#xff0c;而精心设下的一个局。本文先假设这个结论成立&#xff0c;并基于此展开讨论。 我们知道&#xff0c;弱电管理强电是电气工程中的一门专门学问&…

数据结构学习笔记——查找算法中的树形查找(红黑树)

目录 一、红黑树的定义&#xff08;一&#xff09;黑/红结点、叶子节点&#xff08;二&#xff09;黑色完美平衡 二、红黑树的性质&#xff08;一&#xff09;黑高和高度&#xff08;二&#xff09;叶子结点个数 三、红黑树与AVL对比 一、红黑树的定义 红黑树是一棵二叉排序树…

【嵌入式AI】CanMVk230开发板学习笔记(一)

嵌入式AI学习&#xff1a;CanMVk230开发板学习笔记 官方链接: k230快速入门 github固件下载地址&#xff1a; https://github.com/kendryte/k230_canmv/releases K230的相关软硬件资料&#xff0c;请参考 https://developer.canaan-creative.com/k230/dev/index.html https://g…

2 - 配置/管理 Bean-IOC

1. 基于 XML 配置 Bean 在上一篇里已经模拟了通过 id 来配置获取 bean 依然是使用 Monster 类 1.1 通过类型获取 1&#xff09;xml 配置 <bean id"monster01" class"com.hspedu.spring.beans.Monster"><property name"monsterId" …

解决docker run报错:Error response from daemon: No command specified.

将docker镜像export/import之后&#xff0c;对新的镜像执行docker run时报错&#xff1a; docker: Error response from daemon: No command specified. 解决方法&#xff1a; 方案1&#xff1a; 查看容器的command&#xff1a; docker ps --no-trunc 在docker run命令上增加…

ES的文档操作

一&#xff0c;DSL语句 1&#xff0c;新增文档&#xff1a; 2&#xff0c;查询文档和删除文档&#xff1a; ,3修改文档 修改有两种方式&#xff1a; 全量修改&#xff1a;直接覆盖原来的文档 增量修改&#xff1a;修改文档中的部分字段 全量修改 全量修改是覆盖原来的文档…

基于PyQT的图片批处理系统

项目背景&#xff1a; 随着数字摄影技术的普及&#xff0c;人们拍摄和处理大量图片的需求也越来越高。为了提高效率&#xff0c;开发一个基于 PyQt 的图片批处理系统是很有意义的。该系统可以提供一系列图像增强、滤波、水印、翻转、放大缩小、旋转等功能&#xff0c;使用户能够…

thinkphp学习09-数据库的数据新增

单数据新增 使用 insert()方法可以向数据表添加一条数据&#xff0c;更多的字段采用默认 public function index() {$data [username > 犬夜叉,password > 123,gender > 男,email > wjl163.com,price > 999,details > 犬夜叉介绍];echo Db::name(user)-&g…