الرسم البياني بيثون واحد
سؤال
هناك العديد من الطرق لكتابة برنامج بيثون يحسب الرسم البياني.
بواسطة الرسم البياني ، أعني وظيفة تحسب حدوث الكائنات في iterable
ويخرج التهم في القاموس. فمثلا:
>>> L = 'abracadabra'
>>> histogram(L)
{'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}
طريقة واحدة لكتابة هذه الوظيفة هي:
def histogram(L):
d = {}
for x in L:
if x in d:
d[x] += 1
else:
d[x] = 1
return d
هل هناك طرق أكثر إيجازًا لكتابة هذه الوظيفة؟
إذا كان لدينا شمولية قاموس في بيثون ، فيمكننا الكتابة:
>>> { x: L.count(x) for x in set(L) }
ولكن بما أن Python 2.6 لا تملكهم ، علينا أن نكتب:
>>> dict([(x, L.count(x)) for x in set(L)])
على الرغم من أن هذا النهج قد يكون قابلاً للقراءة ، إلا أنه غير فعال: يتم السير إلى عدة مرات. علاوة على ذلك ، لن ينجح هذا مع المولدات الواحدة ؛ يجب أن تعمل الوظيفة بشكل جيد على قدم المساواة لمولدات التكرار مثل:
def gen(L):
for x in L:
yield x
قد نحاول استخدام reduce
وظيفة (RIP):
>>> reduce(lambda d,x: dict(d, x=d.get(x,0)+1), L, {}) # wrong!
عفوًا ، هذا لا يعمل: الاسم الرئيسي هو 'x'
, ، ليس x
. :(
انتهيت مع:
>>> reduce(lambda d,x: dict(d.items() + [(x, d.get(x, 0)+1)]), L, {})
(في بيثون 3 ، يجب أن نكتب list(d.items())
بدلاً من d.items()
, ، لكنها افتراضية ، لأنه لا يوجد reduce
هناك.)
من فضلك ضربني مع خط واحد أفضل ، أكثر قابلية للقراءة! ؛)
المحلول
Python 3.x لديه reduce
, ، عليك فقط القيام from functools import reduce
. كما أن لديها "شمولية DICT" ، والتي لديها مباشرة بناء الجملة في مثالك.
Python 2.7 و 3.x لديهم أيضًا يعداد الفصل الذي يفعل بالضبط ما تريد:
from collections import Counter
cnt = Counter("abracadabra")
في Python 2.6 أو قبل ذلك ، كنت شخصياً أستخدم ملف DefaultDict وافعل ذلك في سطرين:
d = defaultdict(int)
for x in xs: d[x] += 1
هذا نظيف وفعال ، مدهش ، وأسهل بكثير لمعظم الناس لفهمه من أي شيء ينطوي عليه reduce
.
نصائح أخرى
من الغش نوعًا ما في استيراد وحدات Oneliners ، لذا فهناك oneliner الذي هو O (n) ويعود على الأقل مثل Python2.4
>>> f=lambda s,d={}:([d.__setitem__(i,d.get(i,0)+1) for i in s],d)[-1]
>>> f("ABRACADABRA")
{'A': 5, 'R': 2, 'B': 2, 'C': 1, 'D': 1}
وإذا كنت تعتقد __
الأساليب اختراق ، يمكنك دائمًا القيام بذلك
>>> f=lambda s,d=lambda:0:vars(([setattr(d,i,getattr(d,i,0)+1) for i in s],d)[-1])
>>> f("ABRACADABRA")
{'A': 5, 'R': 2, 'B': 2, 'C': 1, 'D': 1}
:)
$d{$_} += 1 for split //, 'abracadabra';
import pandas as pd
pd.Series(list(L)).value_counts()
بالنسبة لـ Python 2.7 ، يمكنك استخدام فهم القائمة الصغيرة:
v = list('abracadabra')
print {x: v.count(x) for x in set(v)}
واحد يعمل مرة أخرى إلى 2.3 (أقصر قليلاً من Timmerman ، أعتقد أكثر قابلية للقراءة):
L = 'abracadabra'
hist = {}
for x in L: hist[x] = hist.pop(x,0) + 1
print hist
{'a': 5, 'r': 2, 'b': 2, 'c': 1, 'd': 1}
لفترة من الوقت هناك ، أي شيء يستخدم itertools
كان بحكم التعريف بيثوني. ومع ذلك ، هذا قليلاً على الجانب غير الشرير:
>>> from itertools import groupby
>>> grouplen = lambda grp : sum(1 for i in grp)
>>> hist = dict((a[0], grouplen(a[1])) for a in groupby(sorted("ABRACADABRA")))
>>> print hist
{'A': 5, 'R': 2, 'C': 1, 'B': 2, 'D': 1}
أقوم حاليًا بتشغيل Python 2.5.4.
الخاص بك واحد باستخدام reduce
كان على ما يرام تقريبًا ، فأنت بحاجة فقط إلى تعديله قليلاً:
>>> reduce(lambda d, x: dict(d, **{x: d.get(x, 0) + 1}), L, {})
{'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2}
بالطبع ، لن يتغلب هذا على حلول في مكانه (ولا في السرعة ، ولا في الإثنية) ، ولكن في المقابل ، حصلت على مقتطف وظيفي لطيف. راجع للشغل ، سيكون هذا أجمل إلى حد ما إذا كان لدى بيثون طريقة dict.merge()
.
كنت بحاجة إلى تنفيذ رسم بياني للعمل في بيثون 2.2 حتى 2.7 ، وتوصلت إلى هذا:
>>> L = 'abracadabra'
>>> hist = {}
>>> for x in L: hist[x] = hist.setdefault(x,0)+1
>>> print hist
{'a': 5, 'r': 2, 'b': 2, 'c': 1, 'd': 1}
لقد استلهمت من وظيفة Eli Courtwright من الافتراضي. تم تقديم هذه في Python 2.5 لذلك لا يمكن استخدامها. ولكن يمكن محاكاةها باستخدام dict.setDefault (المفتاح ، الافتراضي).
هذا هو في الأساس نفس الشيء الذي يفعله Gnibbler ، لكن كان علي أن أكتب هذا أولاً قبل أن أتمكن من فهم وظيفة Lambda تمامًا.