通过排除四列值的异常值(四列中位数的+-25%)来计算中位数

calculate the median by excluding outliers (+-25% of median of four columns) for values of four columns values

我有四列包含过去 4 年的销售量。我想计算中位数,然后根据条件(+-25% 的中位数)排除列值来计算平均值。

df = df[['Date','ID','amount']] # df has daily data

df['dayofYear'] = df['Date'].dt.dayofyear

df['Year_Lag1']= df.groupby(['ID','dayofYear'])['amount'].transform(lambda x: x.shift(1))

df['Year_Lag2']= df.groupby(['ID','dayofYear'])['amount'].transform(lambda x: x.shift(2))

df['Year_Lag3']= df.groupby(['ID','dayofYear'])['amount'].transform(lambda x: x.shift(3))

df['Year_Lag4']= df.groupby(['ID','dayofYear'])['amount'].transform(lambda x: x.shift(4))


df['YearLag_median']= df[['Year_Lag1','Year_Lag2','Year_Lag3','Year_Lag4']].median(axis=1) #median amount on same date (is there a better way to calculate median by avoiding outliers)

如何通过避免四列中位于中位数 +- 25% 的值来计算平均值。

假设Year_Lag1 = 5000, Year_Lag2= 230, Year_Lag3=4500, Year_Lag4= 4300.

如何通过避免 Year_Lag2 值来计算平均值。

我希望对数据框中的所有行执行此操作。

(如果有人可以通过避免异常值来提供更好的计算中位数的方法) 数据集[数据具有1月2日和3日(2014、15、16、17、18)的值。 Year_Lag1(shift(1)) 具有去年 1 月 2 日和 3 日的值。 Year_Lag2(shift(2) 的值从去年到去年)等等..]

最后一行是我想忽略 589.0 来计算平均值的示例。 [1]: https://i.stack.imgur.com/26Dvp.pngenter code here

如果您能添加一个数据框以供参考,那就太好了。尝试通过生成今年的随机日期、随机浮点数和随机 ID 来生成您在下面指定的 df。我的 df 的 5 行头部看起来像这样:

df.head(5)
   amount                          Date   ID
0      93 2019-01-01 00:00:00.000000000  AAA
1      40 2019-01-03 08:43:38.181818181  AAA
2      47 2019-01-05 17:27:16.363636363  BBB
3      37 2019-01-08 02:10:54.545454545  CCC
4      13 2019-01-10 10:54:32.727272727  CCC

这是您的数据集的样子吗? 如果是这样,那么 运行 使您提到的导致滞后的命令似乎无法按预期工作。当我 运行 它时,我得到如下所述:

df['dayofYear'] = df['Date'].dt.dayofyear
df.head(5)
                           Date   ID  amount  dayofYear
0 2019-01-01 00:00:00.000000000  AAA      93          1
1 2019-01-03 08:43:38.181818181  AAA      40          3
2 2019-01-05 17:27:16.363636363  BBB      47          5
3 2019-01-08 02:10:54.545454545  CCC      37          8
4 2019-01-10 10:54:32.727272727  CCC      13         10

df['Year_Lag1']= df.groupby(['ID','dayofYear'])['amount'].transform(lambda x: x.shift(1))
df.head(5)
                           Date   ID  amount  dayofYear  Year_Lag1
0 2019-01-01 00:00:00.000000000  AAA      93          1        NaN
1 2019-01-03 08:43:38.181818181  AAA      40          3        NaN
2 2019-01-05 17:27:16.363636363  BBB      47          5        NaN
3 2019-01-08 02:10:54.545454545  CCC      37          8        NaN
4 2019-01-10 10:54:32.727272727  CCC      13         10        NaN

已经确定head不是唯一有nans的。整列都得到了 nans。如果您可以修改原始 post 以包含 df ,那么回答您的问题会容易得多。

这是一个解决方案,我认为应该有更好的办法,但仍然有效:

  1. 定义一个函数来根据您的规格计算平均值
def calculateMean(row):
    s = 0
    n = 0
    for i in range(4):
        if ~np.isnan(row[i]) and abs(row[i] - row[-1]) < 0.25 * row[-1]:
            s += row[i]
            n += 1
    return (s/n if n else np.nan)
  1. 对每一行应用这个函数
df["YearLag_mean"] = df.loc[:, ['Year_Lag1','Year_Lag2','Year_Lag3','Year_Lag4', 'YearLag_median']]\
                       .apply(lambda row: calculateMean(row), axis=1)

输出:

            Date   ID  amount  dayofYear  Year_Lag1  Year_Lag2  Year_Lag3  Year_Lag4  YearLag_median  YearLag_mean
2258  2014-01-02  200  1778.0          2        NaN        NaN        NaN        NaN             NaN           NaN
2259  2014-01-03  200  2149.0          3        NaN        NaN        NaN        NaN             NaN           NaN
2623  2015-01-02  200  2057.0          2     1778.0        NaN        NaN        NaN          1778.0       1778.00
2624  2015-01-03  200  2401.0          3     2149.0        NaN        NaN        NaN          2149.0       2149.00
2988  2016-01-02  200  2315.0          2     2057.0     1778.0        NaN        NaN          1917.5       1917.50
2989  2016-01-03  200   589.0          3     2401.0     2149.0        NaN        NaN          2275.0       2275.00
3354  2017-01-02  200  1709.0          2     2315.0     2057.0     1778.0        NaN          2057.0       2050.00
3355  2017-01-03  200  1659.0          3      589.0     2401.0     2149.0        NaN          2149.0       2275.00
3719  2018-01-02  200  1991.0          2     1709.0     2315.0     2057.0     1778.0          1917.5       1964.75
3720  2018-01-03  200  1570.0          3     1659.0      589.0     2401.0     2149.0          1904.0       1904.00

如您所见,由于您的阈值,最后一行没有使用 589 但也使用了 2401

要删除异常值而不是使用中位数,您可以查看 IQR 或 Z-score,但我不确定它是否适用于小数据,您可以尝试一下,只是调整或创建新函数。