web-dev-qa-db-ja.com

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

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

12345
23456
67891
-20000
200
600
20
...

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

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

13
user288609

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

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

$m行数を含みます。

18
Rob Wouters

この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
5
Peter.O

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

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

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

sort -R input_file | split -l $m output_prefix

これにより、m行とN-m行の2つのファイルが作成され、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 <>;'に置き換えることができます。

4
Kevin

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

そのディスパッチを行うための理想的な方法はありません。 headは先にバッファリングするため、tailheadをチェーンすることはできません。 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

または、プラットフォームに/dev/fdがある場合は、teeを使用してデータを複製できます。 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

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$mなどの変数に置き換える場合は、{from..to}表記ではなくseqを使用する必要があります。 t変数展開を行います。

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

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

2
user unknown