0

0

NumPy高效处理分层库存分配与客户平均价格计算

花韻仙語

花韻仙語

发布时间:2025-08-31 20:44:02

|

879人浏览过

|

来源于php中文网

原创

NumPy高效处理分层库存分配与客户平均价格计算

本文介绍如何使用NumPy高效解决多价库存按先进先出原则分配给客户订单的问题,并计算每位客户的平均购买价格。通过利用np.repeat和np.add.reduceat等向量化操作,避免了创建大型中间数组,显著提升了处理大规模数据的性能和内存效率。

1. 问题描述

库存管理和订单处理场景中,我们经常会遇到需要将不同价格的商品分配给多个客户订单的情况。一个典型的场景是,商品以不同的批次或在不同时间点采购,导致其成本(或售价)存在差异。当客户下订单时,通常会遵循“先进先出”或“成本最低优先”的原则进行分配。我们的目标是,在满足客户订单需求的同时,计算出每位客户为其所购商品支付的平均价格。

假设我们有以下数据:

  • orders: 一个NumPy数组,表示每位客户的订单数量。例如 [21, 6, 3] 表示第一个客户需要21单位,第二个6单位,第三个3单位。
  • quantity: 一个NumPy数组,表示在特定价格下可用的商品数量。例如 [16, 14] 表示有16单位商品以某个价格出售,另有14单位商品以另一个价格出售。
  • price: 一个NumPy数组,与 quantity 对应,表示每批商品的单价。例如 [30.5, 35.5] 表示16单位商品单价为30.5,14单位商品单价为35.5。

关键约束和条件:

  1. 所有客户订单的总量 (np.sum(orders)) 始终等于所有可用商品的总量 (np.sum(quantity))。
  2. orders 数组通常按客户需求量降序排列(尽管这不是解决问题的严格要求,但反映了常见场景)。
  3. quantity 和 price 数组是关联的,且 price 数组按升序排列,这意味着我们总是优先分配价格最低的商品。

我们的任务是计算出每位客户的平均购买价格。

2. 低效的解决方案及其局限性

一种直观但低效的方法是创建一个巨大的数组,将所有商品的单价“展开”到这个数组中,然后根据客户订单的起始和结束索引来计算平均值。

import numpy as np

orders = np.array([21, 6, 3], dtype=np.int64)
quantity = np.array([16, 14], dtype=np.int64)
price = np.array([30.5, 35.5], dtype=np.double)

# 步骤1: 创建一个包含所有商品单价的“扁平化”数组
start = 0
total_supply_units = np.sum(quantity)
supply_prices_flat = np.zeros(total_supply_units, dtype=np.double)
for i, quant in enumerate(quantity):
    idx = start + quant
    supply_prices_flat[start:idx] = price[i]
    start = idx

print("扁平化商品价格数组:", supply_prices_flat)

# 步骤2: 根据客户订单计算平均价格
fin_avg_prices = []
current_pos = 0
for order_size in orders:
    idx = current_pos + order_size
    fin_avg_prices.append(np.mean(supply_prices_flat[current_pos:idx]))
    current_pos = idx

print("每位客户的平均价格 (低效方法):", fin_avg_prices)

输出示例:

扁平化商品价格数组: [30.5 30.5 30.5 30.5 30.5 30.5 30.5 30.5 30.5 30.5 30.5 30.5 30.5 30.5
 30.5 30.5 35.5 35.5 35.5 35.5 35.5 35.5 35.5 35.5 35.5 35.5 35.5 35.5
 35.5 35.5]
每位客户的平均价格 (低效方法): [31.69047619047619, 35.5, 35.5]

局限性: 当商品总数量 np.sum(quantity) 非常大时,supply_prices_flat 数组会变得极其庞大,导致严重的内存消耗和性能问题。这种方法在处理大规模数据集时是不可接受的。

3. 高效的NumPy解决方案

NumPy提供了强大的向量化操作,可以避免显式循环和创建大型中间数组。我们可以利用 np.repeat 和 np.add.reduceat 来高效地解决这个问题。

import numpy as np

orders = np.array([21, 6, 3], dtype=np.int64)
quantity = np.array([16, 14], dtype=np.int64)
price = np.array([30.5, 35.5], dtype=np.double)

# 步骤1: 使用 np.repeat 展开价格
# np.repeat(price, quantity) 会根据 quantity 中指定的次数重复 price 中的每个元素。
# 例如,如果 price=[P1, P2] 和 quantity=[Q1, Q2],结果将是 [P1, ..., P1 (Q1次), P2, ..., P2 (Q2次)]
repeated_prices = np.repeat(price, quantity)
# 结果: [30.5, ..., 30.5 (16次), 35.5, ..., 35.5 (14次)]

# 步骤2: 计算 reduceat 的索引
# np.cumsum(orders) 计算订单数量的累积和,用于确定每个客户订单在 repeated_prices 中的结束位置。
# np.r_[0, ...] 会在累积和数组前添加一个0,表示第一个客户订单的起始位置。
# [:-1] 移除最后一个元素,因为 reduceat 的索引是每个段的起始位置。
# 例如,orders=[21, 6, 3],cumsum=[21, 27, 30],indices=[0, 21, 27]
indices = np.r_[0, np.cumsum(orders)][:-1]

# 步骤3: 使用 np.add.reduceat 计算每个客户订单的总成本
# np.add.reduceat(array, indices) 会在指定索引处“切分” array,并对每个切分段进行求和。
# 这将直接计算出每个客户订单的总成本。
total_cost_per_customer = np.add.reduceat(repeated_prices, indices)

