Python 如何将带有if语句的嵌套ItError转换为矢量化函数或其他更快的方法
我知道pandas.DataFrame.iterrows非常慢,对于pandas/python中的简单函数,如“将每一列乘以另一列”,向量化很容易 我有一个稍微复杂一点的问题,我想不出一个方法来矢量化或映射这个问题。我正在对照主数据帧检查工作数据帧df1,以查看是否有任何条目是新的。这个问题有一些怪癖,每个名字可以有多个帐户,每个帐户可以有多个可能的名字 如果没有这些小怪癖,就很容易做海螺或类似的事情。不幸的是,数据集就是这样来的,没有办法提前知道哪个帐户将使用哪个昵称(而不需要在运行时花费同样多的处理能力) 我能想到的解决这个问题的最简单方法是使用嵌套的for/iterrow循环和if语句,如下面的示例所示。如果有人知道一种更快的方法,那就太好了,因为这种方法极其缓慢,以至于无法用于千行数据帧Python 如何将带有if语句的嵌套ItError转换为矢量化函数或其他更快的方法,python,python-3.x,pandas,dataframe,Python,Python 3.x,Pandas,Dataframe,我知道pandas.DataFrame.iterrows非常慢,对于pandas/python中的简单函数,如“将每一列乘以另一列”,向量化很容易 我有一个稍微复杂一点的问题,我想不出一个方法来矢量化或映射这个问题。我正在对照主数据帧检查工作数据帧df1,以查看是否有任何条目是新的。这个问题有一些怪癖,每个名字可以有多个帐户,每个帐户可以有多个可能的名字 如果没有这些小怪癖,就很容易做海螺或类似的事情。不幸的是,数据集就是这样来的,没有办法提前知道哪个帐户将使用哪个昵称(而不需要在运行时花费同样
import pandas as pd
df1 = pd.DataFrame({'names' : ['tim;timothy','tim', 'joseph', 'joe;joseph', 'bill', 'tim', 'bill', 'joe'],
'account' : ['w213', 'o993','x332', 'y313', 'z641', 'r323', 'p881', 'k445']})
df2 = pd.DataFrame({'names' : ['jill', 'joseph', 'tim', 'bill', 'timothy', 'bill', 'phil'],
'account' : ['y554', 'x332', 'w213', 'z641', 'w213', 'p881','k913']})
df1['new account'] = 1
for index_1, row_1 in df1.iterrows():
possible_names = row_1['names'].split(';')
for index_2, row_2 in df2.iterrows():
if row_2['names'] in possible_names and row_1['account'] == row_2['account']:
df1.loc[index_1, 'new account'] = 0
print(df1)
print(df2)
它不是最漂亮的,但一种方法是扩展并展平df1,然后与df2合并:
# flatten
df1_v2 = df1[["account"]].join(df1.names.str.split(";", expand=True))
df1_v2 = pd.melt(df1_v2.reset_index(),
["index", "account"], value_name="names").dropna()
# merge
common = df1_v2.merge(df2)
df1["new account"] = 1
df1.loc[common["index"].values, "new account"] = 0
这让我
account names new account
0 w213 tim;timothy 0
1 o993 tim 1
2 x332 joseph 0
3 y313 joe;joseph 1
4 z641 bill 0
5 r323 tim 1
6 p881 bill 0
7 k445 joe 1
这要经过中间层
In [145]: df1_v2.head()
Out[145]:
index account variable names
0 0 w213 0 tim
1 1 o993 0 tim
2 2 x332 0 joseph
3 3 y313 0 joe
4 4 z641 0 bill
新答案
一起
d1 = df1.set_index('account').names.str.split(';').apply(set)
d2 = df2.groupby('account').names.apply(set).reindex(d1.index, fill_value=set())
new = (d1 - (d1 - d2)).astype(bool).__neg__().astype(np.uint8).rename('new')
df1.join(new, on='account')
解释使用
设置
操作
-
运算符获取设置的差值。这样做两次,如果没有交叉点,我得到一个空集。空集的计算结果为False
。但是没有交集意味着一个新帐户,所以我使用\uuuu neg\uuuu
(否定,又称~
)将假
切换到真
# find where there is no intersection
new = (d1 - (d1 - d2)).astype(bool).__neg__().astype(np.uint8).rename('new')
# join
df1.join(new, on='account')
account names new
0 w213 tim;timothy 0
1 o993 tim 1
2 x332 joseph 0
3 y313 joe;joseph 1
4 z641 bill 0
5 r323 tim 1
6 p881 bill 0
7 k445 joe 1
旧答案
@DSM的答案要好得多这更多的是一个解决方案的思想流
d2 = df2.assign(dummy=1).set_index(['account', 'names'])
split_df = df1.names.str.split(';', expand=True).rename(columns='name{}'.format)
d1 = df1.drop('names', 1).join(split_df)
d1 = d1.set_index('account').stack() \
.reset_index('account', name='names') \
.set_index(['account', 'names'])
df1.join(
(~d1.join(d2).dummy.unstack().any(1)).astype(int).rename('new'),
on='account')
account names new
0 w213 tim;timothy 0
1 o993 tim 1
2 x332 joseph 0
3 y313 joe;joseph 1
4 z641 bill 0
5 r323 tim 1
6 p881 bill 0
7 k445 joe 1
[次要:请粘贴文本,而不是图像;外观上的细微改进不值得实际失去复制和粘贴的能力。]@root:我不仅看到了它们,还使用了这些定义来创建我的答案。但是能够复制和粘贴输出的用途仍然存在,特别是当(这里发生的)样本发生变化时。我已经更新了我的答案。检查这个:这太棒了!!!它提供相同的输出,并提供数量级的加速。我的大文件的运行时间从几个小时变为不到一秒。我已经更新了答案。我想你可能会喜欢这种方法。
d2
account
w213 {timothy, tim}
o993 {}
x332 {joseph}
y313 {}
z641 {bill}
r323 {}
p881 {bill}
k445 {}
Name: names, dtype: object
# find where there is no intersection
new = (d1 - (d1 - d2)).astype(bool).__neg__().astype(np.uint8).rename('new')
# join
df1.join(new, on='account')
account names new
0 w213 tim;timothy 0
1 o993 tim 1
2 x332 joseph 0
3 y313 joe;joseph 1
4 z641 bill 0
5 r323 tim 1
6 p881 bill 0
7 k445 joe 1
d2 = df2.assign(dummy=1).set_index(['account', 'names'])
split_df = df1.names.str.split(';', expand=True).rename(columns='name{}'.format)
d1 = df1.drop('names', 1).join(split_df)
d1 = d1.set_index('account').stack() \
.reset_index('account', name='names') \
.set_index(['account', 'names'])
df1.join(
(~d1.join(d2).dummy.unstack().any(1)).astype(int).rename('new'),
on='account')
account names new
0 w213 tim;timothy 0
1 o993 tim 1
2 x332 joseph 0
3 y313 joe;joseph 1
4 z641 bill 0
5 r323 tim 1
6 p881 bill 0
7 k445 joe 1