메모리 효율적인 방식으로 여러 키가 동일한 목록을 가리키는 Python 사전


9

이 코드로 설명 할 수있는이 독특한 요구 사항이 있습니다. 이것은 작동하는 코드이지만 메모리 효율적이지 않습니다.

data = [[
        "A 5408599",
        "B 8126880",
        "A 2003529",
    ],
    [
        "C 9925336",
        "C 3705674",
        "A 823678571",
        "C 3205170186",
    ],
    [
        "C 9772980",
        "B 8960327",
        "C 4185139021",
        "D 1226285245",
        "C 2523866271",
        "D 2940954504",
        "D 5083193",
    ]]

temp_dict = {
    item: index for index, sublist in enumerate(data)
        for item in sublist
}

print(data[temp_dict["A 2003529"]])

out: ['A 5408599', 'B 8126880', 'A 2003529']

요컨대, 하위 목록의 각 항목을 색인 가능하게 만들고 하위 목록을 반환해야합니다.

위의 방법은 작동하지만 데이터가 클 때 많은 메모리가 필요합니다. 더 나은 메모리와 CPU 친화적 인 방법이 있습니까? 데이터는 JSON 파일로 저장됩니다.

편집 가장 큰 사용 사례 시나리오 (1000 하위 목록, 각 하위 목록의 100 개 항목, 백만 개의 쿼리)에 대한 답변을 시도했으며 결과는 10 회 평균입니다.

Method,    Time (seconds),    Extra Memory used
my,        0.637              40 Mb
deceze,    0.63               40 Mb
James,     0.78               200 kb
Pant,      > 300              0 kb
mcsoini,   forever            0 kb

{item: sublist for sublist in data for item in sublist}좀 더 효율적이고 직접적 일 수 있습니다… ?!
사망

예. 내 샘플 케이스. 필자의 실제 시나리오에서 하위 목록에는 100 개의 항목과 수천 개의 이러한 하위 목록이 있습니다. 코드의 사용자는 작은 메모리 (<2gb)를 가지고 있으므로 다른 무거운 앱이 실행될 때 스크립트 속도가 느리다는 불평을합니다.
Rahul

정확히 해결하려는 문제는 무엇입니까? 아마도 첫 번째 문자로 색인을 생성 한 다음 해시 테이블 충돌 해결 알고리즘과 같은 정확한 값을 찾기 위해 몇 개의 후보 목록을 반복하는 하이브리드 방식이 작동 할 것입니다.
사망

효율적인 방법으로 yield ()와 같은 생성기를 사용하십시오.
Saisiva A

감사. "해시 테이블 충돌 해결"의 의미를 배우겠습니다.
Rahul

답변:


2

실제로 사전을 생성하는 데 걸리는 시간 / 메모리와 온전한 방법으로 전체 데이터를 스캔하는 데 걸리는 시간 사이의 절충점에 있습니다.

메모리 부족 방법을 원하면 각 하위 목록에서 값을 검색하는 기능을 사용할 수 있습니다. 제너레이터를 사용하면 사용자에게 초기 결과가 더 빨라지지만 큰 데이터 세트의 경우 수익률이 느려집니다.

data = [[
        "A 5408599",
        "B 8126880",
        "A 2003529",
    ],
    [
        "C 9925336",
        "C 3705674",
        "A 823678571",
        "C 3205170186",
    ],
    [
        "C 9772980",
        "B 8960327",
        "C 4185139021",
        "D 1226285245",
        "C 2523866271",
        "D 2940954504",
        "D 5083193",
    ]]


def find_list_by_value(v, data):
    for sublist in data:
        if v in sublist:
            yield sublist

for s in find_list_by_value("C 9772980", data):
    print(s)

주석에서 언급했듯이 첫 번째 문자 또는 처음 2 또는 3자를 기반으로 해시 테이블을 작성하는 것이 좋습니다. 이를 통해 후보 목록의 후보 목록을 작성한 다음 값을 검사하여 값이 하위 목록에 있는지 확인할 수 있습니다.

from collections import defaultdict

def get_key(v, size=3):
    return v[:size]

def get_keys(sublist, size=3):
    return set(get_key(v, size) for v in sublist)

