Как оптимизировать вложенность для l oop с фильтрацией в python - PullRequest
1 голос
/ 04 февраля 2020

Я пытаюсь оптимизировать вложенную форму -l oop с фильтрацией, код выглядит следующим образом:

user_ids = ['A', 'B', 'C']

all_dict_1 = [
    {
       'id': 'all',
       'user_id': 'B',
    },
    {
       'id': 'foo',
       'user_id': 'B',
    },
    {
       'id': 'bar',
       'user_id': 'A',
    },
    {
       'id': 'bar',
       'user_id': 'D',
    },
]

all_dict_2 = [
    {
        'id': 'all',
        'percentage': 0.2,
    },
    {
        'id': 'foo',
        'percentage': 0.3,
    },
]


def _filter(dict_1, dict_2, user_ids):
    if str(dict_1['user_id']) in user_ids:

        if dict_2['id'] == 'all':
            dict_1['percentage'] = dict_2['percentage']
            return dict_1

        if dict_1['id'] == dict_2['id']:
            dict_1['percentage'] = dict_2['percentage']
            return dict_1

    return None


hits = [_filter(x, y, user_ids) for x in all_dict_1 for y in all_dict_2]

hits = [i for i in hits if i] # Removing None values

список all_dict_1 особенно длинный (тысячи объектов), поэтому Для запуска функции требуется более 1 с. 100

Существуют ли какие-либо библиотеки или технические приемы, позволяющие сделать это быстрее?

Ответы [ 2 ]

0 голосов
/ 04 февраля 2020

Сделать user_ids a set для ускорения item in user_ids испытаний. Сначала отфильтруйте это, поскольку он отклоняет записи, которые вам вообще не нужно обрабатывать. Используйте filter, чтобы избежать повторных глобальных поисков имен.

user_ids = {'A', 'B', 'C'}
filtered_dict_1 = filter(
    lambda item, ids=user_ids: item['user_id'] in ids,
    all_dict_1
)

Измените all_dict_2 на фактический диктант, чтобы разрешить доступ O (1) вместо сканирования O (n). При переборе своих записей для их изменения, напрямую обращайтесь к необходимому проценту или используйте явное значение по умолчанию.

all_dict_2 = {
    'foo': 0.3,
}
def add_percentage(item, default=0.2, percentages=all_dict_2):
    item["percentage"] = percentages.get(item['id'], default)
    return item

Примените преобразование, используя map, чтобы избежать повторного поиска вашей функции преобразования.

hits = list(map(add_percentage, filtered_dict_1))
0 голосов
/ 04 февраля 2020

Лог c в вашем вопросе может быть уменьшен до следующего понимания списка, которое должно быть немного быстрее:

>>> hits = [{**x, 'percentage': y['percentage']} 
                for x in all_dict_1 for y in all_dict_2
                    if x['user_id'] in user_ids and 
                       (y['id'] == 'all' or x['id'] == y['id'])]
>>> hits
[{'id': 'all', 'user_id': 'B', 'percentage': 0.2},
 {'id': 'foo', 'user_id': 'B', 'percentage': 0.2},
 {'id': 'foo', 'user_id': 'B', 'percentage': 0.3},
 {'id': 'bar', 'user_id': 'A', 'percentage': 0.2}]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...