私は次のようなデータリストを持っています
12345
23456
67891
-20000
200
600
20
...
このデータセット(ファイルの行)のサイズはであると仮定しますN。mこのデータファイルからランダムに線を引きたいです。したがって、出力は2つのファイルになります。1つはこれらmのデータ行を含むファイルで、もう1つはN-mデータ行を含みます。
Linuxコマンドを使用してそれを行う方法はありますか?
私は次のようなデータリストを持っています
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行数を含みます。
sort -Rは、ランダム性を処理します。その答えを下したかどうかはわかりませんが、最初にマンページで調べてください。
sort -R入力をランダムに正確にソートするわけではないことに注意してください。同一の行をグループ化します。入力されたのであれば例えばfoo、foo、bar、barかつm = 2、そして一つのファイルには両方が含まれますfooのを、もう一方は、両方が含まれますbar秒。GNU coreutilsにはshuf、入力行をランダム化するがあります。また、一時ファイルは必要ありません。
shuf <file> |head -n $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、ここで私の答えを参照してください。
さて、実際のコード。本当に簡単です、本当に:
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 <>;'。
sort -Rいくつかのバージョンのソート(おそらくgnuバージョン)にのみ存在するようです。他のプラットフォームについては、stdinをランダム化するだけの「randline」というツールを作成しました。それはATのbeesbuzz.biz/codeそれを必要とする人のため。(ファイルの内容をかなりシャッフルする傾向があります。)
sort -R入力をランダムに正確にソートするわけではないことに注意してください。同一の行をグループ化します。入力されたのであれば例えばfoo、foo、bar、barかつm = 2、そして一つのファイルには両方が含まれますfooのを、もう一方は、両方が含まれますbar秒。GNU coreutilsにはshuf、入力行をランダム化するがあります。また、の代わりにとを使用して、出力ファイル名を選択できますheadtailsplit。
行の並べ替えを気にせず、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
awk例: -v N=$(wc -l <file) -v m=4...とそれだけで印刷し、「ランダム」、ランダムな値未満であるときに、ラインを$mむしろ印刷するよりも、$mランダムな行をそれはそれはそう... perlと同じことをやっているかもしれランド、私ドンperlコンパイルエラーを通過するのに十分な知識がない:-e行7の構文エラー、 ")printの近く
shuf例で特に興味深い便利なファイル分割です。
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-4は5000 4539(良くない)..違いは関連するファイルサイズによって異なります...ここに私のテストコード
head先読みします。先読みして印刷されないものは破棄されます。私はあまりエレガントではないが(合理的に確信している)正しい解決策で答えを更新しました。
と仮定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行ずつ削除することで機能します。ファイルはますます短くなるため、削除できる行番号はますます小さくする必要があります。
including them、元の行も意味するはずだと思ったのでincluding、ではなくconsisting of、使用していないonlyが、ユーザーの解釈はuser288609の意味だと思います。それに応じてスクリプトを調整します。
+1間違った場所にいます。rnd=$((RANDOM%(N-i)+1))あなたの例ではN = 21であるはずです。現在のところ、sedにrnd評価されるとクラッシュし0ます。..また、すべてのファイルの再書き込みではうまくスケーリングしません。例えば123秒万行ファイルから5,000ランダムライン抽出する対 0.03秒を ...もっと直接的な方法のために