बेतरतीब ढंग से एक फ़ाइल का सबसेट नमूना कैसे करें


38

क्या कोई लिनक्स कमांड है जो किसी फ़ाइल के सबसेट का नमूना ले सकता है? उदाहरण के लिए, किसी फ़ाइल में एक मिलियन रेखाएँ होती हैं, और हम उस फ़ाइल से केवल एक हज़ार लाइनों को यादृच्छिक रूप से नमूना करना चाहते हैं।

यादृच्छिक के लिए मेरा मतलब है कि हर पंक्ति को चुने जाने की समान संभावना मिलती है और चुनी गई लाइनों में से कोई भी दोहराई नहीं जाती है।

headऔर tailफ़ाइल का एक सबसेट चुन सकते हैं लेकिन बेतरतीब ढंग से नहीं। मुझे पता है कि मैं हमेशा ऐसा करने के लिए एक अजगर स्क्रिप्ट लिख सकता हूं, लेकिन बस सोच रहा था कि इस उपयोग के लिए एक कमांड है।


यादृच्छिक क्रम में लाइनें, या उस फ़ाइल की 1000 लगातार लाइनों का एक यादृच्छिक ब्लॉक?
१०:

हर लाइन को चुने जाने की समान संभावना मिलती है। लगातार होने की आवश्यकता नहीं है, हालांकि एक छोटी संभावना है कि लाइनों के लगातार ब्लॉक को एक साथ चुना जाए। मैंने अपना प्रश्न उस बारे में स्पष्ट करने के लिए अद्यतन किया है। धन्यवाद।
१en:०६

मेरी github.com/barrycarter/bcapps/tree/master/bc-fastrand.pl यह लगभग फ़ाइल में एक यादृच्छिक स्थान प्राप्त करने और निकटतम newlines खोजने के द्वारा करता है।
बैरीकेटर

जवाबों:


65

shufआदेश (coreutils का हिस्सा) कर सकते हैं:

shuf -n 1000 file

और कम से कम अब गैर-प्राचीन संस्करणों के लिए ( 2013 से कमिट में जोड़ा गया है ), जो उचित होने पर जलाशय के नमूने का उपयोग करेगा, जिसका अर्थ है कि यह मेमोरी से बाहर नहीं चलना चाहिए और एक तेज एल्गोरिथ्म का उपयोग कर रहा है।


प्रलेखन के अनुसार, इसे इनपुट के रूप में एक सॉर्ट की गई फ़ाइल चाहिए: gnu.org/software/coreutils/manual/…
mcc

@ केतन, ऐसा नहीं लगता
ठंढकूट

2
@ किटन यह सिर्फ मैनुअल के गलत सेक्शन में है, मेरा मानना ​​है। ध्यान दें कि मैनुअल में भी उदाहरणों को क्रमबद्ध नहीं किया गया है। ध्यान दें कि sortएक ही खंड में है, और यह स्पष्ट रूप से हल किए गए इनपुट की आवश्यकता नहीं है।
व्युत्पन्न

2
shufसंस्करण में coreutils के लिए पेश किया गया था 6.0 (2006-08-15), और यह विश्वास है या नहीं, कुछ यथोचित-सामान्य प्रणालियों (विशेष रूप से CentOS 6.5) में वह संस्करण नहीं है: - |
19

2
@petrelharp shuf -nजलाशय का नमूना लेता है, कम से कम जब इनपुट 8K से अधिक होता है, तो उनके द्वारा निर्धारित आकार बेहतर होता है। स्रोत कोड देखें (जैसे, github.com/coreutils/coreutils/blob/master/src/shuf.c#L46 पर )। इस बहुत देर से जवाब के लिए क्षमा करें। जाहिरा तौर पर यह 6 साल पहले की तरह नया है।
derobert

16

यदि आपके पास बहुत बड़ी फ़ाइल है (जो नमूना लेने का एक सामान्य कारण है) तो आप पाएंगे कि:

  1. shuf स्मृति समाप्त
  2. का उपयोग $RANDOMसही ढंग से काम नहीं करेगा अगर फाइल 32767 लाइनों से अधिक है

यदि आपको "बिल्कुल" एन सैंपल लाइनों की आवश्यकता नहीं है, तो आप इस तरह से एक अनुपात का नमूना ले सकते हैं :

cat input.txt | awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01) print $0}' > sample.txt

