किसी डेटा फ़ाइल से रैंड की निश्चित संख्या खींचना


13

मेरे पास एक डेटा सूची है, जैसे

12345
23456
67891
-20000
200
600
20
...

इस डेटा सेट का आकार मान लें (यानी फ़ाइल की लाइनें) N। मैं mइस डेटा फ़ाइल से बेतरतीब ढंग से लाइनें खींचना चाहता हूं । इसलिए, आउटपुट दो फाइलें होनी चाहिए, एक mडेटा की इन पंक्तियों सहित फ़ाइल है , और दूसरे N-mमें डेटा की लाइनें शामिल हैं।

क्या लिनक्स कमांड का उपयोग करने का कोई तरीका है?


1
क्या आप लाइनों के अनुक्रम के बारे में चिंतित हैं? जैसे। क्या आप स्रोत के क्रम को बनाए रखना चाहते हैं, या क्या आप चाहते हैं कि अनुक्रम खुद यादृच्छिक होने के साथ-साथ लाइनों की पसंद भी यादृच्छिक हो?
पीटर।

जवाबों:


18

यह सबसे कारगर तरीका नहीं हो सकता है लेकिन यह काम करता है:

shuf <file> > tmp
head -n $m tmp > out1
tail -n +$(( m + 1 )) tmp > out2

साथ $mलाइनों की संख्या से युक्त।


@userunknown, sort -Rयादृच्छिकता का ख्याल रखता है। निश्चित नहीं है कि आपने इसके लिए उत्तर को अस्वीकार कर दिया है, लेकिन इसे पहले मैनपेज में देखें।
रोब वूउटर्स

2
ध्यान दें कि sort -Rइसके इनपुट को यादृच्छिक रूप से क्रमबद्ध नहीं करता है: यह समान रेखाओं को समूहित करता है। तो अगर इनपुट है जैसे foo, foo, bar, barऔर मीटर = 2, फिर एक फ़ाइल दोनों शामिल होंगे fooऔर अन्य दोनों शामिल होंगे barरों। GNU Coreutils में भी है shuf, जो इनपुट लाइनों को यादृच्छिक बनाता है । इसके अलावा, आपको एक अस्थायी फ़ाइल की आवश्यकता नहीं है
गिल्स एसओ- बुराई को रोकना '

क्यों नहीं shuf <file> |head -n $m?
इमानुएल

@ नागरिक: क्योंकि हमें दो अलग-अलग फ़ाइलों में सिर और पूंछ दोनों की आवश्यकता होती है।
रॉब वाउटर्स

5

यह bash / awk स्क्रिप्ट यादृच्छिक रूप से लाइनें चुनता है, और दोनों आउटपुट फ़ाइलों में मूल अनुक्रम को बनाए रखता है।

awk -v m=4 -v N=$(wc -l <file) -v out1=/tmp/out1 -v out2=/tmp/out2 \
 'BEGIN{ srand()
         do{ lnb = 1 + int(rand()*N)
             if ( !(lnb in R) ) {
                 R[lnb] = 1
                 ct++ }
         } while (ct<m)
  } { if (R[NR]==1) print > out1 
      else          print > out2       
  }' file
cat /tmp/out1
echo ========
cat /tmp/out2

आउटपुट, प्रश्न में डेटा पर आधारित है।

12345
23456
200
600
========
67891
-20000
20

4

यूनिक्स की सभी चीजों के साथ, टीएम के लिए एक उपयोगिता है ।

दिन का कार्यक्रम: split
splitफ़ाइल को कई अलग-अलग तरीकों से विभाजित करेगा, -bबाइट्स, -lलाइनें, -nआउटपुट फ़ाइलों की संख्या। हम -lविकल्प का उपयोग करेंगे । चूँकि आप रैंडम लाइन्स चुनना चाहते हैं और सिर्फ पहले नहीं m, हम sortपहले रैंडमली फाइल करेंगे । यदि आप के बारे में पढ़ना चाहते हैं sort, तो यहां मेरे उत्तर का संदर्भ दें ।

अब, वास्तविक कोड। यह वास्तव में काफी सरल है:

sort -R input_file | split -l $m output_prefix

यह दो फाइलें बनाएगा, एक mलाइनों के साथ और एक N-mलाइनों के साथ , नाम output_prefixaaऔर output_prefixab। सुनिश्चित करें कि mवह बड़ी फ़ाइल है जिसे आप चाहते हैं या आपको लंबाई की कई फाइलें मिलेंगी m(और एक के साथ N % m)।

यदि आप यह सुनिश्चित करना चाहते हैं कि आप सही आकार का उपयोग करें, तो ऐसा करने के लिए थोड़ा कोड यहां है:

m=10 # size you want one file to be
N=$(wc -l input_file)
m=$(( m > N/2 ? m : N - m ))
sort -R input_file | split -l $m output_prefix

