私はのようなデータリストを持っています
12345
23456
67891
-20000
200
600
20
...
このデータセットのサイズ(ファイルの行など)はN
であると想定します。このデータファイルからランダムにm
行を描画します。したがって、出力は2つのファイルである必要があります。1つはこれらのm
行のデータを含むファイルで、もう1つはN-m
行のデータを含みます。
Linuxコマンドを使用してそれを行う方法はありますか?
これは最も効率的な方法ではないかもしれませんが、機能します:
shuf <file> > tmp
head -n $m tmp > out1
tail -n +$(( m + 1 )) tmp > out2
$m
行数を含みます。
この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
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 <>;'
に置き換えることができます。
行の並べ替えを気にせず、GNU coreutils(つまり、組み込みでないLinuxまたはCygwinの場合、shuf
がバージョン6.0で登場したため、それほど古くない)場合)- shuf
(“ shuffle”)はファイルの行をランダムに並べ替えるので、ファイルをシャッフルして最初の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
または、プラットフォームに/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行ずつ削除することで機能し、ファイルはどんどん短くなるため、削除できる行番号はどんどん小さくする必要があります。