यह निरंतर मेमोरी का उपयोग करता है , फ़ाइल का 1% नमूने (यदि आप फ़ाइल की पंक्तियों की संख्या जानते हैं तो आप इस कारक को एक सीमित संख्या में लाइनों को बंद करने के लिए समायोजित कर सकते हैं), और फ़ाइल के किसी भी आकार के साथ काम करता है लेकिन ऐसा नहीं होगा लाइनों की एक सटीक संख्या लौटाएं , बस एक सांख्यिकीय अनुपात।

नोट: कोड से आता है: https://stackoverflow.com/questions/692312/randomly-pick-lines-from-a-file-without-slurping-it-with-unix


यदि कोई उपयोगकर्ता लगभग 1% गैर-रिक्त लाइनों चाहता है, तो यह एक बहुत अच्छा जवाब है। लेकिन अगर उपयोगकर्ता सटीक संख्या में लाइनें (जैसे, 1000000-लाइन फ़ाइल में से 1000) चाहता है, तो यह विफल हो जाता है। जैसा कि उत्तर आपको यह कहा गया है, यह केवल एक सांख्यिकीय अनुमान देता है। और क्या आप इस उत्तर को अच्छी तरह से समझते हैं कि यह रिक्त लाइनों को अनदेखा कर रहा है? यह एक अच्छा विचार हो सकता है, व्यवहार में, लेकिन अनिर्दिष्ट विशेषताएं हैं, सामान्य तौर पर, एक अच्छा विचार नहीं है।
जी-मैन ने

1
32767 लाइनों से बड़ी फ़ाइलों के लिए PS   सरलीकृत दृष्टिकोण $RANDOMसही ढंग से काम नहीं करेगा। " $RANDOMपूरी फ़ाइल तक पहुँच नहीं है " का कथन थोड़ा व्यापक है।
जी-मैन ने

@ जी-मैन सवाल एक उदाहरण के रूप में एक मिलियन से 10k लाइनें प्राप्त करने के बारे में बात करता है। मेरे आसपास के किसी भी जवाब ने मेरे लिए काम नहीं किया (फाइलों और हार्डवेयर सीमाओं के आकार के कारण) और मैंने इसे एक उचित समझौता के रूप में प्रस्तावित किया। यह आपको एक मिलियन में से 10k लाइन्स नहीं देगा लेकिन यह अधिकांश व्यावहारिक उद्देश्यों के लिए पर्याप्त हो सकता है। मैंने आपकी सलाह के बाद इसे थोड़ा और स्पष्ट कर दिया है। धन्यवाद।
Txangel

यह सबसे अच्छा जवाब है, मूल फ़ाइल के कालानुक्रमिक क्रम का सम्मान करते हुए, लाइनों को यादृच्छिक रूप से उठाया जाता है, यदि यह एक आवश्यकता है। इसके अलावा awkअधिक संसाधन के अनुकूल हैshuf
पॉलीमरेज़

यदि आपको एक सटीक संख्या की आवश्यकता है, तो आप हमेशा कर सकते हैं ... इसे अपनी आवश्यकता से अधिक% के साथ चलाएं। परिणाम गिनें। काउंट मिलान अंतर अंतर वाली लाइनें निकालें।
ब्रूनो ब्रोंस्की

6

@ Txangel के संभावित समाधान के समान लेकिन तेजी से 100x तक पहुंचना।

perl -ne 'print if (rand() < .01)' huge_file.csv > sample.csv

यदि आपको उच्च प्रदर्शन, एक सटीक नमूना आकार की आवश्यकता है, और फ़ाइल के अंत में एक नमूना अंतर के साथ रहने के लिए खुश हैं, तो आप निम्न के जैसा कुछ कर सकते हैं (1m लाइन फ़ाइल से नमूने 1000 लाइनें):

perl -ne 'print if (rand() < .0012)' huge_file.csv | head -1000 > sample.csv

.. या वास्तव में के बजाय एक दूसरे नमूना विधि श्रृंखला head


5

