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

J'ai une fonction à laquelle je passe deux arguments sous forme de dictionnaires python. Les clés du premier dictionnaire apparaissent comme les valeurs du second. J'ai besoin de comparer si, pour chaque clé du premier dictionnaire, il existe une valeur dans le deuxième dictionnaire, utilisez la clé, la valeur du premier dictionnaire et la clé du deuxième dictionnaire pour ajouter la valeur dans un défaut. Veuillez consulter le code ci-dessus, cela vous aidera à mieux comprendre le code. Les multiples boucles imbriquées rendent le code très lent. J'ai plus de 50 millions de paires valeur / clé dans le premier dictionnaire. Et plus de 3 millions de clés dans la deuxième dictature, chaque clé contenant en moyenne 3 valeurs.

L'idée derrière le code est de trouver toutes les paires possibles d'inventeurs qui ont déjà travaillé sur un brevet dans une combinaison et celles-ci sont requises en sortie avec {patent_id: inventor_team, team_id}. Actuellement, il faut des heures pour exécuter le même code. Je l'ai exécuté pour 100 000 paires de valeurs clés et cela a pris environ 2000 secondes, ce qui est beaucoup de temps.

Veuillez me fournir la meilleure approche possible pour la solution globale. De plus, quelle est la meilleure façon de gérer une telle quantité de données?

0
Balvaishwer Salaria 13 mars 2019 à 23:28

2 réponses

Meilleure réponse

Si l'habillage de liste est vraiment superflu et que vous ignorez les cas où la clé correspondante n'est pas dans team_ids, vous pouvez réduire considérablement le nombre de boucles et de tests d'appartenance ici:

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

J'ai inversé les boucles, puisque les clés patent_teams sont les clés du résultat externe defaultdict, il est logique de boucler d'abord sur patent_teams, en évitant les recherches répétées dans output_dict pour chaque patent_teams clé. Cela signifie également que vous pouvez utiliser la valeur de patent_teams pour rechercher directement ce dont vous avez besoin à partir de teams_id, plutôt que de parcourir teams_id pour rechercher.

Si l'habillage list n'est pas superflu, remplacez:

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

Avec:

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

En veillant à inclure l'importation from itertools import chain en haut de votre fichier.

1
ShadowRanger 25 sept. 2020 à 23:24

Deux façons de vous améliorer, voyez celle qui vous convient:

Je voudrais d'abord inverser l'ordre de votre boucle:

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])

Car je suppose que patent_teams est un dict plus petit que teams_id et je peux utiliser la recherche O (1) sur teams_id, au lieu de l'itération O (n) sur chaque élément.

La deuxième approche consiste à convertir vos données avant de les combiner. Vous pouvez essayer d'aplanir les deux dictionnaires sous forme de tableau et de les mettre dans pandas DataFrame ou même de les enregistrer dans une base de données (SQLite dans ce cas serait pratique). L'avantage de cela est que vous déchargerez probablement votre opération de fusion de table join / DataFrame de votre interpréteur Python. Ainsi plus vite.

1
adrtam 14 mars 2019 à 00:35