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


23

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


R也许可以list.files()
眨眼间

4
我会隐约地插在一起shufhead(或者只是使用shuf -n,应该已经阅读了手册页...)
Ulrich Schwarz 2012年

Answers:


32

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

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

如果您没有,shuf但是有一个sortthat -R,这应该可以工作:

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

7
嗯,是的,因为除了改版工具之外,还有哪些地方需要改组。(至少shuf没有被调用tros是因为它与排序相反。)
Ulrich Schwarz 2012年

2
没有排序相反的东西(就好像没有“天气”一样)。随机仍然排序,只是随机排序。
Plutor

1
什么是“ -zen200”?关于shuf的任何文档都没有,或者在Internet上的任何地方都没有,但是如果没有它,您的示例将无法工作。相当神秘。
SigmaX 2015年

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

您可能会多次选择同一文件。
格伦·杰克曼

非常好的shell脚本。要解决无法获取200个文件的问题,您可能需要使用Reservoir采样:en.wikipedia.org/wiki/Reservoir_sampling 我将变得很虚弱,不包括此类的shell脚本示例。
布鲁斯·埃迪格

@glennjackman:我是这样写的,是的。需要几分钟的时间才能弄清楚如何从阵列中删除条目。
用户未知

次要警告:$RANDOM只能具有0到32767的值,因此,如果您拥有32768个以上的文件,则此操作将无法正常工作。同样,获取偏向第一个文件。
l0b0 2012年

@ 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

凯文的解决方案效果很好!我已经使用了很多其他东西,因为它发现更容易记住我的头顶是这样的:

cp `ls | shuf -n 200` destination

0

重击一班

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

请详细说明; U&L是一个知识库。
countermode
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.