Python: (Pandas) 如何忽略按 id 分组的值的最低和最高 25% 以进行均值计算

Python: (Pandas) How to ignore the lowest and highest 25% of values grouped by id for mean calculation

我试图在按 id 分组时获取每列的平均值,但是对于计算,只应使用第一个 25% 分位数和第三个 75% 分位数之间的 50%。 (因此忽略最低的 25% 和最高的 25%)

数据:

ID       Property3   Property2   Property3
1        10.2        ...         ...
1        20.1
1        51.9
1        15.8
1        12.5
...
1203     104.4
1203     11.5
1203     19.4
1203     23.1

我试过的:

data.groupby('id').quantile(0.75).mean();
#data.groupby('id').agg(lambda grp: grp.quantil(0.25, 0,75)).mean(); something like that?
CW            67.089733
fd             0.265917
fd_maxna   -1929.522001
fd_maxv    -1542.468399
fd_sumna   -1928.239954
fd_sumv    -1488.165382
planc        -13.165445
slope         13.654163

类似的东西,但是 GroupByDataFrame.quantil 据我所知不知道中间值,我现在也不知道如何删除较低的 25%。 这也不是 return 数据框。

我想要的
理想情况下,我希望有一个数据框如下:

ID       Property3   Property2   Property3
1        37.8        5.6         2.3
2        33.0        1.5         10.4
3        34.9        91.5        10.3
4        33.0        10.3        14.3

其中仅使用 25% 分位数和 75% 分位数之间的数据进行均值计算。所以只有中间的50%。

您可以使用 quantile 函数来 return 多个分位数。然后,您可以根据此筛选出值,并计算平均值:

def filter_mean(df):
    bounds = df.quantile([.25, .75])
    mask = (df < bounds.loc[0.75]) & (df > bounds.loc[0.25])
    return df[mask].mean()

means = data.groupby("id").apply(filter_mean)

请试试这个。

def mean_of_25_to_75_pct(s: pd.Series):
    low, high = s.quantile(.25), s.quantile(.75)
    return s.loc[(s >= low) & (s < high)].mean()

data.groupby("id").apply(lambda x: x.apply(mean_of_25_to_75_pct))

您可以使用 scipy 现成的函数来计算均值,trim_mean():

from scipy import stats

means = data.groupby("id").apply(stats.trim_mean, 0.25)

如果您坚持要获取数据框,您可以:

data.groupby("id").agg(lambda x: stats.trim_mean(x, 0.25)).reset_index()

在这里使用 GroupBy.apply 可能会很慢所以我想这是你的数据框:

print(df)
     ID  Property3   Property2   Property1
0     1       10.2   58.337589   45.083237
1     1       20.1   70.844807   29.423138
2     1       51.9   67.126043   90.558225
3     1       15.8   17.478715   41.492485
4     1       12.5   18.247211   26.449900
5  1203      104.4  113.728439  130.698964
6  1203       11.5   29.659894   45.991533
7  1203       19.4   78.910591   40.049054
8  1203       23.1   78.395974   67.345487

所以我会使用 GroupBy.cumcount + DataFrame.pivot_table 在不使用应用的情况下计算分位数:

df['aux']=df.groupby('ID').cumcount()
new_df=df.pivot_table(columns='ID',index='aux',values=['Property1','Property2','Property3'])
print(new_df)

     Property1              Property2             Property3       
ID        1           1203       1           1203      1      1203
aux                                                               
0    45.083237  130.698964  58.337589  113.728439      10.2  104.4
1    29.423138   45.991533  70.844807   29.659894      20.1   11.5
2    90.558225   40.049054  67.126043   78.910591      51.9   19.4
3    41.492485   67.345487  17.478715   78.395974      15.8   23.1
4    26.449900         NaN  18.247211         NaN      12.5    NaN

#remove aux column
df=df.drop('aux',axis=1)

现在我们用boolean indexing计算平均值:

new_df[(new_df.quantile(0.75)>new_df)&( new_df>new_df.quantile(0.25) )].mean()

           ID  
Property1  1       59.963006
           1203    70.661294
Property2  1       49.863814
           1203    45.703292
Property3  1       15.800000
           1203    21.250000
dtype: float64

或者用平均值创建DataFrame:

mean_df=( new_df[(new_df.quantile(0.75)>new_df)&( new_df>new_df.quantile(0.25) )].mean()
                                                                                 .rename_axis(index=['Property','ID'])
                                                                                 .unstack('Property') )
print(mean_df)

Property  Property1  Property2  Property3
ID                                       
1         41.492485  58.337589      15.80
1203      56.668510  78.653283      21.25

测量次数:

%%timeit
df['aux']=df.groupby('ID').cumcount()
new_df=df.pivot_table(columns='ID',index='aux',values=['Property1','Property2','Property3'])
df=df.drop('aux',axis=1)
( new_df[(new_df.quantile(0.75)>new_df)&( new_df>new_df.quantile(0.25) )].mean()
                                                                         .rename_axis(index=['Property','ID'])
                                                                         .unstack('Property') )

25.2 ms ± 1.09 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
def mean_of_25_to_75_pct(s: pd.Series):
    low, high = s.quantile(.25), s.quantile(.75)
    return s.loc[(s >= low) & (s < high)].mean()

df.groupby("ID").apply(lambda x: x.apply(mean_of_25_to_75_pct))

33 ms ± 1.32 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
def filter_mean(df):
    bounds = df.quantile([.25, .75])
    mask = (df < bounds.loc[0.75]) & (df > bounds.loc[0.25])
    return df[mask].mean()

means = df.groupby("ID").apply(filter_mean)

23 ms ± 809 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

对于小数据帧甚至几乎更快,在更大的数据帧(例如其原始数据帧)中,它会比其他提出的方法快得多,请参见: