【问题标题】:Calculate average based on available data points根据可用数据点计算平均值
【发布时间】:2022-01-11 19:32:13
【问题描述】:

假设我有以下数据框:

Product Month 1 Month 2 Month 3 Month 4 Total
Stuff A 5 0 3 3 11
Stuff B 10 11 4 8 33
Stuff C 0 0 23 30 53

可以通过以下方式构造:

df = pd.DataFrame({'Product': ['Stuff A', 'Stuff B', 'Stuff C'],
                   'Month 1': [5, 10, 0],
                   'Month 2': [0, 11, 0],
                   'Month 3': [3, 4, 23],
                   'Month 4': [3, 8, 30],
                   'Total': [11, 33, 53]})

此数据框显示每个产品每月的销售量。

现在,我要做的是创建一个名为“平均”的新列,用于计算每月销售的平均单位数。但是,请注意,在此示例中,Stuff C 在第 1 个月和第 2 个月的值为 0。该产品可能是在第 3 个月推出的,因此应仅根据第 3 个月和第 4 个月计算其平均值。另请注意,Stuff A 在第 2 个月的销量为 0,但这并不意味着该产品是在第 3 个月推出的,因为在第 1 个月售出了 5 台。也就是说,它的平均值应基于所有四个月计算。假设提供的数据框可能包含任意月数。

基于这些条件,我想出了下面的伪代码解决方案:

months = ["list of index names of months to calculate"]
x = len(months)

if df["Month 1"] != 0:
  df["Average"] = df["Total"] / x
elif df["Month 2"] != 0:
  df["Average"] = df["Total"] / x - 1
...
elif df["Month " + str(x)] != 0:
  df["Average"] = df["Total"] / 1
else:
  df["Average"] = 0

这样,平均值将从销售量不为 0 的第一个月开始计算。但是,我无法将这种逻辑抽象转换为实际的工作代码。我无法在保持 elif 条件的同时迭代 len(months)。或者也许有更好、更实用的方法。

我将不胜感激,因为我已经尝试解决这个问题一段时间但没有成功。

【问题讨论】:

  • 也许您应该使用 0 以外的其他名称,例如 nanNA 来表示没有数据并完全避免复杂化。见:Working with missing data
  • @Mark 但是 Stuff A 的平均值将不正确,因为它的平均值将通过忽略第 2 个月来计算...
  • 不,你会在那里使用0,因为它代表实际数字0NA 的要点是您需要一种方法来指示缺失值。 Stuff A 中的 0 不是缺失值,它实际上是零。
  • @Mark 不幸的是,我得到的数据不能区分 0 和 NaN。都是零。要么我把它们都变成 NaN(这对我不起作用),要么我想出一种方法来区分它们。

标签: python pandas dataframe loops


【解决方案1】:

有 numpy 方法 np.trim_zeros 可以修剪前导和/或尾随零。使用列表推导,您可以遍历相关的 DataFrame 行,修剪前导零并找到每行剩余内容的平均值。

请注意,由于'Month 1''Month 4' 是连续的,您可以使用.loc 对它们之间的列进行切片。

import numpy as np
df['Average Sales'] = [np.trim_zeros(row, trim='f').mean() for row in df.loc[:, 'Month 1':'Month 4'].to_numpy()]

输出:

   Product  Month 1  Month 2  Month 3  Month 4  Total  Average Sales
0  Stuff A        5        0        3        3     11     2.75
1  Stuff B       10       11        4        8     33     8.25
2  Stuff C        0        0       23       30     53    26.50

【讨论】:

    【解决方案2】:

    试试:

    df = df.set_index(['Product','Total'])
    df['Average'] = df.where(df.ne(0).cummax(axis=1)).mean(axis=1)
    df_out=df.reset_index()
    print(df_out)
    

    输出:

       Product  Total  Month 1  Month 2  Month 3  Month 4  Average
    0  Stuff A     11        5        0        3        3     2.75
    1  Stuff B     33       10       11        4        8     8.25
    2  Stuff C     53        0        0       23       30    26.50
    

    详情:

    将 Product 和 Total 移动到数据帧索引中,这样我们就可以对数据帧的其余部分进行计算。

    首先使用 ne 到零创建一个布尔矩阵。然后,沿行使用 cummax ,这意味着如果有一个非零值,它将保持 True 直到该行结束。如果它从零开始,那么 False 将一直保持到第一个非零,然后转向 Turn 并保持 True。

    接下来,使用 pd.DataFrame.where 仅选择该布尔矩阵的值是 Turn,其他值(前导零)将为 NaN,并且不用于计算平均值。

    【讨论】:

    • cummax 的可爱用法。还是喜欢在这里学习函数的新用途
    • 谢谢斯科特。有趣的方法。我会研究它以了解你在那里做了什么。
    • @David 从理论上讲,回答者应该澄清代码的作用,但拥有 126,556 的声誉,我猜你不这样做......
    • @Neither 你说得对,我加了一些解释。
    【解决方案3】:

    如果你不介意它的内存效率有点低,你可以将你的数据框放入一个 numpy 数组中。 Numpy 有一个内置函数可以从数组中删除零,然后你可以使用 mean 函数来计算平均值。它可能看起来像这样:

    import numpy as np
    
    arr = np.array(Stuff_A_DF)
    mean = arr[np.nonzero(arr)].mean()
    

    或者,您可以手动将行提取到列表中,然后循环删除零。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多