データファイルから特定の行数をランダムに描画します


13

私は次のようなデータリストを持っています

12345
23456
67891
-20000
200
600
20
...

このデータセット(ファイルの行)のサイズはであると仮定しますNmこのデータファイルからランダムに線を引きたいです。したがって、出力は2つのファイルになります。1つはこれらmのデータ行を含むファイルで、もう1つはN-mデータ行を含みます。

Linuxコマンドを使用してそれを行う方法はありますか?


1
行のシーケンスが心配ですか?例えば。ソースの順序を維持しますか、それともそのシーケンス自体をランダムにし、行の選択をランダムにしますか?
Peter.O

回答:


18

これは最も効率的な方法ではないかもしれませんが、機能します:

shuf <file> > tmp
head -n $m tmp > out1
tail -n +$(( m + 1 )) tmp > out2

$m行数を含みます。


@userunknown sort -Rは、ランダム性を処理します。その答えを下したかどうかはわかりませんが、最初にマンページで調べてください。
ロブWOUTERS

2
sort -R入力をランダムに正確にソートするわけではないことに注意してください。同一の行をグループ化します。入力されたのであれば例えばfoofoobarbarかつm = 2、そして一つのファイルには両方が含まれますfooのを、もう一方は、両方が含まれますbar秒。GNU coreutilsにはshuf、入力行をランダム化するがあります。また、一時ファイルは必要ありません
ジル「SO-悪であるのをやめる」

どうしてshuf <file> |head -n $m
エマヌエーレ14年

@emanuele:2つの別々のファイルに頭と尾の両方が必要だからです。
ロブウーターズ14年

5

このbash / awkスクリプトは、行をランダムに選択し、両方の出力ファイルで元のシーケンスを維持します。

awk -v m=4 -v N=$(wc -l <file) -v out1=/tmp/out1 -v out2=/tmp/out2 \
 'BEGIN{ srand()
         do{ lnb = 1 + int(rand()*N)
             if ( !(lnb in R) ) {
                 R[lnb] = 1
                 ct++ }
         } while (ct<m)
  } { if (R[NR]==1) print > out1 
      else          print > out2       
  }' file
cat /tmp/out1
echo ========
cat /tmp/out2

質問のデータに基づいた出力。

12345
23456
200
600
========
67891
-20000
20

4

Unixのすべてのものと同様に、そのTMにはユーティリティがあります。

今日のプログラム:split
split多くの異なる方法、-bバイト、-l行、-n出力ファイルの数でファイルを分割します。この-lオプションを使用します。最初の行だけでなく、ランダムな行を選択するため、最初msortファイルをランダムに選択します。あなたが読みたいならsortここで私の答えを参照してください

さて、実際のコード。本当に簡単です、本当に:

sort -R input_file | split -l $m output_prefix

これにより、2つのファイルが作成されます。1つにはm行があり、もう1つにはN-m行がoutput_prefixaaありoutput_prefixabます。必要なm大きなファイルであることを確認してください。そうしないと、長さの異なるファイルm(およびが付いたファイル)がいくつか得られますN % m

正しいサイズを使用することを保証したい場合、それを行うための小さなコードがあります:

m=10 # size you want one file to be
N=$(wc -l input_file)
m=$(( m > N/2 ? m : N - m ))
sort -R input_file | split -l $m output_prefix

編集:いくつかのsort実装には-Rフラグがないことに気付きました。持っている場合はperl、代わりに使用できますperl -e 'use List::Util qw/shuffle/; print shuffle <>;'


1
残念ながら、sort -Rいくつかのバージョンのソート(おそらくgnuバージョン)にのみ存在するようです。他のプラットフォームについては、stdinをランダム化するだけの「randline」というツールを作成しました。それはATのbeesbuzz.biz/codeそれを必要とする人のため。(ファイルの内容をかなりシャッフルする傾向があります。)
ふわふわ

1
sort -R入力をランダムに正確にソートするわけではないことに注意してください。同一の行をグループ化します。入力されたのであれば例えばfoofoobarbarかつm = 2、そして一つのファイルには両方が含まれますfooのを、もう一方は、両方が含まれますbar秒。GNU coreutilsにはshuf、入力行をランダム化するがあります。また、の代わりにを使用して、出力ファイル名を選択できますheadtailsplit
ジル 'SO-邪悪なことをやめる

4

行の並べ替えを気にせず、GNU coreutils(つまり、非組み込みLinuxまたはCygwin、shufバージョン6.0で登場してから古すぎない)がある場合、shuf(「シャッフル」)ファイルの行をランダムに並べ替えます。そのため、ファイルをシャッフルし、最初のm行を1つのファイルにディスパッチし、残りを別のファイルにディスパッチできます。

そのディスパッチを行う理想的な方法はありません。チェーンすることはできませんしhead、先にバッファリングするtailからheadです。を使用できますsplitが、出力ファイル名に関して柔軟性が得られません。awkもちろん使用できます:

