我有一个数据帧,如下所示:
input_df:
name name_group value
foo1 a 2
foo2 a 2
foo3 a 2
foo4 a 2
bar1 b
bar2 b
bar3 b
buzz1 c 6
buzz2 c 6
buzz3 c 6
buzz4 c 6
buzz5 c 6
其中每个name_group中的每一行都具有相同的"值",因此在这种情况下,name_group"a"中的每个名称都具有相同的"值","b"name_group中的每个名称都具有相同的"值",依此类推。
我想创建一个新列"new_vals",它等于该"name_group"、"值"列中行的 min(#)。如果任何"name_group"的"value"列中缺少值,则应仅使用该"name_group"中的#行。对于我的示例数据帧,所需的输出为:
output_df:
name name_group value new_vals
foo1 a 2 2
foo2 a 2 2
foo3 a 2 2
foo4 a 2 2
bar1 b 3
bar2 b 3
bar3 b 3
buzz1 c 6 5
buzz2 c 6 5
buzz3 c 6 5
buzz4 c 6 5
buzz5 c 6 5
目前,我实现这一目标的方法是遍历"name_group"列中的每个唯一值,找到该name_group中的 # 行,将其与"value"列中的值进行比较,然后根据两者之间的最小值设置"new_val"列的值。每个"name_group"的结果都连接到另一个数据帧,直到我得到最终输出。
虽然这种方法有效,但我觉得必须有一种更有效的方法来做到这一点,而不是获取起始数据帧的子集,分别处理每个"name_group",然后将所有内容重新组合在一起。有没有人有更pythonic/更有效的方式来重现此功能?
以下是一些反映我当前流程的代码:
output_df = pd.DataFrame()
for name_group in input_df['name_group'].unique():
# process the data one name group at a time
temp_df = input_df.loc[input_df['name_group'] == name_group]
max_val = temp_df['value'].max()
name_group_cnt = temp_df.shape[0]
# if the "value" column is empty, set new_val equal to the number of
# rows in that name_group
if max_val == '':
new_val = name_group_cnt
else:
new_val = min(max_val, name_group_cnt)
temp_df['new_val'] = new_val
output_df = pd.concat([output_df, temp_df])
我将使用transform
和np.where
s=df.groupby('name_group').name_group.transform('count')
df['New']=np.where(s>df.value,df.value,s)
df
Out[13]:
name name_group value New
0 foo1 a 2.0 2.0
1 foo2 a 2.0 2.0
2 foo3 a 2.0 2.0
3 foo4 a 2.0 2.0
4 bar1 b NaN 3.0
5 bar2 b NaN 3.0
6 bar3 b NaN 3.0
7 buzz1 c 6.0 5.0
8 buzz2 c 6.0 5.0
9 buzz3 c 6.0 5.0
10 buzz4 c 6.0 5.0
11 buzz5 c 6.0 5.0
一种解决方案是创建一个临时count
,然后比较值。
vs = df.groupby("new_group").size().to_dict()
# vs = {'a': 4, 'c': 5, 'b': 3}
df["count"] = df["new_group"].apply(lambda k: vs[k])
def comp(row):
if row["value"] is np.nan: return row["count"]
return min(row["value"], row["count"])
df["new_vals"] = df.apply(comp, 1)
# equivalent to
# df = df[['value', 'count']].min(axis=1)
输出:
new_group value count new_vals
0 a 2.0 4 2.0
1 a 2.0 4 2.0
2 a 2.0 4 2.0
3 a 2.0 4 2.0
4 b NaN 3 3,0
5 b NaN 3 3.0
6 b NaN 3 3.0
7 c 6.0 5 5.0
8 c 6.0 5 5.0
9 c 6.0 5 5.0
10 c 6.0 5 5.0
11 c 6.0 5 5.0
这里有一种方法可以做到这一点。这个想法是计算每个name_group
的行数,并将其合并到数据帧中作为新列(称为count
)。然后new_vals
将仅计算为value
列和count
列的最小值:
df = df.merge(df.groupby('name_group').size().reset_index(name='count'), on='name_group')
df['new_vals'] = df[['value', 'count']].min(axis=1)
然后,如果需要,您可以使用df.drop(columns='count', inplace=True)
删除count
列,以给出:
name name_group value new_vals
0 foo1 a 2.0 2.0
1 foo2 a 2.0 2.0
2 foo3 a 2.0 2.0
3 foo4 a 2.0 2.0
4 bar1 b NaN 3.0
5 bar2 b NaN 3.0
6 bar3 b NaN 3.0
7 buzz1 c 6.0 5.0
8 buzz2 c 6.0 5.0
9 buzz3 c 6.0 5.0
10 buzz4 c 6.0 5.0
11 buzz5 c 6.0 5.0