Melhor método para coletar uma amostra aleatória de uma coleção de arquivos

23

Suponha que haja um diretório com 300 arquivos de dados. Eu quero selecionar aleatoriamente 200 desses arquivos e movê-los para outro diretório. Existe uma maneira de fazer isso no Unix / Linux?

    
por bit-question 10.05.2012 / 16:01

6 respostas

32

Se o seu sistema tiver shuf , você poderá usá-lo de maneira bastante conveniente (mesmo lidando com nomes de arquivos desagradáveis):

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

Se você não tem shuf , mas tem um sort que leva -R , isso deve funcionar:

find source -type f -print0 | sort -Rz | cut -d $'
shuf -zen200 source/* | xargs -0 mv -t dest
' -f-200 | xargs -0 mv -t dest
    
por 10.05.2012 / 17:39
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
    
por 10.05.2012 / 16:31
2

Coloque todos os nomes de arquivos em um array chamado "arquivos" no bash:

files=( * )

tamanho da matriz:

echo ${#files[@]}

defina 2/3 deles como tamanho da amostra:

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

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

Isto irá selecionar duplicatas, e é não testado com nomes de arquivos com espaços em branco e tal.

A maneira mais simples de evitar duplicatas é fazer uma iteração em todos os arquivos e escolher cada um com 2/3 de chance, mas isso não levará necessariamente a 200 arquivos.

Isso removerá um arquivo se ele foi escolhido na lista e atenderá aos seus requisitos:

#!/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
    
por 10.05.2012 / 16:36
2

Se isso precisa ser estatisticamente aleatório, você não deve usar RANDOM % ${#keys[@]} . Considere:

  1. $RANDOM tem 32768 valores exclusivos
  2. A primeira seleção é de 1 de 300 elementos
  3. 32768 = 109 * 300 + 68

Assim, ao selecionar o primeiro item, há uma chance de 110/32768 ~ = 0,33569% para cada um dos 68 primeiros elementos, e 109/32768 ~ = 0,33264% de chance para cada um dos outros 232 elementos a serem selecionados. Picking é repetido várias vezes com diferentes chances, mas inclinado para os primeiros elementos sempre que 32768 % ${#keys[@]} -ne 0 , então o erro aumenta.

Isso deve ser imparcial e funciona com qualquer nome de arquivo:

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

A solução de Kevin funciona muito bem! Outra coisa que eu usei muito, porque acho mais fácil lembrar do topo da minha cabeça é algo como:

cp 'ls | shuf -n 200' destination

Espero que isso ajude!

    
por 22.11.2017 / 20:33
0

Um forro no bash:

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