def find_list_by_hash(v, data, hash_table, size=3):
    key = get_key(v, size)
    candidate_indices = hash_table.get(key, set())
    for ix in candidates:
        if v in data[ix]:
            yield data[ix]

# generate the small hash table
quick_hash = defaultdict(set)
for i, sublist in enumerate(data):
    for k in get_keys(sublist, 3):
        quick_hash[k].add(i)

# lookup a value by the small hash
for s in find_list_by_hash("C 9772980", data, quick_hash, 3):
    print(s)

이 코드에서는 quick_hash전체 데이터 구조를 스캔하기 때문에 빌드하는 데 시간이 걸립니다. 그러나 메모리 풋 프린트는 훨씬 작습니다. 성능 조정을위한 주요 매개 변수는 size입니다. 크기가 작을수록 메모리 풋 프린트는 작아 지지만 실행시 find_list_by_hash후보 풀이 커지므로 시간이 더 오래 걸립니다 . 몇 가지 테스트를 수행 size하여 데이터에 적합한 것이 무엇인지 확인할 수 있습니다 . 그냥 모든 값에 있음을 염두에 적어도 만큼으로 size.


그리고 나는 파이썬과 프로그래밍을 알고 있다고 생각했습니다. 감사. 배울 것이 많다.
Rahul

2

다음과 같이 시도해보십시오.

list(filter(lambda x: any(["C 9772980" in x]),data))

매핑 구조를 만들 필요가 없습니다.


고마워요. 이것이 더 빠른지 확인해야합니다.
Rahul

1
계산에 대한 이해가 없기 때문에 시작시 훨씬 빠르지 만 각 요소를 찾을 때 마다이 방법은 전체 데이터를 다시 스캔하기 때문에 사용 속도가 훨씬 느립니다.
Edouard Thiel

물론, 이것이 당신에게 효과가 있는지 알려주십시오.
부샨 바지

@EdouardThiel : 나도 같은 느낌입니다. 내 실제 사용은 시작 사례보다 더 많은 사용 사례가 있습니다.
Rahul

@EdouardThiel 사실입니다. 그러나 정확한 사용 사례는 확실하지 않습니다.
부샨 바지

2

팬더를 사용하여 이것을보십시오

import pandas as pd
df=pd.DataFrame(data)
rows = df.shape[0]
for row in range(rows):
    print[[row]]    #Do something with your data

데이터가 커지더라도 간단한 해결책으로 보입니다.


크기를 확인하십시오 df: 주어진 예제 데이터 의 목록 data(> x12) 및 dict temp_dict(~ x2) 보다 상당히 큽니다. 정확히 말하면 메모리 효율적이지 않습니다.
MrFuppes

팬더가 물리적으로이 경우 문자열을 복사하지 않기 때문에, 나는이 인수가 유효 생각하지 않는다 @MrFuppes
mcsoini

@ mcsoini, 나는 내 의견이 약간 피상적임을 인정한다- pandas내장 된 파이썬 기능 보다이 문제를보다 효율적으로 처리 하는지를 결정하기 위해서는 더 자세한 분석이 필요할 것이다 .
MrFuppes

@ MrFuppes : 동의합니다. 를 사용 pandas하여 수행 할 수있는 경우 사용 하는 이유 stdlib. 멋져 보이기 때문에?
Rahul

1
그러나 데이터 프레임을 쿼리하는 방법을 제공하지 않았습니다. 솔루션이 어떻게 내 문제를 해결하는지 보여줄 수 있습니까? 팬더를 위해 @mcsoini의 솔루션을 시도했지만 백만 건의 쿼리에 영원히 걸리고 있습니다. 이유를 모르겠습니다. 다양한 방법의 결과에 대해서는 업데이트 된 질문을 참조하십시오.
Rahul

0

나는 이것이 더 많은 양의 데이터에 대해 어떻게 작동하는지 확실하지 않지만 다음 줄을 따라 무언가를 시도 할 수 있습니다.

import pandas as pd
df = pd.DataFrame(data).T
df.loc[:, (df == 'A 2003529').any(axis=0)]
Out[39]: 
           0
0  A 5408599
1  B 8126880
2  A 2003529
3       None
4       None
5       None
6       None

편집 : 가짜 큰 규모의 데이터로 빠른 테스트를 기반으로 시간면에서 유리하지 않은 것 같습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.