我正在逐行比较两个数据框。
对于data
中的每一行,我想检查reference
中是否有匹配的行。
要使比赛被视为正确,必须满足某些条件:
np.isclose
来做到这一点)找到匹配项后,我会将两行的名称都添加到列表中。 如果没有匹配项,我将数据的行名附加在“与以上相同的列表中”和“未找到”。最后,我创建一个汇总表,以查看哪行对应(或不对应)什么。
让您了解我的数据框的结构:
name col1 col2 col3 col4 col5 col6 col7 col8
0 X 10 20 30 40 50 60 70 80
1 X 20 30 NaN NaN NaN NaN NaN NaN
2 X 10 25 30 50 NaN NaN NaN NaN
3 X 20 25 30 50 NaN NaN NaN NaN
我有一个使用2个for循环的工作代码,但是在大数据帧上使用它的速度相当慢(这里我在一些“样本”数据帧上测试代码):
data = pd.DataFrame({'name':['read 1','read 2','read 3','read 4'],
'start 1':[100,102,100,103],
'end 1':[198,504,500,200],
'start 2':[np.NaN,600,650,601],
'end 2':[np.NaN,699, 700,702],
'start 3':[np.NaN,800,800,np.NaN],
'end 3':[np.NaN,901, 900,np.NaN]},
columns=['name', 'start 1', 'end 1', 'start 2', 'end 2', 'start 3', 'end 3'],
dtype='float64')
reference = pd.DataFrame({'name':['a-1','a-2','b-1','c-1'],
'start 1':[100,100,100,300],
'end 1':[200,200,500,400],
'start 2':[300,np.NaN,600,600],
'end 2':[400,np.NaN, 700,700],
'start 3':[np.NaN,np.NaN,800,np.NaN],
'end 3':[np.NaN,np.NaN, 900,np.NaN]},
columns=['name', 'start 1', 'end 1', 'start 2', 'end 2', 'start 3', 'end 3'],
dtype='float64')
match = []
checklist = set()
for read in data.itertuples():
ndata = np.count_nonzero(~np.isnan(read[2:]),axis=0)
end = ndata+1 if ndata>2 and read[1] not in checklist else 4
for ref in reference.itertuples():
nref = np.count_nonzero(~np.isnan(ref[2:]),axis=0)
if np.isclose(read[2:end],ref[2:end], atol=5).all() == True and ndata == nref:
match.append([read[1], ref[1]])
checklist.add(read[1])
break
if read[1] not in checklist:
match.append([read[1], "not found"])
checklist.add(read[1])
match_table = pd.DataFrame(match)
match_table:
read name reference
0 read 1 a-2
1 read 2 b-1
2 read 3 not found
3 read 4 not found
因此,我决定尝试使用矢量化对其进行优化。 现在,我仅使用1个for循环,并能够使用np.isclose将第三个条件向量化,但没有为其他条件进行管理。
我可以通过允许equal_nan=True
来绕过它,但是由于我的大多数行都充满了NaN值,所以我认为如果不需要进行这些比较,我将获得一些时间。
这是我到目前为止所得到的:
count = []
for read in data.itertuples(index=False):
idx = np.argwhere(np.isclose(read[1:], reference.iloc[:,1:], atol=5, equal_nan=True).all(axis=1) == True).flatten()
if idx.size == 0:
count.append([read[0], "not found"])
else:
idx = idx.item()
count.append([read[0], reference['name'][idx]])
match = pd.DataFrame(count)
我用25×130 data
数据帧上的400×130 reference
数据帧对其进行了测试,它的性能比第一个版本快6倍,但仍然需要1秒钟才能完成。但是也许没有太多的改进空间。
我如何向量化处理条件1和2的操作,从而不执行NaN比较?
是否有可能摆脱内部for循环?如果是,那是否可以提高速度?
为什么我必须在代码的第一版和第二版之间将索引从read[1]
更改为read[0]
才能选择['name']
列?似乎在一个版本中它是基于0的,而在另一个版本中却不是,或者类似的。但是对python陌生并自己学习,我真的不明白这里发生了什么。
答案 0 :(得分:0)
可以通过使用io.on('connection', function(client) {
// var address = io.handshake.address;
console.log('Ein neuer Client hat sich zum Chat verbunden! IP: ')
client.on('sendmsg', function(data) {
console.log(data);
});
});
来避免循环。 df.apply
很慢,仅在绝对必要时才应使用。
itertuples
如果您进入numpy层和用户# index-setting not technically required, but makes the
# rest of the code simpler
data = data.set_index('name')
reference = reference.set_index('name')
# define a helper function to use with apply
# taking the same logic as you have used
def get_ref(x):
m = np.isclose(x, reference.values, atol=5, equal_nan=True).all(axis=1)
return reference.index[m].item() if m.any() else np.nan
out = data.apply(get_ref, axis=1).rename('reference').reset_index()
# Outputs:
name reference
0 read 1 a-2
1 read 2 b-1
2 read 3 NaN
3 read 4 NaN
np.apply_along_axis
在我的机器上,带有示例数据