news 2026/8/27 8:16:50

生鲜超市动态定价与补货的pandas实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
生鲜超市动态定价与补货的pandas实战指南

1. 这不是一道数学题,而是一套生鲜超市的“呼吸系统”设计指南

如果你翻过2023年全国大学生数学建模竞赛C题的原始赛题文档,第一印象可能是:一堆蔬菜价格、库存、损耗率、补货周期的表格,外加几段模糊的业务描述——“某连锁生鲜超市需优化蔬菜类商品定价与补货策略”。但真正做过一线零售系统开发、参与过商超供应链中台搭建的人一眼就能看出:这道题根本不是考你解微分方程的能力,而是考你能不能把一个活生生的、每天都在“喘气”的生鲜生意,翻译成可计算、可干预、可迭代的数字模型。我带过三届校队打数模,也给两家区域型生鲜连锁做过定价引擎重构,实话说,90%的参赛队伍栽在第一步——没把“蔬菜会烂”这个常识,真正嵌进模型骨架里。它不像电子产品有固定折旧率,也不像快消品能靠促销清库存;西兰花放三天叶边发黄,菠菜隔夜就蔫,番茄表皮裂开就直接进报损区。这些不是噪声,是核心变量。所以本篇不讲“标准答案”,只拆解我们团队最终落地的那套方案:用Python+pandas构建的轻量级决策流,它跑在一台i5+16G的办公笔记本上,却支撑起日均300+SKU、覆盖87家门店的动态调价与补货建议生成。代码不是炫技的装饰,而是把“早市降价5毛促清货”“暴雨天提前两小时补货”这类经验,固化成可复用、可回溯、可压测的逻辑模块。你会看到真实数据集里的字段含义(比如shelf_life_hours不是理论保质期,而是按门店温控实测的货架存活时间),看到pandas里一个.groupby().apply()如何替代传统for循环完成千店千策,看到为什么我们放弃LSTM预测销量,转而用加权滑动窗口+异常值截断——因为凌晨三点系统要出明天早上的补货单,不能等模型收敛。适合刚学完pandas基础、正为课程设计发愁的同学;也适合想快速验证算法想法、手头只有Excel和Python环境的运营同学。你不需要懂蒙特卡洛模拟,但得知道pd.cut()怎么把连续价格区间映射成离散策略标签;你不必精通运筹学,但得明白为什么补货量公式里那个“安全系数”必须随季节动态调整——去年冬至前大白菜囤货系数是1.8,今年寒潮预警升级后,系统自动拉到2.1。这才是C题真正的战场:在数学框架里,种出能结果的菜。

2. 整体架构设计:三层漏斗式决策流,拒绝“一步到位”的幻觉

2.1 为什么不用端到端深度学习?——从货架到服务器的物理约束倒逼架构选择

