改进Python中仅基于特定列的重复数据消除列表的运行时

改进Python中仅基于特定列的重复数据消除列表的运行时,python,performance,list,deduplication,Python,Performance,List,Deduplication,我有两个文件。我正在尝试删除某些列匹配的所有行。我想我应该使用Python中的列表来实现这一点。我以为它会很快,但它跑得太慢了 我只想比较前3列,因为后2列不可靠。但是,我想导出最后两列 例如: A = [ (Jack, Smith, New York, USA, 100), (Jim, Doe, Cleveland, UK, 200), (Frank, Johnson, Chicago, USA, 300) ] B = [ (Jack, Smith, New York, United Sta

我有两个文件。我正在尝试删除某些列匹配的所有行。我想我应该使用Python中的列表来实现这一点。我以为它会很快,但它跑得太慢了

我只想比较前3列,因为后2列不可靠。但是,我想导出最后两列

例如:

A = [
(Jack, Smith, New York, USA, 100),
(Jim, Doe, Cleveland, UK, 200),
(Frank, Johnson, Chicago, USA, 300)
]

B = [
(Jack, Smith, New York, United States, blank),
(Jerry, Smith, Cleveland, USA, blank),
(Frank, Johnson, Chicago, America, blank)
]

Matched List = [
(Jack, Smith, New York, USA, 100)
(Frank, Johnson, Chicago, USA, 300)
]

Desired List = [
(Jim, Doe, Cleveland, UK, 200)
]
因此,我编写了两个嵌套For循环来比较这两个列表并删除匹配项。然而,我的列表A是~50000行,列表B是600000行。这需要3.5个小时。我需要在一组300000行和4000000行上运行它;但在看到这需要多长时间后,它将运行数天

下面是两个For循环(我正在比较第0、7、9和10列)

有没有办法加快速度?有更好的方法吗?我应该使用不同的编程语言吗?也许可以将它们上传到SQL db中的临时表中并使用SQL


谢谢

@kindall建议
set()
dict
跟踪到目前为止看到的内容是正确的

def getKey(row):
    return (row[0], row[7], row[9], row[10])

# create a set of all the keys you care about
lead_keys = {getKey(r) for r in Leads_rows}

# save this off due to reverse indexing gyration
len_ac_list = len(Acquisition_list)

for i, ac_row in enumerate(Acquisition_list[::-1]):
    ac_key = getKey(ac_row)
    if ac_key in lead_keys:   ## this look up is O(1)
        index = len_ac_list - i - 1
        Acquisition_list.pop(index)
        Leads_list.append(ac_row)
        ## maybe: lead_keys.add(ac_key)
好处是:在创建密钥集时,您只需在Leads_列表上迭代一次(我选择Leads_列表是因为它是一个较大的列表,因此将为您节省更多时间);您查找采集列表需要固定的时间,O(1)而不是O(n),其中n是len(Leads_list)


在最初的设置中,最坏的情况是,(n*m)或(300000*4000000)操作,这是。。。一吨。使用
set
s,您将只执行(n+m)或(30000+4000000),这要少得多。大概少了30万倍。这就是1.2万亿事物和0.000004万亿(400万)事物之间的区别。

使用
set()
dict()
来跟踪您以前见过的事物。在列表中查找项是O(n),在哈希表中查找项是O(1)。您说要删除前3列匹配的所有行,但所需输出包括前3列匹配的行,但不包括不匹配的2列。你能澄清一下吗?此外,文件A中提供的最后两列的值总是要使用的吗?@kindall我不确定如何使用set()和dict()来减少我已经查看过的值,循环应该迭代,对吗?文件B中的
(Jerry,Smith,Cleveland,USA,blank)
?那条记录会放在匹配的列表中还是放在所需的列表中?对ac_索引执行
,将ac_行反向(列表(枚举(采集列表[:]))
以获取索引,这样做会更快,而不是执行
采集列表。删除(采集行)
(必须扫描列表中的对象),您可以执行
删除采集列表[ac_索引]
。是的,我在写答案的时候考虑过这一点,但如果不真正投入到python会话中,我就无法完全理解这一点。我不完全了解OPs的设置,所以我不太确定他们想要什么;我可能至少会在for循环中使用
枚举
,然后使用
采集列表.pop(I)
,尽管我不知道这是O(n)还是O(1);您的解决方案听起来也很合适/正确如果您执行了
Acquisition\u list.pop(i)
(或
del
),则必须反向枚举。否则,在每次
.pop(i)
之后,您的
i
将指向目标元素的另一个方向,因为
.pop(i)
将把
i
之后的所有内容向左移动一个。是的,我只是写了一条注释询问这一点,但这很有意义;是否有必要执行
reverse(列表)(枚举(…
)操作?虽然有点不清楚,但我认为您不必重新执行这么多转换,从而节省了大量内存?检查更新的代码,看看这是否有意义。感谢您的反馈-非常有用且重要!
reversed(列表)(枚举(…
是按相反顺序获取索引和行所必需的。或者您可以对xrange中的i执行
(len(Acquisition\u list)-1,-1,-1):ac\u row=Acquisition\u list[i]
这取决于制作
Acquisition\u list
的浅层副本的成本。
def getKey(row):
    return (row[0], row[7], row[9], row[10])

# create a set of all the keys you care about
lead_keys = {getKey(r) for r in Leads_rows}

# save this off due to reverse indexing gyration
len_ac_list = len(Acquisition_list)

for i, ac_row in enumerate(Acquisition_list[::-1]):
    ac_key = getKey(ac_row)
    if ac_key in lead_keys:   ## this look up is O(1)
        index = len_ac_list - i - 1
        Acquisition_list.pop(index)
        Leads_list.append(ac_row)
        ## maybe: lead_keys.add(ac_key)