我有一个包含两列的DataFrame:键和值。我想构造一个新的列,如下所示。对于每个键,请从该键的总值中计算每个值的频率。
我有实现它的代码,但是我怀疑在熊猫中必须有一种更简单的方法来做到这一点。这是一个示例:
def fun(sd):
uniqueValuesList = list(sd.drop_duplicates().dropna())
if len(uniqueValuesList)==0:
return pd.Series([0]*sd.shape[0], index=sd.index)
elif len(uniqueValuesList)==1:
return pd.Series([1]*sd.shape[0], index=sd.index)
else:
valuesList = list(sd)
valuesArr = np.array(valuesList)
stackedValuesDf = pd.DataFrame([valuesArr]*len(valuesArr))
boolDf = stackedValuesDf==valuesList
frac = boolDf.sum() / boolDf.shape[0]
return frac
keys = ['1', '1', '1', '2', '3']
values = ['a', 'b', 'b', 'c', np.nan]
df = pd.DataFrame([keys, values]).T
df.columns = ['keys', 'values']
print(df.groupby('keys').values.apply(fun))
这将提供所需的输出:
0 0.333333
1 0.666667
2 0.666667
3 1.000000
4 0.000000
也就是说,对于键'1'
,'a'
出现一次,而'b'
出现两次,因此它们分别得到0.33和0.67。对于'2'
,有一个单例键,所以它得到1。对于'3'
,没有键,所以它得到0。
实现这一目标的更简单的熊猫方法是什么?
答案 0 :(得分:1)
您不能transform
pd.Series.value_counts
,因此可以对size
使用两个变换:
m = df['values'].notnull()
df.loc[m, 'per'] = (df.loc[m].groupby(['keys', 'values'])['values'].transform('size')
/ df.groupby('keys')['values'].transform('size'))
df['per'] = df['per'].fillna(0)
# keys values per
#0 1 a 0.333333
#1 1 b 0.666667
#2 1 b 0.666667
#3 2 c 1.000000
#4 3 NaN 0.000000
或者,通过合并:
df1 = (df.groupby('keys')['values']
.apply(pd.Series.value_counts, normalize=True)
.to_frame('per'))
df1.index.names=['keys', 'values']
df = df.merge(df1.reset_index(), how='left')
df['per'] = df['per'].fillna(0)