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?请帮助我优化此代码。
答案 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)
以下是运行时结果:
请注意,查找测试大约需要15ms,而原始算法大约需要14s。那快了几个数量级。