संपादित करें: यह मेरे ध्यान में आया है कि कुछ sortकार्यान्वयनों में -Rध्वज नहीं है । यदि आपके पास है perl, तो आप स्थानापन्न कर सकते हैं perl -e 'use List::Util qw/shuffle/; print shuffle <>;'


1
दुर्भाग्य से, sort -Rकेवल सॉर्ट के कुछ संस्करणों (शायद गन्न संस्करण) में प्रतीत होता है। अन्य प्लेटफार्मों के लिए मैंने 'रेंडलाइन' नामक एक उपकरण लिखा, जो स्टड को यादृच्छिक करने के अलावा कुछ नहीं करता है। यह beesbuzz.biz/code पर है, जिसे इसकी आवश्यकता है। (मैं फ़ाइल सामग्री को बहुत फेरबदल करता हूं।)
शराबी

1
ध्यान दें कि sort -Rइसके इनपुट को यादृच्छिक रूप से क्रमबद्ध नहीं करता है: यह समान रेखाओं को समूहित करता है। तो अगर इनपुट है जैसे foo, foo, bar, barऔर मीटर = 2, फिर एक फ़ाइल दोनों शामिल होंगे fooऔर अन्य दोनों शामिल होंगे barरों। GNU Coreutils में भी है shuf, जो इनपुट लाइनों को यादृच्छिक बनाता है । इसके अलावा, आप आउटपुट फ़ाइल नामों का उपयोग करके headऔर tailइसके बजाय चुन सकते हैंsplit
गिलेस एसओ- बुराई को रोकना '

4

यदि आपको लाइनों को रीक्रिएट करने में कोई आपत्ति नहीं है और आपके पास GNU कोरूटिल्स हैं (यानी नॉन-एम्बेडेड लिनक्स या साइगविन पर, shufसंस्करण 6.0 में दिखाई देने के बाद भी प्राचीन नहीं हैं ), shuf("फेरबदल") किसी फ़ाइल की लाइनों को अनियमित रूप से पुन: व्यवस्थित करता है। तो आप फ़ाइल को फेरबदल कर सकते हैं और पहली m लाइनों को एक फ़ाइल में भेज सकते हैं और बाकी को दूसरी फ़ाइल में भेज सकते हैं।

उस प्रेषण को करने का कोई आदर्श तरीका नहीं है। आप सिर्फ चेन नहीं कर सकते headऔर tailक्योंकि headआगे बफर होगा। आप उपयोग कर सकते हैं split, लेकिन आपको आउटपुट फ़ाइल नामों के संबंध में कोई लचीलापन नहीं मिलता है। आप निश्चित रूप से उपयोग कर सकते हैं awk:

<input shuf | awk -v m=$m '{ if (NR <= m) {print >"output1"} else {print} }'

आप उपयोग कर सकते हैं sed, जो अस्पष्ट है लेकिन संभवतः बड़ी फ़ाइलों के लिए तेज़ है।

<input shuf | sed -e "1,${m} w output1" -e "1,${m} d" >output2

या आप teeडेटा को डुप्लिकेट करने के लिए उपयोग कर सकते हैं , यदि आपका प्लेटफ़ॉर्म है /dev/fd; अगर m छोटा है तो ठीक है:

<input shuf | { tee /dev/fd/3 | head -n $m >output1; } 3>&1 | tail -n +$(($m+1)) >output2

बदले में, आप प्रत्येक पंक्ति को बदले में भेजने के लिए awk का उपयोग कर सकते हैं। ध्यान दें कि awk अपने यादृच्छिक संख्या जनरेटर को शुरू करने में बहुत अच्छा नहीं है; यादृच्छिकता न केवल क्रिप्टोग्राफी के लिए उपयुक्त है, बल्कि संख्यात्मक सिमुलेशन के लिए भी बहुत अच्छी नहीं है। बीज एक सेकंड की अवधि के साथ किसी भी सिस्टम पर सभी awk invocations के लिए समान होगा।

<input awk -v N=$(wc -l <input) -v m=3 '
    BEGIN {srand()}
    {
        if (rand() * N < m) {--m; print >"output1"} else {print >"output2"}
        --N;
    }'

यदि आपको बेहतर यादृच्छिकता की आवश्यकता है, तो आप पर्ल में वही काम कर सकते हैं, जो इसके आरएनजी को शालीनता से बीज देता है।

<input perl -e '
    open OUT1, ">", "output1" or die $!;
    open OUT2, ">", "output2" or die $!;
    my $N = `wc -l <input`;
    my $m = $ARGV[0];
    while (<STDIN>) {
        if (rand($N) < $m) { --$m; print OUT1 $_; } else { print OUT2 $_; }
        --$N;
    }
    close OUT1 or die $!;
    close OUT2 or die $!;
' 42

