web-dev-qa-db-ja.com

ファイルのサブセットをランダムにサンプリングする方法

ファイルのサブセットをサンプリングするために使用できるLinuxコマンドはありますか?たとえば、ファイルに100万行が含まれている場合、そのファイルからランダムに1000行だけをサンプリングしたいとします。

ランダムとは、すべてのラインが同じ確率で選択されることを意味し、選択されたラインはどれも反復的ではありません。

headおよびtailは、ファイルのサブセットを選択できますが、ランダムにはできません。私は常にpythonスクリプトを記述できることを知っていますが、この使用法のためのコマンドがあるのだろうかと思います。

49
clwen

shufコマンド(coreutilsの一部)はこれを行うことができます:

shuf -n 1000 file

そして、少なくとも今のところ非古代バージョン( commit from 201 で追加)では、必要に応じてリザーバーサンプリングを使用します。つまり、メモリ不足にならず、高速アルゴリズムを使用しています。

75
derobert

非常に大きなファイルがある場合(これはサンプルを取る一般的な理由です)、次のことがわかります:

  1. shufはメモリを使い果たします
  2. ファイルが32767行を超える場合、_$RANDOM_の使用は正しく機能しません

n個のサンプリングされた行を正確に必要としない場合比率をサンプリングすることができます このような:

cat input.txt | awk 'BEGIN {srand()} !/^$/ { if (Rand() <= .01) print $0}' > sample.txt