很多同学拿到题第一反应是“上神经网络”,查资料、搭PyTorch、调参……结果发现:赛题给的数据集只有12周×42个蔬菜品类×87家门店的销售记录,总量不到20万行。而一个像样的LSTM模型,光是训练就得吃掉3GB显存,且需要至少50万条样本才能避免过拟合。更致命的是业务现实——超市的补货决策必须在每日22:00前生成,留给计算的时间窗口只有45分钟。我们实测过:在i5-10210U+16G内存的机器上,用TensorFlow训练一个包含3层LSTM的销量预测模型,单次训练耗时17分钟,而实际生产中需要每晚对87家门店×42个SKU分别预测未来3天销量,这意味着要并行跑3654次模型——显然不可行。于是我们彻底转向“分而治之”的三层漏斗架构:

  • 第一层:数据清洗与特征工程漏斗
    输入原始CSV(含store_id,veg_id,date,sales_qty,price,stock_init,stock_end,temp_avg,rainfall_mm等字段),输出结构化特征矩阵。关键动作不是“标准化”,而是“业务归一化”:比如将sales_qty除以当日营业时长(单位:小时),得到“每小时销售速率”,再按门店历史均值做Z-score,这样同一数值在冷链完善的老城区店和温控较差的郊区店,代表的缺货风险等级才具可比性。

  • 第二层:策略规则引擎漏斗
    不依赖黑箱预测,而是用显性规则驱动核心决策。例如定价策略:

    # 真实代码片段:基于损耗率与库存健康度的动态调价 df['spoil_rate'] = (df['stock_init'] - df['stock_end']) / df['stock_init'].replace(0, 1) df['inventory_health'] = df['stock_end'] / df['sales_qty_7d_avg'].replace(0, 1) # 库存可售天数 df['price_adj_factor'] = np.where( (df['spoil_rate'] > 0.15) & (df['inventory_health'] > 2.0), 0.85, # 高损耗+高库存 → 降价15% np.where( (df['spoil_rate'] < 0.03) & (df['inventory_health'] < 0.8), 1.05, # 低损耗+低库存 → 涨价5% 1.0 # 其他情况维持原价 ) )

    这段逻辑背后是生鲜采购总监亲口告诉我们的经验:“损耗超15%还不降价,当天剩下的菜基本全报损;库存低于0.8天又不涨价,第二天开门就断货,顾客流失率翻倍。”——规则不是拍脑袋,而是把老师傅的晨会口头指令,变成可审计、可回滚的代码。

  • 第三层:仿真推演漏斗
    对第二层输出的策略组合(如“A店明日对菠菜执行降价12%,补货量+20%”),在虚拟环境中跑1000次蒙特卡洛模拟:随机扰动温度、降雨、周末客流增幅等参数,观察30天内总毛利、损耗率、缺货次数的分布。只有当95%的模拟结果中,毛利提升≥3%且损耗率增幅≤0.5个百分点,该策略才被标记为“可执行”。这步看似冗余,实则规避了“纸上谈兵”——去年某队提交的方案在静态数据上毛利提升8%,但仿真发现其在连续阴雨天会导致菠菜损耗率飙升至32%,实际落地等于烧钱。

提示:三层漏斗不是线性流水线,而是带反馈环的闭环。比如第三层仿真发现某策略在高温天失效,会自动触发第二层规则引擎的“高温专项模式”开关,临时启用更激进的降价阈值。这种动态适应能力,才是C题区分普通建模和工业级方案的关键分水岭。

2.2 数据集结构解析:别再把“日期”当字符串处理了

官方提供的数据集名为c_data_2023.csv,但直接pd.read_csv()会踩三个深坑:

  1. 日期字段date是字符串而非datetime
    表面看是2023-01-01格式,但部分记录存在2023/01/0101-Jan-2023混用。若不做统一解析,后续按周聚合时会出现2023-01-012023/01/01被识别为不同日期,导致周销量统计错误。正确做法:

    df['date'] = pd.to_datetime(df['date'], infer_datetime_format=True, errors='coerce') # errors='coerce'将无法解析的值转为NaT,后续用df.dropna(subset=['date'])清理
  2. sales_qty存在负值,这不是错误而是退货
    赛题说明里没提,但数据中约0.7%的记录sales_qty为负数。经我们联系组委会确认,这是顾客退菜产生的负销量。若简单剔除,会低估实际销售波动;若直接参与计算,又会使日销量均值失真。解决方案:新增return_flag列,将负值转为绝对值计入退货量,并在计算“有效销售速率”时,用max(sales_qty, 0)作为分子:

    df['effective_sales'] = df['sales_qty'].clip(lower=0) # clip()比max()更高效 df['sales_rate_h'] = df['effective_sales'] / df['operating_hours']
  3. veg_id编码隐含品类层级关系
    表面看是纯数字ID(如101,102),但实际前两位代表大类:10x为叶菜类,20x为根茎类,30x为瓜果类。这个信息在赛题附件《蔬菜分类说明.docx》里有,但多数队伍没注意到。利用它可做分层建模:叶菜类损耗率高,适用更短的预测窗口(3天);根茎类耐储,可用7天窗口。代码实现:

    df['veg_category'] = df['veg_id'] // 100 # 101→1, 205→2, 312→3 # 后续按category分组,设置不同模型参数

这些细节看似琐碎,却决定了模型能否从“能跑通”走向“真有用”。我见过太多队伍,代码写得天花乱坠,但因没处理date字段,最终所有时间序列分析全盘作废。

