2016-02-16 28 views
36

Tôi có từ điển từ điển trong Python 2.7.Lấy số lượng tất cả các khóa trong từ điển từ điển trong Python

Tôi cần phải nhanh chóng đếm số lượng tất cả các khóa, bao gồm cả các khóa trong mỗi từ điển.

Vì vậy, trong ví dụ này tôi sẽ cần số lượng của tất cả các phím được 6:

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

Tôi biết tôi có thể lặp qua mỗi phím với vòng lặp for, nhưng tôi đang tìm kiếm một cách nhanh hơn để làm điều này , vì tôi sẽ có hàng ngàn/triệu chìa khóa và làm điều này chỉ là không hiệu quả:

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

Mặc dù bạn không yêu cầu này, nếu bạn muốn số lượng * phím * rõ rệt sau đó bạn có thể làm một cái gì đó như 'len (set (itertools.chain (dict_test, * dict_test.values ​​())))' –

Trả lời

29

Giữ nó đơn giản

Nếu chúng ta biết tất cả các giá trị là từ điển và không muốn kiểm tra xem bất kỳ giá trị nào của chúng cũng là từ điển hay không, sau đó nó đơn giản như sau:

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

Tinh chỉnh nó một chút, để thực sự kiểm tra các giá trị là từ điển trước khi đếm:

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

Và cuối cùng, nếu bạn muốn làm một độ sâu tùy ý, giống như sau:

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 

Trong trường hợp cuối cùng này, chúng tôi xác định một hàm sẽ được gọi đệ quy. Cho một giá trị d, chúng tôi trở lại một trong hai:

  • 0 nếu giá trị đó không phải là một cuốn từ điển; hoặc
  • số lượng khóa trong từ điển, cộng với tổng số khóa trong tất cả các con của chúng tôi.

Làm cho nó nhanh hơn

Trên đây là một cách tiếp cận ngắn gọn và dễ hiểu. Chúng tôi có thể nhận được nhanh hơn một chút bằng cách sử dụng máy phát điện:

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

này được chúng tôi thực hiện nhiều hơn một chút:

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

Cảm ơn, điều này rất hữu ích. Đặc biệt là bit hiệu suất. –

9

làm thế nào về

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

Những gì bạn đang làm đang lặp lại trên tất cả các khóa k và các giá trị v. Các giá trị v là các từ điển của bạn. Bạn nhận được độ dài của các từ điển đó và thêm một từ điển để bao gồm khóa được sử dụng để lập chỉ mục từ điển phụ.

Sau đó, bạn tổng hợp trong danh sách để nhận được số lượng khóa hoàn chỉnh.

EDIT:

Để làm rõ, đoạn mã này chỉ hoạt động cho từ điển từ điển theo yêu cầu. Không từ điển của từ điển của từ điển ...
Vì vậy, không sử dụng nó ví dụ lồng nhau :)

+1

Điều này không đếm "cha mẹ" phím. – Maroun

+3

đó là lý do tại sao tôi thêm một cho mỗi chiều dài – MaxBenChrist

+0

Hãy thử nó, vẫn không hoạt động. – Maroun

5

Cái gì như:

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

+1

Không hoạt động cho nhiều ví dụ lồng nhau hơn, hãy thử điều này - {1: {1: {1: {1: 2}}, 2: 3}, 2: {1: 2, 2: 3}} – AlokThakur

+2

@AlokThakur nên nó? Ý tôi là nó không được yêu cầu. –

+0

nó đang in 6 cho từ điển tôi đã đề cập ở trên, bạn có nhận được kết quả khác không? – AlokThakur

3

Hãy thử điều này,

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

Bạn có thể thử sử dụng gấu trúc DataFrame cho rằng:

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

Dòng pd.DataFrame(data) sẽ chuyển đổi từ điển của bạn vào một ma trận M N x, trong đó N là số phím "cha mẹ" và M là số lượng các phím con độc đáo:

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

Đối với mỗi [hàng, cột] bạn có giá trị hoặc NaN. Bạn chỉ cần đếm các giá trị không phải là NaN, số này sẽ cung cấp cho bạn số lượng khóa con và thêm len(df.columns), viết tắt của số cột (nghĩa là khóa chính).

+1

Điều này sẽ không hiệu quả/bộ nhớ chuyên sâu, như OP cho biết các từ điển có thể có hàng ngàn/hàng triệu mục? Tôi nghĩ rằng các giá trị "trống rỗng" (trong đó sẽ có rất nhiều) có thể ăn nhiều bộ nhớ bị lãng phí, nhưng tôi không hoàn toàn chắc chắn cách gấu trúc lưu trữ những giá trị đó. – Matthew

+0

@Matthew Chắc chắn sẽ có một ma trận lớn được tạo ra. Tôi cũng quan tâm đến câu trả lời này. –

+2

OP không đề cập gì về bộ nhớ nhưng tốc độ xử lý;) – matino

9

Như một cách tổng quát hơn, bạn có thể sử dụng một hàm đệ quy và phát biểu:

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

Ví dụ:

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

Note: Trong sử dụng python 3.x dict.items() phương pháp thay vì iteritems() .

Một benchmark với câu trả lời được chấp nhận trong đó cho thấy rằng chức năng này là nhanh hơn so với câu trả lời được chấp nhận:

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

kết quả:

1st: 4.65556812286 
2nd : 4.09120802879 
+3

Điều này cũng không thành công: 'dict_test = {" a ": {" b ":" 1 "," c ":" 2 "," d ": "3", "e": {"f": 1}}} ' – Idos

+1

@Idos Yep, đó là loại 2 cấp. – Kasramvd

+1

@Idos OP chỉ hỏi về từ điển từ điển không phải là một tổ hợp tùy ý. – Matthew

6

Sử dụng một chức năng máy phát điện và các yield from cú pháp mới trong Python 3.x. Điều này sẽ làm việc cho một từ điển lồng nhau tùy

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

Trong Python 2.x bạn cần một để làm điều này:

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

hàm đệ quy:

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) sẽ trở lại số lượng khóa trong từ điển, vì vậy, giả sử bạn biết cách lồng nhau và tất cả các giá trị là từ điển:

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

Bạn có thể bọc này trong một sự hiểu biết danh sách:

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

mà có lẽ là pythonic nhất.Bạn có thể mở rộng nó dưới dạng:

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

nhưng tôi có xu hướng nghĩ rằng điều này là không thể đọc được.

5

Dưới đây là hàm đệ quy để tìm tổng số các từ điển lồng nhau chìa khóa ...

s=0 
def recurse(v): 
    if type(v)==type({}): 
    for k in v.keys(): 
     global s 
     s+=1 
     recurse(v[k]) 
Các vấn đề liên quan