【问题标题】:Loop in python for moving averages based on condition在python中循环根据条件移动平均线
【发布时间】:2016-08-12 13:18:58
【问题描述】:

我有 5 周的产品和商店组合的销售数据。在这 5 周中,前三个可以参加促销活动。第一个正在促销中,或者第二个或第三个或所有三个或一个都没有促销。所以总共有8个条件。现在我想在 python 中使用循环计算移动平均类型的东西。简单来说,我将根据检查特定周是否在促销来计算平均值,然后以不同方式计算。 (每周循环的开始和结束如下

表 1

                week    week    week    week    week
                1       2       3       4       5
loop_start_1    1       1       1       2       3
loop_end_1      3       4       5       5       5
loop_start_2    1       1       2       3       4
loop_end_2      2       3       4       5       5

对于第 1 周,我将取第 1、第 2 和第 3 周的平均值,但对于第 2 周,我将取 1、2、3、4 的平均值,依此类推。现在,如果有一周的促销活动,这种情况将会改变。我的输入数据为:

表 2

prod_id store_id    sales_week1 sales_week2 sales_week3 sales_week4  sales_week5    promo_1 promo_2 promo_3 promo_4 promo_5
12345     22222         40          44          50          20            21            0       0       0       0       0
12346     22222         82          85          51          72            97            1       0       0       0       0
12347     22222         74          113         31          25            19            0       1       0       0       0
12348     22222         74          105         195         216           142           0       0       1       0       0
12349     22222         78          81          23          10            67            1       1       0       0       0
12243     22222         159         190         223         137           89            0       1       1       0       0
12240     22222         591         457         556         278           726           1       0       1       0       0
22240     22222         76          49          84          132           121           1       1       1       0       0

最多可以有 8 种不同的组合。根据前三周中的哪一周我们有促销活动。我希望输出如下(基于下面提到的循环的移动平均值)

表 3

prod_id store_id    sales_week1 sales_week2 sales_week3 sales_week4  sales_week5    promo_1 promo_2 promo_3 promo_4 promo_5
12345     22222         44.6667         38.5            35          33.75             30.3333            0       0      0       0       0
12346     22222         82              69.3333         76.25       76.25             73.3333            1       0      0       0       0
12347     22222         52.5            113             37.25       25                25                 0       1      0       0       0
12348     22222         89.5            131.6667        195         154.3333          179                0       0      1       0       0
12349     22222         79.5            79.5            33.3333     33.3333           33.3333            1       1      0       0       0
12243     22222         159             206.5           206.5       113               113                0       1      1       0       0
12240     22222         591             367.5           556         487               502                1       0      1       0       0
22240     22222         62.5            69.6667         66.5        126.5             126.5              1       1      1       0       0

现在我有一个虚拟代码(8 个条件),我想根据促销条件将其转换为循环。不想写这么多 if 条件。请帮助提前谢谢。

if promo_1 =1 then:
sales_new[1]=sales_old[1]
sales_new[2]=average(sales_old[2],sales_old[3],sales_old[4])
sales_new[3]=avergae(sales_old[2],sales_old[3],sales_old[4],sales[5])
sales_new[4]=avergae(sales_old[2],sales_old[3],sales_old[4],sales[5])
sales_new[5]=avergae(sales_old[3],sales_old[4],sales[5])

if promo_2 =1 then:
sales_new[1]=average(sales_old[1],sales_old[3])
sales_new[2]=sales_old[2]
sales_new[3]=avergae(sales_old[1],sales_old[3],sales_old[4],sales[5])
sales_new[4]=avergae(sales_old[3],sales_old[4],sales[5])
sales_new[5]=avergae(sales_old[3],sales_old[4],sales[5])

if promo_3 =1 then:
sales_new[1]=average(sales_old[1],sales_old[2])
sales_new[2]=average(sales_old[1],sales[2],sales[4]
sales_new[3]=sales_old[3]
sales_new[4]=avergae(sales_old[2],sales_old[4],sales[5])
sales_new[5]=avergae(sales_old[4],sales[5])

if promo_1 =1 and promo_2=1 then:
sales_new[1]=average(sales_old[1],sales_old[2])
sales_new[2]=average(sales_old[1],sales_old[2])
sales_new[3]=average(sales_old[3],sales_old[4],sales_old[5])
sales_new[4]=average(sales_old[3],sales_old[4],sales_old[5])
sales_new[5]=average(sales_old[3],sales_old[4],sales_old[5])

if promo_2 =1 and promo_3=1 then:
sales_new[1]=sales_old[1]
sales_new[2]=average(sales_old[2],sales_old[3])
sales_new[3]=average(sales_old[2],sales_old[3])
sales_new[4]=average(sales_old[4],sales_old[5])
sales_new[4]=average(sales_old[4],sales_old[5])

if promo_1 =1 and promo_3=1 then:
sales_new[1]=sales_old[1]
sales_new[2]=average(sales_old[2],sales_old[4])
sales_new[3]=sales_old[3]
sales_new[4]=average(sales_old[2],sales_old[4],sales_old[5])
sales_new[4]=average(sales_old[4],sales_old[5])

if promo_1 =1 and promo_2=1 and promo_3=1 then:
sales_new[1]=average(sales_old[1],sales_old[2])
sales_new[2]=average(sales_old[1],sales_old[2],sales_old[3])
sales_new[3]=average(sales_old[2],sales[3])
sales_new[4]=average(sales_old[4],sales_old[5])
sales_new[5]=average(sales_old[4],sales_old[5])

if promo_1 =0 and promo_2=0 and promo_3=0 then:
sales_new[1]=average(sales_old[1],sales_old[2],sales_old[3])
sales_new[2]=average(sales_old[1],sales_old[2],sales_old[3],sales_old[4])
sales_new[3]=average(sales_old[1],sales_old[2],sales_old[3],sales_old[4],sales_old[5])
sales_new[3]=average(,sales_old[2],sales_old[3],sales_old[4],sales_old[5])
sales_new[5]=average(sales_old[3],sales_old[4],sales_old[5])

业务规则

在本节中,我提供了业务规则来阐明我的代码的要求。

在最高级别,我的目标是计算销售额的移动平均值。我通过一个脚本来执行此操作,该脚本根据“旧”(原始)数据计算新的销售数据(包括这些平均值)。

对于未升级的任何一周,我们将考虑 loop_start_1 和 loop_end_1,但如果一周升级,我们将考虑 >loop_start_2 和 loop_end_2

一个规定是每个新周的销售数据 - 默认情况下 - 来自旧周销售数据的子集。这在表 1 中指定。例如,如果没有促销周,则(默认情况下)根据第 1-3 周的旧销售数据计算第 1 周的新销售数据。

但是,每周可能是也可能不是“促销”周,这将影响该周的旧销售数据是否可以用于计算新的销售数据。 这会覆盖表 1 loop_start_1 和 loop_end_1 指定的任何内容。对于促销,我们将考虑表 1 中的 loop_start_2 和 loop_end_2,特别是:

  • 如果本周没有促销活动,我们将考虑 loop_start_1 和 loop_end_1 else
  • 如果前三周中的任何一周被提升,那么我们将考虑 loop_start_2 和 loop_end_2

例如,如果第 1 周是促销活动,那么我们需要执行以下操作:

  • 计算week_1_new = avg(week_1_old)

    • 表 1 指定我们使用旧周 1-2。(loop_start_2 和 loop_end_2)
    • 第 1 周是促销活动,第 2 周不是 - 所以请参加第 1 周
  • 计算week_2_new = avg(week_2_old, week_3_old, week_4_old)

    • 表 1 指定我们使用旧周 1-4。(loop_start_1 和 loop_end_1)
    • 第 1 周正在进行促销,第 2、3 和 4 周没有 - 所以大部分时间没有促销。所以促销的销售额会被夸大,所以我们会将它们排除在平均计算之外
    • 我们只考虑未促销的那几周的数据 - 2、3 和 4。
  • ...

另一方面,如果第 1 周和第 2 周都在促销,那么考虑我们如何计算第 1 周的新销售数据:

  • week_1_new = avg(week_1_old, week_2_old)
    • 表 1 指定我们使用旧周 1-2。(loop_start_2 和 loop_end_2)
    • 第 1 周和第 2 周正在进行促销 - 所以大部分时间都在促销。我们平均需要这两个星期
    • 我们只考虑促销周的数据 - 1 和 2。

另一方面,如果第 1 周和第 3 周都在促销,那么考虑我们如何计算第 1 周的新销售数据:

  • week_1_new = avg(week_1_old) (loop_start_2 and loop_end_2) 1-2 周中只有第 1 周在促销。我们从不将产品促销时的销售额与未促销的星期一起添加。因为这会夸大平均值。
  • week_2_new = avg(week_2_old,week_4_old)((loop_start_1 和 loop_end_1 作为 1-4 中只有 2 和 4 没有提升)
  • week_3_new = avg(week_3_old)

    • 我们只考虑促销周的数据 - 1 和 3

另一方面,如果第 1 周到第 3 周都在促销,那么请考虑我们如何计算第 1 周的新销售数据:

  • week_1_new = avg(week_1_old,week_2_old)我们需要(loop_start_2 和 loop_end_2
  • week_2_new = avg(week_1_old,week_2_old,week_4_old)as(loop_start_2 和 loop_end_2)将第 1-3 周列为第 2 周的晋升周
  • week_3_new = avg(week_2_old,week_3_old)as(loop_start_2 和 loop_end_2)状态为第 2-4 周。但是在 2,3,4 中只有 2 和 3 被提升了。所以只需要提升的周数

【问题讨论】:

  • 也许你应该展示更多最小的例子。
  • 您的代码有效吗?你只是想优化它,对吧?
  • 不,代码不是用 python 编写的,它们是我用文字写的 8 种不同的条件。也会添加一个例子
  • 那么您在寻找什么?如果您只提供概念,我只能得出结论,您希望有人为您编写代码。根据 Stackoverflow 规则,这是不允许的。您需要编写并尝试调试自己,然后在遇到困难时提出问题。
  • @Mukal 如果在给定的一周内有促销活动,您能否解释一下计算新销售数据的规则是什么?对业务规则有更清晰的理解将使我们能够创建更好的解决方案。

标签: python loops python-3.x for-loop


【解决方案1】:

在撰写本文时,我对您问题的修改仍在等待中,您尚未确认我是否正确理解了您的业务规则。假设我有,这是我尝试为您整理的解决方案:

from typing import Generator, Dict, List, Tuple

import numpy

TOTAL_WEEKS = 5


def create_default_sales_data_map() -> Dict[int, List[int]]:
    """
    Create and return the default mapping of old week sales data
    to new averaged week sales data.

    The mapping is a tuple specifying the (inclusive) lower and upper
    range of weeks to consider the sales data of by default when calculating
    the new sales data for the week specified by the dictionary key.

    This is based on Table 1.

    :return: the default sales data mapping
    """
    sales_data_map = {1: (1, 3), 2: (1, 4), 3: (1, 5), 4: (2, 5), 5: (3, 5)}

    return sales_data_map


def are_majority_weeks_promoted(promoted_weeks: [bool]) -> bool:
    """
    Return if the majority of weeks are promoted or not.

    :param promoted_weeks: The promotion status of each week
    :return: `true` if the majority of weeks are promoted
    """
    return sum(promoted_weeks) >= len(promoted_weeks) / 2


def calculate_new_sales_data(old_sales_data: List[int], promoted_weeks: List[bool]) -> int:
    """
    Calculate new (averaged) sales data for a week based on old (raw) sales data and the
    promotion status of the relevant weeks.

    :param old_sales_data: the old week sales data used by default (i.e. according to Table 1)
    :param promoted_weeks: the promotion status of the weeks corresponding to `old_sales_data`
    :return: the new sales data for a particular week
    """
    majority = are_majority_weeks_promoted(promoted_weeks)
    relevant_data = [data for i, data in enumerate(old_sales_data) if promoted_weeks[i] == majority]
    new_sales_data = numpy.mean(relevant_data)
    return new_sales_data


def calculate_all_new_sales_data(complete_old_sales_data: List[int], complete_promoted_weeks: List[bool]) -> Generator[int, None, None]:
    """
    Generates new sales data for all possible weeks based on the supplied sales week data.

    :param complete_old_sales_data: the complete set of information on old week sales data
    :param complete_promoted_weeks: the promotion status for all weeks
    :return: a generator for new sales data
    """
    sales_data_map = create_default_sales_data_map()
    for week, (lower, upper) in sales_data_map.items():
        old_sales_data = complete_old_sales_data[lower-1 : upper]
        promoted_weeks = complete_promoted_weeks[lower-1 : upper]
        yield calculate_new_sales_data(old_sales_data, promoted_weeks)


def query_user(total_weeks: int) -> Generator[Tuple[int, bool], None, None]:
    """
    Query the user for the old sales data values and promoted weeks and return the results.

    Each generated tuple contains the old sales data for the week, and whether the week is promoted.

    :param total_weeks: the number of weeks' worth of data to query the user for.
    :return: a generator of sales data tuples
    """
    for i in range(total_weeks):
        while True:
            try:
                old_sales_data = int(input("Total sales data for week {}: $".format(i+1)))
                break
            except ValueError:
                continue

        while True:
            promotion_status_str = input("Is week promoted (Y/N)? ").lower()
            if promotion_status_str in ["y", "n"]:
                promotion_status = promotion_status_str == "y"
                break

        yield old_sales_data, promotion_status


def main():
    old_sales_data, promoted_weeks = zip(*query_user(TOTAL_WEEKS))

    print()
    for week, new_sales_data in enumerate(calculate_all_new_sales_data(old_sales_data, promoted_weeks), 1):
        print("Week {} averaged sales data: ${:.2f}".format(week, new_sales_data))


if __name__ == '__main__':
    main()

输入

Total sales data for week 1: $10
Is week promoted (Y/N)? y
Total sales data for week 2: $20
Is week promoted (Y/N)? y
Total sales data for week 3: $30
Is week promoted (Y/N)? n
Total sales data for week 4: $40
Is week promoted (Y/N)? y
Total sales data for week 5: $50
Is week promoted (Y/N)? n

输出

Week 1 averaged sales data: $15.00
Week 2 averaged sales data: $23.33
Week 3 averaged sales data: $23.33
Week 4 averaged sales data: $30.00
Week 5 averaged sales data: $40.00

说明

  • 对于“新”第 1 周,表 1 表示要考虑“旧”第 1-3 周。第 1 周和第 2 周得到提升,但第 3 周没有提升,因此我们仅平均“旧”第 1 周和第 2 周 - 即我们平均 10 美元和 20 美元才能获得 15 美元。
  • 对于“新”第 2 周,我们默认考虑“旧”第 1-4 周。这些周中的大部分(1、2 和 4)都被提升了,所以我们只考虑这些和平均值。 10 美元、20 美元和 40 美元的平均值为 23.33 美元。
  • ...

我们可以使用这些值来测试行为是否正确。例如,让我们让默认情况下考虑的“新”销售第 1 周数据的大多数周不被提升:

Total sales data for week 1: $10
Is week promoted (Y/N)? y
Total sales data for week 2: $20
Is week promoted (Y/N)? n
Total sales data for week 3: $30
Is week promoted (Y/N)? n
...
Week 1 averaged sales data: $25.00

在这种情况下,只考虑第 2 周和第 3 周。

如果前三周都升职了呢?

Total sales data for week 1: $10
Is week promoted (Y/N)? y
Total sales data for week 2: $20
Is week promoted (Y/N)? y
Total sales data for week 3: $30
Is week promoted (Y/N)? y
...
Week 1 averaged sales data: $20.00

在这里,我们考虑所有的星期,并平均它们的销售数据。

【讨论】:

  • 这是我想学习的东西:)。有史以来最好的答案。根据我的编辑,仍然需要一些修改。你可以为真棒 sn-p 制作它们。+1
  • 不用担心@Mukul,但我可以把这些剩余的修改留给你练习吗?我从中得到了很好的练习,通过完成它你也可以。我想继续讨论其他问题。 ;)
  • ImportErrorTraceback (最近一次调用最后一次) in () 22 23 # print([i for i, data in enumerate(tuples)]) -- -> 24 from typing import Generator, Dict, List, Tuple ImportError: No module named 'typing'
  • 您使用的是 Python 3.5 吗?如果没有,您可以删除这些导入和代码中的所有类型提示。
  • NameErrorTraceback(最近一次通话最后) in () 17 18 ---> 19 def calculate_new_sales_data(product_week_ros: List[int],promoted_weeks: List [bool]) -> int: 20 21 多数 = are_majority_weeks_promoted(promoted_weeks) NameError: name 'List' is not defined
【解决方案2】:

这有意义吗?

sales_new[1] = average(sales_old[1],sales_old[2])
sales_new[2] = average(sales_old[1],sales_old[2],sales_old[3])
sales_new[3] = average(sales_old[2],sales_old[3],sales_old[4])
sales_new[4] = average(sales_old[3],sales_old[4],sales_old[5])
sales_new[5] = average(sales_old[4],sales_old[5])

if promo_1 then:
    sales_new[1] = sales_old[1]

if promo_2 then:
    sales_new[2] = sales_old[2]

if promo_3 then:
    sales_new[3] = sales_old[3]

if promo_4 then:
    sales_new[3] = sales_old[3]

if promo_5 then:
    sales_new[3] = sales_old[3]

计算“运行”平均值,然后根据促销活动对其进行修改。

注意:您的数组可能是基于索引的,因此它将从 sales_new[0] 开始。

【讨论】:

  • 我的论文是基于分解 OP 的问题 (stackoverflow.com/a/38922747/1636276) 的,我不相信您的解决方案有效,恐怕。例如,week_1_new = avg(week_1_old, week_2_old, week_3_old) 如果 all ofnone of 第 1、2 和 3 周正在促销。您对week_1_new 的计算永远不会包含week_3_old
猜你喜欢
  • 1970-01-01
  • 2017-09-01
  • 1970-01-01
  • 2013-12-22
  • 1970-01-01
  • 2021-05-30
  • 2019-02-16
  • 2021-06-19
  • 2022-11-18
相关资源
最近更新 更多