2.3 工具链选型逻辑:为什么坚持用pandas而非Dask或Polars?

当前大数据生态里,Dask常被吹捧为“pandas的分布式升级版”,Polars则以“比pandas快10倍”吸睛。但我们团队在C题中坚持纯pandas栈,理由很实在:

  • 数据规模决定工具上限:赛题数据集最大也就20万行×30列,pandas在16G内存机器上处理毫无压力。而引入Dask,需额外部署调度器、管理集群状态,调试成本远超收益。实测对比:对同一数据集做groupby().agg(),pandas耗时1.2秒,Dask(单机模式)耗时3.8秒——多出来的2.6秒全花在序列化/反序列化上。

  • 生态兼容性压倒性能:C题后续要对接Excel报表(财务部要看)、微信消息推送(店长手机接收)、甚至打印小票(补货单需带二维码)。pandas的to_excel()to_markdown()qrcode库无缝集成;而Polars导出Excel需先转pandas DataFrame,徒增转换开销。

  • 学习曲线即生产力曲线:参赛学生平均Python经验<6个月。让他们花两天学Dask的延迟计算图,不如用半天掌握pandas的rolling().apply()实现滑动窗口预测。我们提供的代码里,所有复杂操作都控制在3行以内,比如计算7日移动平均销量:

    df['sales_7d_avg'] = df.groupby(['store_id', 'veg_id'])['sales_qty'].transform( lambda x: x.rolling(window=7, min_periods=1).mean() )

    这行代码同时解决分组、滚动、缺失值填充(min_periods=1保证首日也有值),且语义清晰——比写一个自定义函数再apply,可读性高得多。

注意:所谓“工具无好坏,场景定生死”。当你的数据量突破千万行、需要实时响应时,自然该切Polars;但C题的战场是“如何用最朴素的工具,把业务逻辑刻进每一行代码”。这恰恰是工业界最看重的工程师素养。

3. 核心模块实现:从数据加载到决策输出的完整流水线

3.1 数据加载与初始校验:用10行代码守住质量底线

很多队伍输在第一步:数据还没看清就急着建模。我们设计了一个极简但致命的校验模块,放在load_data.py最开头:

def validate_data(df): """强制校验数据质量,失败则中断流程""" assert not df.empty, "数据集为空!检查文件路径" assert 'date' in df.columns, "缺少date列" assert pd.api.types.is_datetime64_any_dtype(df['date']), "date列未转为datetime" assert df['sales_qty'].min() >= -1000, "sales_qty出现异常负值(<-1000),疑似录入错误" assert df['price'].min() > 0, "price出现非正数,不符合商业逻辑" # 关键校验:检查是否有重复记录(同一门店+同一天+同一蔬菜) dup_mask = df.duplicated(subset=['store_id', 'veg_id', 'date'], keep=False) if dup_mask.any(): print(f"警告:发现{dup_mask.sum()}条重复记录,已自动去重") df = df.drop_duplicates(subset=['store_id', 'veg_id', 'date']) return df # 使用方式 df_raw = pd.read_csv("c_data_2023.csv") df_clean = validate_data(df_raw)

这段代码的价值不在技术难度,而在思维范式:把业务约束(如价格必为正)转化为代码断言。当某队用我们代码跑出AssertionError: price出现非正数时,他们才发现原始数据里有3条记录price=0.0——原来是系统故障导致的无效价格,必须剔除。这种“防御性编程”习惯,让我们的模型从未因数据脏而产出荒谬结果。

3.2 特征工程实战:用pandas的cutqcut做业务分桶

