我怎样才能加快这个迭代?

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 是相同的组合。

以上是关于我怎样才能加快这个迭代?的主要内容,如果未能解决你的问题,请参考以下文章

我怎样才能加快这个 Anagram 算法

SQLite3-我怎样才能加快这个 SELECT 查询?

我怎样才能加快这个汇总报价行行负载的视图?

我的子查询将执行时间增加了 20 秒。我怎样才能加快速度?

*** 错误:我怎样才能避免它或将这个 DFS 变成一个迭代的?

双连接查询需要 540 秒才能运行 - 我怎样才能加快速度?