# 步骤4: 计算平均价格
# 将每个客户的总成本除以其订单数量,得到平均价格。
average_price_per_customer = total_cost_per_customer / orders

print("每位客户的平均价格 (高效NumPy方法):", average_price_per_customer)

输出:

每位客户的平均价格 (高效NumPy方法): [31.69047619 35.5        35.5       ]

4. 核心NumPy函数详解

4.1 numpy.repeat(a, repeats)

此函数用于重复数组 a 中的元素。repeats 可以是一个整数(所有元素重复相同次数),也可以是一个与 a 形状相同的数组(每个元素重复不同次数)。 在本例中,np.repeat(price, quantity) 的作用是根据 quantity 数组中每个对应的数量,将 price 数组中的价格值进行重复。这有效地模拟了将所有单独的商品单位及其价格平铺在一个数组中的过程,但它是通过NumPy的底层优化实现的,避免了显式创建巨大的中间列表。

4.2 numpy.add.reduceat(a, indices)

ufunc.reduceat 是一个非常强大的NumPy函数,它允许在指定索引处对数组进行“分段”操作。np.add.reduceat(array, indices) 的功能是:

  1. 在 indices 数组中指定的每个位置开始一个新的“段”。
  2. 对每个段内的元素执行 add 操作(即求和)。
  3. 返回一个数组,其中包含每个段的求和结果。

例如,如果 repeated_prices 是 [P1, P1, P1, P2, P2, P2] 且 indices 是 [0, 3]:

PageOn
PageOn

AI驱动的PPT演示文稿创作工具

下载
  • 第一个段从索引0开始,到索引3之前结束:[P1, P1, P1],求和为 3*P1。
  • 第二个段从索引3开始,到数组末尾结束:[P2, P2, P2],求和为 3*P2。 结果将是 [3*P1, 3*P2]。

在我们的解决方案中,indices 数组 np.r_[0, np.cumsum(orders)][:-1] 精确地标记了每个客户订单在 repeated_prices 数组中的起始位置。np.add.reduceat 随后对每个客户所购买的商品价格进行求和,直接得到每个客户的总成本。

5. 浮点数精度问题考量

在进行浮点数计算时,精度问题是普遍存在的。NumPy在内部使用IEEE 754标准来表示浮点数,这在大多数科学和工程计算中是足够的。对于本例中的平均价格计算: mean_prices * quantity == original_prices * quantities

由于浮点数的特性,直接比较两个浮点数是否“相等”通常是不可靠的。更好的做法是比较它们之间的绝对差是否小于一个很小的容忍值(epsilon)。

# 示例:验证总成本的准确性
# 计算高效方法得到的总成本
calculated_total_costs = average_price_per_customer * orders
print("高效方法计算的总成本:", calculated_total_costs)

# 原始的总供应成本
original_total_supply_cost = np.sum(price * quantity)
print("原始总供应成本:", original_total_supply_cost)

# 高效方法计算的所有客户总成本之和
sum_calculated_total_costs = np.sum(calculated_total_costs)
print("所有客户总成本之和:", sum_calculated_total_costs)

# 比较(考虑浮点精度)
tolerance = 1e-9 # 设置一个小的容忍值
if np.abs(sum_calculated_total_costs - original_total_supply_cost) < tolerance:
    print("总成本在浮点精度范围内一致。")
else:
    print("总成本存在显著差异。")

输出示例:

高效方法计算的总成本: [665.5 213.  106.5]
原始总供应成本: 985.0
所有客户总成本之和: 985.0
总成本在浮点精度范围内一致。

可以看到,在合理的浮点精度范围内,总成本是匹配的。对于极度严格的金融计算,可能需要考虑使用decimal模块或专门的定点数库,但对于大多数业务场景,NumPy的float64精度已足够。

6. 总结与注意事项

通过利用NumPy的np.repeat和np.add.reduceat函数,我们能够以高度优化的方式解决多价库存分配和客户平均价格计算问题。这种方法具有以下显著优点:

  • 内存效率高: 避免了创建可能非常庞大的中间数组,显著降低了内存消耗。
  • 计算速度快: 利用NumPy底层C语言实现,通过向量化操作极大地提升了计算速度,尤其适用于大规模数据集。
  • 代码简洁: 相比于基于循环的实现,代码更加紧凑和易读。

注意事项:

  • 数据预处理: 确保 price 数组与 quantity 数组严格对应,并且 price 数组已按升序排列,以保证“最低价优先”的分配原则。
  • 总量匹配: 务必确认 np.sum(orders) 和 np.sum(quantity) 相等,否则分配逻辑会出错。
  • 浮点数精度: 虽然NumPy通常表现良好,但在对结果进行精确比较或在金融等对精度要求极高的领域使用时,应注意浮点数的固有特性,并考虑使用适当的容忍值进行比较。

这种高效的NumPy方法是处理类似库存分配和成本计算问题的推荐实践,它充分体现了NumPy在数据处理方面的强大能力。

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

397

2023.06.20

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

618

2023.07.25

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

354

2023.08.02

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

258

2023.08.09

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

600

2023.09.05

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

525

2023.09.20

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

640

2023.09.20

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

601

2023.09.22

云朵浏览器入口合集
云朵浏览器入口合集

本专题整合了云朵浏览器入口合集,阅读专题下面的文章了解更多详细地址。

20

2026.01.20

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Rust 教程
Rust 教程

共28课时 | 4.6万人学习

Kotlin 教程
Kotlin 教程

共23课时 | 2.7万人学习

Go 教程
Go 教程

共32课时 | 4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号