1

私は非常に大きなデータセットを持っているので、以下の小さな例で質問を要約しようとしています.

X という名前の 3X3 行列があり、列名が a、b、c で​​あるとします。

X = (1, 10, 0.1,
     2, 20, 0.2,
     3, 30, 0.3)

ここでa = c(1, 2, 3)、繰り返す回数、繰り返すb = c(10, 20, 30)実際の値、および回数が 4 (行列 Y の列数) 未満c = c(0.1, 0.2, 0.3)の場合に入力する値を示します。a

私の目標は、次のような 3X4 行列 Y を生成することです。

Y = (10, 0.1, 0.1, 0.1,
     20,  20, 0.2, 0.2,
     30,  30,  30, 0.3)

この例を実行するには多くの方法があることは理解していますが、実際のデータは非常に大きいため (X には 100 万行、Y には 480 列あります)、実際にはこれをループなしで実行する必要があります (480 回の反復など)。関数 を使用してみましrepたが、まだこれを行うことができませんでした。

4

2 に答える 2

1

解決

簡単ではありませんでしたが、単一のベクトル化された への呼び出しrep()といくつかの足場コードを使用して、このタスクを達成する方法を見つけました。

XR <- 3;
YC <- 4;
X <- matrix(c(1:XR%%(YC+1),seq(10,by=10,length.out=XR),seq(0.1,by=0.1,length.out=XR)),XR,dimnames=list(NULL,c('rep','val','fill')));
X;
##      rep val fill
## [1,]   1  10  0.1
## [2,]   2  20  0.2
## [3,]   3  30  0.3
Y <- matrix(rep(t(X[,c('val','fill')]),times=c(rbind(X[,'rep'],YC-X[,'rep']))),XR,byrow=T);
Y;
##      [,1] [,2] [,3] [,4]
## [1,]   10  0.1  0.1  0.1
## [2,]   20 20.0  0.2  0.2
## [3,]   30 30.0 30.0  0.3

(マイナーポイント: 質問で指定されているのではなく、列名rep val fillをに割り当てることを選択し、インデックス作成時に(数値インデックスを使用するのではなく)それらの列名をソリューションで使用しました。ただし、ソリューションの正確性とパフォーマンスに関しては、この詳細は無視できます。)Xa b cX

パフォーマンス

@josilber のソリューションよりもパフォーマンスが大幅に向上しますapply()。これは、マトリックスの行を内部的にループする (伝統的に R スピークでは「隠しループ」と呼ばれる) を使用しているためです。一方、私のソリューションのコアは、rep(). @josilber の解決策を否定するためにこれを言っているのではありません。これは、この問題に対する最善の解決策ではありません。

質問で示した多額のパラメーターを使用したパフォーマンス上の利点のデモを次に示します。