C题最大的陷阱是把所有蔬菜当“均质商品”处理。现实中,生菜和土豆的定价逻辑天差地别。我们用pandas的分桶功能,构建品类专属特征:

  • 价格弹性分桶:先计算每个蔬菜的“价格变动率”与“销量变动率”的相关系数(用滚动30天数据),得到42个弹性系数。然后用pd.qcut()按分位数分成高/中/低三档:

    # 计算价格弹性(简化版) df['price_change_pct'] = df.groupby(['store_id', 'veg_id'])['price'].pct_change() df['sales_change_pct'] = df.groupby(['store_id', 'veg_id'])['sales_qty'].pct_change() elasticity = df.groupby('veg_id').apply( lambda x: x['sales_change_pct'].corr(x['price_change_pct']) ).fillna(0) # 分桶:qcut确保每档样本量均衡 elasticity_bins = pd.qcut(elasticity, q=3, labels=['low', 'medium', 'high']) df['elasticity_level'] = df['veg_id'].map(elasticity_bins.to_dict())

    结果显示:菠菜(高弹性)降价10%销量增25%,而土豆(低弹性)降价10%销量仅增3%。后续定价策略据此差异化——高弹性品类用小幅高频调价,低弹性品类用大幅低频调价。

  • 损耗敏感度分桶:用pd.cut()按绝对值分档,更符合业务直觉:

    # 计算各蔬菜平均损耗率 spoil_rate = df.groupby('veg_id')['spoil_rate'].mean() # cut按数值区间分档:[0,0.05)为'low',[0.05,0.15)为'medium',[0.15,1.0]为'high' spoil_bins = pd.cut(spoil_rate, bins=[0, 0.05, 0.15, 1.0], labels=['low', 'medium', 'high']) df['spoil_sensitivity'] = df['veg_id'].map(spoil_bins.to_dict())

    这样分档后,“高损耗敏感度”蔬菜(如香菜、韭菜)自动触发更严格的库存预警阈值——当库存健康度<1.2天即报警,而土豆只需<0.8天才报警。

实操心得:qcutcut的选择本质是业务哲学。qcut追求“每档客户数相同”,适合做用户分群;cut追求“每档业务意义明确”,适合做风控阈值。C题中,损耗率0.15%是行业公认的临界点,必须用cut硬性切割,不能让算法自己找分位数。

3.3 定价决策模块:把“晨会讨论”翻译成向量化运算

定价不是数学问题,是博弈问题——既要对抗损耗,又要防止顾客流失,还要兼顾竞品价格。我们的方案放弃复杂优化,聚焦三个可执行动作:

  1. 基础定价锚定:用过去30天加权均价作为基准,权重按时间衰减(最近一天权重0.05,30天前权重0.001):

    # 构建时间衰减权重 days_ago = (df['date'].max() - df['date']).dt.days weight = np.where(days_ago <= 30, 0.05 - days_ago * 0.0016, 0) df['weighted_price'] = df['price'] * weight base_price = df.groupby(['store_id', 'veg_id'])['weighted_price'].sum() / \ df.groupby(['store_id', 'veg_id'])[weight].sum()
  2. 损耗驱动调价:当spoil_rate > 0.15inventory_health > 2.0,启动“清货模式”,降价幅度=min(0.3, spoil_rate * 2),即损耗率每高1%,多降2%,但封顶30%:

    df['clearance_discount'] = np.where( (df['spoil_rate'] > 0.15) & (df['inventory_health'] > 2.0), np.clip(df['spoil_rate'] * 2, 0, 0.3), 0 )
  3. 竞品跟随调价:赛题虽未给竞品数据,但附件中有“周边3公里内主要竞品价格调研表”。我们将其转为字典,对每个veg_id匹配最近似竞品价,若本店价>竞品价15%,则强制下调至竞品价×1.05:

    # competitor_prices = {'101': 5.8, '102': 4.2, ...} 从Excel读取 df['competitor_price'] = df['veg_id'].map(competitor_prices) df['comp_follow_discount'] = np.where( df['price'] > df['competitor_price'] * 1.15, 1 - (df['competitor_price'] * 1.05) / df['price'], 0 )

最终定价 =base_price * (1 - clearance_discount) * (1 - comp_follow_discount)。整个过程无循环、无嵌套,纯向量化,10万行数据计算耗时<0.3秒。

3.4 补货决策模块:用“安全库存+需求预测”双轨制

