如何根据多个条件按变量组有效更新数据框值?

时间:2018-04-11 23:58:14

标签: python pandas dataframe

我的数据框如下:

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”中的每个名称都具有相同的“值”,name_group“b”中的每个名称都具有相同的“值”,等等。

我想创建一个新列“new_vals”,它等于min(“name_group”,“value”列中的行数)。如果任何“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])

3 个答案:

答案 0 :(得分:2)

我将使用transformnp.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

答案 1 :(得分:1)

这是一种方法。我们的想法是计算每name_group行的行数,并将其作为新列(称为count)合并到数据框中。然后new_vals将被计算为valuecount列的最小值:

df = df.merge(df.groupby('name_group').size().reset_index(name='count'), on='name_group')

df['new_vals'] = df[['value', 'count']].min(axis=1)

然后,您可以根据需要删除count列,使用df.drop(columns='count', inplace=True)来提供:

     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

答案 2 :(得分: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