从文件集合中收集随机样本的最佳方法

从文件集合中收集随机样本的最佳方法

假设有一个目录包含300个数据文件。我想随机选择其中 200 个文件并将它们移动到另一个目录中。有没有办法在 Unix/Linux 下做到这一点?

答案1

如果您的系统有shuf,您可以非常方便地使用它(甚至可以处理难看的文件名):

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

如果你没有,shuf但有一个sortthat take -R,这应该有效:

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

答案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)

答案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[@]}

定义其中的 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

相关内容