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())
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; ouPour 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
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é :)
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
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
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])
Quelque chose comme:
print len(dict_test) + sum(len(v) for v in dict_test.values())
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.
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).
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
Essaye ça,
l = len(dict_test)
for k in dict_test:
l += len(dict_test[k])