我有一个数据帧(df)
df = pd.DataFrame({'No': [123,234,345,456,567,678], 'text': ['60 ABC','1nHG','KL HG','21ABC','K 200','1g HG'], 'reference':['ABC','HG','FL','','200',''], 'result':['','','','','','']}, columns=['No', 'text', 'reference', 'result'])
No text reference result
0 123 60 ABC ABC
1 234 1nHG HG
2 345 KL HG FL
3 456 21ABC
4 567 K 200 200
5 678 1g HG
和包含元素的列表
list
['ABC','HG','FL','200','CP1']
现在我有以下编码:
for idx, row in df.iterrows():
for item in list:
if row['text'].strip().endswith(item):
if pd.isnull(row['reference']):
df.at[idx, 'result'] = item
elif pd.notnull(row['reference']) and row['reference'] != item:
df.at[idx, 'result'] = 'wrong item'
if pd.isnull(row['result']):
break
我浏览了df和列表,然后检查匹配项。
输出:
No text reference result
0 123 60 ABC ABC
1 234 1nHG HG
2 345 KL HG FL wrong item
3 456 21ABC ABC
4 567 K 200 200
5 678 1g HG HG
break指令很重要,因为否则可以在列表中找到第二个元素,然后该第二个元素将覆盖结果中的内容。
现在,我需要另一种解决方案,因为数据帧巨大且for循环效率低下。认为使用Apply可能有效,但是如何?
谢谢!
答案 0 :(得分:2)
您可以迭代后缀而不是迭代行,而后缀可能要小得多。这样,您就可以利用基于序列的方法和布尔索引。
我还创建了一个额外的系列来标识何时更新了一行。与按行迭代的开销相比,这种额外检查的开销应该很小。
L = ['ABC', 'HG', 'FL', '200', 'CP1']
df['text'] = df['text'].str.strip()
null = df['reference'].eq('')
df['updated'] = False
for item in L:
ends = df['text'].str.endswith(item)
diff = df['reference'].ne(item)
m1 = ends & null & ~df['updated']
m2 = ends & diff & ~null & ~df['updated']
df.loc[m1, 'result'] = item
df.loc[m2, 'result'] = 'wrong item'
df.loc[m1 | m2, 'updated'] = True
结果:
No text reference result updated
0 123 60 ABC ABC False
1 234 1nHG HG False
2 345 KL HG FL wrong item True
3 456 21ABC ABC True
4 567 K 200 200 False
5 678 1g HG HG True
您可以删除最后一列,但可能会发现它对于其他目的很有用。