यदि shuf -nबड़ी फ़ाइलों की चाल मेमोरी से बाहर हो जाती है और आपको अभी भी एक निश्चित आकार के नमूने की आवश्यकता है और एक बाहरी उपयोगिता स्थापित की जा सकती है तो नमूना आज़माएँ :

$ sample -N 1000 < FILE_WITH_MILLIONS_OF_LINES 

चेतावनी यह है कि नमूना (उदाहरण में 1000 लाइनें) स्मृति में फिट होना चाहिए।

अस्वीकरण: मैं अनुशंसित सॉफ्टवेयर का लेखक हूं।


1
उन लोगों के लिए जो इसे स्थापित करते हैं और उनके रास्ते में /usr/local/binपहले आते हैं /usr/bin/, सावधान रहें कि macOS एक अंतर्निहित कॉल-स्टैक नमूने के साथ आता है sample, जो कुछ पूरी तरह से अलग करता है, जिसमें /usr/bin/
डेनिस डे बर्नार्डी

2

किसी भी एक आदेश के बारे में पता नहीं है जो आप पूछ सकते हैं लेकिन यहाँ एक लूप है जो मैंने एक साथ रखा है जो काम कर सकता है:

for i in `seq 1000`; do sed -n `echo $RANDOM % 1000000 | bc`p alargefile.txt; done > sample.txt

sed1000 पासों में से प्रत्येक पर एक यादृच्छिक लाइन उठाएगा। संभवतः अधिक कुशल समाधान हैं।


क्या इस दृष्टिकोण में एक ही पंक्ति को कई बार प्राप्त करना संभव है?
clwen

1
हां, एक ही लाइन नंबर को एक से अधिक बार प्राप्त करना काफी संभव है। इसके अतिरिक्त, $RANDOM0 और 32767 के बीच एक सीमा होती है। इसलिए, आपको एक अच्छी तरह से फैला हुआ लाइन नंबर नहीं मिलेगा।
mcc

काम नहीं करता है - यादृच्छिक को एक बार कहा जाता है
Bohdan

2

आप एक फ़ाइल में अनुसरण कोड को सहेज सकते हैं (उदाहरण के लिए randextract.sh) और इस तरह निष्पादित करें:

randextract.sh file.txt

---- BEGIN फ़ाइल ----

#!/bin/sh -xv

#configuration MAX_LINES is the number of lines to extract
MAX_LINES=10

#number of lines in the file (is a limit)
NUM_LINES=`wc -l $1 | cut -d' ' -f1`

#generate a random number
#in bash the variable $RANDOM returns diferent values on each call
if [ "$RANDOM." != "$RANDOM." ]
then
    #bigger number (0 to 3276732767)
    RAND=$RANDOM$RANDOM
else
    RAND=`date +'%s'`
fi 

#The start line
START_LINE=`expr $RAND % '(' $NUM_LINES - $MAX_LINES ')'`

tail -n +$START_LINE $1 | head -n $MAX_LINES

---- अंत फ़ाइल ----


3
मुझे यकीन नहीं है कि आप यहां रैंड के साथ क्या करने की कोशिश कर रहे हैं, लेकिन $RANDOM$RANDOM"0 से 3276732767" पूरी रेंज में यादृच्छिक संख्या उत्पन्न नहीं करता है (उदाहरण के लिए, यह 1000100000 उत्पन्न करेगा, लेकिन 1000099999 नहीं)।
गिल्स एसओ- बुराई को रोकना '

ओपी कहते हैं, “हर पंक्ति को चुने जाने की समान संभावना मिलती है। ... एक छोटी सी संभावना है कि लाइनों के एक लगातार ब्लॉक को एक साथ चुना जाए। "मुझे यह उत्तर भी गुप्त लग रहा है, लेकिन ऐसा लगता है कि यह एक यादृच्छिक प्रारंभिक बिंदु से लगातार लाइनों के 10-लाइन ब्लॉक को निकाल रहा है। ओपी से जो मांगा गया है, वह नहीं है।
जी-मैन ने

2

यदि आपको फ़ाइल में लाइनों की संख्या पता है (जैसे आपके मामले में 1e6), तो आप कर सकते हैं:

