2016-02-16 6 views
36

Ich habe ein Wörterbuch von Wörterbüchern in Python 2.7.Holen Sie sich die Anzahl aller Schlüssel in einem Wörterbuch von Wörterbüchern in Python

Ich muss schnell die Anzahl aller Schlüssel zählen, einschließlich der Schlüssel in jedem der Wörterbücher.

So in diesem Beispiel würde ich die Anzahl aller Schlüssel sein muß 6:

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

Ich weiß, dass ich durch jede Taste mit for-Schleifen durchlaufen kann, aber ich bin auf der Suche nach einem schnelleren Weg, dies zu tun da ich tausende/Millionen von Schlüsseln haben und tun dies nur wirkungslos:

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()) 
+1

Obwohl Sie für diese fragen nicht, wenn Sie die Anzahl von * * verschiedene Tasten dann wollte konnte man etwas wie 'len (set (itertools.chain (dict_test, * dict_test.values ​​()))) –

Antwort

29

Keeping Einfach es

Wenn wir wissen alle Werte sind Wörterbücher, und möchten nicht überprüfen, ob irgendwelche ihrer Werte auch Wörterbücher sind, dann ist es so einfach wie:

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

es ein wenig Verfeinerung, tatsächlich zu überprüfen, dass die Werte Wörterbücher sind vor ihnen zählen:

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

Und schließlich, wenn Sie eine beliebige Tiefe, etwa wie folgt tun wollen:

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 

In diesem letzten Fall definieren wir eine Funktion, die rekursiv aufgerufen wird. Bei einem Wert d kehren wir entweder:

  • 0 wenn dieser Wert kein Wörterbuch ist; oder
  • die Anzahl der Schlüssel im Wörterbuch plus die Summe der Schlüssel in allen unseren Kindern.

so dass es schneller

Das Vorstehende ist ein prägnanter und leicht Ansatz verstanden. Wir können ein wenig schneller mit einem Generator erhalten:

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

Dies bringt uns ein bisschen mehr Leistung:

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 
+0

Danke, das ist sehr hilfreich. Vor allem das Performance-Bit. –

9

Wie wäre es

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

Was Sie tun, ist die Iteration über alle Schlüssel k und Werte v. Die Werte v sind Ihre Unterwörterbücher. Sie erhalten die Länge dieser Wörterbücher und fügen eine hinzu, um den Schlüssel zum Indexieren des Unterverzeichnisses einzuschließen.

Anschließend summieren Sie die Liste, um die vollständige Anzahl der Schlüssel zu erhalten.

EDIT:

Um zu klären, funktioniert dieser Schnipsel nur für Wörterbücher Wörterbücher als gefragt. Nicht Wörterbücher Wörterbücher Wörterbücher ...
So verwenden Sie es nicht für verschachtelte Beispiel :)

+1

Dies zählt nicht" Eltern "-Tasten. – Maroun

+3

weshalb ich eine zu jeder Länge hinzufügen – MaxBenChrist

+0

Versuchen Sie es, immer noch nicht funktioniert. – Maroun

5

Etwas wie:

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

+1

Funktioniert nicht für verschachtelte Beispiele, versuchen Sie dies - {1: {1: {1: {1: 2}}, 2: 3}, 2: {1: 2, 2: 3}} – AlokThakur

+2

@AlokThakur sollte es? Ich meine, es wurde nicht verlangt. –

+0

es ist Drucken 6 für das Wörterbuch, das ich oben erwähnte, erhalten Sie unterschiedliche Ergebnis? – AlokThakur

3

die Sie interessieren,

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

Sie könnten versuchen, mit pandas DataFrame dafür:

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

Die pd.DataFrame(data) Linie wird Ihr Wörterbuch auf eine N x M-Matrix umwandeln, wobei n die Anzahl der „Eltern“ Schlüssel ist und M die Anzahl der einzigartigen Kinder Schlüssel ist:

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

Für jede [Zeile, Spalte] Sie habe einen Wert oder NaN. Sie müssen nur die nicht NaN Werte zählen, die Ihnen die Anzahl der untergeordneten Schlüssel geben und len(df.columns) hinzufügen, die für die Anzahl der Spalten steht (d. H. Übergeordnete Schlüssel).

+1

Wäre das nicht ineffizient/speicherintensiv, da das OP sagte, dass die Wörterbücher Tausende/Millionen Einträge haben könnten? Ich denke, dass die "leeren" Werte (von denen es eine Menge geben würde) eine Menge verschwendeter Erinnerungen auffressen könnten, aber ich bin mir nicht ganz sicher, wie Pandas diese aufbewahren. – Matthew

+0

@Matthew Dort würde sicher eine große Matrix erstellt werden. Die Antwort darauf interessiert mich auch. –

+2

OP erwähnt nichts über Speicher, aber die Verarbeitungsgeschwindigkeit;) – matino

9

Als allgemeiner Weise können Sie eine Rekursion Funktion und Generator Ausdruck verwenden können:

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

Beispiel:

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

Hinweis: In Python 3.x dict.items() Methode statt iteritems() .

Ein Benchmark mit akzeptierter Antwort, die zeigt, dass diese Funktion als akzeptierte Antwort schneller ist:

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'}}") 

Ergebnis:

1st: 4.65556812286 
2nd : 4.09120802879 
+3

Dies funktioniert auch nicht: 'dict_test = {" a ": {" b ":" 1 "," c ":" 2 "," d ": "3", "e": {"f": 1}}} ' – Idos

+1

@Idos Ja, es ist für einen 2-Level-Typ. – Kasramvd

+1

@Idos Das OP fragte nur nach einem Wörterbuch von Wörterbüchern, nicht nach einer willkürlichen Verschachtelung. – Matthew

6

eine Generatorfunktion verwenden und die yield from Syntax neu in Python 3.x Dies wird für eine beliebige verschachtelte Wörterbuch arbeiten

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

In Python 2.x Sie müssen dies tun:

>>> 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 
4

rekursive Funktion:

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

len (dict) zurückkehren die Anzahl der Schlüssel in einem Wörterbuch, vorausgesetzt, Sie wissen, wie verschachtelt es ist und dass alle Werte Wörterbücher sind:

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

Sie können dies in einer Liste Verständnis wickeln:

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

das ist wahrscheinlich der am meisten pythonic.Sie können es wie folgt erweitern:

aber ich neige dazu zu denken, dass dies ziemlich unlesbar ist.

5

Hier ist die rekursive Funktion der verschachtelten Wörterbücher Gesamtzahl der Schlüssel zu finden ...

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