补货的核心矛盾是:补少了断货,补多了烂掉。我们的解法是双轨并行:

  • 安全库存轨:应对不确定性,公式为安全库存 = Z × √(L × σ_d² + d² × σ_l²),其中Z为服务水平系数(取1.65对应95%满足率),L为补货前置期(天),σ_d为日销量标准差,d为平均日销量,σ_l为前置期标准差。pandas实现:

    # 计算各门店-蔬菜组合的L和σ_l(从历史订单数据提取) lead_time_stats = df_orders.groupby(['store_id', 'veg_id']).agg( L=('lead_days', 'mean'), sigma_l=('lead_days', 'std') ).reset_index() # 计算日销量波动(用滚动30天标准差) df['daily_sales_std'] = df.groupby(['store_id', 'veg_id'])['sales_qty'].transform( lambda x: x.rolling(30).std(ddof=0) ) # 合并计算安全库存 df_merged = df.merge(lead_time_stats, on=['store_id', 'veg_id']) df_merged['safety_stock'] = 1.65 * np.sqrt( df_merged['L'] * df_merged['daily_sales_std']**2 + df_merged['sales_qty']**2 * df_merged['sigma_l']**2 )
  • 需求预测轨:用加权滑动窗口预测未来3天销量,权重按距离递减(当日权重0.4,前1日0.3,前2日0.2,前3日0.1):

    def weighted_forecast(series): if len(series) < 4: return series.mean() if len(series) > 0 else 0 weights = [0.1, 0.2, 0.3, 0.4] # 倒序:最旧数据权重最小 return np.average(series[-4:], weights=weights) df['forecast_3d'] = df.groupby(['store_id', 'veg_id'])['sales_qty'].transform( lambda x: x.rolling(4).apply(weighted_forecast, raw=True) )

最终补货量 =max(0, forecast_3d + safety_stock - stock_end)。注意max(0,...)防止负补货——这是业务铁律,系统绝不能建议“退货”。

4. 实操避坑指南:那些只在深夜debug时才懂的真相

4.1 时间窗口陷阱:为什么“过去7天”不等于“最近7天”

几乎所有队伍都用df.tail(7)取最近7天数据做预测,但这是致命错误。原因在于:赛题数据存在缺失!比如某店某菜在2023-03-15无销售记录(可能休市),tail(7)会取到2023-03-08至2023-03-14,跳过了真实的最新日期。正确做法是用时间索引:

# 错误示范 recent_7 = df.groupby(['store_id', 'veg_id']).apply(lambda x: x.tail(7)) # 正确示范:先设date为索引,再用date_range定位 df_indexed = df.set_index('date') recent_7 = df_indexed.groupby(['store_id', 'veg_id']).apply( lambda x: x.loc[x.index.max() - pd.Timedelta(days=6):x.index.max()] )

我们曾因此导致某队的菠菜补货量少算42%,因为跳过的那天恰逢周末销量峰值。这个坑,只有在真实数据里填过才刻骨铭心。

4.2 内存泄漏隐形杀手:groupby().apply()的闭包陷阱

pandas的groupby().apply()极其方便,但滥用会导致内存爆炸。典型场景:在apply函数里定义大型对象(如整个数据集副本):

# 危险代码!每次apply都复制df_full def risky_func(group): temp_df = df_full.copy() # ❌ 复制整个DataFrame return group['sales_qty'].mean() df.groupby('veg_id').apply(risky_func) # 内存占用飙升

修复方案:用groupby().agg()替代,或确保apply内只操作当前group:

# 安全代码 def safe_func(group): # 只用group内部数据 return { 'avg_sales': group['sales_qty'].mean(), 'std_sales': group['sales_qty'].std() } result = df.groupby('veg_id').apply(safe_func, result_type='expand') # result_type='expand'返回DataFrame

我们在测试机上监控到,危险写法使内存峰值达4.2GB,安全写法仅0.8GB——这对部署在老旧服务器上的系统至关重要。

4.3 输出格式合规性:评委只看“可验证”的结果

C题评分细则明确要求:“所有结论必须有数据支撑,图表需标注数据来源”。很多队伍交的PDF报告里,柱状图坐标轴没标单位,折线图没写数据源文件名。我们的自动化输出模块强制包含:

  • 每张图表底部添加Source: c_data_2023.csv | Generated: 2023-09-12 21:45
  • 补货建议Excel中,每行增加reason_code列,值为SPOIL_HIGH(损耗过高)、COMP_BEHIND(竞品价低)等,方便评委追溯逻辑
  • 所有数值结果保留4位小数,但显示时用f"{x:.2f}",避免科学计数法

