سؤال

أحتاج إلى تخزين قائمة كبيرة من الأعداد الصحيحة في Bigtable (DB). بالنسبة للكفاءة ، أقوم بتخزينهم كأنه من عنصرين متتاليين.

على سبيل المثال:

 original_list = [1005, 1004, 1003, 1004, 1006] 

تخزين القائمة أعلاه (التي تحتوي فعليًا على أكثر من 1000 كيل

start = 1005
diff = [-1, -1, 1, 2]

الأقرب الذي يمكنني إدارته هو ،

ltp = [start]
map(lambda x: ltp.append(ltp[-1] + x), tick)

أنا أبحث عن طريقة فعالة لتحويلها إلى القائمة الأصلية.

هل كانت مفيدة؟

المحلول

ما يلي يعمل بالنسبة لي:

orig = [start]
for x in diff:
    orig.append(orig[-1] + x)

استخدام map سيقوم بإنشاء مجموعة جديدة من نفس الحجم ، مليئة None. أجد أيضًا بسيطًا for حلقة أكثر قابلية للقراءة ، وفي هذه الحالة بأسرع ما يمكنك الحصول عليها.

نصائح أخرى

بالنسبة لهذه الهياكل الكبيرة للبيانات ، ستعمل Numpy بشكل جيد. على سبيل المثال ، إنه أكثر من 200x أسرع (انظر أدناه) ، وأسهل قليلاً للرمز ، بشكل أساسي فقط

add.accumulate(diff)

المقارنة بين معالجة القائمة المباشرة والمعالجة المباشرة:

import numpy as nx
import timeit

N = 10000

diff_nx = nx.zeros(N, dtype=nx.int)
diff_py = list(diff_nx)

start = 1005

def f0():
    orig = [start]
    for x in diff_py: 
        orig.append(orig[-1] + x)

def f1():
    diff_nx[0] = start
    nx.add.accumulate(diff_nx)

t = timeit.Timer("f0()", "from __main__ import f0, f1, diff_nx, diff_py, nx, start")
print t.timeit(number=1000)
t = timeit.Timer("f1()", "from __main__ import f0, f1, diff_nx, diff_py, nx, start")
print t.timeit(number=1000)

يعطي

13.4044158459     # for list looping
0.0474112033844   # for numpy accumulate

حقًا ، على الرغم من ذلك ، يبدو من الأفضل إعادة استخدام خوارزمية ضغط راسخة ، كما يمكن القيام به بسهولة pytables, ، بدلاً من أن تتدحرج بنفسك كما يبدو أنك تفعل هنا.

وأيضًا ، أقترح هنا أن تقرأ في البيانات التي تحتوي على مساحة لمصطلح البدء مسبقًا ، بدلاً من إعادة بناء القائمة بالمصطلح المسبق ، بالطبع ، لذلك لا يتعين عليك القيام بالنسخة.

مثالي للمولدات:

def diff2abs( diffs, start ):
    yield start
    for diff in diffs:
        start += diff
        yield start

start = 1005
diffs = [-1, -1, 1, 2]
original_list = list( diff2abs( diffs, start ))

لدى العديد من المجيبين الآخرين تطبيقات معقولة للخوارزمية التي طلبتها ، لكنني غير واضح بشأن المشكلة التي تحاول حلها حقًا.

ما لم تكن الأرقام التي يتم تخزينها كبيرة جدًا (على سبيل المثال ، تدفق عدد صحيح وتتطلب bignums) ، لن تكتسبك قائمة الاختلاف أي كفاءة - قائمة عددًا صحيحًا من Python Runtime POV ، لذلك مثالك "Diff" من [-1, -1, 1, 2] سوف تستهلك الكثير من الذاكرة مثل القائمة الأصلية [1005, 1004, 1003, 1004, 1006].

class runningtotal:
    def __init__(self, start = 0):
        self.total = start
    def __call__(self, value):
        self.total += value
        return self.total

جرب الان:

>>> map(runningtotal(start), [0,]+diff)
[1005, 1004, 1003, 1004, 1006]

كما اقترح MSHSAYEM ، استخدم شاملات قائمة - فهي أسرع عمومًا من الحلقات أو الخريطة/lambdas (وفقًا لما ذكره Do Mark Lutz's Book Learning Python).

إذا كنت ترغب حقًا في استخدام حل FP-ish ، فإن الوظيفة المناسبة ستكون "Scan" ، مع عدم تنفيذ [أعتقد] في Python ، لذلك يجب عليك تنفيذها بنفسك (وهي ليست مهمة صعبة).

"المسح الضوئي" هو في الأساس انخفاض ، ولكن بدلاً من تقليل القائمة إلى قيمة واحدة ، فإنه يخزن نتيجة كل "تكرار" في قائمة جديدة.

إذا قمت بتنفيذها ، يمكنك أن تفعل شيئًا مثل:

scan(lambda x,y: x+y, [start]++diff)

على الرغم من أنني لا أفهم لماذا يجب أن يكون هذا أكثر كفاءة ، إلا أنني متأكد تمامًا من أن حلقة ستعطي أفضل أداء:

l = [start]
for i in diff:
    l.append(l[-1] + i)

لا أعرف عن تفكيرك لتخزين الأعداد الصحيحة كما فرق - أعطى RCODER إجابة جيدة حول سبب عدم وجود هذا بشكل عام أكثر من تخزين الأعداد الصحيحة بأنفسهم - ولكن إذا لم تكن بحاجة إلى الوصول إلى القائمة بأكملها في وقت واحد ، من المحاكم الذاكرة أكثر كفاءة لك لاستخدام مولد. بما أنك تقول إن هذه "قائمة كبيرة" ، يمكنك توفير الكثير من الذاكرة بهذه الطريقة ، بدلاً من تخصيص القائمة بأكملها في وقت واحد. إليك فهم مولد لاستعادة قائمتك:

start = 1005
def mod_start(x):
    global start
    start += x
    return start
int_generator = (mod_start(i) for i in diffs)

يمكنك بعد ذلك التكرار عبر int_generator كما لو كنت قائمة ، دون وجود قائمة كاملة في الذاكرة في وقت واحد. لاحظ ، ومع ذلك ، أنه لا يمكنك فرز المولد أو تقطيعه ، ولكن يمكنك استخدامه في العديد من المواقف المفيدة.

يمكنك تنظيف المثال بحيث لا يحتاج متغير البدء إلى أن يكون عالميًا. لا يمكن أن يكون محليًا في وظيفة mod_start.

تعديل: ليس عليك استخدام فهم المولد للحصول على مولد. يمكنك أيضًا استخدام وظيفة المولد مع تعبير العائد ، مثل THC4K. هذا يتجنب مشكلة START Variable Scope وربما يكون أنظف قليلاً. يمكنك أيضًا الحصول على قائمة من مولد في أي وقت عن طريق نقلها إلى الوظيفة المدمجة في القائمة ().

لا تعليق على أداء هذا ، ولكن يمكنك استخدام تقليل هنا.

start = 1005
diffs = [-1,-1,1,2]
reduce(lambda undiffed_list, diff: undiffed_list + [undiffed_list[-1] + diff],diffs,[start])

يحصل عليك ما تريد.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top