比较列表和提取一些数据的最佳实践

Best practice to compare lists and extract some data

我有一个旧的电子邮件列表,应该更新为一个新的电子邮件列表。

我需要最有效的方法来比较它们和从旧列表中删除的select封电子邮件。

电子邮件列表存储在数据库中,因此我可以获得电子邮件 ID(电子邮件是唯一的)。

我使用的代码:

old_ids_list = [1, 2, 3]
new_ids_list = [1, 2]
old_emails_list = ['toto@gmail.com', 'lolo@gmail.com', 'momo@gmail.com']
new_emails_list = ['toto@gmail.com', 'lolo@gmail.com',]

if len(old_ids_list) == len(new_ids_list) & len(set(old_ids_list) & set(new_ids_list)) == len(old_ids_list):
    pass
else:
    deleted = numpy.setdiff1d(old_emails_list, new_emails_list, assume_unique=False)

这是一个好习惯吗?或者最好使用 for loop ?为什么?

您可以使用列表理解。正如你所说,电子邮件本身是独一无二的,我不确定你想用这些 ID 做什么,我们不需要转换成集合(如果我弄错了请纠正我)。

deleted = [email for email in old_emails_list if email not in new_emails_list]

恕我直言,这种方法的优点是可读性好,不需要外部包。

编辑
检查新列表是否与旧列表不同,有两种情况:
a) 如果已知新列表是旧列表的子列表,只需检查deleted中是否有任何元素,如上计算。
b) 如果新列表中可能有新邮件,检查deleted是否包含任何邮件,如果没有,另外检查是否len(new_emails_list)==len(old_emails_list)

首先,在if条件下你已经在努力工作了,所以没有太多需要事先测试。 其次,不清楚你的起点是什么,如果是ID或电子邮件或两者之一,你的终点是什么。

但似乎最干净的方法是一直使用 set

我假设您对 ID 没问题(但同样的代码也适用于电子邮件地址):

n = 3
a = set(range(1, n))  # *old* items
b = set(range(n - 1))  # *new* items


c = a - b  # items present in b but not in a (added)
# {0}
d = b - a  # items present in a but not in b (deleted)
# {2}

现在,让我们假设起点是两个 list(同样,ID 或电子邮件是无关紧要的,为了简单起见,我只假设 ID),并且让我们假设您想知道添加和删​​除的项目。有几种可能的方法:

def diffs_set(a, b):
    a = set(a)
    b = set(b)
    return a - b, b - a
def diffs_loop(a, b):
    a = set(a)
    b = set(b)
    return [x for x in a if x in b], [x for x in b if x in a]
def diffs_loop2(a, b):
    return [x for x in a if x in b], [x for x in b if x in a]
def diffs_np(a, b):
    return np.setdiff1d(a, b, assume_unique=True), np.setdiff1d(b, a, assume_unique=True)

对于某些输入大小,其计时结果如下:

funcs = diffs_set, diffs_loop, diffs_loop2, diffs_np
for n in (10, 100, 1000, 10000):
    print(n)
    old_items = list(range(1, n))
    new_items = list(range(n - 1))
    for func in funcs:
        print(func.__name__)
        %timeit func(old_items, new_items)
    print()
# 10
# diffs_set
# The slowest run took 4.52 times longer than the fastest. This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 914 ns per loop
# diffs_loop
# 1000000 loops, best of 3: 1.97 µs per loop
# diffs_loop2
# 100000 loops, best of 3: 2.09 µs per loop
# diffs_np
# 10000 loops, best of 3: 65.6 µs per loop

# 100
# diffs_set
# 100000 loops, best of 3: 5.23 µs per loop
# diffs_loop
# 100000 loops, best of 3: 13.6 µs per loop
# diffs_loop2
# 10000 loops, best of 3: 116 µs per loop
# diffs_np
# The slowest run took 5.74 times longer than the fastest. This could mean that an intermediate result is being cached.
# 10000 loops, best of 3: 65.9 µs per loop

# 1000
# diffs_set
# 10000 loops, best of 3: 57.7 µs per loop
# diffs_loop
# 10000 loops, best of 3: 132 µs per loop
# diffs_loop2
# 100 loops, best of 3: 10.7 ms per loop
# diffs_np
# 1000 loops, best of 3: 374 µs per loop

# 10000
# diffs_set
# 1000 loops, best of 3: 818 µs per loop
# diffs_loop
# 1000 loops, best of 3: 1.6 ms per loop
# diffs_loop2
# 1 loop, best of 3: 1.06 s per loop
# diffs_np
# 100 loops, best of 3: 3.5 ms per loop

最重要的一点是,通过使用集合,可以获得最快和最干净的方法。 需要注意的是 set 甚至对于 list 理解也是有用的,因为 if 条件变为 O(1)(导致整体 O(n)O(n)(导致总体 O(n²))。 因为最昂贵的操作是在开始时实际构建 set,所以如果只需要 a - bb - a,列表理解可能会比仅使用集合更有竞争力,因为那么只需要一个 set() 调用。 相反,基于 NumPy 的方法在这里没有竞争力。