最后分享一个小技巧:用pandas.DataFrame.style.set_properties(**{'text-align': 'center'})统一表格对齐,再用set_table_styles([{'selector': 'th', 'props': [('background-color', '#4CAF50'), ('color', 'white')]}])给表头上色——这些细节能让评委在快速浏览时,瞬间抓住你的专业度。

5. 常见问题速查表:从报错到逻辑悖论的实战应答

问题现象根本原因解决方案实操验证
KeyError: 'date'原始CSV列名含不可见空格,如'date 'df.columns = df.columns.str.strip()清洗列名load_data.py开头加入此行
补货量全为0stock_end字段存在NaN,max(0, forecast - NaN)结果为NaNdf['stock_end'] = df['stock_end'].fillna(0)填充添加校验:assert not df['stock_end'].isna().any()
定价结果出现负数clearance_discount计算中,spoil_rate为NaN导致结果NaN,再乘base_price得负无穷在discount计算前加df['spoil_rate'] = df['spoil_rate'].fillna(0)所有涉及除法的字段,先fillna(0)再运算
模型在A店有效,在B店失效B店温控差,导致同一蔬菜损耗率比A店高3倍,但特征工程未做门店标准化spoil_rate按门店做Z-score:df['spoil_z'] = df.groupby('store_id')['spoil_rate'].transform(lambda x: (x-x.mean())/x.std())新增特征列spoil_z替代原始spoil_rate
输出Excel打开乱码to_excel()默认保存为ANSI编码,中文显示为方块显式指定engine='openpyxl',并用workbook.encoding='utf-8'df.to_excel("output.xlsx", engine='openpyxl')

这些答案不是来自文档,而是我们连续72小时debug后记下的血泪笔记。比如那个“补货量全为0”的问题,源于某次数据清洗脚本意外删掉了stock_end列的最后12行——因为原始数据里这12行是空值,脚本用了dropna()全局删除。从此我们立下规矩:任何dropna()操作必须指定subset参数,绝不裸奔。

6. 数据集与代码使用说明:如何让这套方案在你电脑上跑起来

6.1 环境配置清单(实测通过)

  • 操作系统:Windows 10/11 或 Ubuntu 20.04(macOS未测试)
  • Python版本:3.8.10(严格限定!3.9+的pandas在某些agg操作上有行为差异)
  • 核心包:pandas==1.3.5, numpy==1.21.6, openpyxl==3.0.10
  • 安装命令
    python -m venv c_model_env c_model_env\Scripts\activate # Windows # source c_model_env/bin/activate # Linux/Mac pip install pandas==1.3.5 numpy==1.21.6 openpyxl==3.0.10

注意:不要用pip install -r requirements.txt一键安装。我们提供的requirements.txt精确锁定版本,因为pandas 1.4.0修复了一个rolling().apply()的bug,但也引入了新的NaN传播逻辑,会导致C题特定计算结果偏移0.3%——这个偏差在赛题精度要求内,但会影响你的结果复现。

6.2 代码结构说明(解压即用)

下载包解压后目录结构:

c_model_2023/ ├── data/ # 原始数据与处理后数据 │ ├── c_data_2023.csv # 官方原始数据 │ └── competitor_prices.xlsx # 竞品价格表(需自行填写,模板已提供) ├── src/ # 核心代码 │ ├── load_data.py # 数据加载与校验 │ ├── feature_engineer.py # 特征工程主模块 │ ├── pricing_engine.py # 定价决策引擎 │ ├── replenishment_engine.py # 补货决策引擎 │ └── output_generator.py # 报告与Excel输出 ├── notebooks/ # Jupyter实验记录(含可视化代码) │ └── debug_analysis.ipynb └── run_all.py # 一键运行全流程(推荐新手从这里开始)

运行方式:

cd c_model_2023 python run_all.py

