まず、Python 2.x を使用している場合は、xrange()
代わりにrange()
. Python 3.x には はありませんxrange()
が、組み込みrange()
は基本的に と同じxrange()
です。
次に、スピードを追求する場合は、記述するコードを減らし、Python の組み込み機能 (スピードのために C で記述されています) にもっと依存する必要があります。
sum()
次のようにジェネレーター式を内部で使用することで、処理を高速化できます。
from itertools import izip
def find_best(weights,fields):
winner = -1
best = -float('inf')
for c in xrange(num_category):
score = sum(float(t[0]) * t[1] for t in izip(fields, weights[c]))
if score > best:
best = score
winner = c
return winner
max()
同じ考え方をもう一度適用して、最良の結果を見つけるために使用してみましょう。このコードは見にくいと思いますが、ベンチマークを行って十分に高速である場合は、価値があるかもしれません。
from itertools import izip
def find_best(weights, fields):
tup = max(
((i, sum(float(t[0]) * t[1] for t in izip(fields, wlist))) for i, wlist in enumerate(weights)),
key=lambda t: t[1]
)
return tup[0]
うーん!しかし、私が間違いを犯していなければ、これは同じことを行い、Python の C 機構に大きく依存しているはずです。それを測定して、それがより速いかどうかを確認します。
だから、私たちは を呼んでmax()
います。ジェネレーター式を指定すると、ジェネレーター式から返される最大値が検出されます。しかし、最良の値のインデックスが必要なため、ジェネレータ式はインデックスと重み値のタプルを返します。したがって、ジェネレーター式を最初の引数として渡す必要があり、2 番目の引数は、タプルからの重み値を調べてインデックスを無視するキー関数でなければなりません。ジェネレーター式は唯一の引数ではないためmax()
、括弧で囲む必要があります。i
次に、上で使用したのと同じ方法で計算された、計算された重みのタプルを作成しsum()
ます。最後に、 からタプルをmax()
取得したら、インデックスを作成してインデックス値を取得し、それを返します。
関数を分解すれば、これをもっと醜くすることができます。これにより、関数呼び出しのオーバーヘッドが追加されますが、測定すると、それほど遅くはないと思います。また、考えてみると、すでに;fields
に事前に強制されている値のリストを作成することは理にかなっています。float
その後、それを複数回使用できます。また、 を使用して 2 つのリストを並行して反復処理する代わりにizip()
、反復子を作成して明示的に値を要求しましょう。Python 2.x では、.next()
メソッド関数を使用して値を要求します。Python 3.x ではnext()
組み込み関数を使用します。
def fweight(field_float_list, wlist):
f = iter(field_float_list)
return sum(f.next() * w for w in wlist)
def find_best(weights, fields):
flst = [float(x) for x in fields]
tup = max(
((i, fweight(flst, wlist)) for i, wlist in enumerate(weights)),
key=lambda t: t[1]
)
return tup[0]
30,000 個のフィールド値がある場合、値を事前に計算float()
すると、速度が大幅に向上する可能性があります。
編集:1つのトリックを逃しました。lambda
関数の代わりにoperator.itemgetter()
、受け入れられた回答のコードの一部のように使用する必要がありました。また、受け入れられた回答のタイミングはのものであり、関数呼び出しのオーバーヘッドが大きかったようです。しかし、Numpy の回答は非常に高速だったため、この回答で遊ぶ価値はもうありません。
2番目の部分については、あまり高速化できないと思います。私が試してみます:
def update_weights(weights,fields,toincrease,todecrease):
w_inc = weights[toincrease]
w_dec = weights[todecrease]
for i, f in enumerated(fields):
f = float(f) # see note below
w_inc[i] += f
w_dec[i] -= f
したがって、 を反復処理する代わりにxrange()
、ここではフィールド値を直接反復処理します。フロートを強制する行があります。
重みの値が既に float の場合、ここで強制的に float にする必要はなく、その行を削除するだけで時間を節約できることに注意してください。
あなたのコードは、重みリストを 4 回インデックス付けしていました。インクリメントを行うために 2 回、デクリメントを行うために 2 回です。このコードは、( toincrease
orを使用してtodecrease
) 最初のインデックスを 1 回だけ実行します。i
が機能するためには、引き続きインデックスを作成する必要があり+=
ます。(私の最初のバージョンでは、イテレータを使用してこれを回避しようとしましたが、機能しませんでした。投稿する前にテストする必要がありましたが、現在は修正されています。)
試す最後のバージョン: 値を増減する代わりに、リスト内包表記を使用して、必要な値で新しいリストを作成します。
def update_weights(weights, field_float_list, toincrease, todecrease):
f = iter(field_float_list)
weights[toincrease] = [x + f.next() for x in weights[toincrease]]
f = iter(field_float_list)
weights[todecrease] = [x - f.next() for x in weights[todecrease]]
これは、上記のように、すべてのフィールド値を強制的に float に設定していることを前提としています。
この方法でリスト全体を置き換える方が速いですか、遅いですか? もっと早く推測するつもりですが、よくわかりません。測って見て!
ああ、追加する必要があります: 上記の私のバージョンは をupdate_weights()
返さないことに注意してくださいweights
。これは、Python では、どの関数がクエリを実行し、どの関数が何かを変更するかについて誰も混乱しないようにするためだけに、データ構造を変更する関数から値を返さないことが良い習慣であると考えられているためです。
http://en.wikipedia.org/wiki/Command-query_separation
メジャーメジャーメジャー。私の提案がどれほど速いか、またはそうでないかを見てください。