Từ điển Python có nhiều khóa trỏ đến cùng một danh sách theo cách hiệu quả trong bộ nhớ


9

Tôi có yêu cầu duy nhất này có thể được giải thích bằng mã này. Đây là mã làm việc nhưng không hiệu quả bộ nhớ.

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

Nói tóm lại, tôi muốn mỗi mục của danh sách phụ có thể được lập chỉ mục và nên trả về danh sách phụ.

Phương pháp trên hoạt động nhưng mất nhiều bộ nhớ khi dữ liệu lớn. Có cách nào tốt hơn, bộ nhớ và CPU thân thiện không? Dữ liệu được lưu trữ dưới dạng tệp JSON.

Chỉnh sửa Tôi đã thử các câu trả lời cho kịch bản ca sử dụng lớn nhất có thể (1000 danh sách phụ, 100 mục trong mỗi danh sách phụ, 1 triệu truy vấn) và đây là kết quả (trung bình của 10 lần chạy):

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}có thể hiệu quả hơn một chút và trực tiếp?!
lừa dối

Đúng. cho trường hợp mẫu của tôi. Trong trường hợp thực tế của tôi, danh sách phụ đang có 100 mục và hàng ngàn danh sách phụ như vậy. Người dùng mã đang có bộ nhớ nhỏ (<2gb) vì vậy khi ứng dụng nặng khác đang chạy, họ sẽ phàn nàn rằng tập lệnh của bạn bị chậm.
Rahul

Vấn đề gì bạn đang cố gắng giải quyết chính xác? Có lẽ một cách tiếp cận hỗn hợp sẽ hoạt động, trong đó bạn lập chỉ mục bằng chữ cái đầu tiên, và sau đó lặp qua một vài danh sách ứng cử viên để tìm giá trị chính xác của bạn, giống như thuật toán giải quyết va chạm bảng băm.
lừa dối

Đối với cách hiệu quả sử dụng máy phát điện như suất ().
Saisiva A

Cảm ơn. Tôi sẽ tìm hiểu "giải pháp va chạm bảng băm" nghĩa là gì.
Raul

Câu trả lời:


2

Bạn đang thực sự ở trong một không gian cân bằng giữa thời gian / bộ nhớ cần thiết để tạo từ điển so với thời gian cần thiết để quét toàn bộ dữ liệu cho một phương pháp nhanh chóng.

Nếu bạn muốn một phương thức bộ nhớ thấp, bạn có thể sử dụng một chức năng tìm kiếm từng danh sách con cho giá trị. Sử dụng một trình tạo sẽ nhận được kết quả ban đầu nhanh hơn cho người dùng, nhưng đối với các tập dữ liệu lớn, điều này sẽ chậm giữa các lần trả về.

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)

Như đã đề cập trong các bình luận, xây dựng bảng băm chỉ dựa trên chữ cái đầu tiên hoặc 2 hoặc 3 ký tự đầu tiên có thể là một nơi tốt để bắt đầu. Điều này sẽ cho phép bạn xây dựng một danh sách ứng cử viên của danh sách phụ, sau đó quét chúng để xem giá trị có trong danh sách phụ hay không.

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)

Trong mã quick_hashnày sẽ mất một chút thời gian để xây dựng, bởi vì bạn đang quét toàn bộ cấu trúc dữ liệu của mình. Tuy nhiên, in chân bộ nhớ sẽ nhỏ hơn nhiều. Bạn tham số chính để điều chỉnh hiệu suất là size. Kích thước nhỏ hơn sẽ có dung lượng bộ nhớ nhỏ hơn, nhưng sẽ mất nhiều thời gian hơn khi chạy find_list_by_hashvì nhóm ứng cử viên của bạn sẽ lớn hơn. Bạn có thể thực hiện một số thử nghiệm để xem những gì phải phù hợp sizevới dữ liệu của bạn. Chỉ cần lưu ý rằng tất cả các giá trị của bạn ít nhất là miễn là size.


Và tôi nghĩ rằng tôi biết python và lập trình. Cảm ơn. Có rất nhiều thứ để học.
Rahul

2

Bạn có thể thử một cái gì đó như thế này:

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

Không cần phải thực hiện một cấu trúc ánh xạ.


Cảm ơn bạn. Tôi sẽ phải kiểm tra nếu điều này nhanh hơn.
Rahul

1
Nó sẽ nhanh hơn nhiều khi bắt đầu vì không có sự hiểu biết để tính toán, nhưng cách sử dụng chậm hơn nhiều vì với mỗi phần tử cần tìm, phương pháp này sẽ quét lại toàn bộ dữ liệu.
Edouard Thiel

Chắc chắn, cho tôi biết nếu điều này làm việc cho bạn.
Bhushan Pant

@EdouardThiel: Tôi cũng cảm thấy như vậy. Sử dụng thực tế của tôi có nhiều trường hợp sử dụng hơn các trường hợp bắt đầu.
Raul

@EdouardThiel đúng. Nhưng tôi không chắc chắn về trường hợp sử dụng chính xác.
Bhushan Pant

2

Hãy thử điều này, sử dụng gấu trúc

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

Đây là giải pháp đơn giản, ngay cả khi dữ liệu của bạn phát triển lớn, điều này sẽ xử lý hiệu quả


kiểm tra kích thước của bạn df: nó lớn hơn đáng kể so với danh sách data(> x12) và dict temp_dict(~ x2) cho dữ liệu ví dụ đã cho - không chính xác về hiệu quả bộ nhớ mà tôi nói
MrFuppes

@MrFuppes Tôi không nghĩ lập luận này là hợp lệ, vì gấu trúc không sao chép vật lý các chuỗi trong trường hợp này
mcsoini

@mcsoini, tôi thừa nhận nhận xét của tôi hơi hời hợt - một phân tích chi tiết hơn sẽ là cần thiết để xác định xem pandasxử lý vấn đề này hiệu quả hơn chức năng python tích hợp.
MrFuppes

@MrFuppes: Tôi đồng ý. Tại sao sử dụng pandasnếu nó có thể được thực hiện bằng cách sử dụng stdlib. Chỉ vì nó trông lạ mắt?
Raul

1
Nhưng bạn không cung cấp cách tôi sẽ truy vấn dataframe. Bạn có thể chỉ cho tôi cách giải quyết của bạn sẽ giải quyết vấn đề của tôi. Tôi đã thử giải pháp của @ mcsoini cho gấu trúc nhưng phải mất mãi mãi cho 1 triệu truy vấn. Tôi không biết tại sao. Xin vui lòng xem câu hỏi cập nhật của tôi cho kết quả của các phương pháp khác nhau.
Rahul

0

Tôi không hoàn toàn chắc chắn làm thế nào điều này sẽ hoạt động với lượng dữ liệu lớn hơn, nhưng bạn có thể thử điều gì đó dọc theo dòng:

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

Chỉnh sửa: Dường như không có lợi về mặt thời gian, dựa trên thử nghiệm nhanh với một số dữ liệu quy mô lớn hơn giả mạo.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.