web-dev-qa-db-fra.com

Obtenez le nombre de toutes les clés dans un dictionnaire de dictionnaires en Python

J'ai un dictionnaire de dictionnaires en Python 2.7.

J'ai besoin de compter rapidement le nombre de toutes les clés, y compris les clés de chacun des dictionnaires.

Donc, dans cet exemple, j'aurais besoin que le nombre de toutes les clés soit 6:

dict_test = {'key2': {'key_in3': 'value', 'key_in4': 'value'}, 'key1': {'key_in2': 'value', 'key_in1': 'value'}}

Je sais que je peux parcourir chaque clé avec des boucles for, mais je cherche un moyen plus rapide de le faire, car j'aurai des milliers/millions de clés et cela est tout simplement inefficace:

count_the_keys = 0

for key in dict_test.keys():
    for key_inner in dict_test[key].keys():
       count_the_keys += 1

# something like this would be more effective
# of course .keys().keys() doesn't work
print len(dict_test.keys()) * len(dict_test.keys().keys())
37
ivan_bilan

Garder les choses simples

Si nous savons que toutes les valeurs sont des dictionnaires et que nous ne voulons pas vérifier que leurs valeurs sont également des dictionnaires, alors c'est aussi simple que:

len(dict_test) + sum(len(v) for v in dict_test.itervalues())

Affiner un peu, pour vérifier réellement que les valeurs sont des dictionnaires avant de les compter:

len(dict_test) + sum(len(v) for v in dict_test.itervalues() if isinstance(v, dict))

Et enfin, si vous souhaitez faire une profondeur arbitraire, quelque chose comme ceci:

