วิธีที่ดีที่สุดในการรวบรวมตัวอย่างแบบสุ่มจากกลุ่มของไฟล์


23

สมมติว่ามีไดเรกทอรีเก็บไฟล์ข้อมูล 300 ไฟล์ ฉันต้องการสุ่มเลือกไฟล์ 200 ไฟล์และย้ายไปไว้ในไดเรกทอรีอื่น มีวิธีการทำภายใต้ Unix / Linux?


Rอาจทำสิ่งนี้ได้ในพริบตาด้วยlist.files()...
sr_

4
ฉันรางเข้าด้วยกันอย่างคลุมเครือshufและhead(หรือใช้เพียงshuf -nควรอ่านหน้าคน ... )
Ulrich Schwarz

คำตอบ:


32

หากระบบของ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

7
อาใช่เพราะคนอื่นจะมองหาสับมากกว่าในเครื่องมือสำหรับการเรียงลำดับ (อย่างน้อยshufก็ไม่ได้ถูกเรียกtrosเพราะมันตรงกันข้ามกับการเรียงลำดับ)
Ulrich Schwarz

2
ไม่มีสิ่งใดตรงกันข้ามกับการเรียงลำดับ (ในแง่เดียวกับที่ไม่มีสิ่งเช่น "ไม่มีสภาพอากาศ") Random ยังคงเรียงมันเป็นเพียงการจัดเรียงแบบสุ่ม
พลูโต

1
"-zen200" คืออะไร? ไม่ได้อยู่ในเอกสารใด ๆ สำหรับ shuf หรือที่ใดก็ได้บนอินเทอร์เน็ต แต่ตัวอย่างของคุณใช้ไม่ได้หากไม่มีมัน ค่อนข้างลึกลับ
SigmaX

2
@ SigmaX แน่นอนเซนก็ใช่แล้ว คำแนะนำ: เป็น 3 ธงแยกกัน
เควิน


2

ใส่ชื่อไฟล์ทั้งหมดลงในอาร์เรย์ชื่อ "files" ใน bash:

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

คุณอาจเลือกไฟล์เดียวกันมากกว่าหนึ่งครั้ง
เกล็นแจ็

เชลล์สคริปต์ที่ดีมาก เพื่อหลีกเลี่ยงปัญหาของคุณที่จะไม่ได้รับ 200 ไฟล์คุณอาจต้องการใช้การสุ่มตัวอย่างอ่างเก็บน้ำ: en.wikipedia.org/wiki/Reservoir_sampling ฉันจะอ่อนแอและไม่รวมตัวอย่างสคริปต์เชลล์ของสิ่งนี้
Bruce Ediger

@glennjackman: ฉันเขียนอย่างนั้นใช่ ต้องการเวลาสักครู่ในการคิดออกวิธีการลบรายการออกจากอาร์เรย์
ผู้ใช้ไม่ทราบ

Minor caveat: $RANDOMสามารถมีค่า 0 ถึง 32767 เท่านั้นดังนั้นสิ่งนี้จะทำงานไม่ถูกต้องหากคุณมีไฟล์มากกว่า 32768 ไฟล์ นอกจากนี้การดึงข้อมูลจะมีอคติต่อไฟล์แรก
l0b0

@ l0b0: ข้อกำหนดที่จะเลือก 200 จาก 300 หากไฟล์ไม่ได้อยู่ในไดเรกทอรีปัจจุบัน แต่บนเซิร์ฟเวอร์ไฟล์มันจะไม่ทำงานเช่นกัน ความต้องการที่แตกต่างกันคำตอบที่แตกต่างกัน
ผู้ใช้ไม่รู้จัก

2

RANDOM % ${#keys[@]}ในกรณีนี้จะต้องมีการสุ่มทางสถิติที่คุณไม่ควรใช้ พิจารณา:

  1. $RANDOM มีค่าที่ไม่ซ้ำ 32768
  2. ตัวเลือกแรกคือ 1 จาก 300 องค์ประกอบ
  3. 32768 = 109 * 300 + 68

ดังนั้นเมื่อเลือกรายการแรกจะมีโอกาส 110/32768 ~ = 0.33569% สำหรับแต่ละองค์ประกอบแรก 68 รายการและ 109/32768 ~ = โอกาส 0.33264% สำหรับแต่ละองค์ประกอบ 232 รายการอื่นที่จะเลือก การเลือกซ้ำหลายครั้งด้วยโอกาสที่แตกต่างกัน แต่เอนเอียงไปที่องค์ประกอบแรกเมื่อใดก็ตาม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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.