أضف مفاتيح جديدة إلى قاموس مع زيادة القيم الموجودة
-
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
في هذه الحالة).