优化此代码:python list iter比较两个列表

时间:2018-10-10 21:36:59

标签: python list loops itertools

def cross(listMaster, listSlave, criteria="email"):
    if criteria == "email":
        emailListSlave = []
        returnUnique = []

        for item in listSlave: 
            emailListSlave.append(item[2]) #appends emails

        for no,item in enumerate(listMaster):
            if no % 10000 == 0: 
                print("Status: {percent:.2f} %".format(percent=(no/len(listMaster))))

            if item[2] not in emailListSlave:
                returnUnique.append(item)

        return returnUnique

我有2个列表:listMaster和listSlave。 这两个列表中都有大约2,000,000个项目,而它们自身内部大约有24个项目。我的目标是按照列表中的第三个元素对列表进行“排序”,这恰好是一封电子邮件。然后,我想在“主”和“从”列表之间找到唯一的电子邮件。因此,如果“从属”列表的“主”列表中存在电子邮件,则将其丢弃,然后继续。

我的算法:

1)将从列表(email)中每个项目的第3个元素加载到新列表(emailListSlave)中

2)遍历MasterList并检查MasterList中每个项目的第三个元素是否在emailListSlave中

3)如果2为True,则继续,如果为false,则将returnUnique列表附加仅在listMaster中找到的唯一电子邮件

运行此程序非常慢。我设法在20分钟内完成了10%的工作。我可以通过迭代来加快此过程吗? itertools?请帮助我优化此代码。

2 个答案:

答案 0 :(得分:1)

之所以这么慢,是因为搜索时间是线性的。使用关键字作为搜索字符串的字典。应该有很大的不同。

答案 1 :(得分:1)

TL; DR:这是您的解决方案...

def cross(listMaster, listSlave, criteria="email"):
    if criteria == "email":
        returnUnique = listMaster[:]  # create a copy of the master list
        emails_in_master = set()

        for item in listMaster:
            emails_in_master.add(item[2])  # add the master emails to the set

        for item in listSlave:
            if item[2] in emails_in_master:
                returnUnique.append(item)

        return returnUnique

您的算法为O(n ^ 2),因为您要遍历一个列表,然后根据上述每次迭代搜索另一个列表。这将导致指数运行时间,这基本上是您可以获得的最差结果。您需要尝试使算法达到线性复杂度,才能获得不错的运行时间。

您的算法基本上如下:

loop for n:                             # this costs n
    loop for n:                         # this costs n for each of the n's above
         add an item or continue        # so total, this is O(n * n)

您想要的是以下内容:

loop for n:                             # this costs n
    build a lookup

loop for n:                             # this costs n
    add item if in lookup or continue   # so total, this is O(n)

我在本地计算机上将测试数据生成为CSV。这是我创建CSV文件的方法...

>>> import csv
>>> from faker import Faker
>>> fake = Faker()
>>> with open('masters.csv', 'wb') as csvfile:
...     writer = csv.writer(csvfile, delimiter=',', quotechar='"')
...     for i in range(20000):
...         writer.writerow([fake.name(), fake.address(), fake.email(), fake.job(), fake.ssn()])
...
>>> with open('slaves.csv', 'wb') as csvfile:
...     writer = csv.writer(csvfile, delimiter=',', quotechar='"')
...     for i in range(20000):
...         writer.writerow([fake.name(), fake.address(), fake.email(), fake.job(), fake.ssn()])
...

一旦生成了这些文件(请注意,每个文件2万个,因为200万个文件生成时间太长了),我构建了以下测试套件以比较不同的方法...

import csv
import unittest

email = lambda l: l[2]


class TestListComparison(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.masters = []
        cls.slaves = []

        with open('masters.csv', 'rb') as master_csv:
            reader = csv.reader(master_csv, delimiter=',', quotechar='"')
            cls.masters = list(reader)

        with open('slaves.csv', 'rb') as slave_csv:
            reader = csv.reader(slave_csv, delimiter=',', quotechar='"')
            cls.slaves = list(reader)

    def test_make_sure_lists_are_well_formed(self):
        self.assertEqual(len(self.masters), len(self.slaves))
        self.assertEqual(len(self.masters), 20000)

    def test_list_combination_original(self):
        emailListSlave = []
        returnUnique = []

        for item in self.slaves:
            emailListSlave.append(email(item))

        for no, item in enumerate(self.masters):  # O(n)
            if email(item) not in self.slaves:    # O(n)
                returnUnique.append(item)         # O(1)

        # Total complexity: O(n * n * 1) => O(n^2)        

    def test_list_combination_using_lookup(self):
        lookup = set()
        returnUnique = self.masters[:]     # create a copy of masters list

        for master in self.masters:        # loop over the master list O(n)
            lookup.add(email(master))      # add the email to the set  O(1)

        for slave in self.slaves:          # loop over the list again  O(n)
            if email(slave) in lookup:     # check the lookup          O(1)
                returnUnique.append(slave) # add the item to the list  O(1)

        # Total complexity: O(n + n) => O(2n) => O(n)

以下是运行时结果:

test comparison

请注意,查找测试大约需要15ms,而原始算法大约需要14s。那快了几个数量级。