成功运行后,会在output/目录生成:

  • pricing_recommendation.xlsx:各门店各蔬菜的建议售价与调价幅度
  • replenishment_plan.xlsx:明日补货清单,含数量、理由码、预计毛利影响
  • report_summary.pdf:含关键指标图表(损耗率趋势、毛利变化、缺货率热力图)

6.3 自定义扩展指南:让你的方案真正落地

这套代码不是终点,而是起点。根据你的真实场景,可快速扩展:

  • 接入实时数据:修改load_data.py中的read_data()函数,将pd.read_csv()替换为数据库查询:

    # 示例:从MySQL读取当日销售 import pymysql conn = pymysql.connect(host='localhost', user='root', password='123', db='supermarket') df_today = pd.read_sql("SELECT * FROM sales WHERE date = CURDATE()", conn)
  • 添加新策略:在pricing_engine.py中新增函数,比如“节日溢价策略”:

    def festival_premium(df): # 从外部JSON读取节日日历 with open('festival_calendar.json') as f: festivals = json.load(f) # {"2023-01-22": "Spring Festival", ...} df['is_festival'] = df['date'].astype(str).isin(festivals.keys()) df['festival_premium'] = np.where(df['is_festival'], 0.15, 0) # 节日涨价15% return df
  • 部署为API:用Flask封装,让店长用微信扫码查看补货单:

    from flask import Flask, jsonify app = Flask(__name__) @app.route('/api/replenishment/<store_id>') def get_replenishment(store_id): plan = pd.read_excel('output/replenishment_plan.xlsx') result = plan[plan['store_id'] == int(store_id)].to_dict('records') return jsonify(result)

我在城东一家社区超市试运行这套方案时,老板盯着手机上弹出的补货单说:“这比我凭感觉订货准多了,昨天按单补的西兰花,今天全卖光了,一筐没烂。”——那一刻我确认:数学建模的终极价值,不是拿奖,而是让菜摊上的每一片叶子,都活得更久一点。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/27 8:16:46

5分钟解锁加密音乐文件:Unlock-Music 实操指南

5分钟解锁加密音乐文件&#xff1a;Unlock-Music 实操指南 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库&#xff1a; 1. https://github.com/unlock-music/unlock-music &#xff1b;2. https://git.unlock-music.dev/um/web 项目地址: https://gitc…

作者头像 李华
网站建设 2026/8/27 8:15:53

用坐标图和雷达图搭建可复用的AI模型能力评测框架

这次我们来看一个模型能力坐标评测方案&#xff0c;项目名叫AI Models – Political Compass。先解释一个容易误会的点&#xff1a;这里的Political Compass不是给现实社会问题做立场判定&#xff0c;而是借用罗盘和坐标图的形式&#xff0c;把不同 AI 模型的能力倾向映射到一张…

作者头像 李华
网站建设 2026/8/27 8:15:48

四路可编程PMIC实战:从选型到调试验证

做硬件的人应该都有过这种经历&#xff1a;板子上一个 CPU 要 1.0V 核心供电&#xff0c;DDR 要 1.2V&#xff0c;外设接口要 3.3V 或者 5V&#xff0c;FPGA 还有单独的核压和 IO 电压&#xff0c;林林总总五六路电源&#xff0c;每路都得单独设计。以前我习惯用几颗独立的 DC-…

作者头像 李华
网站建设 2026/8/27 8:15:36

线程池异常方案

必须做异常处理,而且不能靠开发自觉,必须从「框架层做统一兜底」,否则一定会出现「任务静默失败、业务出问题查不到原因」的生产事故。 你之前看到的代码是简化配置版,生产落地必须补上异常处理;针对「开发忘了写」的问题,核心思路是人防不如技防:不依赖业务开发主动加…

作者头像 李华
网站建设 2026/8/27 8:14:05

正在阅读:简单介绍一款网络运维软件 自动化运维工具clip

Clip是一款工具, 它用于自动化的运维, 适用海量服务器的管理场景, 能够降低掉系统误操作的风险, 还可以提高工作的效率等。Clip把传统的IP管理维度替换成管理维度, 管理方式发生改变从而让海量运维的时候变得更为便捷、可靠和高效。Clip属于C/S架构, 它把IP关系保存在端, 端能够…

作者头像 李华