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

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

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

答え1

システムにこの機能があると、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

答え2

統計的ランダム性が必要な場合は使用しないでくださいRANDOM % ${#keys[@]}

  1. $RANDOM32768個の固有値があります。
  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)

答え3

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

答え4

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

files=( * )

配列サイズ:

echo ${#files[@]}

そのうちの3分の2をサンプルサイズとして定義します。

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

関連情報