أضف مفاتيح جديدة إلى قاموس مع زيادة القيم الموجودة

StackOverflow https://stackoverflow.com/questions/4036474

  •  26-09-2019
  •  | 
  •  

سؤال

أقوم بمعالجة ملف CSV وحساب القيم الفريدة للعمود 4. حتى الآن قمت بترميز هذه الطرق الثلاث. يستخدم أحدهم "If Key in Dictionary" ، والثاني يقوم بفخات Keyerror والثالث يستخدم "DefaultDictionary". على سبيل المثال (حيث X [3] هي القيمة من الملف و "A" هو قاموس):

أول الطريق:

if x[3] in a:
    a[x[3]] += 1
else:
    a[x[3]] = 1

الطريق الثاني:

try:
    b[x[3]] += 1
except KeyError:
    b[x[3]] = 1

الطريق الثالث:

from collections import defaultdict
c = defaultdict(int)
c[x[3]] += 1

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

شكرًا -

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

المحلول

يستخدم collections.Counter. Counter هو السكر النحوي ل defaultdict(int), ، ولكن الأمر الرائع في الأمر هو أنه يقبل موثوقًا في المُنشئ ، وبالتالي توفير خطوة إضافية (أفترض أن جميع الأمثلة المذكورة أعلاه ملفوفة في حلقة.)

from collections import Counter
count = Counter(x[3] for x in my_csv_reader)

قبل إدخال collections.Counter, collections.defaultdict كان الأكثر إهانة لهذه المهمة ، لذلك بالنسبة للمستخدمين <2.7 ، استخدم defaultdict.

from collections import defaultdict
count = defaultdict(int)
for x in my_csv_reader:
    count[x[3]] += 1

نصائح أخرى

لقد سألت أيها أكثر كفاءة. على افتراض أنك تتحدث عن سرعة التنفيذ: إذا كانت بياناتك صغيرة ، فلا يهم. إذا كانت كبيرة ونموذجية ، فإن حالة "موجودة بالفعل" ستحدث في كثير من الأحيان أكثر من حالة "ليس في القول". هذه الملاحظة تشرح بعض النتائج.

فيما يلي بعض التعليمات البرمجية التي يمكن استخدامها مع timeit الوحدة النمطية لاستكشاف السرعة دون أن تكون قراءة الملفات. لقد اتخذت حرية إضافة طريقة خامسة ، وهي ليست غير مؤمنة وسوف تعمل على أي بيثون من 1.5.2 على الأقل [تم اختباره] فصاعدًا.

from collections import defaultdict, Counter

def tally0(iterable):
    # DOESN'T WORK -- common base case for timing
    d = {}
    for item in iterable:
        d[item] = 1
    return d

def tally1(iterable):
    d = {}
    for item in iterable:
        if item in d:
            d[item] += 1
        else:
            d[item] = 1
    return d

def tally2(iterable):
    d = {}
    for item in iterable:
        try:
            d[item] += 1
        except KeyError:
            d[item] = 1
    return d

def tally3(iterable):
    d = defaultdict(int)
    for item in iterable:
        d[item] += 1

def tally4(iterable):
    d = Counter()
    for item in iterable:
        d[item] += 1

def tally5(iterable):
    d = {}
    dg = d.get
    for item in iterable:
        d[item] = dg(item, 0) + 1
    return d

التشغيل النموذجي (في نافذة Windows XP "موجه الأوامر"):

prompt>\python27\python -mtimeit -s"t=1000*'now is the winter of our discontent made glorious summer by this son of york';import tally_bench as tb" "tb.tally1(t)"
10 loops, best of 3: 29.5 msec per loop

فيما يلي النتائج (MSEC لكل حلقة):

0 base case   13.6
1 if k in d   29.5
2 try/except  26.1
3 defaultdict 23.4
4 Counter     79.4
5 d.get(k, 0) 29.2

تجربة توقيت أخرى:

prompt>\python27\python -mtimeit -s"from collections import defaultdict;d=defaultdict(int)" "d[1]+=1"
1000000 loops, best of 3: 0.309 usec per loop

prompt>\python27\python -mtimeit -s"from collections import Counter;d=Counter()" "d[1]+=1"
1000000 loops, best of 3: 1.02 usec per loop

سرعة Counter من المحتمل أن يتم تنفيذه جزئيًا في كود بيثون بينما defaultdict هو تماما في C (في 2.7 ، على الأقل).

لاحظ أن Counter() ليس فقط "السكر النحوي" ل defaultdict(int) - ينفذ ممتلئ bag الملقب ب multiset كائن - انظر المستندات للحصول على التفاصيل ؛ قد ينقذك من إعادة اختراع العجلة إذا كنت بحاجة إلى بعض المعالجة اللاحقة لما بعد. إذا كان كل ما تريد القيام به هو حساب الأشياء ، فاستخدم defaultdict.

تحديث رداً على سؤال من Steven Rumbalski: "" "أنا فضولي ، ماذا يحدث إذا قمت بنقل ماهية إلى مُنشئ مضاد: D = عداد (ITERBLE)؟ (لدي Python 2.6 ولا يمكنني اختباره.)" "" "

Tally6: فقط لا تفعل d = Count(iterable); return d, ، يأخذ 60.0 مللي ثانية

يمكنك إلقاء نظرة على المصدر (collections.py في مستودع SVN) ... إليك ما Python27\Lib\collections.py يفعل متى iterable ليس مثيل رسم الخرائط:

            self_get = self.get
            for elem in iterable:
                self[elem] = self_get(elem, 0) + 1

هل رأيت هذا الرمز في أي مكان من قبل؟ هناك مجموعة كبيرة من الحمل فقط لاتصال الرمز الذي يمكن تشغيله في بيثون 1.5.2 :-O

from collections import Counter
Counter(a)

بما أنه لا يمكنك الوصول إليه يعداد, ، أفضل رهان لك هو نهجك الثالث. إنه أكثر نظافة وأسهل للقراءة. بالإضافة إلى ذلك ، لا يحتوي على الاختبار الدائم (والتفرع) الذي يمتلكه النهجان الأولين ، مما يجعله أكثر كفاءة.

يستخدم setdefault.

a[x[3]] = a.setdefault(x[3], 0) + 1

setdefault يحصل على قيمة المفتاح المحدد (x[3] في هذه الحالة) ، أو إذا لم تكن موجودة ، فإن القيمة المحددة (0 في هذه الحالة).

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