通过排除四列值的离群值(四列中值的+ -25%)来计算中值

时间:2019-08-25 10:21:40

标签: python-3.x pandas dataframe median

我有四个列,其中包含最近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值来计算平均值。

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

(同样,如果有人可以通过避免离群值来提供更好的方法来计算中位数) 数据集[data具有1月2日和3月3日的值(2014、15、16、17、18)。 Year_Lag1(shift(1))具有1月2日和1月3日的上一年的值。 Year_Lag2(shift(2)的值是从去年到去年),依此类推。]

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

2 个答案:

答案 0 :(得分:0)

如果您可以添加数据框以供参考,那将是很好的。尝试通过在今年生成随机日期,随机浮动金额和随机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

确保头部不是唯一一个带有Nans的人。整个专栏都得到了nans。如果您可以修改原始帖子以包含df,则可以轻松回答问题。

答案 1 :(得分:0)

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

  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得分,但是我不确定它在小数据上是否能很好地工作,可以尝试并改编或创建新函数。