Итерация и сравнение двух словарей Python наиболее питонным способом для быстрого выполнения - PullRequest
0 голосов
/ 13 марта 2019
def formoutput(teams_id, patent_team):
    """
    The function to compare team_id and patent_teams to form the default dictionary matching values
    :param teams_id: {('3879797-2', '3930281-2'): 1, ('3930282-1', '3930282-2'): 2, ('3930288-1', '3930288-2'): 3, ... }
    :param patent_team: {3930281: [[('3879797-2', '3930281-2')]], 3930282: [[('3930282-1', '3930282-2')]], 3930288: [[('3930288-1', '3930288-2')]], ... }
    :return: defaultdict(<function formoutput.<locals>.<lambda> at 0x0000022A45228240>, {3930281: defaultdict(<class 'list'>, {'3879797-2': [1], '3930281-2': [1]}), 3930282: defaultdict(<class 'list'>, {'3930282-1': [2], '3930282-2': [2]}), 3930288: defaultdict(<class 'list'>, {'3930288-1': [3], '3930288-2': [3]}), 3930292: defaultdict(<class 'list'>, {'3861607-1': [4], '3861607-2': [4]}), ..}

    """
    print("Forming Output")
    print("Teams id =", teams_id)
    print("Patent_team=", patent_team)
    output_dict = defaultdict(lambda: defaultdict(list))
    try:
        for k,v in teams_id.items():
            for a,b in patent_team.items():
                for i in b:
                    if k in i:
                        for z in k:
                            output_dict[a][z].append(v)
    except Exception as e:
        print(e)
    return output_dict

У меня есть функция, которой я передаю два аргумента в форме словарей Python. Ключи первого словаря появляются как значения во втором. Мне нужно сравнить, если для каждого ключа из первого словаря существует значение во втором словаре, тогда используйте ключ, значение из первого словаря и ключ из второго словаря, чтобы добавить значение в defaultdict. Пожалуйста, посмотрите на приведенный выше код, это поможет лучше понять код. Несколько вложенных циклов делают код очень медленным. У меня более 50 миллионов пар ключ-значение в первом словаре. И более 3 миллионов ключей во втором словаре, каждый из которых содержит в среднем 3 значения.

Вся идея кода заключается в том, чтобы найти все возможные пары изобретателей, которые когда-либо работали над патентом в некоторой комбинации, и которые требуются в качестве выходных данных с помощью {atent_id :ventor_team, team_id}. В настоящее время выполнение одного и того же кода занимает часы. Я запустил его для 100 000 пар ключ-значение, и это заняло примерно 2000 секунд, что много времени.

Пожалуйста, предоставьте мне наилучший из возможных подходов к общему решению. Кроме того, как лучше всего справляться с таким огромным количеством данных?

Ответы [ 2 ]

0 голосов
/ 14 марта 2019

Если перенос списка действительно лишний и вы игнорируете случаи, когда соответствующий ключ отсутствует в team_ids, вы можете значительно сократить количество циклов и тестов членства здесь:

def formoutput(teams_id, patent_team):
    """
    The function to compare team_id and patent_teams to form the default dictionary matching values
    :param teams_id: {('3879797-2', '3930281-2'): 1, ('3930282-1', '3930282-2'): 2, ('3930288-1', '3930288-2'): 3, ... }
    :param patent_team: {3930281: [[('3879797-2', '3930281-2')]], 3930282: [[('3930282-1', '3930282-2')]], 3930288: [[('3930288-1', '3930288-2')]], ... }
    :return: defaultdict(<function defaultdict.copy>, {3930281: defaultdict(list, {'3879797-2': [1], '3930281-2': [1]}), 3930282: defaultdict(list, {'3930282-1': [2], '3930282-2': [2]}), 3930288: defaultdict(list, {'3930288-1': [3], '3930288-2': [3]}), 3930292: defaultdict(list, {'3861607-1': [4], '3861607-2': [4]}), ..}
    ...:
    """
    print("Forming Output")
    print("Teams id =", teams_id)
    print("Patent_team=", patent_team)
    # I hate lambdas, and as it happens, we don't need'em;
    # defaultdict(list).copy is cleaner and faster
    output_dict = defaultdict(defaultdict(list).copy)
    try:
        # [[pvs]] unpacks the superfluous(?) lists wrapping the tuple we care about
        for pk, [[pvs]] in patent_team.items():
            # Get the value to set once up front
            try:
                v = teams_id[pvs]
            except KeyError:
                continue  # Don't have a value to set, so move to next
            # Perform the first layer of dict lookup once since the key is the same
            # each time to avoid cost of repeated lookup
            pkdict = output_dict[pk]
            for pv in pvs:
                pkdict[pv].append(v)
    except Exception as e:
        print(e)
    return output_dict

В инвертированных циклах, поскольку ключи patent_teams являются ключами внешнего результата defaultdict, имеет смысл сначала выполнить цикл по patent_teams, избегая повторных поисков в output_dict для каждой клавиши patent_teams. Это также означает, что вы можете использовать значение из patent_teams, чтобы непосредственно искать то, что вам нужно из teams_id, вместо того, чтобы перебирать teams_id для поиска.

Если упаковка list не лишняя, замените:

for pk, [[pvs]] in patent_team.items():

с:

for pk, pvs_lists in patent_team.items():
    for pvs in chain.from_iterable(pvs_lists):

обязательно включите импорт from itertools import chain вверху файла.

0 голосов
/ 14 марта 2019

Два способа улучшения, посмотрите, какой из них вам удобен:

Сначала я бы изменил порядок вашего цикла:

for a,b in patent_team.items():
    for i in b:
        for k in i:
            if k in teams_id:
                for z in k:
                    output_dict[a][z].append(teams_id[k])

, потому что я бы предположил, что patent_teams - этоменьше, чем teams_id, и я могу использовать поиск O (1) на teams_id вместо итерации O (n) для каждого элемента.

Второй подход заключается в преобразовании ваших данных перед объединением,Вы можете попробовать сгладить два словаря в табличную форму и поместить их в DataFrame для панд или даже сохранить их в базе данных (SQLite в этом случае будет полезен).Преимущество этого состоит в том, что вы, вероятно, разгрузите операцию объединения таблиц / слияния DataFrame из интерпретатора Python.Так быстрее.

...