XR <- 1e6;
YC <- 480;
X <- matrix(c(1:XR%%(YC+1),seq(10,by=10,length.out=XR),seq(0.1,by=0.1,length.out=XR)),XR,dimnames=list(NULL,c('rep','val','fill')));
X;
##        rep  val fill
##   [1,]   1   10  0.1
##   [2,]   2   20  0.2
##   [3,]   3   30  0.3
##   [4,]   4   40  0.4
##   [5,]   5   50  0.5
##   [6,]   6   60  0.6
##   [7,]   7   70  0.7
##   [8,]   8   80  0.8
##   [9,]   9   90  0.9
##  [10,]  10  100  1.0
##  [11,]  11  110  1.1
##  [12,]  12  120  1.2
##  [13,]  13  130  1.3
##
## ... (snip) ...
##
## [477,] 477 4770 47.7
## [478,] 478 4780 47.8
## [479,] 479 4790 47.9
## [480,] 480 4800 48.0
## [481,]   0 4810 48.1
## [482,]   1 4820 48.2
## [483,]   2 4830 48.3
## [484,]   3 4840 48.4
## [485,]   4 4850 48.5
## [486,]   5 4860 48.6
## [487,]   6 4870 48.7
## [488,]   7 4880 48.8
## [489,]   8 4890 48.9
## [490,]   9 4900 49.0
## [491,]  10 4910 49.1
## [492,]  11 4920 49.2
##
## ... (snip) ...
##
## [999986,] 468  9999860  99998.6
## [999987,] 469  9999870  99998.7
## [999988,] 470  9999880  99998.8
## [999989,] 471  9999890  99998.9
## [999990,] 472  9999900  99999.0
## [999991,] 473  9999910  99999.1
## [999992,] 474  9999920  99999.2
## [999993,] 475  9999930  99999.3
## [999994,] 476  9999940  99999.4
## [999995,] 477  9999950  99999.5
## [999996,] 478  9999960  99999.6
## [999997,] 479  9999970  99999.7
## [999998,] 480  9999980  99999.8
## [999999,]   0  9999990  99999.9
## [1e+06,]    1 10000000 100000.0
josilber <- function() t(apply(X,1,function(x) rep(x[2:3],c(x[1],YC-x[1]))));
bgoldst <- function() matrix(rep(t(X[,c('val','fill')]),times=c(rbind(X[,'rep'],YC-X[,'rep']))),XR,byrow=T);
system.time({ josilber(); });
##    user  system elapsed
##  65.719   3.828  71.623
system.time({ josilber(); });
##    user  system elapsed
##  60.375   2.609  66.724
system.time({ bgoldst(); });
##    user  system elapsed
##   5.422   0.593   6.033
system.time({ bgoldst(); });
##    user  system elapsed
##   5.203   0.797   6.002

そして、@josilber と私がこの大規模な入力に対してもまったく同じ結果を得ていることを証明するために:

identical(bgoldst(),josilber());
## [1] TRUE

説明

ここで、ソリューションがどのように機能するかを説明しようと思います。説明のために、次の入力を使用します。

XR <- 6;
YC <- 4;
X <- matrix(c(1:XR%%(YC+1),seq(10,by=10,length.out=XR),seq(0.1,by=0.1,length.out=XR)),XR,dimnames=list(NULL,c('rep','val','fill')));
X;
##      rep val fill
## [1,]   1  10  0.1
## [2,]   2  20  0.2
## [3,]   3  30  0.3
## [4,]   4  40  0.4
## [5,]   0  50  0.5
## [6,]   1  60  0.6

解決策は次のとおりです。

Y <- matrix(rep(t(X[,c('val','fill')]),times=c(rbind(X[,'rep'],YC-X[,'rep']))),XR,byrow=T);
Y;
##      [,1] [,2] [,3] [,4]
## [1,] 10.0  0.1  0.1  0.1
## [2,] 20.0 20.0  0.2  0.2
## [3,] 30.0 30.0 30.0  0.3
## [4,] 40.0 40.0 40.0 40.0
## [5,]  0.5  0.5  0.5  0.5
## [6,] 60.0  0.6  0.6  0.6

val大まかに言うと、ソリューションは、とベクトルを組み合わせた単一のベクトルを形成し、fillその組み合わせたベクトルを特定の方法で繰り返し、結果から新しい行列を構築することを中心に構築されます。

rep()反復ステップは、ベクトル化された反復カウントをサポートしているため、 の 1 回の呼び出しを使用して実行できます。つまり、指定されたベクトル入力に対して、 の各要素を何回繰り返すかを指定xするベクトル入力を受け取ることができます。したがって、課題は、適切な引数と引数を構築することになります。timesxxtimes

したがって、解決策は、 の列valfill列を抽出することから始まりXます。

X[,c('val','fill')];
##      val fill
## [1,]  10  0.1
## [2,]  20  0.2
## [3,]  30  0.3
## [4,]  40  0.4
## [5,]  50  0.5
## [6,]  60  0.6

