사전 검색의 파이썬 목록
내가 이것을 가지고 있다고 가정 :
[
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]
"Pam"을 이름으로 검색하여 관련 사전을 검색하려고합니다. {name: "Pam", age: 7}
이것을 달성하는 방법?
생성기 표현식을 사용할 수 있습니다 .
>>> dicts = [
... { "name": "Tom", "age": 10 },
... { "name": "Mark", "age": 5 },
... { "name": "Pam", "age": 7 },
... { "name": "Dick", "age": 12 }
... ]
>>> next(item for item in dicts if item["name"] == "Pam")
{'age': 7, 'name': 'Pam'}
이것은 나에게 가장 파이썬적인 방법으로 보입니다.
people = [
{'name': "Tom", 'age': 10},
{'name': "Mark", 'age': 5},
{'name': "Pam", 'age': 7}
]
filter(lambda person: person['name'] == 'Pam', people)
결과 (Python 2에서 목록으로 반환) :
[{'age': 7, 'name': 'Pam'}]
참고 : Python 3에서는 필터 객체가 반환됩니다. 따라서 python3 솔루션은 다음과 같습니다.
list(filter(lambda person: person['name'] == 'Pam', people))
@ Frédéric Hamidi의 답변은 훌륭합니다. Python 3.x에서는 구문이 .next()
약간 변경되었습니다. 따라서 약간의 수정 :
>>> dicts = [
{ "name": "Tom", "age": 10 },
{ "name": "Mark", "age": 5 },
{ "name": "Pam", "age": 7 },
{ "name": "Dick", "age": 12 }
]
>>> next(item for item in dicts if item["name"] == "Pam")
{'age': 7, 'name': 'Pam'}
@Matt의 의견에서 언급했듯이 기본값을 다음과 같이 추가 할 수 있습니다.
>>> next((item for item in dicts if item["name"] == "Pam"), False)
{'name': 'Pam', 'age': 7}
>>> next((item for item in dicts if item["name"] == "Sam"), False)
False
>>>
당신은 목록 이해를 사용할 수 있습니다 :
def search(name, people):
return [element for element in people if element['name'] == name]
people = [
{'name': "Tom", 'age': 10},
{'name': "Mark", 'age': 5},
{'name': "Pam", 'age': 7}
]
def search(name):
for p in people:
if p['name'] == name:
return p
search("Pam")
사전 목록을 살펴보고 키 x에 특정 값이있는 사전을 반환하기 위해 다양한 방법을 테스트했습니다.
결과 :
- 속도 : 목록 이해> 생성기 표현 >> 일반 목록 반복 >>> 필터.
- 모든 척도는 목록의 dict 수에 따라 선형입니다 (10x 목록 크기-> 10x 시간).
- 사전 당 키는 대량 (수천) 키의 속도에 큰 영향을 미치지 않습니다. https://imgur.com/a/quQzv 계산 된이 그래프를 참조 하십시오 (방법 이름은 아래 참조).
모든 테스트는 Python 3.6 .4, W7x64로 수행 되었습니다.
from random import randint
from timeit import timeit
list_dicts = []
for _ in range(1000): # number of dicts in the list
dict_tmp = {}
for i in range(10): # number of keys for each dict
dict_tmp[f"key{i}"] = randint(0,50)
list_dicts.append( dict_tmp )
def a():
# normal iteration over all elements
for dict_ in list_dicts:
if dict_["key3"] == 20:
pass
def b():
# use 'generator'
for dict_ in (x for x in list_dicts if x["key3"] == 20):
pass
def c():
# use 'list'
for dict_ in [x for x in list_dicts if x["key3"] == 20]:
pass
def d():
# use 'filter'
for dict_ in filter(lambda x: x['key3'] == 20, list_dicts):
pass
결과 :
1.7303 # normal list iteration
1.3849 # generator expression
1.3158 # list comprehension
7.7848 # filter
@ FrédéricHamidi에 약간만 추가하십시오.
dicts 목록에 키가 확실하지 않은 경우 다음과 같이 도움이 될 것입니다.
next((item for item in dicts if item.get("name") and item["name"] == "Pam"), None)
팬더 패키지를 사용해 본 적이 있습니까? 이러한 종류의 검색 작업에 적합하며 최적화되었습니다.
import pandas as pd
listOfDicts = [
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]
# Create a data frame, keys are used as column headers.
# Dict items with the same key are entered into the same respective column.
df = pd.DataFrame(listOfDicts)
# The pandas dataframe allows you to pick out specific values like so:
df2 = df[ (df['name'] == 'Pam') & (df['age'] == 7) ]
# Alternate syntax, same thing
df2 = df[ (df.name == 'Pam') & (df.age == 7) ]
나는 팬더의 더 빠른 런타임을 더 큰 규모, 즉 100k + 항목으로 설명하기 위해 아래에 약간의 벤치마킹을 추가했습니다.
setup_large = 'dicts = [];\
[dicts.extend(({ "name": "Tom", "age": 10 },{ "name": "Mark", "age": 5 },\
{ "name": "Pam", "age": 7 },{ "name": "Dick", "age": 12 })) for _ in range(25000)];\
from operator import itemgetter;import pandas as pd;\
df = pd.DataFrame(dicts);'
setup_small = 'dicts = [];\
dicts.extend(({ "name": "Tom", "age": 10 },{ "name": "Mark", "age": 5 },\
{ "name": "Pam", "age": 7 },{ "name": "Dick", "age": 12 }));\
from operator import itemgetter;import pandas as pd;\
df = pd.DataFrame(dicts);'
method1 = '[item for item in dicts if item["name"] == "Pam"]'
method2 = 'df[df["name"] == "Pam"]'
import timeit
t = timeit.Timer(method1, setup_small)
print('Small Method LC: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup_small)
print('Small Method Pandas: ' + str(t.timeit(100)))
t = timeit.Timer(method1, setup_large)
print('Large Method LC: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup_large)
print('Large Method Pandas: ' + str(t.timeit(100)))
#Small Method LC: 0.000191926956177
#Small Method Pandas: 0.044392824173
#Large Method LC: 1.98827004433
#Large Method Pandas: 0.324505090714
사전 목록에서 값을 검색하는 일반적인 방법입니다.
def search_dictionaries(key, value, list_of_dictionaries):
return [element for element in list_of_dictionaries if element[key] == value]
names = [{'name':'Tom', 'age': 10}, {'name': 'Mark', 'age': 5}, {'name': 'Pam', 'age': 7}]
resultlist = [d for d in names if d.get('name', '') == 'Pam']
first_result = resultlist[0]
이것은 한 가지 방법입니다 ...
내 첫 번째 생각은 당신이 이러한 사전의 사전을 만드는 것을 고려하고 싶을 것입니다 ... 예를 들어, 당신이 그것을 몇 번 이상 검색하려고한다면.
그러나 그것은 조기 최적화 일 수 있습니다. 무엇이 문제일까요 :
def get_records(key, store=dict()):
'''Return a list of all records containing name==key from our store
'''
assert key is not None
return [d for d in store if d['name']==key]
dicts=[
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]
from collections import defaultdict
dicts_by_name=defaultdict(list)
for d in dicts:
dicts_by_name[d['name']]=d
print dicts_by_name['Tom']
#output
#>>>
#{'age': 10, 'name': 'Tom'}
간단히 목록 이해를 사용하십시오.
[i for i in dct if i['name'] == 'Pam'][0]
샘플 코드 :
dct = [
{'name': 'Tom', 'age': 10},
{'name': 'Mark', 'age': 5},
{'name': 'Pam', 'age': 7}
]
print([i for i in dct if i['name'] == 'Pam'][0])
> {'age': 7, 'name': 'Pam'}
당신은 이것을 시도 할 수 있습니다 :
''' lst: list of dictionaries '''
lst = [{"name": "Tom", "age": 10}, {"name": "Mark", "age": 5}, {"name": "Pam", "age": 7}]
search = raw_input("What name: ") #Input name that needs to be searched (say 'Pam')
print [ lst[i] for i in range(len(lst)) if(lst[i]["name"]==search) ][0] #Output
>>> {'age': 7, 'name': 'Pam'}
리스트의 모든 요소를 거쳐야합니다. 바로 가기가 없습니다!
다른 곳을 제외하고는 목록의 항목을 가리키는 이름 사전을 유지하지만 목록에서 요소를 팝업하는 결과를 처리해야합니다.
다음은 반복 룰 목록을 사용하고 필터 + 람다 또는 리팩토링 (필요하거나 경우에 유효한 경우)을 사용하여 코드를 dict 목록이 아닌 dict의 dict로 비교하는 것입니다.
import time
# Build list of dicts
list_of_dicts = list()
for i in range(100000):
list_of_dicts.append({'id': i, 'name': 'Tom'})
# Build dict of dicts
dict_of_dicts = dict()
for i in range(100000):
dict_of_dicts[i] = {'name': 'Tom'}
# Find the one with ID of 99
# 1. iterate through the list
lod_ts = time.time()
for elem in list_of_dicts:
if elem['id'] == 99999:
break
lod_tf = time.time()
lod_td = lod_tf - lod_ts
# 2. Use filter
f_ts = time.time()
x = filter(lambda k: k['id'] == 99999, list_of_dicts)
f_tf = time.time()
f_td = f_tf- f_ts
# 3. find it in dict of dicts
dod_ts = time.time()
x = dict_of_dicts[99999]
dod_tf = time.time()
dod_td = dod_tf - dod_ts
print 'List of Dictionries took: %s' % lod_td
print 'Using filter took: %s' % f_td
print 'Dict of Dicts took: %s' % dod_td
결과는 다음과 같습니다.
List of Dictionries took: 0.0099310874939
Using filter took: 0.0121960639954
Dict of Dicts took: 4.05311584473e-06
결론 : 딕셔너리 사전을 갖는 것이 ID를 기준으로 검색한다고 말하는 경우에 검색 할 수있는 가장 효율적인 방법입니다. 흥미롭게도 필터를 사용하는 것이 가장 느린 솔루션입니다.
동일한 질문에 대한 답변을 검색 할 때이 스레드를 찾았습니다. 답변이 늦다는 것을 알고 있지만 다른 사람에게 도움이 될 경우에 도움을 줄 것이라고 생각했습니다.
def find_dict_in_list(dicts, default=None, **kwargs):
"""Find first matching :obj:`dict` in :obj:`list`.
:param list dicts: List of dictionaries.
:param dict default: Optional. Default dictionary to return.
Defaults to `None`.
:param **kwargs: `key=value` pairs to match in :obj:`dict`.
:returns: First matching :obj:`dict` from `dicts`.
:rtype: dict
"""
rval = default
for d in dicts:
is_found = False
# Search for keys in dict.
for k, v in kwargs.items():
if d.get(k, None) == v:
is_found = True
else:
is_found = False
break
if is_found:
rval = d
break
return rval
if __name__ == '__main__':
# Tests
dicts = []
keys = 'spam eggs shrubbery knight'.split()
start = 0
for _ in range(4):
dct = {k: v for k, v in zip(keys, range(start, start+4))}
dicts.append(dct)
start += 4
# Find each dict based on 'spam' key only.
for x in range(len(dicts)):
spam = x*4
assert find_dict_in_list(dicts, spam=spam) == dicts[x]
# Find each dict based on 'spam' and 'shrubbery' keys.
for x in range(len(dicts)):
spam = x*4
assert find_dict_in_list(dicts, spam=spam, shrubbery=spam+2) == dicts[x]
# Search for one correct key, one incorrect key:
for x in range(len(dicts)):
spam = x*4
assert find_dict_in_list(dicts, spam=spam, shrubbery=spam+1) is None
# Search for non-existent dict.
for x in range(len(dicts)):
spam = x+100
assert find_dict_in_list(dicts, spam=spam) is None
팬더를 사용 하여이 문제를 해결할 수 있다고 생각합니다.
import pandas as pd
person_list = [
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]
person_df = pd.DataFrame(person_list)
person_df[person_df["name"] == "Pam"].to_dict('records')
출력합니다 :
[{'age': 7, 'name': 'Pam'}]
장점은 다음과 같습니다.
- 고성능 데이터 처리 기능을 제공하는 팬더는 데이터 세트가 크면 검색에 많은 시간이 걸리지 않습니다.
- 데이터 구조는 사용하기 쉽고 추가 분석을 위해 데이터를 테이블로 취급 할 수 있습니다.
참고 URL : https://stackoverflow.com/questions/8653516/python-list-of-dictionaries-search
'development' 카테고리의 다른 글
HTML 컨테이너에 여러 클래스를 할당하는 방법은 무엇입니까? (0) | 2020.02.27 |
---|---|
VSCode에서 단어 줄 바꿈을 켜고 끄는 방법은 무엇입니까? (0) | 2020.02.27 |
기기가 iOS인지 감지 (0) | 2020.02.27 |
객체 직렬화 란 무엇입니까? (0) | 2020.02.27 |
해시 셋과리스트 성능 (0) | 2020.02.27 |