「I Love R」への実用化。 予定外

habrozhiteliで、私は比較的最近beatりました。



私はメインストリームITに帰することはできませんが、「ボタン」を押すこともできます。ミンスク22の最初の学校の選択科目から数えた場合、すでに読んでいます... 40歳...



そうそう、私は脱線します...

私は震えながら(興奮して) Rに関する私の最初の投稿を指で書いたが、受け取ったフィードバックに非常にうれしかった。 Rの約束を果たし、Rの実際のアプリケーションのいくつかを示す欲求をさらに強化したもの。特に、バイオインフォマティクスの分野では、Rが最もポピュラーであり、私たちはそれと協力しています。



同時に、Rが「プロトタイピング」用の言語として使用されるのはこれが初めてではなく、C ++、または最悪の場合Pythonで「本物」に書き換えることを目的としています。



具体的には、この投稿は、 確率を計算するインデックス方法に関する記事によって引き起こされました。 私は今、確率論に関する記事のプレゼンテーションにtoを投げかけています(これも簡単ではありません。

猫の下で-この記事のRコードを(私の主観的な意見では) "動作する"状態にする例。







要するに:

親愛なるkokorinsは、離散ランダム変数ジェネレーターを構築するインデックス法についての私の意見で役に立つ記事を書きました。

この記事では、アルゴリズムの「単純な」バージョンを説明するために次のコードを使用しました。

#Naive approach len<-10 ps<-seq(len,2, by=-1) ps<- 1/ps^2 ps<-ps/sum(ps) ss<-cumsum(ps) gen_naiv <- function() { alpha<-runif(1) return (min(which(alpha<ss))) } #sample val<-c() len<-10000 for(i in 1:len) { val<-append(val, gen_naiv()) } vals<-factor(val) plot(vals)
      
      





私はアルゴリズムに興味がありましたが、引用された投稿から「釣り出した」キーワードはあまり具体的ではなく、著者の姓-Chen-は中国でほとんど最も一般的であり、したがってインターネット上で最も多数の...



著者のRでのプログラミングの「スタイル」は完璧とはほど遠い(穏やかに言えば)が、この記事はRに関するものではなかった。私は最初にそれを無視しようとした。 しかし、著者のRテキストのアルゴリズムを理解しようとして、私は忍耐を失い、このテキストを参考例として使用することにしました。



したがって、すでに述べたように、コードは機能しています。 しかし、非常に悪い。 そして、何を、なぜ、どのように改善するかを示してみます。



私の最初の編集は、関数の形でランダムシーケンスを生成する最終段階を調整することです。これは、乱数のベクトルを返すことに加えて、実行に費やされた時間も出力します。

 len<-10 ps<-seq(len,2, by=-1) ps<- 1/ps^2 ps<-ps/sum(ps) ss<-cumsum(ps) gen_naiv <- function() { alpha<-runif(1) return (min(which(alpha<ss))) } #sample get_naive<-function(len=10000){ val<-c() st<-system.time( for(i in 1:len) { val<-append(val, gen_naiv()) }) print(st) return(val) }
      
      





著者のコードの最後の2行は、グラフィックスのみを対象としているため、省略されています。



R環境で指定されたコードを実行すると、さまざまな長さのシーケンスを取得できると同時に、実行にかかった時間を問い合わせることができます。

 > get_naive(10) user system elapsed 0.006 0.000 0.007 [1] 8 8 8 8 9 5 8 9 9 7 > get_naive(100) user system elapsed 0.008 0.000 0.008 [1] 8 1 7 8 8 4 9 8 7 8 9 9 9 9 5 7 9 9 8 3 7 1 1 9 7 7 9 9 5 8 6 6 4 9 9 6 9 8 9 8 6 8 6 9 2 8 9 9 9 9 7 8 5 9 6 6 5 9 9 8 9 9 7 8 6 8 9 1 8 9 [71] 9 3 5 9 8 9 9 9 9 6 9 9 5 9 8 7 8 9 9 9 8 9 7 9 8 7 8 5 5 8 >
      
      





実行と1〜9の実際の擬似ランダムシーケンスに費やされた1000分の1秒を賞賛した後、変数varの関数の出力を「隠し」ますが、サンプルの長さを増やす実験を続けます。

 > val<-get_naive(1000) user system elapsed 0.021 0.000 0.021 > val<-get_naive(10000) user system elapsed 0.342 0.011 0.354 > val<-get_naive(100000) user system elapsed 24.86 8.55 33.48
      
      





ああああ!

混乱のように見えます! どういうわけか、ランタイムは非常に急成長しています。

これは本当に素朴なアプローチでしょうか? 中国の同志アルゴリズムは数倍速く動作しますか?



最終著者のプログラムのバージョンを作成し、時間を測定する目的でのみ追加します。 さまざまな長さの目的のサンプルで実行を開始します...

 > get_chen(1000) user system elapsed 0.031 0.001 0.032 > get_chen(10000) user system elapsed 0.508 0.063 0.567 > get_chen(100000) user system elapsed 46.303 5.946 53.088
      
      





おっと! なんて驚き...彼は私たちを失望させた、チェン同志。 「改善された」アルゴリズムは、「素朴な」よりもうまく機能することを拒否します。 それとも、私が引用した著者がすべての「輝き」を伝えることができなかったのでしょうか? 私は故意に次の悪いコードのバッチを見せませんでした。 慣れない方がいい。



私の最初の計画は、同僚の間違いを整理し、その方法を示すための一歩一歩でした。 しかし、Rでの経験に基づいて、コメント(「単純な」バージョン)をコメントで書き直し、 kokorinsが自分の欠陥または独自のコードまたは中国のコードの欠陥と戦うために残すことにしました。 すみません-できませんでした。



私が手に入れたコードは次のとおりです。

 M<-10 #    ps<- 1/(M:2)^2 #     ;   -   ps<-ps/sum(ps) #      ss<-c(0,cumsum(ps)) #     lbs=1:(M-1) #  "  " () --    ,     get_vladob <- function(n, brks=ss, labs=lbs) { st<-system.time( val<-as.numeric(cut(runif(n), breaks=brks, labels=labs)) ) print(st) return(val) }
      
      





そして、「実行」の結果



 > val<-get_vladob(1000) user system elapsed 0.010 0.000 0.012 > val<-get_vladob(10000) user system elapsed 0.004 0.000 0.004 > val<-get_vladob(100000) user system elapsed 0.036 0.001 0.038 > val<-get_vladob(1000000) user system elapsed 0.581 0.029 0.606 > val<-get_vladob(10000000) user system elapsed 4.015 0.311 4.318 > val<-get_vladob(100000000) user system elapsed 38.405 3.092 41.578
      
      





ご覧のとおり、「遅い」言語にとっては悪くありません。



私は今、他の言語に興味を持って見ています。 他の環境で実装された同様のテストの結果を興味を持って見るでしょう。 特に、 kokorinsの同僚がC ++の結果を取得するかどうか、またどの結果になるのか、 非常に興味があります。



コードの変更点は何ですか?

まず、Rの「ベクター」プロパティを使用しました。たとえば、runif関数の値が1000個必要な場合、runif(1000)とベクター式を使用して処理し、各forループで1つのrunifを1000回呼び出しません。 私を信じて、これはRにとって不可欠です。



二番目。 Rは関数型プログラミング言語です。 本当に大きな値の配列を操作するとき、特別な努力をせずに中間の値をメモリに配置することができるのがどれほど素晴らしいかを理解します。 ほとんどの場合、これは、プログラミングの基礎知識がありますが、(「不快ではない」)常に「直接手」のユーザーよりも、本当に強力なプログラマーによって書かれたシステムによって、より速く、より確実に行われます。



第三に、最初の投稿で、Rは「統計学者のための統計学者」によって書かれていると述べました。 カット関数は、これらの値が含まれる間隔に応じて、ベクトルの値にラベルを割り当てます。 区間境界ベクトルは、breaksパラメーターとしてcut関数に渡されます。 これは、Rで個別の関数として実装(および最適化)されるデータを処理(グループ化など)する場合のかなり一般的な操作です。



一般的に-R.

愛してください。






All Articles