Я собираюсь нарезать это на несколько кусочков. Сначала создайте списки a
, b
и c
, а затем основной список size
, используя numpy, и, наконец, сделайте то же самое со списками Python.
Получение списков ключей
Итак, если мы посмотрим, то c
на самом деле список ключей от dict_a
и так далее. Я собираюсь предположить, что это специально, но если это не так, замените y
на key_a
, и вы поймете, что я имею в виду.
Мы можем легко вычислить это заранее, не входя в основной цикл. Каждый элемент просто повторяется произведением количества ключей в двух других списках. Мы можем сделать это с чем-то вроде:
from itertools import repeat, chain
def key_summary(dict_1, dict_2, dict_3):
count = len(dict_2) * len(dict_3)
return chain(*(repeat(k, count) for k in dict_1.keys()))
a = list(key_summary(dict_b, dict_a, dict_c))
b = list(key_summary(dict_c, dict_a, dict_b))
c = list(key_summary(dict_a, dict_b, dict_c))
Это должно быть быстрее, так как оно не глубоко вложено в циклы, но, учитывая, как легко его вычислить, я думаю, вы, возможно, захотите подумать, зачем вам это нужно. Можете ли вы достичь своей цели без фактического составления списков?
Получение списка размеров
Не думаю, что вы правильно используете функцию intersect1d()
. В документах говорится, что третий аргумент - assume_unique
, что, как я думаю, вы не пытаетесь сделать. Я предполагаю, что вы хотите, чтобы элементы, которые появляются во всех списках, можно сделать одним из способов:
np.intersect1d(np.intersect1d(val_a, val_b), val_c))
Это предлагает способ оптимизации цикла. Вместо того, чтобы вычислять пересечение val_a
и val_b
внутри каждого цикла, мы можем вместо этого сделать это один раз и использовать его снова.
for val_a in dict_a.values():
for val_b in dict_b.values():
# Get the intersection of a and b first
cache = np.intersect1d(val_a, val_b)
if not len(cache):
# Our first two sets have nothing in common, we know that we are
# just going to add a bunch of zeros for everything in dict_c
size.extend(repeat(0, len(dict_c)))
else:
size.extend(
len(np.intersect1d(cache, val_c)) for val_c in dict_c.values())
Это также позволяет нам применить еще одну оптимизацию, которая вообще пропускает цикл по dict_c
, если в пересечении val_a
и val_b
ничего нет. Мы также можем сделать что-то подобное, если val_a
когда-либо будет пустым.
В качестве окончательной оптимизации у вас всегда должно быть dict_a
быть самым маленьким и dict_c
самым большим, поскольку это дает нам лучший шанс пропустить шаги.
Выполнение вышеизложенного позволило мне увеличить скорость примерно на 200% (1.493 мс -> 0,8 мс в данном примере).
Получение списка размеров (с использованием наборов Python)
Я предполагаю, что вы используете веские функции по уважительной причине, но если они не являются необходимыми, вы можете преобразовать свои списки в наборы, которые очень быстрые для выполнения пересечений в Python. Мы можем следовать довольно похожему подходу, как указано выше:
dset_a = {k: set(v) for k, v in dict_a.items()}
dset_b = {k: set(v) for k, v in dict_b.items()}
dset_c = {k: set(v) for k, v in dict_c.items()}
size = []
for val_a in dset_a.values():
for val_b in dset_b.values():
cache = val_a & val_b
if not cache:
size.extend(repeat(0, len(dict_c)))
else:
size.extend(len(cache & val_c) for val_c in dset_c.values())
Это значительно быстрее в данном примере. Это заняло 0,019 мс против 1,493 мс для оригинала (примерно в 80 раз быстрее!).