ファイルのコレクションからランダムなサンプルを収集する最良の方法


23

300個のデータファイルを保持するディレクトリがあるとします。これらのファイルのうち200個をランダムに選択し、別のディレクトリに移動したいです。Unix / Linuxでそれを行う方法はありますか?


Rは、おそらくこれを次のように瞬く間に行うことができlist.files()ます
。– sr_

4
私は漠然と一緒にプラグインしshufhead(または単に使用してshuf -n、マニュアルページを読むべきです...)
Ulrich Schwarz

回答:


32

システムにがある場合、shufこれを非常に便利に使用できます((いファイル名を処理する場合でも)。

shuf -zen200 source/* | xargs -0 mv -t dest

あなたshufが持っていないが、sortそのを持っている場合-R、これはうまくいくはずです:

find source -type f -print0 | sort -Rz | cut -d $'\0' -f-200 | xargs -0 mv -t dest

7
ええ、そうです、ソート用のツールよりも他の場所でシャッフルを探す人がいるからです。(少なくとも並べ替えの反対を行うため、shuf呼び出されませんtros。)
ウルリッヒ・シュワルツ

2
並べ替えの反対のようなものはありません(同じ意味で、「天気なし」というものはありません)。ランダムは依然としてソートされており、ランダムにソートされています。
Plutor

1
「-zen200」とは何ですか?これはshufのドキュメントやインターネット上のどこにもありませんが、これがないと動作しません。まったく神秘的。
SigmaX

2
@SigmaX確かに、非常に禅ですね。ヒント:3つの独立したフラグです。
ケビン

2
files=(*)
for (( i=0; i<200; i++ )); do
    keys=("${!files[@]}")
    rnd=$(( RANDOM % ${#keys[@]} ))
    key=${keys[$rnd]}
    mv "${files[$key]}" "$otherdir"
    unset files[$key]
done

2

すべてのファイル名をbashの「files」という名前の配列に入れます。

files=( * )

配列のサイズ:

echo ${#files[@]}

それらの2/3をサンプルサイズとして定義します。

take=$((2*${#files[@]}/3)) 

for i in $(seq 1 $take)
do
    r=$((RANDOM%${#files[@]})) 
    echo ${files[r]}
done

これは、重複を選択します、とされていない空白やなとファイル名を使用してテスト。

重複を避けるための最も簡単な方法は、すべてのファイルを反復処理し、2/3のチャンスで各ファイルを選択することですが、これは必ずしも200ファイルになるとは限りません。

これは、リストから選択されたファイルを削除し、要件を満たします。

#!/bin/bash
files=( * )
# define 2/3 of them as sample size:
take=$((2*${#files[@]}/3)) 

while (( i < $take ))
do
    r=$((RANDOM%${#files[@]})) 
    f=${files[r]}
    if [[ -n $f ]]
    then 
        i=$((i+1))    
        echo ${files[r]}
        unset files[r]    
    fi
done

同じファイルを複数回選択する場合があります。
グレンジャックマン

とてもいいシェルスクリプト。200個のファイルを取得していないあなたの問題を回避するために、あなたはおそらく、貯水池サンプリングを使用したい:en.wikipedia.org/wiki/Reservoir_sampling このシェルスクリプトの例を含め、私が弱いことするつもりはありません。
ブルースエディガー

@glennjackman:そう書きました、はい。アレイからエントリを削除する方法を理解するのに数分かかりました。
ユーザー不明

軽微な注意:$RANDOM値は0〜32767 のみであるため、32768を超えるファイルがある場合、これは正しく機能しません。また、フェッチは最初のファイルに偏っています。
l0b0

@ l0b0:300から200を選択するための要件。ファイルが現在のディレクトリではなくファイルサーバー上にある場合は、機能しません。異なる要件、異なる答え。
ユーザー不明

2

これを統計的にランダムにする必要がある場合は、使用しないでくださいRANDOM % ${#keys[@]}。考慮してください:

  1. $RANDOM 32768の一意の値があります
  2. 最初の選択は、300個の要素のうち1個です
  3. 32768 = 109 * 300 + 68

したがって、最初のアイテムを選択すると、68個の最初の要素のそれぞれについて110/32768〜= 0.33569%の確率があり、他の232個の要素のそれぞれについて109/32768〜= 0.33264%の確率が選択されます。ピッキングはさまざまな機会で何度か繰り返され32768 % ${#keys[@]} -ne 0ますが、の場合は常に最初の要素にバイアスがかけられるため、エラーが悪化します。

これは公平である必要があり、任意のファイル名で動作します:

while IFS= read -r -d '' -u 9
do
    mv -- "$REPLY" /target/dir
done 9< <(find /source/dir -mindepth 1 -print0 | shuf -n 200 -z)

2

Kevinのソリューションは素晴らしい作品です!私が頭の外から思い出すのが簡単だと思うので、私がたくさん使ったものは次のようなものです:

cp `ls | shuf -n 200` destination

0

bashの1つのライナー:

ls original_directory/|sort -R|head -number_of_files_to_move|while read file; do cp "new_directory/"$file test; done

詳しく説明してください。U&Lは知識ベースです。
カウンター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.