У меня есть словарь словарей, каждый вложенный словарь имеет одинаковые ключи, например:

all_dicts = {'a':{'name': 'A', 'city': 'foo'},
             'b':{'name': 'B', 'city': 'bar'},
             'c':{'name': 'C', 'city': 'bar'},
             'd':{'name': 'B', 'city': 'foo'},
             'e':{'name': 'D', 'city': 'bar'},
            }

Как получить список (или словарь) всех словарей, где 'city' имеет значение 'bar'?

Следующий код работает, но не масштабируется:

req_key = 'bar'
selected = []
for one in all_dicts.keys():
    if req_key in all_dicts[one]:
    selected.append(all_dicts[one])

Скажем, 'city' может иметь 50 000 уникальных значений, а словарь all_dicts содержит 600 000 значений, итерация по словарю для каждого отдельного значения 'city' не очень эффективна.

Есть ли масштабируемый и эффективный способ сделать это?

7
IM94 10 Янв 2017 в 13:20

4 ответа

Лучший ответ

Что вы можете сделать, это создать индекс для этого словаря, например:

cityIndex={}
for item in all_dicts.values():
    if item['city'] in cityIndex:
        cityIndex[item['city']].append(item)
    else:
        cityIndex[item['city']]=[item]

Это потребует некоторого начального времени обработки, а также некоторой дополнительной памяти, но после этого это будет очень быстро. Если вам нужны все элементы с некоторыми cityName, вы получите их, выполнив:

mylist=cityIndex[cityName] if cityName in cityIndex else []

Это дает вам много преимуществ, если all_dicts создается один раз, а затем запрашивается много раз.

Если all_dicts изменяется во время выполнения вашей программы, вам потребуется еще немного кода для поддержки cityIndex. Если item добавлен в all_dicts, просто выполните:

if item['city'] in cityIndex:
    cityIndex[item['city']].append(item)
else:
    cityIndex[item['city']]=[item]

В то время как если элемент удален, это также простой способ удалить его из индекса (при условии, что комбинация 'name' и 'city' уникальна среди ваших элементов):

for i, val in enumerate(cityIndex[item['city']]):
    if val['name']==item['name']:
        break
del cityIndex[item['city']][i]

Если запросов гораздо больше, чем обновлений, вы все равно получите значительное улучшение производительности.

9
xzoert 10 Янв 2017 в 11:45

Или используйте filter в Python 3:

>>> list(filter(lambda x: x['city']=='bar', all_dicts.values()))
# [{'name': 'D', 'city': 'bar'}, {'name': 'B', 'city': 'bar'}, {'name': 'C', 'city': 'bar'}]

Или с pandas:

import pandas as pd

df = pd.DataFrame(all_dicts).T
df[df.city=='bar'].T.to_dict()

# {'e': {'city': 'bar', 'name': 'D'}, 'c': {'city': 'bar', 'name': 'C'}, 'b': {'city': 'bar', 'name': 'B'}}
3
Colonel Beauvel 10 Янв 2017 в 10:47
all_dicts = {'a':{'name': 'A', 'city': 'foo'},
             'b':{'name': 'B', 'city': 'bar'},
             'c':{'name': 'C', 'city': 'bar'},
             'd':{'name': 'B', 'city': 'foo'},
             'e':{'name': 'D', 'city': 'bar'},
            }

citys = {}
for key, value in all_dicts.items():
    citys[key] = value['city']
#{'a': 'foo', 'b': 'bar', 'e': 'bar', 'd': 'foo', 'c': 'bar'}

for key, value in citys.items():
    if value == 'bar':
        print(all_dicts[key])

Вне:

{'name': 'B', 'city': 'bar'}
{'name': 'D', 'city': 'bar'}
{'name': 'C', 'city': 'bar'}

Создайте вспомогательный диктат для хранения города в качестве индекса, и вы сможете очень быстро на него ссылаться.

0
宏杰李 10 Янв 2017 в 10:44

Вы должны проверить все значения; альтернативы этому нет. Однако вы можете использовать векторизованный подход - списочное понимание - который будет намного быстрее, чем цикл for:

selected = [d for d in all_dicts.values() if d['city']=='bar']
print(selected)
# [{'name': 'B', 'city': 'bar'}, {'name': 'C', 'city': 'bar'}, {'name': 'D', 'city': 'bar'}]

Использование dict.values вместо доступа к ключам словаря также повышает производительность и также эффективно использует память в Python 3.

7
Moses Koledoye 10 Янв 2017 в 10:54