For和if循环组合在Pandas(数据操作)中花费大量时间



我有两个数据集,每个数据集大约有50万个观测值。我正在写下面的代码,似乎代码似乎永远不会停止执行。我想知道是否有更好的做法。欣赏输入。

下面是我的数据框架的示例格式。两个数据框架共享一组'sid'值,这意味着'df2'中的所有'sid'值将与'df1'中的'sid'值匹配。'tid'值和'rid'值('sid'和'tid'值的组合)不能同时出现在两个集合中

任务很简单。我想在df2中创建"tv"列。只要df2中的"rid"与"df1"中的"rid"匹配,df2中的"tv"列就会从df1中获取相应的"tv"值。如果不匹配,'df2'中的'tv'值将是'df1'中匹配'sid'子集的'tv'值的中位数。

事实上,我最初的任务包括在df2中创建一些类似的列,如'tv'(基于它们在'df1'中的值;这些列存在于'df1'中。

我相信我的代码包含for循环结合if else语句和多个值赋值语句,它需要永远执行。谢谢你的建议。

df1
sid     tid     rid     tv
0   0       0       0-0     9
1   0       1       0-1     8
2   0       3       0-3     4
3   1       5       1-5     2
4   1       7       1-7     3
5   1       9       1-9     14
6   1       10      1-10    24
7   1       11      1-11    13
8   2       14      2-14    2
9   2       16      2-16    5
10  3       17      3-17    6
11  3       18      3-18    8
12  3       20      3-20    5
13  3       21      3-21    11
14  4       23      4-23    6

df2
sid     tid     rid
0   0       0       0-0
1   0       2       0-2
2   1       3       1-3
3   1       6       1-6
4   1       9       1-9
5   2       10      2-10
6   2       12      2-12
7   3       1       3-1
8   3       15      3-15
9   3       1       3-1
10  4       19      4-19
11  4       22      4-22
rids = [rid.split('-') for rid in df1.rid]
for r in df2.rid:
s,t = r.split('-')
if [s,t] in rids:
df2.loc[df2.rid== r,'tv'] = df1.loc[df1.rid == r,'tv']
else:
df2.loc[df2.rid== r,'tv'] = df1.loc[df1.sid == int(s),'tv'].median()

预期的df2如下:

sid     tid     rid     tv
0   0       0       0-0     9.0
1   0       2       0-2     8.0
2   1       3       1-3     13.0
3   1       6       1-6     13.0
4   1       9       1-9     14.0
5   2       10      2-10    3.5
6   2       12      2-12    3.5
7   3       1       3-1     7.0
8   3       15      3-15    7.0
9   3       1       3-1     7.0
10  4       19      4-19    6.0
11  4       22      4-22    6.0

你可以在df2上留下一个子集(因为你只需要tv列,你也可以传递df1没有任何子集)的df1在'rid'上然后计算中位数和填充值:

out=df2.merge(df1[['rid','tv']],on='rid',how='left')
out['tv']=out['tv_y'].fillna(out['sid'].map(df1.groupby('sid')['tv'].median()))
out= out.drop(['tv_x','tid_y','tv_y'], axis=1)
out = out.rename(columns = {'tid_x': 'tid'})
out

既然你这么说了:

'df2'中的所有'sid'值将在'df1'中的'sid'值

因此,您也可以将它们留在['sid','rid']上,然后通过使用map()方法映射值,将tv的fillna()值与df1 'tv'列的中位数合并:

out=df2.merge(df1,on=['sid','rid'],how='left')
out['tv']=out['tv_y'].fillna(out['sid'].map(df1.groupby('sid')['tv'].median()))
out= out.drop(['tv_x','tv_y'], axis=1)
out

输出:

sid     tid     rid     tv
0   0       0       0-0     9.0
1   0       2       0-2     8.0
2   1       3       1-3     13.0
3   1       6       1-6     13.0
4   1       9       1-9     14.0
5   2       10      2-10    3.5
6   2       12      2-12    3.5
7   3       1       3-1     7.0
8   3       15      3-15    7.0
9   3       1       3-1     7.0
10  4       19      4-19    6.0
11  4       22      4-22    6.0

这是一个基于字典的没有任何循环的建议:

matching_values = dict(zip(df1['rid'][df1['rid'].isin(df2['rid'])], df1['tv'][df1['rid'].isin(df2['rid'])]))
df2[df2['rid'].isin(df1['rid'])]['tv'] = df2[df2['rid'].isin(df1['rid'])]['rid']
df2[df2['rid'].isin(df1['rid'])]['tv'].replace(matching_values)

median_values = df2[(~df2['rid'].isin(df1['rid']) & (df2['sid'].isin(df1['sid'])].groupby('sid')['tv'].median().to_dict() 
df2[(~df2['rid'].isin(df1['rid']) & (df2['sid'].isin(df1['sid'])]['tv'] = df2[(~df2['rid'].isin(df1['rid']) & (df2['sid'].isin(df1['sid'])]['sid']
df2[(~df2['rid'].isin(df1['rid']) & (df2['sid'].isin(df1['sid'])]['tv'].replace(median_values)

这应该能奏效。这里的逻辑是,我们首先创建一个字典,其中的"rid"one_answers"sid"值是键和中值以及匹配的"tv"值是字典值。接下来,我们替换"电视"使用rid和sid键分别替换df2中的值(因为它们是字典键),因此可以通过调用.replace()轻松地替换为正确的tv值。

不要在pandas中使用for循环,这是众所周知的很慢。这样你就不能从已经做过的所有内部优化中获益。

尝试使用split-apply-combine模式:

  • 将df1拆分为sid计算中值:df1.groupby('sid')['tv'].median()
  • join df2 on df1:df2.join(df1.set_index('rid'), on='rid')
  • 用第1步计算的中位数填充NaN值。

(尚未测试代码)。

最新更新