我怎样才能加快这个迭代?
Posted
技术标签:
【中文标题】我怎样才能加快这个迭代?【英文标题】:How can I speed up this iteration? 【发布时间】:2021-11-21 14:27:10 【问题描述】:我有一个包含两列 'left_index'
和 'right_index'
的超过一千万行的数据框。
'left_index'
是值的索引,'right_index'
包含可能匹配的行的索引。
问题是这包含重复的匹配项(例如:0,1 和 1,0)。
我想过滤这个数据框,只保留每个匹配的一个组合。
我在这里使用列表作为示例。
在:[(0,1), (1,0), (3,567)]
输出:[(0,1), (3, 567)]
下面的代码产生了我想要的,但是它很慢。有没有更快的方法来解决这个问题?
lst2 = []
for i in lst1:
if(i in lst2):
lst1.remove(i)
else:
lst2.append((i[1],i[0]))
【问题讨论】:
那个代码不能产生你想要的……它给了我[(1, 0)]
。问题似乎是lst1.remove(i)
,它搞砸了迭代,for i in lst1
。
lst2 = set(tuple(sorted(x)) for x in lst1)
?
您的限制是什么?例如,@Robin 提出了一个很好的解决方案,但它只有在订单(内部和外部)不重要时才有效。
您好,欢迎您。如果您可以查看how-to-ask,然后尝试生成mcve,那就太好了。
@rpanai 提示:您可以使用shorthands in comments! [ask]
变成 How to Ask 和 [mre]
变成 minimal reproducible example。
【参考方案1】:
使用 numpy 保持非唯一数组的第一次出现:
import numpy as np
lst1 = [(1,0), (0,1), (2, 5), (3,567), (5,2)]
arr = np.array(lst1)
result = arr[np.unique(np.sort(arr), 1, axis=0)[1]]
>>> result
array([[ 1, 0],
[ 2, 5],
[ 3, 567]])
【讨论】:
【参考方案2】:您提到数据在数据框中并标记为pandas
,因此我们可以使用numpy
通过矢量化为我们完成这项工作。
首先,由于您没有提供创建数据的方法,因此我根据您的描述生成了一个数据框:
import numpy as np
import pandas
def build_dataframe():
def rand_series():
"""Create series of 1 million random integers in range [0, 9999]."""
return (np.random.rand(1000000) * 10000).astype('int')
data = pandas.DataFrame(
'left_index': rand_series(),
'right_index': rand_series()
)
return data.set_index('left_index')
data = build_dataframe()
由于根据您的要求,(0,1)
与 (1,0)
相同,因此我们只需创建一个为我们排序的值的索引。首先新建两个列,左右索引的最小值和最大值:
data['min_index'] = np.minimum(data.index, data.right_index)
data['max_index'] = np.maximum(data.index, data.right_index)
print(data)
right_index min_index max_index left_index 4270 438 438 4270 1277 9378 1277 9378 20 7080 20 7080 4646 6623 4646 6623 3280 4481 3280 4481 ... ... ... ... 3656 2492 2492 3656 2345 210 210 2345 9241 1934 1934 9241 369 8362 369 8362 5251 6047 5251 6047 [1000000 rows x 2 columns]
然后我们可以将索引重置为这两个新列(实际上我们只是想要一个多索引,这是为我们获取它的一种方法)。
data = data.reset_index().set_index(keys=['min_index', 'max_index'])
print(data)
left_index right_index min_index max_index 438 4270 4270 438 1277 9378 1277 9378 20 7080 20 7080 4646 6623 4646 6623 3280 4481 3280 4481 ... ... ... 2492 3656 3656 2492 210 2345 2345 210 1934 9241 9241 1934 369 8362 369 8362 5251 6047 5251 6047 [1000000 rows x 2 columns]
那么我们只需要索引的唯一值。这是最耗时的操作,但仍应比使用列表的简单实现快得多。
unique = data.index.unique()
print (unique)
MultiIndex([( 438, 4270), (1277, 9378), ( 20, 7080), (4646, 6623), (3280, 4481), (4410, 9367), (1864, 7881), ( 516, 3287), (1678, 6946), (1253, 7890), ... (6669, 9527), (1095, 8866), ( 455, 7800), (2862, 8587), (8221, 9808), (2492, 3656), ( 210, 2345), (1934, 9241), ( 369, 8362), (5251, 6047)], names=['min_index', 'max_index'], length=990197)
【讨论】:
【参考方案3】:我相信 Pandas 可以让你免于使用循环。
import pandas as pd
df = pd.DataFrame([
[(0, 0), (0, 0), 123],
[(0, 0), (0, 1), 234],
[(1, 0), (0, 1), 345],
[(1, 1), (0, 1), 456],
], columns=['left_index', 'right_index', 'value'])
print(df)
left_index right_index value
0 (0, 0) (0, 0) 123
1 (0, 0) (0, 1) 234
2 (1, 0) (0, 1) 345
3 (1, 1) (0, 1) 456
df['left_index_set'] = df['left_index'].apply(set)
df['right_index_set'] = df['right_index'].apply(set)
我不确定在此之后您需要什么。如果要过滤重复项,请执行以下操作。
df = df[df['left_index_set'] != df['right_index_set']]
df_final1= df[['left_index', 'right_index', 'value']]
print(df_final1)
left_index right_index value
1 (0, 0) (0, 1) 234
3 (1, 1) (0, 1) 456
但是,如果您不想过滤数据框而是修改它:
df.loc[df['left_index_set'] != df['right_index_set'], 'right_index'] = None # None, '' or what you want. It's up to you
df_final2 = df[['left_index', 'right_index', 'value']]
print(df_final2)
left_index right_index value
0 (0, 0) (0, 0) 123
1 (0, 0) None 234
2 (1, 0) (0, 1) 345
3 (1, 1) None 456
【讨论】:
对不起,如果我的问题不清楚,left_index 只包含一个索引值,而 right_index 只包含一个索引值。这些索引值处的值是匹配的,例如索引 1 处的值与索引 0 处的值匹配。因此我不保留 0,1 匹配,因为 1,0 是相同的组合。以上是关于我怎样才能加快这个迭代?的主要内容,如果未能解决你的问题,请参考以下文章