ご覧のとおり、2 つの列にインデックスを付けたのでdrop=F、インデックス操作を指定していなくても、まだマトリックスがあります (「R: オブジェクトの一部を抽出または置換する」を参照)。これは便利です。

R では、マトリックスの「マトリックス ペルソナ」の下にあるのは、実際には単純な古いアトミック ベクトルであり、マトリックスの「ベクトル ペルソナ」はベクトル化された操作に活用できます。これは、valfillデータを に渡し、rep()それらの要素を適切に繰り返す方法です。

ただし、これを行う場合、行列がベクトルとしてどのように扱われるかを正確に理解することが重要です。答えは、ベクトルはを横切って要素をたどってからを横切ることによって形成されるということです。(高次元配列の場合、後続の次元が続きます。IOW、ベクトルの順序は、行、列、z スライスなどです)

上記の行列を注意深く見ると、最初に s が続き、次に s が続くため、xへの引数として使用できないことがわかります。実際には、各要素を正しい回数繰り返す引数をかなり簡単に作成できますが、結果のベクトルは完全に順不同になり、目的の行列に再形成する方法はありません。rep()valfilltimesY

実際、説明に進む前に、これを簡単に示してみませんか。

rep(X[,c('val','fill')],times=c(X[,'rep'],YC-X[,'rep']))
##  [1] 10.0 20.0 20.0 30.0 30.0 30.0 40.0 40.0 40.0 40.0 60.0  0.1  0.1  0.1  0.2  0.2  0.3  0.5  0.5  0.5  0.5  0.6  0.6  0.6

上記のベクトルは、すべての適切な繰り返しですべての適切な要素を持っていますが、順序は、目的の出力行列を形成できないようなものYです。

したがって、最初に抽出物を転置することでこれを解決できます。

t(X[,c('val','fill')]);
##      [,1] [,2] [,3] [,4] [,5] [,6]
## val  10.0 20.0 30.0 40.0 50.0 60.0
## fill  0.1  0.2  0.3  0.4  0.5  0.6

これで、valとベクトルが相互にインターリーブされ、ベクトルにフラット化するときに、の引数fillで行うように、ベクトルとして内部的に使用する関数に引数として渡すと発生します。 、それらから行列を再構築するための適切な順序でおよび対応する値を取得します。これがどのように見えるかを示すために、行列をベクトルに明示的にフラット化することでこれを実証しましょう (ご覧のとおり、この「フラット化」は単純な呼び出しで実行できます)。rep()xvalfillc()

c(t(X[,c('val','fill')]));
##  [1] 10.0  0.1 20.0  0.2 30.0  0.3 40.0  0.4 50.0  0.5 60.0  0.6

それで、私たちのx議論があります。timesあとは、引数を作成するだけです。

これを理解するのは実際にはかなりトリッキーでした。まず、値の繰り返し回数がの列にval直接提供されていることがわかります。また、値の繰り返し回数は、で取得した出力行列 の列数と、 、または IOWの前述の繰り返し回数との差から計算できます。問題は、これらの 2 つのベクトルをインターリーブして、議論に合わせる必要があることです。repXX[,'rep']fillYYCvalYC-X[,'rep']x

Rで2つのベクトルをインターリーブする「組み込み」の方法を知りません。それを行う機能はないようです。この問題に取り組んでいるときに、このタスクに対して 2 つの異なる解決策を思いつきました。そのうちの 1 つが、パフォーマンスと簡潔さの両方の点で優れているようです。しかし、私は最初の解決策を「より悪い」ものを使用するように書き、後で (実際にはこの説明を書いているときに) 2 番目の「より良い」ものを考えたので、最初と悪いものから始めて、ここで両方のアプローチを説明します。 1。

インターリーブ ソリューション #1

2 つのベクトルをインターリーブするには、ベクトルを順番に組み合わせてから、その組み合わせたベクトルを慎重に作成されたインデックス ベクトルでインデックス付けします。このインデックス ベクトルは基本的に、組み合わせたベクトルの前半から後半に前後にジャンプし、各要素を順番に引き出します。交互に半分ずつ。