awk -v n=1e6 -v p=1000 '
  BEGIN {srand()}
  rand() * n-- < p {p--; print}' < file

यदि नहीं, तो आप हमेशा कर सकते हैं

awk -v n="$(wc -l < file)" -v p=1000 '
  BEGIN {srand()}
  rand() * n-- < p {p--; print}' < file

यह फ़ाइल में दो पास करेगा, लेकिन फिर भी पूरी फ़ाइल को मेमोरी में स्टोर करने से बचें।

जीएनयू पर एक और लाभ shufयह है कि यह फ़ाइल में लाइनों के क्रम को संरक्षित करता है।

ध्यान दें कि यह मानता n है कि फ़ाइल में लाइनों की संख्या है। आप प्रिंट करना चाहते हैं pमें से पहले n फ़ाइल की तर्ज (जो संभवतः अधिक लाइनों है), तो आप को रोकने के लिए आवश्यकता होगी awkपर nवीं की तरह लाइन:

awk -v n=1e6 -v p=1000 '
  BEGIN {srand()}
  rand() * n-- < p {p--; print}
  !n {exit}' < file

2

मुझे इसके लिए awk का उपयोग करना पसंद है जब मैं हेडर पंक्ति को संरक्षित करना चाहता हूं, और जब नमूना फ़ाइल का अनुमानित प्रतिशत हो सकता है। बहुत बड़ी फ़ाइलों के लिए काम करता है:

awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01 || FNR==1) print > "data-sample.txt"}' data.txt

1

या इस तरह:

LINES=$(wc -l < file)  
RANDLINE=$[ $RANDOM % $LINES ]  
tail -n $RANDLINE  < file|head -1  

बैश मैन पेज से:

        रैंडम हर बार इस पैरामीटर को संदर्भित किया जाता है, एक यादृच्छिक पूर्णांक
              0 से 32767 के बीच उत्पन्न होता है। यादृच्छिक का क्रम
              RANized को मान निर्दिष्ट करके संख्याएँ आरंभ की जा सकती हैं
              डोम। अगर रैंडम परेशान है, तो यह अपना विशेष अधिकार खो देता है
              यदि यह बाद में रीसेट हो जाता है, तो भी संबंध।

यह बुरी तरह से विफल रहता है यदि फ़ाइल में 32767 से कम लाइनें हैं।
offby1

यह फाइल से एक लाइन आउटपुट करेगा । (मुझे लगता है कि आपका विचार उपरोक्त आदेशों को एक लूप में निष्पादित करना है?) यदि फ़ाइल में 32767 से अधिक लाइनें हैं, तो ये आदेश केवल पहली 32767 लाइनों से चुनेंगे। संभावित अक्षमता के अलावा, मुझे इस उत्तर के साथ कोई बड़ी समस्या नहीं दिखती है यदि फ़ाइल में 32767 से कम लाइनें हैं।
जी-मैन ने

1

यदि आप फ़ाइल का आकार बड़ा नहीं है, तो आप क्रमबद्ध यादृच्छिक का उपयोग कर सकते हैं। यह shuf की तुलना में थोड़ा अधिक समय लेता है, लेकिन यह संपूर्ण डेटा को यादृच्छिक करता है। इसलिए, आप आसानी से सिर का उपयोग करने के लिए आसानी से कर सकते हैं जैसा आपने अनुरोध किया था:

sort -R input | head -1000 > output

यह फ़ाइल को यादृच्छिक रूप से सॉर्ट करेगा और आपको पहली 1000 लाइनें देगा।


0

जैसा कि स्वीकृत उत्तर में बताया गया है, GNU shufसरल यादृच्छिक नमूने ( shuf -n) का अच्छी तरह से समर्थन करता है । यदि उन समर्थित तरीकों से नमूना लेने की shufआवश्यकता है, तो eBay के TSV यूटिलिटीज से tsv-नमूना पर विचार करें । यह कई अतिरिक्त नमूने मोड का समर्थन करता है, जिसमें भारित यादृच्छिक नमूनाकरण, बर्नौली नमूनाकरण और अलग नमूनाकरण शामिल हैं। प्रदर्शन GNU के समान है (दोनों काफी तेज हैं)। डिस्क्लेमर: मैं लेखक हूं।shuf

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