@ गिल्स: उदाहरण के awkलिए: -v N=$(wc -l <file) -v m=4... और यह केवल एक "यादृच्छिक" लाइन को प्रिंट करता है जब यादृच्छिक मान को $mप्रिंट करने के बजाय $mयादृच्छिक लाइनों को कम किया जाता है ... ऐसा लगता है कि यह रैंड केperl साथ एक ही काम कर सकता है , लेकिन मैं डॉन '' संकलित त्रुटि के अतीत को प्राप्त करने के लिए पर्याप्त रूप से नहीं जाना जाता है: -e लाइन 7 पर वाक्य रचना त्रुटि, ") प्रिंट"perl
पीटर.ओ.

@ पीटर। धन्यवाद, यही एक ब्राउज़र में टाइप करने और लापरवाही से संपादन करने से आता है। मैंने awk और perl कोड तय कर लिया है।
गिल्स एसओ- बुराई को रोकना '

सभी 3 तरीके अच्छी तरह से और तेजी से काम कर रहे हैं .. धन्यवाद (+1) ... मैं धीरे-धीरे पर्ल के आसपास अपना सिर पा रहा हूं ... और यह shufउदाहरण में एक विशेष रूप से दिलचस्प और उपयोगी फ़ाइल विभाजन है ।
पीटर.ओ.

एक बफ़रिंग समस्या? । क्या मैं कुछ भूल रहा हूँ? head catकॉम्बो में दूसरे टेस्ट निम्नलिखित डेटा की हानि का कारण बनता है 3-4 .... टेस्ट 1-2 { for i in {00001..10000} ;do echo $i; done; } | { head -n 5000 >out1; cat >out2; } .. टेस्ट 3-4 { for i in {00001..10000} ;do echo $i; done; } >input; cat input | { head -n 5000 >out3; cat >out4; } ... wc -lके आउटपुट के लिए परिणाम टेस्ट 1-2 कर रहे हैं 5000 5000 (अच्छा), लेकिन के लिए टेस्ट 3-4 कर रहे हैं 5000 4539 (नहीं अच्छा) .. differnece फ़ाइल शामिल आकार पर निर्भर करता है ... यहाँ मेरी के लिए एक लिंक है परीक्षण कोड
Peter.O

@ पीटर। ठीक है फिर, धन्यवाद। दरअसल, headआगे पढ़ता है; यह आगे पढ़ता है और प्रिंट नहीं निकलता है। मैंने अपना उत्तर कम सुरुचिपूर्ण के साथ अपडेट किया है (लेकिन मुझे यकीन है कि) सही समाधान हैं।
गिल्स एसओ- बुराई को रोकना '

2

मान लिया m = 7और N = 21:

cp ints ints.bak
for i in {1..7}
do
    rnd=$((RANDOM%(21-i)+1))
    # echo $rnd;  
    sed -n "${rnd}{p,q}" 10k.dat >> mlines 
    sed -i "${rnd}d" ints 
done

नोट: यदि आप बदलते हैं 7की तरह एक चर के साथ $1या $m, आप उपयोग करने के लिए है seq, न {from..to}-notation, जो चर विस्तार नहीं करता है।

यह फ़ाइल से लाइन को हटाकर काम करता है, जो छोटा और छोटा हो जाता है, इसलिए लाइन नंबर, जिसे हटाया जा सकता है, को छोटा और छोटा होना पड़ता है।

यह लंबे समय तक फ़ाइलें, और कई लाइनों के लिए इस्तेमाल नहीं किया जाना चाहिए के बाद से हर नंबर के लिए, औसत पर, आधा फ़ाइल की जरूरत है 1 के लिए पढ़ने के लिए, और 2 के लिए पूरी फ़ाइल sed कोड।


उसे उन पंक्तियों के साथ एक फ़ाइल की आवश्यकता है जो हटा दी जाती हैं।
रोब वाउचर

मुझे लगा कि "डेटा की इन m लाइनों सहित" का मतलब होना चाहिए including themलेकिन मूल लाइनों के साथ - इसलिए including, नहीं consisting of, और उपयोग नहीं करना only, लेकिन मुझे लगता है कि आपकी व्याख्या का मतलब है, user288609 का क्या मतलब है। मैं अपनी स्क्रिप्ट को उसी हिसाब से समायोजित करूंगा।
यूजर अनजान

अछा लगता है। `` ``
रॉब वाउटर्स

@user अज्ञात: आपके पास +1गलत जगह है। यह rnd=$((RANDOM%(N-i)+1))आपके उदाहरण में जहां N = 21 होना चाहिए .. इसका मूल्यांकन sedहोने पर वर्तमान में क्रैश हो rndजाता है 0। .. इसके अलावा, यह उस फ़ाइल को फिर से लिखने के साथ बहुत अच्छी तरह से स्केल नहीं करता है। जैसे 123 सेकंड 10,000 लाइन फ़ाइल से 5,000 यादृच्छिक लाइनों को निकालने के लिए बनाम 0.03 सेकंड में एक और अधिक प्रत्यक्ष विधि के लिए ...
Peter.O

@ पीटर.ओ: आप सही (सही) हैं और आप सही हैं।
उपयोगकर्ता अज्ञात
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.