<input shuf | awk -v m=$m '{ if (NR <= m) {print >"output1"} else {print} }'

を使用できますsed。これはあいまいですが、大きなファイルの場合は高速になります。

<input shuf | sed -e "1,${m} w output1" -e "1,${m} d" >output2

またはtee、プラットフォームに/dev/fd;がある場合、データの複製に使用できます。mが小さい場合は問題ありません。

<input shuf | { tee /dev/fd/3 | head -n $m >output1; } 3>&1 | tail -n +$(($m+1)) >output2

移植性の高い方法として、awkを使用して各行を順番にディスパッチできます。awkは、乱数ジェネレーターの初期化にはあまり適していません。ランダム性は、明らかに暗号化に適していないだけでなく、数値シミュレーションにもあまり適していません。シードは、1秒の期間でシステム上のすべてのawk呼び出しに対して同じになります。

<input awk -v N=$(wc -l <input) -v m=3 '
    BEGIN {srand()}
    {
        if (rand() * N < m) {--m; print >"output1"} else {print >"output2"}
        --N;
    }'

より良いランダム性が必要な場合は、Perlで同じことを行うことができます。これにより、RNGが適切にシードされます。

<input perl -e '
    open OUT1, ">", "output1" or die $!;
    open OUT2, ">", "output2" or die $!;
    my $N = `wc -l <input`;
    my $m = $ARGV[0];
    while (<STDIN>) {
        if (rand($N) < $m) { --$m; print OUT1 $_; } else { print OUT2 $_; }
        --$N;
    }
    close OUT1 or die $!;
    close OUT2 or die $!;
' 42

:@Gillesの場合awk例: -v N=$(wc -l <file) -v m=4...とそれだけで印刷し、「ランダム」、ランダムな値未満であるときに、ラインを$mむしろ印刷するよりも、$mランダムな行をそれはそれはそう... perlと同じことをやっているかもしれランド、私ドンperlコンパイルエラーを通過するのに十分な知識がない:-e行7の構文エラー、 ")printの近く
Peter.O

@ Peter.Oありがたいことに、それはブラウザに入力して不注意に編集することから来ています。awkとperlのコードを修正しました。
ジル「SO-悪であるのをやめる」

3つの方法すべてがうまく機能し、高速に動作します。ありがとう(+1)... perlをゆっくりと頭を動かしています...これは、このshuf例で特に興味深い便利なファイル分割です。
Peter.O

バッファリングの問題?。何か不足していますか?head catコンボは、次の第二の試験では、データの損失の原因となる3-4 .... TEST 1-2 { for i in {00001..10000} ;do echo $i; done; } | { head -n 5000 >out1; cat >out2; } .. TEST 3-4 { for i in {00001..10000} ;do echo $i; done; } >input; cat input | { head -n 5000 >out3; cat >out4; } ... wc -lの出力の結果TEST 1-2がある5000 5000(良い)が、ためにTEST 3-45000 4539(良くない)..違いは関連するファイルサイズによって異なります...ここに私のテストコード
-Peter.O

@ Peter.Oもう一度、ありがとう。実際、head先読みします。先読みして印刷されないものは破棄されます。私はあまりエレガントではないが(合理的に確信している)正しい解決策で答えを更新しました。
ジル「SO-悪であるのをやめる」

2

と仮定m = 7してN = 21

cp ints ints.bak
for i in {1..7}
do
    rnd=$((RANDOM%(21-i)+1))
    # echo $rnd;  
    sed -n "${rnd}{p,q}" 10k.dat >> mlines 
    sed -i "${rnd}d" ints 
done

注:またはなどの7変数で置き換える場合は、変数の展開を行わない-表記ではなく、を使用する必要があります。$1$mseq{from..to}

ファイルから1行ずつ削除することで機能します。ファイルはますます短くなるため、削除できる行番号はますます小さくする必要があります。

これは、長いファイルや多くの行には使用しないでください。すべての数値について、平均して、最初の半分のファイルを読み取り、2番目のsedコードのファイル全体を読み取る必要があるためです。


彼も削除された行を含むファイルが必要です。
ロブウーターズ

「これらのm行のデータを含む」とはincluding them、元の行も意味するはずだと思ったのでincluding、ではなくconsisting of、使用していないonlyが、ユーザーの解釈はuser288609の意味だと思います。それに応じてスクリプトを調整します。
ユーザー不明

いいね。`` ``
ロブウーターズ

@user unknown:あなたは+1間違った場所にいます。rnd=$((RANDOM%(N-i)+1))あなたの例ではN = 21であるはずです。現在のところ、sedrnd評価されるとクラッシュし0ます。..また、すべてのファイルの再書き込みではうまくスケーリングしません。例えば123秒万行ファイルから5,000ランダムライン抽出する 0.03秒を ...もっと直接的な方法のために
Peter.O

@ Peter.O:あなたは正しい(訂正された)そしてあなたは正しい。
ユーザー不明
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.