8

CSV ファイルを処理し、列 4 の一意の値を数えています。これまでのところ、この 3 つの方法をコーディングしました。1 つは「if key in dictionary」を使用し、2 番目は KeyError をトラップし、3 番目は「DefaultDictionary」を使用します。例 (x[3] はファイルの値で、"a" は辞書):

最初の方法:

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

2 番目の方法:

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

3 番目の方法:

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

私の質問は次のとおりです。どちらの方法がより効率的ですか...クリーン...より良い...など。または、より良い方法があります。どちらの方法も機能し、同じ答えが得られますが、学習ケースとしてハイブ マインドを利用することにしました。

ありがとう -

4

5 に答える 5

6

を使用しcollections.Counterます。Counterの構文糖衣ですdefaultdict(int)が、コンストラクターでイテラブルを受け入れるため、余分なステップを節約できるという点が優れています (上記の例はすべて for ループでラップされていると想定しています)。

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

が導入される前はcollections.Countercollections.defaultdictがこのタスクで最も一般的だったため、2.7 歳未満のユーザーの場合は を使用してdefaultdictください。

from collections import defaultdict
count = defaultdict(int)
for x in my_csv_reader:
    count[x[3]] += 1
于 2010-10-27T18:42:50.763 に答える
6

あなたはどちらがより効率的かを尋ねました。実行速度について話していると仮定すると、データが小さい場合は問題ありません。大きくて典型的な場合、「既に存在する」ケースは「辞書にない」ケースよりもはるかに頻繁に発生します。この観察は、いくつかの結果を説明しています。

timeit以下は、ファイル読み取りのオーバーヘッドなしで速度を調査するためにモジュールで使用できるコードです。私は自由に 5 番目の方法を追加しました。これは競争力がなく、少なくとも 1.5​​.2 [テスト済み] 以降の任意の Python で実行されます。

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

結果は次のとおりです(ループあたりのミリ秒):

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はおそらく、部分的に Python コードで実装されているのに対し、defaultdict完全に C で実装されているためです (少なくとも 2.7 では)。

Counter()単なる「構文糖衣」ではないことに注意してください-それは完全な別名オブジェクトdefaultdict(int)を実装しています-詳細についてはドキュメントを参照してください。派手な後処理が必要な場合は、ホイールを再発明する必要がなくなります。数を数えることだけが目的の場合は、 を使用します。bagmultisetdefaultdict

@Steven Rumbalski からの質問に応じて更新: """ iterable を Counter コンストラクターに移動するとどうなるか興味があります: d = Counter(iterable)? (私は python 2.6 を持っていて、テストできません。) " ""

tally6: 実行するだけでd = Count(iterable); return d、60.0 ミリ秒かかります

ソース (SVN リポジトリの collections.py ) を見ることができます ...が Mapping インスタンスでないPython27\Lib\collections.py場合の私の動作は次のとおりです。iterable

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

そのコードをどこかで見たことがありますか? Python 1.5.2 で実行可能なコードを呼び出すためだけに、多くのキャリーオンがあります。:-O

于 2010-10-27T20:37:34.677 に答える
1
from collections import Counter
Counter(a)
于 2010-10-27T18:34:44.743 に答える
0

Counterにアクセスできないため、最善の策は 3 番目のアプローチです。はるかにクリーンで読みやすくなっています。さらに、最初の 2 つのアプローチにある永続的なテスト (および分岐) がないため、より効率的です。

于 2010-10-27T18:39:14.297 に答える
0

を使用しsetdefaultます。

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

setdefault指定されたキーの値 (x[3]この場合) を取得するか、存在しない場合は指定された値 (0この場合) を取得します。

于 2010-10-27T23:00:13.600 に答える