फ़ाइलों के संग्रह से एक यादृच्छिक नमूना लेने के लिए सबसे अच्छी विधि


23

मान लीजिए कि 300 डेटा फ़ाइलों को रखने वाली एक निर्देशिका है। मैं बेतरतीब ढंग से उन फ़ाइलों में से 200 का चयन करना चाहता हूं और उन्हें किसी अन्य निर्देशिका में स्थानांतरित करना चाहता हूं। क्या यूनिक्स / लिनक्स के तहत ऐसा करने का कोई तरीका है?


आर शायद एक आंख की जगमगाहट में ऐसा कर सकता है list.files()...
sr_

4
मैं थोड़ा एक साथ प्लग चाहते हैं shufऔर head(या बस उपयोग shuf -n, आदमी पेज को पढ़े चाहिए ...)
उलरिच श्वार्ज

जवाबों:


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क्योंकि यह छंटाई के विपरीत करता है।)
उलरिच श्वार्ज

2
छँटाई के विपरीत जैसी कोई चीज नहीं है (उसी अर्थ में जैसे "कोई मौसम नहीं" जैसी कोई चीज नहीं है)। यादृच्छिक अभी भी हल है, यह सिर्फ यादृच्छिक रूप से हल किया गया है।
प्लूटोर

1
"-ZZ200" क्या है? यह shuf के लिए किसी भी प्रलेखन में या इंटरनेट पर कहीं भी नहीं है, लेकिन आपका उदाहरण इसके बिना काम नहीं करता है। काफी रहस्यमय।
सिग्मा

2
@SigmaX वास्तव में, काफी ज़ेन है, है ना। संकेत: यह 3 अलग झंडे हैं।
केविन


2

सभी फ़ाइलनामों को बैश में "फाइल" नामक एक सरणी में रखें:

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 फाइलें न मिलने की अपनी समस्या के आसपास जाने के लिए, आप शायद Reservoir Sampling का उपयोग करना चाहते हैं: en.wikipedia.org/wiki/Reservoir_sampling मैं कमजोर होने जा रहा हूं और इसमें शेल स्क्रिप्ट का उदाहरण शामिल नहीं है।
ब्रूस एडिगर

@glennjackman: मैंने ऐसा लिखा, हाँ। यह पता लगाने के लिए कुछ मिनटों की आवश्यकता है कि सरणी से प्रविष्टियां कैसे निकालें।
उपयोगकर्ता अज्ञात

माइनर कैविएट: $RANDOMकेवल मान 0 हो सकता है 32767 के माध्यम से, इसलिए यह ठीक से काम नहीं करेगा यदि आपके पास 32768 से अधिक फाइलें हैं। इसके अलावा, पहली फ़ाइलों के लिए पक्षपाती पक्षपाती है।
l0b0

@ 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.