Kamus python dengan beberapa tombol yang menunjuk ke daftar yang sama dalam cara yang efisien memori

9

Saya memiliki persyaratan unik yang dapat dijelaskan oleh kode ini. Ini adalah kode yang berfungsi tetapi tidak efisien memori.

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

Singkatnya, saya ingin setiap item dari sub-daftar dapat diindeks dan harus mengembalikan sublist.

Metode di atas berfungsi tetapi dibutuhkan banyak memori saat data berukuran besar. Apakah ada cara yang lebih baik, ramah memori dan CPU? Data disimpan sebagai file JSON.

Sunting Saya mencoba jawaban untuk skenario penggunaan terbesar (1000 sublist, 100 item di setiap sublist, 1 juta kueri) dan berikut ini hasilnya (rata-rata 10 run):

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
Rahul
sumber
{item: sublist for sublist in data for item in sublist}mungkin sedikit lebih efisien dan langsung ... ?!
tipuan
Iya. untuk kasus sampel saya. Dalam skenario kasus saya yang sebenarnya, sublist memiliki 100-an item dan ribuan sublists semacam itu. pengguna kode memiliki memori kecil (<2gb) sehingga ketika aplikasi berat lainnya sedang berjalan, mereka mengeluh bahwa skrip Anda lambat.
Rahul
Masalah apa yang ingin Anda pecahkan dengan tepat? Mungkin pendekatan hybrid akan berhasil, di mana Anda mengindeks dengan huruf pertama, dan kemudian beralih melalui beberapa daftar kandidat untuk menemukan nilai tepat Anda, semacam algoritma resolusi tabrakan tabel hash.
tipuan
Untuk cara yang efisien gunakan generator seperti hasil ().
Saisiva A
Terima kasih. Saya akan belajar apa artinya "resolusi tabrakan tabel hash".
Rahul

Jawaban:

2

Anda benar-benar berada dalam ruang trade-off antara waktu / memori yang diperlukan untuk menghasilkan kamus versus waktu yang diperlukan untuk memindai seluruh data untuk metode on-the-fly.

Jika Anda menginginkan metode memori rendah, Anda dapat menggunakan fungsi yang mencari nilai masing-masing sublist. Menggunakan generator akan mendapatkan hasil awal lebih cepat bagi pengguna, tetapi untuk set data besar, ini akan lambat di antara pengembalian.

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)

Seperti disebutkan dalam komentar, membangun tabel hash hanya berdasarkan huruf pertama atau 2 atau 3 karakter pertama mungkin merupakan tempat yang baik untuk memulai. Ini akan memungkinkan Anda untuk membuat daftar kandidat sublists, kemudian memindai mereka untuk melihat apakah nilainya ada dalam sublist.

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)

Dalam kode ini quick_hashakan membutuhkan waktu untuk membangun, karena Anda memindai seluruh struktur data Anda. Namun, cetakan kaki memori akan jauh lebih kecil. Parameter utama Anda untuk kinerja tuning adalah size. Ukuran yang lebih kecil akan memiliki jejak memori yang lebih kecil, tetapi akan memakan waktu lebih lama ketika berjalan find_list_by_hashkarena kumpulan calon Anda akan lebih besar. Anda dapat melakukan beberapa pengujian untuk melihat apa yang sizeseharusnya untuk data Anda. Ingatlah bahwa semua nilai Anda setidaknya selama size.

James
sumber
Dan saya pikir saya tahu python dan pemrograman. Terima kasih. Banyak yang harus dipelajari.
Rahul
2

Anda dapat mencoba sesuatu seperti ini:

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

Tidak perlu membuat struktur pemetaan.

Celana Bhushan
sumber
Terima kasih kawan Saya harus memeriksa apakah ini lebih cepat.
Rahul
1
itu akan jauh lebih cepat di awal karena tidak ada pemahaman untuk menghitung, tetapi jauh lebih lambat digunakan karena untuk setiap elemen untuk menemukan, metode ini akan memindai ulang seluruh data.
Edouard Thiel
Tentu, beri tahu saya jika ini cocok untuk Anda.
Bhushan Pant
@ EdouardThiel: Saya juga merasakan hal yang sama. Penggunaan aktual saya memiliki lebih banyak kasus penggunaan daripada kasus awal.
Rahul
@ EdouardThiel benar. Tapi saya tidak yakin dengan case use yang tepat.
Bhushan Pant
2

coba ini, menggunakan panda

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

ini terlihat solusi sederhana, bahkan jika data Anda tumbuh besar, ini akan menangani itu secara efisien

vgp2018
sumber
periksa ukuran Anda df: itu jauh lebih besar dari daftar data(> x12) dan dict temp_dict(~ x2) untuk contoh data yang diberikan - tidak persis hemat memori saya akan mengatakan
MrFuppes
@ McFuppes Saya tidak berpikir argumen ini valid, karena panda tidak secara fisik menyalin string dalam kasus ini
mcsoini
@ mcsoini, saya akui komentar saya agak dangkal - analisis yang lebih terperinci akan diperlukan untuk menentukan apakah pandasmenangani masalah ini lebih efisien daripada fungsi python bawaan.
MrFuppes
@ McFuppes: Saya setuju. Mengapa menggunakan pandasjika itu bisa dilakukan menggunakan stdlib. Hanya karena terlihat mewah?
Rahul
1
Tapi Anda tidak memberikan bagaimana saya akan meminta dataframe. Bisakah Anda menunjukkan kepada saya bagaimana solusi Anda akan menyelesaikan masalah saya. Saya mencoba solusi @ mcsoini untuk panda tetapi butuh selamanya untuk 1 juta pertanyaan. Saya tidak tahu kenapa. Silakan lihat pertanyaan saya yang diperbarui untuk hasil berbagai metode.
Rahul
0

Saya tidak sepenuhnya yakin bagaimana ini akan berperilaku untuk data jumlah yang lebih besar, tetapi Anda dapat mencoba sesuatu di sepanjang baris:

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

Sunting: Tampaknya tidak menguntungkan dalam hal waktu, berdasarkan tes cepat dengan beberapa data skala besar palsu.

mcsoini
sumber