根据条件替换并聚合pandas中的行

问题描述 投票:0回答:6

我有一个数据框:

   lft rel rgt num
0   t3  r3  z2  3
1   t1  r3  x1  9
2   x2  r3  t2  8
3   x4  r1  t2  4
4   t1  r1  z3  1
5   x1  r1  t2  2
6   x2  r2  t4  4
7   z3  r2  t4  5
8   t4  r3  x3  4
9   z1  r2  t3  4

还有参考词典:

replacement_dict = {
    'X1' : ['x1', 'x2', 'x3', 'x4'],
    'Y1' : ['y1', 'y2'],
    'Z1' : ['z1', 'z2', 'z3']
}

我的目标是将所有出现的

replacement_dict['X1']
替换为“X1”,然后计算
num
行的分组总和。

例如,'x1'、'x2'、'x3'或'x4'的任何实例将被替换为'X1'等,以及'X1'-'r1'-'t2'组的总和(通过上面的重新映射创建)是 6,等等

所以我想要的输出是:

    lft rel rgt num
0   X1  r3  t2  8
1   X1  r1  t2  6
2   X1  r2  t4  4
3   t1  r3  X1  9
4   t4  r3  X1  4

我正在处理一个包含 600 万行的数据框和一个包含 60,000 个键的替换字典。使用简单的逐行提取和替换会花费很长时间。

如何有效地扩展这一点(特别是最后一部分)?有人可以推荐一个熊猫技巧吗?

python pandas dataframe numpy group-by
6个回答
10
投票

反转

replacement_dict
映射并将
map()
这个新映射到每个 lft 和 rgt 列以替换某些值(例如 x1->X1、y2->Y1 等)。由于 lft 和 rgt 列中的某些值在映射中不存在(例如 t1、t2 等),请调用
fillna()
来填写这些值。1

您还可以

stack()
需要替换值的列(lft和rgt),调用map+fillna和
unstack()
回来,但因为只有2列,对于这种特殊情况可能不值得麻烦。

问题的第二部分可以通过按 lft、rel 和 rgt 列分组后对 num 值求和来回答;所以

groupby().sum()
应该可以解决问题。

# reverse replacement map
reverse_map = {v : k for k, li in replacement_dict.items() for v in li}

# substitute values in lft column using reverse_map
df['lft'] = df['lft'].map(reverse_map).fillna(df['lft'])
# substitute values in rgt column using reverse_map
df['rgt'] = df['rgt'].map(reverse_map).fillna(df['rgt'])

# sum values in num column by groups
result = df.groupby(['lft', 'rel', 'rgt'], as_index=False)['num'].sum()

1

map()
+
fillna()
可能比
replace()
更适合您的用例,因为在幕后,
map()
实现了 Cython 优化的
take_nd()
方法,如果有很多值,该方法会表现得特别好来替换,而
replace()
实现了使用 Python 循环的
replace_list()
方法。因此,如果
replacement_dict
特别大(在您的情况下),性能差异将会很大,但如果
replacement_dict
很小,
replace()
可能会优于
map()


6
投票

如果你翻转

replacement_dict
的键和值,事情就会变得容易得多:

new_replacement_dict = {
    v: key
    for key, values in replacement_dict.items()
    for v in values
}

cols = ["lft", "rel", "rgt"]
df[cols] = df[cols].replace(new_replacement_dict)
df.groupby(cols).sum()

5
投票

试试这个,我评论了步骤

#reverse dict to dissolve the lists as values
reversed_dict = {v:k for k,val in replacement_dict.items() for v in val}

# replace the values
cols = ['lft', 'rel', 'rgt']
df[cols] = df[cols].replace(reversed_dict)

# filter rows where X1 is anywhere in the columns
df = df[df.eq('X1').any(axis=1)]

# sum the duplicate rows
out = df_filtered.groupby(cols).sum().reset_index()
print(out)

输出:

  lft rel rgt  num
0  X1  r1  t2    6
1  X1  r2  t4    4
2  X1  r3  t2    8
3  t1  r3  X1    9
4  t4  r3  X1    4

3
投票

Pandas 内置函数 replace 比使用 .loc 遍历整个数据帧更快

您还可以在其中传递一个列表,使我们的字典非常适合它

keys = replacement_dict.keys() # Loop through every value in our dictionary and get the replacements for key in keys: DF = DF.replace(to_replace=replacement_dict[key], value=key)
    

3
投票
这里有一种方法可以满足您的问题:

df[['lft','rgt']] = ( df[['lft','rgt']] .replace({it:k for k, v in replacement_dict.items() for it in v}) ) df = ( df[(df.lft == 'X1') | (df.rgt == 'X1')] .groupby(['lft','rel','rgt']).sum().reset_index() )
输出:

lft rel rgt num 0 X1 r1 t2 6 1 X1 r2 t4 4 2 X1 r3 t2 8 3 t1 r3 X1 9 4 t4 r3 X1 4
说明:

  • replace()
     使用字典的反向版本将原始字典中列表中的项目替换为相关 df 列中的相应键 
    lft
    rgt
    
    
  • 过滤出在
  • 'X1'
    lft
     中找到的带有 
    rgt
     的行后,使用 
    groupby()
    sum()
    reset_index()
    num
     列求和以获得唯一的 
    lft, rel, rgt
     组键并恢复组从索引级别到列的组件。

作为替代方案,我们可以使用

query()

 仅选择包含 
'X1'
 的行:

df[['lft','rgt']] = ( df[['lft','rgt']] .replace({it:k for k, v in replacement_dict.items() for it in v}) ) df = ( df.query("lft=='X1' or rgt=='X1'") .groupby(['lft','rel','rgt']).sum().reset_index() )
    

1
投票
很多很棒的答案。我避免了对字典的需要,并使用像这样的

df.apply()

 来生成新数据。

import io import pandas as pd # # create the data x = ''' lft rel rgt num t3 r3 z2 3 t1 r3 x1 9 x2 r3 t2 8 x4 r1 t2 4 t1 r1 z3 1 x1 r1 t2 2 x2 r2 t4 4 z3 r2 t4 5 t4 r3 x3 4 z1 r2 t3 4 ''' data = io.StringIO(x) df = pd.read_csv(data, sep=' ') print(df) replacement_dict = { 'X1' : ['x1', 'x2', 'x3', 'x4'], 'Y1' : ['y1', 'y2'], 'Z1' : ['z1', 'z2', 'z3'] } def replace(x): # which key to check key_check = x[0] + '1' key_check = key_check.upper() return key_check df['new'] = df['lft'].apply(replace) df
返回此:

lft rel rgt num 0 t3 r3 z2 3 1 t1 r3 x1 9 2 x2 r3 t2 8 3 x4 r1 t2 4 4 t1 r1 z3 1 5 x1 r1 t2 2 6 x2 r2 t4 4 7 z3 r2 t4 5 8 t4 r3 x3 4 9 z1 r2 t3 4 lft rel rgt num new 0 t3 r3 z2 3 T1 1 t1 r3 x1 9 T1 2 x2 r3 t2 8 X1 3 x4 r1 t2 4 X1 4 t1 r1 z3 1 T1 5 x1 r1 t2 2 X1 6 x2 r2 t4 4 X1 7 z3 r2 t4 5 Z1 8 t4 r3 x3 4 T1 9 z1 r2 t3 4 Z1
    
© www.soinside.com 2019 - 2024. All rights reserved.