Bir dosya koleksiyonundan rastgele örnek toplamak için en iyi yöntem


23

300 veri dosyasını tutan bir dizin olduğunu varsayalım. Bu dosyaların 200'ünü rastgele seçmek ve başka bir dizine taşımak istiyorum. Bunu Unix / Linux altında yapmanın bir yolu var mı?


R muhtemelen bunu göz list.files()
açıp kapayıncaya kadar yapabilir

4
Belli bir şekilde takardım shufve head(ya da sadece kullanmak shuf -n, adam sayfasını okumalıydım ...)
Ulrich Schwarz

Yanıtlar:


32

Sisteminizde varsa shuf, bunu oldukça rahatça kullanabilirsiniz (çirkin dosya isimlerini bile kullanabilirsiniz):

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

Eğer sahip değilseniz shufama sortbunlardan birine sahipseniz -R, bunun çalışması gerekir:

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

7
Ah evet, çünkü başka biri sıralama için bir araçtan daha fazla karıştırmaya bakardı. (En azından shufçağrılmadığı trosiçin sıralama yapılmıyor çünkü çağrılmadı .)
Ulrich Schwarz

2
Sıralamanın zıttı gibi bir şey yoktur ("hava yok" diye bir şey yoktur). Rasgele hala sıralanır, sadece rasgele sıralanır.
Plutor

1
"-Zen200" nedir? Bu, hiçbir belgeye veya internetteki herhangi bir belgeye dahil değildir, ancak örneğiniz onsuz çalışmaz. Oldukça mistik.
SigmaX

2
@SigmaX Gerçekten, oldukça zen, öyle değil mi? İpucu: 3 ayrı bayrak.
Kevin,

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

Tüm dosya adlarını bash'de "files" adında bir diziye yerleştirin:

files=( * )

dizinin boyutu:

echo ${#files[@]}

Bunların 2 / 3'ünü örneklem büyüklüğü olarak tanımlayın:

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

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

Bu çiftleri seçecek ve edilir değil parçalarını ve bu ile dosya adları ile test.

Yinelemeleri engellemenin en basit yolu, tüm dosyaları yinelemek ve her birini 2/3 şansla seçmek, ancak bu mutlaka 200 dosyaya yol açmayacak.

Bu, listeden seçilmişse bir dosyayı kaldıracak ve gereksinimlerinizi karşılayacaktır:

#!/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

Aynı dosyayı bir defadan fazla seçebilirsiniz.
glenn jackman,

Çok güzel bir kabuk betiği. 200 dosya alamama probleminizi çözmek için, muhtemelen Reservoir Sampling kullanmak istersiniz: en.wikipedia.org/wiki/Reservoir_sampling Zayıf olacağım ve bunun bir kabuk betiği örneğini içermeyeceğim.
Bruce Ediger

@glennjackman: Ben yazdım, evet. Dizideki girişlerin nasıl kaldırılacağını anlamak için birkaç dakika gerekir.
kullanıcı bilinmeyen,

Küçük uyarı: $RANDOMyalnızca 0 - 32767 arasında bir değere sahip olabilir, bu nedenle 32768'den fazla dosyanız varsa, bu düzgün çalışmayacaktır. Ayrıca, alma ilk dosyalara doğru önyargılıdır.
l0b0

@ l0b0: Gereksinimler, 300'den 200'ü seçmeli. Dosyalar geçerli dizinde değilse de, bir dosya sunucusundaysa, çalışmazlar. Farklı gereksinimler, farklı cevaplar.
kullanıcı bilinmeyen,

2

Bunun istatistiksel olarak rasgele olması gerekiyorsa, kullanmamalısınız RANDOM % ${#keys[@]}. Düşünmek:

  1. $RANDOM 32768 benzersiz değere sahiptir
  2. İlk seçim, 300 öğeden 1'i
  3. 32768 = 109 * 300 + 68

Bu nedenle, ilk öğeyi seçerken, ilk 68 öğenin her biri için% 110/32768 ~ =% 0.33569 şans ve diğer 232 öğenin her biri için% 109/32768 ~ =% 0.33264 şans vardır. Toplama, farklı şanslarla birkaç kez tekrarlanır, ancak ne zaman olursa olsun, ilk elementlere doğru önyargılı olur 32768 % ${#keys[@]} -ne 0, bu nedenle hata birleşir.

Bu tarafsız olmalı ve herhangi bir dosya adı ile çalışır:

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'in çözümü harika çalışıyor! Çok kullandığım başka bir şey çünkü kafamın üstünden hatırlamayı daha kolay buluyorum:

cp `ls | shuf -n 200` destination

0

Bash bir astar:

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

Lütfen detaylandırın; U&L bir bilgi tabanıdır.
countermode
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.