LoginSignup
8
12

More than 5 years have passed since last update.

全体の行数がわからないデータからランダムにN行取り出す

Posted at

テキストデータからランダムにN行取り出す方法ですが、まず「シャッフルしてN行取り出す」というのがあります。
しかし、データが大きいとシャッフルにも大量のメモリが必要になるので、別の方法を考えたいところです。
ここで、全体の行数がわかっていれば簡単に書ける(後で書きます)ところですが、一度行数を調べてから取り出すとなると二度手間になってしまいます。

こういうときは、Perl(-nオプション)で次のように書くと、$n個を配列に格納することができます(順序はランダムではありません)。

$t = $. <= $n ? $. - 1 : rand($.);
$r[$t] = $_ if $t < $n;

スクリプト全体はこのGistです。
これを保存して実行可能にしてパスを通せば、次のようにランダム抽出ができます。

$ randpick -n 5 KEN_ALL.CSV | piconv -f cp932
01665,"08833","0883332","ホッカイドウ","カワカミグンテシカガチョウ","サツトモナイゲンヤ","北海道","川上郡弟子屈町","札友内原野",0,0,0,1,0,0
08227,"30801","3080103","イバラキケン","チクセイシ","ツジ","茨城県","筑西市","辻",0,0,0,0,0,0
15202,"940  ","9400872","ニイガタケン","ナガオカシ","イナバマチ","新潟県","長岡市","稲葉町",0,0,0,0,0,0
08204,"306  ","3060053","イバラキケン","コガシ","ナカダ","茨城県","古河市","中田",0,0,0,0,0,0
26109,"612  ","6128344","キョウトフ","キョウトシフシミク","オオツマチ","京都府","京都市伏見区","大津町",0,0,0,0,0,0

アルゴリズムはReservoir samplingというもので、以下の記事に詳しい解説があります。

大量のテキストからランダムに少数の行を抽出したい - Reservoir Sampling

ところで、このアルゴリズムの欠点はO(N)だけメモリを使うというところで、Nが大きくなってくると問題になるかもしれません(そういうことは私のユースケースではあまりないですが)。
そういう場合のために、「全体の行数がわかっているデータからランダムにN行取り出す」スクリプトも書いておきます。
こちらはメモリをほとんど使いません。

if (rand($all - $. + 1) < $n) {
    print;
    --$n;
}

Gist

このアルゴリズムは、以下の記事でKnuthの方法として紹介されているものです。

非復元抽出の高速かつ実装が簡単な方法を考える

これを使う場合は次のようになります。

$ wc -l < KEN_ALL.CSV
  124263
$ randpick_a -a 124263 -n 5 KEN_ALL.CSV | piconv -f cp932
13206,"183  ","1830041","トウキョウト","フチュウシ","キタヤマチョウ","東京都","府中市","北山町",0,0,1,0,0,0
15222,"94201","9420114","ニイガタケン","ジョウエツシ","クビキクカキノ","新潟県","上越市","頸城区柿野",0,0,0,0,0,0
26106,"600  ","6008167","キョウトフ","キョウトシシモギョウク","トキワチョウ","京都府","京都市下京区","常葉町",0,0,0,0,0,0
27206,"595  ","5950045","オオサカフ","イズミオオツシ","シタノチョウ","大阪府","泉大津市","下之町",0,0,0,0,0,0
36202,"77903","7790305","トクシマケン","ナルトシ","オオアサチョウタカバタケ","徳島県","鳴門市","大麻町高畑",0,1,0,0,0,0
8
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
12