سؤال

هناك العديد من الطرق لكتابة برنامج بيثون يحسب الرسم البياني.

بواسطة الرسم البياني ، أعني وظيفة تحسب حدوث الكائنات في 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 تمامًا.

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