def sum_keys(d):
    return (0 if not isinstance(d, dict) 
            else len(d) + sum(sum_keys(v) for v in d.itervalues())

print sum_keys({'key2': {'key_in3': 'value', 'key_in4': 'value'}, 
                'key1': {'key_in2': 'value', 
                         'key_in1': dict(a=2)}})
# => 7

Dans ce dernier cas, nous définissons une fonction qui sera appelée récursivement. Étant donné une valeur d, nous retournons soit:

  • 0 si cette valeur n'est pas un dictionnaire; ou
  • le nombre de clés dans le dictionnaire, plus le total de clés dans tous nos enfants.

Pour le rendre plus rapide

Ce qui précède est une approche succincte et facilement compréhensible. On peut aller un peu plus vite en utilisant un générateur:

def _counter(d):
    # how many keys do we have?
    yield len(d)

    # stream the key counts of our children
    for v in d.itervalues():
        if isinstance(v, dict):
            for x in _counter(v):
                yield x

def count_faster(d):
    return sum(_counter(d))

Cela nous donne un peu plus de performances:

In [1]: %timeit sum_keys(dict_test)
100000 loops, best of 3: 4.12 µs per loop

In [2]: %timeit count_faster(dict_test)
100000 loops, best of 3: 3.29 µs per loop
30
donkopotamus

Que diriez-vous

n = sum([len(v)+1 for k, v in dict_test.items()])

Ce que vous faites est d'itérer sur toutes les clés k et les valeurs v. Les valeurs v sont vos sous-autorités. Vous obtenez la longueur de ces dictionnaires et en ajoutez un pour inclure la clé utilisée pour indexer le sous-répertoire.

Ensuite, vous additionnez la liste pour obtenir le nombre complet de clés.

MODIFIER:

Pour clarifier, cet extrait ne fonctionne que pour les dictionnaires de dictionnaires comme demandé. Pas des dictionnaires de dictionnaires de dictionnaires ...
Ne l'utilisez donc pas comme exemple imbriqué :)

9
MaxBenChrist

De manière plus générale, vous pouvez utiliser une fonction de récursivité et une expression de générateur:

>>> def count_keys(dict_test):
...     return sum(1+count_keys(v) if isinstance(v,dict) else 1 for _,v in dict_test.iteritems())
... 

Exemple:

>>> dict_test = {'a': {'c': '2', 'b': '1', 'e': {'f': {1: {5: 'a'}}}, 'd': '3'}}
>>> 
>>> count(dict_test)
8

Note: Dans python 3.X utilisez la méthode dict.items() au lieu de iteritems().

Un benchmark avec réponse acceptée qui montre que cette fonction est plus rapide que la réponse acceptée:

from timeit import timeit

s1 = """
def sum_keys(d):
    return 0 if not isinstance(d, dict) else len(d) + sum(sum_keys(v) for v in d.itervalues())

sum_keys(dict_test)
"""

s2 = """
def count_keys(dict_test):
    return sum(1+count_keys(v) if isinstance(v,dict) else 1 for _,v in dict_test.iteritems())

count_keys(dict_test)
   """

print '1st: ', timeit(stmt=s1,
                      number=1000000,
                      setup="dict_test = {'a': {'c': '2', 'b': '1', 'e': {'f': {1: {5: 'a'}}}, 'd': '3'}}")
print '2nd : ', timeit(stmt=s2,
                       number=1000000,
                       setup="dict_test = {'a': {'c': '2', 'b': '1', 'e': {'f': {1: {5: 'a'}}}, 'd': '3'}}")

résultat:

1st:  4.65556812286
2nd :  4.09120802879
9
Kasrâmvd

En utilisant une fonction de générateur et le yield from syntaxe nouvelle dans Python 3.x. Cela fonctionnera pour un dictionnaire imbriqué arbitraire

>>> from collections import Mapping
>>> def count_keys(mydict):
...     for key, value in mydict.items():
...         if isinstance(value, Mapping):
...             yield from count_keys(value)
...     yield len(mydict)
... 
>>> dict_test = {'key2': {'key_in3': 'value', 'key_in4': 'value'}, 'key1': {'key_in2': 'value', 'key_in1': 'value'}}
>>> sum(count_keys(dict_test))
6

Dans Python 2.x vous avez besoin d'un pour faire ceci:

>>> def count_keys(mydict):
...     for key, value in mydict.items():
...         if isinstance(value, Mapping):
...             for item in count_keys(value):
...                 yield 1
...         yield 1
... 
>>> sum(count_keys(dict_test))
6
6
styvane

Voici la fonction récursive pour trouver le nombre total de clés des dictionnaires imbriqués ...

s=0
def recurse(v):
   if type(v)==type({}):
     for k in v.keys():
      global s
      s+=1
      recurse(v[k])
5
Dharmik

Quelque chose comme:

print len(dict_test) + sum(len(v) for v in dict_test.values())

5
Viacheslav Kondratiuk

len (dict) renverra le nombre de clés dans un dictionnaire, donc, en supposant que vous savez à quel point il est imbriqué et que toutes les valeurs sont des dictionnaires:

counter = len(outer_dict)
for v in outer_dict.values :
    counter += len(v)

Vous pouvez envelopper cela dans une liste de compréhension:

counter = len(outer_dict)
counter += sum([len(inner_dict) for inner_dict in outer_dict.values])

qui est probablement le plus pythonique. Vous pouvez l'étendre comme:

counter = len(outer_dict)
counter += sum([len(inner_dict) if isinstance(inner_dict, dict) else 0 for inner_dict in outer_dict.values])

mais j'ai tendance à penser que c'est assez illisible.

4
phil_20686

Vous pouvez essayer d'utiliser pandas DataFrame pour cela:

>>> import pandas as pd
>>> data = {'1': {'2': 'a', '3': 'b'}, '4': {'5': 'c', '6': 'd'}, '7': {'5': 'x'}}
>>> df = pd.DataFrame(data)
>>> print (df.count().sum() + len(df.columns))  # 8

La ligne pd.DataFrame(data) convertira votre dictionnaire en une matrice N x M, où N est le nombre de clés "parent" et M est le nombre de clés enfants uniques:

     1    4    7
2    a  NaN  NaN
3    b  NaN  NaN
5  NaN    c    x
6  NaN    d  NaN

Pour chaque [ligne, colonne], vous avez une valeur ou NaN. Il vous suffit de compter les valeurs non NaN, qui vous donneront le nombre de clés enfants et ajouter len(df.columns), qui représente le nombre de colonnes (c'est-à-dire les clés parent).

4
matino

fonction récursive:

def count_keys(some_dict):
    count = 0
    for key in some_dict:
        if isinstance(some_dict[key], dict):
            count += count_keys(some_dict[key])
        count += 1
    return count
4
Konstantyn

Essaye ça,

l = len(dict_test)
for k in dict_test:
    l += len(dict_test[k])
3
Dharmik