これ定数メモリを使用し、ファイルの1%をサンプリングします(もしファイルの行数がわかっているので、この係数を調整して、限られた数の行に近い値をサンプリングできます。任意のサイズのファイルで機能しますしかし、それはできませんprecise行の数を返します比。

注:コードは次のようになります: https://stackoverflow.com/questions/692312/randomly-pick-lines-from-a-file-without-slurping-it-with- UNIX

17
Txangel

@Txangelの確率的ソリューションに似ていますが、100倍速く近づきます。

Perl -ne 'print if (Rand() < .01)' huge_file.csv > sample.csv

高いパフォーマンスと正確なサンプルサイズが必要であり、ファイルの最後にサンプルギャップがあっても問題ない場合は、次のようなことができます(1m行のファイルから1000行をサンプリングします)。

Perl -ne 'print if (Rand() < .0012)' huge_file.csv | head -1000 > sample.csv

..またはheadの代わりに2番目のサンプルメソッドを実際にチェーンします。

6
geotheory

shuf -n大きなファイルでトリックを実行するとメモリが不足し、固定サイズのサンプルが必要であり、外部ユーティリティをインストールしてから sample を試すことができます。

$ sample -N 1000 < FILE_WITH_MILLIONS_OF_LINES 

注意点は、sample(この例では1000行)がメモリに収まる必要があるということです。

免責事項:私は推奨ソフトウェアの作成者です。

5
hroptatyr

あなたが求めることを実行できる単一のコマンドを認識していませんが、ここに私が一緒に仕事をすることができるループがあります:

for i in `seq 1000`; do sed -n `echo $RANDOM % 1000000 | bc`p alargefile.txt; done > sample.txt

sedは、1000回のパスごとにランダムなラインをピックアップします。おそらくより効率的なソリューションがあります。

3
mkc

ファイルの行数がわかっている場合(例では1e6のように)、次のことができます。

awk -v n=1e6 -v p=1000 '
  BEGIN {srand()}
  Rand() * n-- < p {p--; print}' < file

そうでない場合は、いつでも行うことができます

awk -v n="$(wc -l < file)" -v p=1000 '
  BEGIN {srand()}
  Rand() * n-- < p {p--; print}' < file

これはファイルで2つのパスを実行しますが、ファイル全体をメモリに保存することは避けます。

GNU shufに対するもう1つの利点は、ファイル内の行の順序が保持されることです。

nisファイルの行数を想定していることに注意してください。ファイルのfirstp行からnを出力したい場合は(これにより多くの行が含まれる可能性があります)、awkn番目 次のような行:

awk -v n=1e6 -v p=1000 '
  BEGIN {srand()}
  Rand() * n-- < p {p--; print}
  !n {exit}' < file
2

次のコードをファイル(例:randextract.sh)に保存し、次のように実行できます。

randextract.sh file.txt

---- BEGIN FILE ----

#!/bin/sh -xv

#configuration MAX_LINES is the number of lines to extract
MAX_LINES=10

#number of lines in the file (is a limit)
NUM_LINES=`wc -l $1 | cut -d' ' -f1`

#generate a random number
#in bash the variable $RANDOM returns diferent values on each call
if [ "$RANDOM." != "$RANDOM." ]
then
    #bigger number (0 to 3276732767)
    Rand=$RANDOM$RANDOM
else
    Rand=`date +'%s'`
fi 

#The start line
START_LINE=`expr $Rand % '(' $NUM_LINES - $MAX_LINES ')'`

tail -n +$START_LINE $1 | head -n $MAX_LINES

----ファイル終了----

2
razzek

ヘッダー行を保持したい場合、およびサンプルがファイルのおおよそのパーセンテージになる可能性がある場合は、これにawkを使用するのが好きです。非常に大きなファイルで機能します:

awk 'BEGIN {srand()} !/^$/ { if (Rand() <= .01 || FNR==1) print > "data-sample.txt"}' data.txt
2
Merlin

またはこのように:

LINES=$(wc -l < file)  
RANDLINE=$[ $RANDOM % $LINES ]  
tail -n $RANDLINE  < file|head -1  

Bashのmanページから:

 RANDOMこのパラメータが参照されるたびに、0〜32767のランダムな整数
が生成されます。ランダムな
番号のシーケンスは、RAN-
 DOMに値を割り当てることで初期化できます。 RANDOMが設定されていない場合、その後リセットされても、特別な固有の
関係は失われます。
1
user55518

隣接する行の単一のランダムなブロックを取得するには、shufを使用して1つのランダムな行を取得し、次にgrepを使用してランダムに選択された行の後の行のブロックを取得します。

$ shuf -n 1 file | grep -f - -A 10 file

これにより、ファイルに2回アクセスします。 -fパラメータは、ファイルから検索パターンを取得するようにgrepに指示します。この場合、ファイルからランダムに選択された単一の行であるstdin(fパラメータの値としてダッシュを使用)です。

簡単な関数:

function random-block {
  shuf -n 1 $1 | grep -f - -A $(($2>0?$2-1:0)) $1
}

使用例:

$ random-block /var/log/syslog 10

実際、ランダムな選択が遠すぎてブロックを選択するときに十分な行が残っていない場合、これは要求された行数の出力を保証しません。

拡張機能は次のようになります。

function random-block {
  head -n $(($(wc -l | cut -f1 -d ' ')-$2+1)) $1 | shuf -n 1 | grep -f - -A $(($2>0?$2-1:0)) $1
}

これにより、最後のn行を除くファイル内のすべての行が取得され、その行のリストがシャッフルされます。これにより、grepは常に要求された行数を選択できるようになります。

1
s1037989

ファイルサイズが大きくない場合は、ランダムに並べ替えを使用できます。これはshufよりも少し時間がかかりますが、データ全体をランダム化します。したがって、次のようにして、要求どおりにヘッドを使用することが簡単にできます。

sort -R input | head -1000 > output

これにより、ファイルがランダムにソートされ、最初の1000行が表示されます。

1
DomainsFeatured

受け入れられた回答で述べたように、GNU shufは単純なランダムサンプリングをサポートします(shuf -n)結構です。 shufでサポートされている以外のサンプリング方法が必要な場合は、 evayのTSVユーティリティ から tsv-sample を検討してください。加重ランダムサンプリング、ベルヌーイサンプリング、個別サンプリングなど、いくつかの追加のサンプリングモードをサポートしています。パフォーマンスはGNU shufに似ています(どちらも非常に高速です)。免責事項:私は著者です。

0
JonDeg