每月基于列的大值的子集数据框

Subset dataframe based on large values of a column per month

我有一个这样的 df:

month stock MV
1994-07 A 50
1994-07 B 60
1994-07 C 70
1994-07 D 80
1994-08 A 90
1994-08 B 60
1994-08 C 70
1994-08 D 95
1994-08 E 100
1994-08 F 110

我想以一种我每月只有最高 MV 的 50% 的方式对我的 df 进行子集化。对于 July/1994 我只有 4 个股票,所以 50% 将是 2 个最高的 MV。之后的一个月,我有 6 只股票,这给了我 3 个最高值:

month stock MV
1994-07 C 70
1994-07 D 80
1994-08 D 95
1994-08 E 100
1994-08 F 110

我试过:

df = df.groupby(pd.Grouper(freq="M")).nlargest(2, "MV")

但我收到错误:AttributeError: 'DataFrameGroupBy' object has no attribute 'nlargest' 此外,每个月的 n 值都需要不同。我也不知道怎么处理。

你需要通过apply

df = df.groupby(pd.Grouper(freq="M")).apply(lambda x : x.nlargest(2, "MV"))

df.groupby('month').apply(lambda monthly_data: monthly_data[monthly_data['MV'] >= monthly_data['MV'].median())

您需要使用 apply 并计算最终长度:

df.groupby(pd.Grouper(freq="M")).apply(lambda g: g.nlargest(len(g)//2, 'MV'))

将提供的数据作为字符串:

(df.groupby('month', as_index=False, group_keys=False) # use pd.Grouper if needed here
   .apply(lambda g: g.nlargest(len(g)//2, 'MV'))
   .sort_values(by=['month', 'MV'])
)

输出:

     month stock   MV
2  1994-07     C   70
3  1994-07     D   80
7  1994-08     D   95
8  1994-08     E  100
9  1994-08     F  110

您可以对值进行排序,然后 select lambda 函数中值长度的 50%:

df = (df.sort_values(['month','MV'])
        .groupby(pd.Grouper(freq="M"), group_keys=False)
        .apply(lambda x: x.head(int(len(x)) // 2)))

您可以使用median(或quantile):

out = df.loc[df['MV'] > df.groupby('month')['MV'].transform('median')]
print(out)

# Output
     month stock   MV
2  1994-07     C   70
3  1994-07     D   80
7  1994-08     D   95
8  1994-08     E  100
9  1994-08     F  110

quantile 相同的版本,但如果您想更改百分比,您有更多的控制权:

out = df.loc[df['MV'] > df.groupby('month')['MV'].transform('quantile', 0.5)]
print(out)

# Output
     month stock   MV
2  1994-07     C   70
3  1994-07     D   80
7  1994-08     D   95
8  1994-08     E  100
9  1994-08     F  110