このインデックス ベクトルを構築するために、結合されたベクトルの長さの半分に等しい長さの順次ベクトルから始めます。各要素は 1 回繰り返されます。

rep(1:nrow(X),each=2);
##  [1] 1 1 2 2 3 3 4 4 5 5 6 6

0次に、結合されたベクトルの長さの半分で構成される 2 要素ベクトルを追加します。

nrow(X)*0:1;
## [1] 0 6

2 番目の加数は最初の加数を循環し、必要なインターリーブを実現します。

rep(1:nrow(X),each=2)+nrow(X)*0:1;
##  [1]  1  7  2  8  3  9  4 10  5 11  6 12

したがって、結合された繰り返しベクトルにインデックスを付けて、times引数を取得できます。

c(X[,'rep'],YC-X[,'rep'])[rep(1:nrow(X),each=2)+nrow(X)*0:1];
##  [1] 1 3 2 2 3 1 4 0 0 4 1 3

インターリーブ ソリューション #2

2 つのベクトルをインターリーブすることは、2 つのベクトルを組み合わせて行列にし、それらが自然にインターリーブされるようにもう一度平坦化することによっても実現できます。これを行う最も簡単な方法は、rbind()それらを一緒にしてから、次のようにすぐに平らにすることだと思いc()ます。

c(rbind(X[,'rep'],YC-X[,'rep']));
##  [1] 1 3 2 2 3 1 4 0 0 4 1 3

いくつかの大まかなパフォーマンス テストに基づくと、解決策 2 の方がパフォーマンスが高く、より簡潔であることが明らかにわかります。また、追加のベクトルを呼び出しに非常に簡単に追加できますがrbind()、解決策 1 に追加するには少し手間がかかります (2 回の増分)。

パフォーマンス テスト (大規模なデータセットを使用):

il1 <- function() c(X[,'rep'],YC-X[,'rep'])[rep(1:nrow(X),each=2)+nrow(X)*0:1];
il2 <- function() c(rbind(X[,'rep'],YC-X[,'rep']));
identical(il1(),il2());
## [1] TRUE
system.time({ replicate(30,il1()); });
##    user  system elapsed
##   3.750   0.000   3.761
system.time({ replicate(30,il1()); });
##    user  system elapsed
##   3.810   0.000   3.815
system.time({ replicate(30,il2()); });
##    user  system elapsed
##   1.516   0.000   1.512
system.time({ replicate(30,il2()); });
##    user  system elapsed
##   1.500   0.000   1.503

したがって、完全なrep()呼び出しにより、適切な順序でデータが提供されます。

rep(t(X[,c('val','fill')]),times=c(rbind(X[,'rep'],YC-X[,'rep'])));
##  [1] 10.0  0.1  0.1  0.1 20.0 20.0  0.2  0.2 30.0 30.0 30.0  0.3 40.0 40.0 40.0 40.0  0.5  0.5  0.5  0.5 60.0  0.6  0.6  0.6

最後のステップは、 を使用して、それからマトリックスを構築することです。これbyrow=Tは、データが から返される方法であるためですrep()。また、必要な行数も指定する必要があります。これは、入力行列と同じです(または、必要に応じて、列数、または両方をXR指定することもできます)。YC

Y <- matrix(rep(t(X[,c('val','fill')]),times=c(rbind(X[,'rep'],YC-X[,'rep']))),XR,byrow=T);
Y;
##      [,1] [,2] [,3] [,4]
## [1,] 10.0  0.1  0.1  0.1
## [2,] 20.0 20.0  0.2  0.2
## [3,] 30.0 30.0 30.0  0.3
## [4,] 40.0 40.0 40.0 40.0
## [5,]  0.5  0.5  0.5  0.5
## [6,] 60.0  0.6  0.6  0.6

これで完了です。

于 2015-05-12T01:35:01.657 に答える