我有一个数据帧:
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']
}
我的目标是用"X1"替换所有出现的replacement_dict['X1']
,然后将行合并在一起。例如,"x1"、"x2"、"x3"或"x4"的任何实例都将被"x1"等替换。
我可以通过选择包含这些字符串中任何一个的行并将其替换为"X1"来完成此操作:
keys = replacement_dict.keys()
for key in keys:
DF.loc[DF['lft'].isin(replacement_dict[key]), 'lft'] = key
DF.loc[DF['rgt'].isin(replacement_dict[key]), 'rgt'] = key
给予:
lft rel rgt num
0 t3 r3 Z1 3
1 t1 r3 X1 9
2 X1 r3 t2 8
3 X1 r1 t2 4
4 t1 r1 Z1 1
5 X1 r1 t2 2
6 X1 r2 t4 4
7 Z1 r2 t4 5
8 t4 r3 X1 4
9 Z1 r2 t3 4
现在,如果我选择所有包含"X1"的行并合并它们,我应该得到:
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
因此,这三列["ft"、"rel"、"rgt"]是唯一的,而"num"列是为每一行加起来的。上面的第1行:['X1''r1't2'6]是两行['X1'r1't2'4]和['X1'r1't2'2]的总和。
对于少量的行,我可以很容易地做到这一点,但我使用的是一个有600万行的数据帧和一个有60000个键的替换字典。使用简单的逐行提取和替换需要花费很长时间。
如何有效地缩放(特别是最后一部分(?有人可以推荐熊猫的把戏吗?
将replacement_dict
映射和map()
这个新映射反转到lft和rgt列中的每一列,以替换某些值(例如x1->x1、y2->Y1等(。由于lft和argt列中有些值在映射中不存在(例如t1、t2等(,请调用fillna()
来填充这些值1
您也可以stack()
需要替换值的列(lft和rgt(,调用map+fillna和unstack()
,但由于只有2列,因此在这种特殊情况下可能不值得麻烦。
问题的第二部分可以通过对按lft、rel和rgt列分组后的num个值求和来回答;所以CCD_ 7应该起作用。
# 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()
。
如果翻转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()
试试这个,我评论了的步骤
#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
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)
这里有一种方法可以满足您的问题:
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()
使用字典的反向版本将原始dict中列表中的项替换为相关df列lft
和rgt
中的相应键- 在筛选
lft
或rgt
中具有'X1'
的行之后,使用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() )
很多很棒的答案。我避免了dict的需要,并使用像这样的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