2行N列のndarrayを一発で(1行目≦2行目)にするnumpy芸

各国における国民1人当たりの魚の年間消費量をランク付けすると、1位はモルディブなんだそうです。土地柄を考えると妥当な結果と言えそうで、6位の日本の2倍以上です。

Numpyにて、行同士の大小関係を強制したい、つまり「2行N列のndarrayがあるんだけど、各列ごとに、常に1行目の値が2行目よりも小さい値を取る必要がある」「そうでない列の値を強制的に上下交換させたい」という状況に遭遇しました。
生ループ回してひとつひとつチェックしては…とやってもいいんですが、それをPython側でやっていてはパフォーマンス上どうかなということで、Numpyのインデクス芸・スライス芸・ブロードキャスト芸あたりを駆使してやってみました。

もちろん、N行2列のndarrayの列間の大小関係を統一するのも同様に可能。

インデクシングなどの詳細の説明はここではしません。
日本語であればこのあたりがわかりやすそう(まだ見てない)。

性能やいかに?

Benchmarker.pyを使って、実行時間を比べてみました。
比較対象は、まぁ何も考えずに書くとそうなりますわな、というような雰囲気のコード(だとおもう)。
#ところで標準の時間計測モジュールtimeitが最高に嫌いなんだけどあれなんであんなふうになってしまったのかな。。。

こちらが実行結果。
proposedが本記事で紹介した黒魔術、conventionalがシンプルな方法。

##                             real    (total    = user    + sys)
col-proposed                 3.6327    3.6200    2.8100    0.8100
row-proposed                 3.6977    3.6900    2.8700    0.8200
col-conventional            49.9127   49.8600   49.7600    0.1000
row-conventional            51.3075   51.2600   51.1600    0.1000

だいたい14倍高速ですね。
メモリ上では行オーダーで格納される都合上、row-*とcol-*の間で速度差が出るのかなと思いましたが、たかだか2行/2列なので、関係ない模様(σが未知なのでなんともいえませんが。。)。

Pythonの高速化の鉄則は『生Python書くな』に尽きますが、それが改めて示された形になりましたとさ。

ただこのようなコード、後から読んだり他人に読ませたりするのはとても困難なので、あまり多用しないようにしましょう。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です