読者です 読者をやめる 読者になる 読者になる

Cocoaで統計処理 四分位数実装編

Objective-C, 仕様検討, 個人的見解, 思案中

元OS屋から見て普通に考えるとCocoaでは演算高速化のため、NSExpressionによる統計関数を用意している(のだと思う)

なぜなら、開発サイドから見ればそうすることにより

  • 開発者が独自で変な実装しだす確率を下げる (テストしてないところをがんがんほじくり返されるから。何が起きるか解らないため、提供者としてはむっちゃいや。特に演算処理は高速化を容赦なく求められるところなので、開発者は1命令でも削りたくてうずうずしている)
  • 自分たちの都合に合わせて、実装を自由に変えてリリースできる (当時ああは言ったけど、経営者の気が変わったときとか、頭のおかしいエンジニアが新製品や論文の売り文句に騙されて昨日と全く違った実装をこっそり入れてリリースしたくなったとか。)
  • なんかややこしいH/W仕様を使いたくなったらベンダー最適化し放題  (リリース時は時間に追われ、仕様もよくわかんなかったけど、よくよく見たらむっちゃ便利機能が見つかった。よくある。本当に。)

なんて戦略が取れるから。

しかしググった感じ、iOSではNSExpression#expressionForFunction@”median:”は使えない?なんてレポートもちらほら…いや、中央値出せなくてpercentile出すのはめんどくさい。なにより統計コードの天敵は計算ミス。

 

中央値算出くらいなら奇数サイズなら配列の真ん中、偶数サイズなら中央2項の算術平均取るだけなので、簡単でバグも入りにくいから自分で書いてもいいけど、sciPyが使えれば演算速度以外のほぼ全ての問題は解決するので、iOSへのPython組み込みとのProsVSConsに関しては非常に悩ましい&#12290 ;

ちなみに、自分の間抜けさを熟知しているプログラマほど自分で書くことを避ける。なにより、世の中で既に誰かが書いてて、公開されてて、かつ動いてるコードを僕がスクラッチから起こす必要はどこにもない。

 

とも思いつつ、だめもとで

NSExpression#expressionForFunction@”median:” argumentsSmilie: :(NSArray*)…

使ってみた。

—– [XXX:XXX] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[theObject compare:]: unrecognized selector sent to instance 0xXXXXXX’

いやいやいや…自分の間抜けさを実感させられただけでした。このログはtheObjectを直比較してるので、どの属性で比較したいか解らんと怒られてます。

それだけ?使えんじゃん!

 

ちなみにiOS8.4 SDKのヘッダにはこうコメントされている

+ (NSExpression *)expressionForFunctionSmilie: :(NSString *)name argumentsSmilie: :(NSArray *)parameters;
// Expression that invokes one of the predefined functions. Will throw immediately if the selector is bad; will throw at runtime if the parameters are incorrect.
// Predefined functions are:
// name parameter array contents returns
//————————————————————————————————————————————-
// sum: NSExpression instances representing numbers NSNumber
// count: NSExpression instances representing numbers NSNumber
// min: NSExpression instances representing numbers NSNumber
// max: NSExpression instances representing numbers NSNumber
// average: NSExpression instances representing numbers NSNumber
// median: NSExpression instances representing numbers NSNumber
// mode: NSExpression instances representing numbers NSArray (returned array will contain all occurrences of the mode)
// stddev: NSExpression instances representing numbers NSNumber
………. 

 つまり、公式に記述のあるOSX 10.5 laterにくわえ少なくともiOS8.4では最頻値まで使えるんじゃないか?

インチキ英語しか身につけてないので読み飛ばしただけかもしれないけど、だったらそう書け…

 

アルゴリズムとしては

  1. CoreDataから全アイテムをソートした配列にして取り出す
  2. 全配列から中央値出す -> 2Q
  3. lastObject -> 4Q
  4. 全配列前後を再帰させる -> 前1Q、後3Q

以上。ヒンジならこれで必要十分なはず、あとはこれをネタ元に配列が更新されたら四分位数再設定

sciPyのscoreatpercentileみたいにするなら四分位数からバイナリ探索して何percentileか出す。

レコード数が大きいから毎回Zone評価してリンク組み替えよりは算術探索の方が速い(はず…Smilie: ;) 。ステップ数は測ってないけど、NSSetのcontainsObject:は意外と遅いので…内部でループ回してるんじゃなかろうかと思うくらい。

テスト用に組み込んであるPythonの検算コードとも値は一致したので、とりあえず一件落着?

pyObjC組み込んだはいいけど、全然速度が出ないなんていう事態が怖くていまいち腰の重い僕でした。



from WordPress
How to Struggle Against S/W
http://ift.tt/1Ns1dKL