2012-08-26 16 views
6

मेरे पास एकाधिक उप फ़ोल्डरों में .txt फ़ाइलों में कई कार्य हैं। मैं इन फ़ोल्डर्स से कुल 10 कार्यों को यादृच्छिक रूप से लेने की कोशिश कर रहा हूं, उनकी निहित फाइलें और आखिर में एक फ़ाइल में एक टेक्स्ट लाइन। चयनित रेखा को हटाया जाना चाहिए या चिह्नित किया जाना चाहिए ताकि इसे अगले निष्पादन में नहीं चुना जाएगा। यह एक सवाल बहुत व्यापक हो सकता है लेकिन मैं किसी भी इनपुट या दिशा की सराहना करता हूं।उपफोल्डर्स से पाइथन यादृच्छिक रेखाएं

कोड यह मैं अब तक है:

#!/usr/bin/python 
import random 
with open('C:\\Tasks\\file.txt') as f: 
    lines = random.sample(f.readlines(),10)  
print(lines) 
+3

आप हर फ़ाइल से 10 यादृच्छिक लाइनों या total_ _in 10 लाइनों करना चाहते हैं? –

+1

धन्यवाद, कुल में 10 यादृच्छिक रेखाएं। – user1582596

+0

क्या इन फ़ाइलों में लाइनें अद्वितीय हैं? क्या आप रनों के बीच लाइनों/फ़ाइलों को जोड़ने की उम्मीद करते हैं? क्या इन फ़ाइलों में दसियों या लाखों रेखाएं हैं? –

उत्तर

4

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

हालांकि आपको स्मृति में सभी लाइनों को रखने की आवश्यकता नहीं है। आपको इसे दो चरणों में करना होगा: प्रत्येक फाइल में लाइनों की संख्या गिनने के लिए अपनी फ़ाइलों को इंडेक्स करें, फिर इन फ़ाइलों से पढ़ने के लिए 10 यादृच्छिक रेखाएं चुनें।

पहले अनुक्रमण:

import os 

root_path = r'C:\Tasks\\' 
total_lines = 0 
file_indices = dict() 

# Based on https://stackoverflow.com/q/845058, bufcount function 
def linecount(filename, buf_size=1024*1024): 
    with open(filename) as f: 
     return sum(buf.count('\n') for buf in iter(lambda: f.read(buf_size), '')) 

for dirpath, dirnames, filenames in os.walk(root_path): 
    for filename in filenames: 
     if not filename.endswith('.txt'): 
      continue 
     path = os.path.join(dirpath, filename) 
     file_indices[total_lines] = path 
     total_lines += linecount(path) 

offsets = list(file_indices.keys()) 
offsets.sort() 

अब हम फ़ाइल नाम की ओर इशारा करते ऑफसेट की मैपिंग है, और कुल लाइन गिनती। अब हम दस यादृच्छिक सूचकांक लेने, और आपकी फ़ाइलों से इन पढ़ें:

import random 
import bisect 

tasks = list(range(total_lines)) 
task_indices = random.sample(tasks, 10) 

for index in task_indices: 
    # find the closest file index 
    file_index = offsets[bisect.bisect(offsets, index) - 1] 
    path = file_indices[file_index] 
    curr_line = file_index 
    with open(path) as f: 
     while curr_line <= index: 
      task = f.readline() 
      curr_line += 1 
    print(task) 
    tasks.remove(index) 

नोट है कि आप केवल एक बार अनुक्रमण की जरूरत है; आप कहीं भी परिणाम संग्रहीत कर सकते हैं और जब आपकी फ़ाइलें अपडेट हो तो इसे अपडेट कर सकते हैं।

यह भी ध्यान रखें कि आपके कार्य अब tasks सूची में 'संग्रहीत' हैं; ये आपकी फाइलों में लाइनों के सूचकांक हैं, और चयनित कार्य को मुद्रित करते समय मैं उस चर से सूचकांक को हटा देता हूं। अगली बार जब आप random.sample() विकल्प चलाते हैं, तो पहले उठाए गए कार्यों को अगली बार चुनने के लिए अब उपलब्ध नहीं होगा। अगर आपकी फाइलें कभी भी बदलती हैं, तो इस संरचना को अद्यतन करने की आवश्यकता होगी, क्योंकि इंडेक्स को फिर से गणना की जानी चाहिए। file_indices उस कार्य के साथ आपकी सहायता करेगा, लेकिन यह इस उत्तर के दायरे से बाहर है। :-)

आप केवल एक 10 आइटम नमूना की जरूरत है, Blckknght's solution बजाय का उपयोग करें, क्योंकि यह केवल फ़ाइलों के माध्यम से एक बार जाना होगा, जबकि मेरा 10 अतिरिक्त फ़ाइल खुलने की आवश्यकता है। यदि आपको एकाधिक नमूने की आवश्यकता है, तो इस समाधान के लिए आपको प्रत्येक नमूना की आवश्यकता होने पर केवल 10 अतिरिक्त फ़ाइल खोलने की आवश्यकता होती है, यह फिर से सभी फ़ाइलों को स्कैन नहीं करेगा। यदि आपके पास 10 से कम फ़ाइलें हैं, तो अभी भी ब्लैककॉन्ग के उत्तर का उपयोग करें। :-)

+0

धन्यवाद, इंडेक्सिंग करते समय, निम्नलिखित त्रुटि मिली ट्रेसबैक (सबसे हालिया कॉल अंतिम): फ़ाइल "", लाइन 1, विशेषताएँ त्रुटि: 'dict_keys' ऑब्जेक्ट में कोई विशेषता 'सॉर्ट' नहीं है। बीटीडब्ल्यू, मैं इसे पायथन 3.2.3 – user1582596

+0

@ user1582596 के साथ कोशिश कर रहा हूं: आह, महत्वपूर्ण भेद, मैंने अब आपके लिए कोड अपडेट किया है। –

+2

आपको वास्तव में यह जानने की ज़रूरत नहीं है कि 10 यादृच्छिक रूप से चुनने के लिए कितने कुल लाइनें हैं।आप प्रत्येक पंक्ति के लिए संभावना को कम करके यादृच्छिक रूप से एक पंक्ति का चयन कर सकते हैं: http://www.perlmonks.org/?node_id=1910। एन लाइनों के लिए, आप एन की एक सूची रखते हैं, और प्रत्येक नई लाइन के लिए, आप इसे रखने की संभावना को कम करते हैं: http://www.perlmonks.org/?node_id=1910 (सभी पर्ल के लिए खेद है)। –

0

संपादित करें: नज़दीकी जांच पर यह उत्तर बिल में फिट नहीं है। इस पर काम करने से मुझे जलाशय नमूना एल्गोरिदम का नेतृत्व हुआ, जिसे @ ब्लैकनघट ने अपने जवाब में इस्तेमाल किया। तो इस जवाब को अनदेखा करें।

इसे करने के कुछ तरीके। यहाँ एक है ... जब तक हम लाइनों की वांछित संख्या है

  1. यादृच्छिक
  2. पर
  3. एक का चयन करें यादृच्छिक
  4. दोहराएँ पर उस फ़ाइल से एक ही पंक्ति का चयन करें सभी कार्य फ़ाइलों की एक सूची प्राप्त करें

कोड ...

import os 
import random 

def file_iterator(top_dir): 
    """Gather all task files""" 
    files = [] 
    for dirpath, dirnames, filenames in os.walk(top_dir): 
     for filename in filenames: 
      if not filename.endswith('.txt'): 
       continue 
      path = os.path.join(dirpath, filename) 
      files.append(path) 
    return files 


def random_lines(files, number=10): 
    """Select a random file, select a random line until we have enough 
    """ 
    selected_tasks = [] 

    while len(selected_tasks) < number: 
     f = random.choice(files) 
     with open(f) as tasks: 
      lines = tasks.readlines() 
      l = random.choice(lines) 
      selected_tasks.append(l) 
    return selected_tasks 


## Usage 
files = file_iterator(r'C:\\Tasks') 
random_tasks = random_lines(files) 
+0

इससे डुप्लिकेट विकल्प हो सकते हैं, और मुझे संदेह है कि नमूना का वितरण वर्दी होगा। भविष्य में चलाने वाले चुने हुए कार्यों को आप कैसे याद या हटाते हैं? ओपी से: * चयनित रेखा को हटाया जाना चाहिए या चिह्नित किया जाना चाहिए ताकि इसे अगले निष्पादन में नहीं चुना जाएगा। * –

+0

दोह, मुझे और सावधानी से पढ़ना चाहिए था। अब मेरे जवाब को संशोधित करने के लिए देर हो चुकी है। मैं कल इसे प्राप्त करूंगा। मुझे संदेह है कि लाइनों की सूची को सेट –

+0

10x में बदलना है, @ मार्टिजन पीटर्स को निम्नलिखित त्रुटियां मिलीं, ट्रेसबैक (सबसे हालिया कॉल अंतिम): फ़ाइल "सी: \ ड्रॉपबॉक्स \ पायथन \ testr1.py", रेखा 31, फाइलें = file_iterator (आर'सी: \\ ड्रॉपबॉक्स \\ ans7i \\ ') फ़ाइल "सी: \ ड्रॉपबॉक्स \ पायथन \ testr1.py", पंक्ति 11, फ़ाइल_इटरेटर पथ = os.path में फ़ाइल। शामिल हों (डायरपाथ, फ़ाइल नाम) अनबाउंड लॉक एरर: स्थानीय चर 'फ़ाइल नाम' असाइनमेंट से पहले संदर्भित – user1582596

14

यहां एक साधारण समाधान है जो प्रति नमूना फ़ाइलों के माध्यम से केवल एक को पास करता है। यदि आप जानते हैं कि आप फ़ाइलों से कितने सामान नमूना लेंगे, तो यह शायद इष्टतम है।

पहला ऑफ नमूना फ़ंक्शन है। यह उसी एल्गोरिदम का उपयोग करता है जो @NedBatchelder को पहले के उत्तर पर एक टिप्पणी में लिंक किया गया था (हालांकि पर्ल कोड दिखाया गया है कि कई लोगों के बजाय केवल एक पंक्ति का चयन किया गया है)। यह लाइनों के पुनरावर्तनीय से मूल्यों का चयन करता है, और केवल वर्तमान में चयनित लाइनों को किसी भी समय (साथ ही अगली उम्मीदवार लाइन) में स्मृति में रखा जाना चाहिए। यह ValueError उठाता है यदि अनुरोधनीय नमूना आकार की तुलना में पुनरावृत्त कम मूल्य है।

import random 

def random_sample(n, items): 
    results = [] 

    for i, v in enumerate(items): 
     r = random.randint(0, i) 
     if r < n: 
      if i < n: 
       results.insert(r, v) # add first n items in random order 
      else: 
       results[r] = v # at a decreasing rate, replace random items 

    if len(results) < n: 
     raise ValueError("Sample larger than population.") 

    return results 

संपादित करें: एक और सवाल में, उपयोगकर्ता @DzinX देखा है कि इस कोड में insert के उपयोग आप मूल्यों की एक बहुत बड़ी संख्या में नमूने रहे हैं प्रदर्शन बुरा (O(N^2)) बनाता है। उनका बेहतर संस्करण जो उस मुद्दे से बचाता है here है। /संपादित करें

अब हमें अपने फ़ंक्शन के नमूने के लिए आइटमों के उपयुक्त पुन: प्रयोज्य बनाने की आवश्यकता है। यहां जनरेटर का उपयोग करके मैं इसे कैसे करूँगा। यह कोड केवल एक फ़ाइल को एक ही समय में खुला रखेगा, और इसे एक समय में स्मृति में एक से अधिक पंक्ति की आवश्यकता नहीं है। वैकल्पिक exclude पैरामीटर, यदि मौजूद है, तो set होना चाहिए जिसमें पिछले रन पर चयनित लाइनें हों (और इसलिए इसे फिर से नहीं दिया जाना चाहिए)।

import os 

def lines_generator(base_folder, exclude = None): 
    for dirpath, dirs, files in os.walk(base_folder): 
     for filename in files: 
      if filename.endswith(".txt"): 
       fullPath = os.path.join(dirpath, filename) 
       with open(fullPath) as f: 
        for line in f: 
         cleanLine = line.strip() 
         if exclude is None or cleanLine not in exclude: 
          yield cleanLine 

अब, हम सिर्फ एक आवरण समारोह उन दो टुकड़ों को एक साथ बांधने के लिए (और देखा लाइनों के सेट को प्रबंधित) की जरूरत है। यह आकार n या count नमूने की एक सूची का एक नमूना वापस कर सकता है, इस तथ्य का लाभ उठाते हुए कि एक यादृच्छिक नमूना से एक टुकड़ा भी एक यादृच्छिक नमूना है।

_seen = set() 

def get_sample(n, count = None): 
    base_folder = r"C:\Tasks" 
    if count is None: 
     sample = random_sample(n, lines_generator(base_folder, _seen)) 
     _seen.update(sample) 
     return sample 
    else: 
     sample = random_sample(count * n, lines_generator(base_folder, _seen)) 
     _seen.update(sample) 
     return [sample[i * n:(i + 1) * n] for i in range(count)] 

यह ऐसे इस्तेमाल किया जा सकता:

def main(): 
    s1 = get_sample(10) 
    print("Sample1:", *s1, sep="\n") 

    s2, s3 = get_sample(10,2) # get two samples with only one read of the files 
    print("\nSample2:", *s2, sep="\n") 
    print("\nSample3:", *s3, sep="\n") 

    s4 = get_sample(5000) # this will probably raise a ValueError! 
+0

आप लिख सकते हैं: '(वाक्य में अक्षर के लिए वाक्य में अक्षर के लिए अक्षर' अक्षर 'के बजाय' चेन .from_iterable ((वाक्य में शब्द के लिए अच्छा (अक्षर)) शब्द के लिए शब्द ' – jfs

+0

हम्म, आप सही हे। मुझे लगता है कि जब मैंने कुछ अलग करने की कोशिश की थी तो मैंने 'chain.from_iter' का उपयोग करना शुरू कर दिया था और संस्करण के साथ समाप्त होने के बाद यह अनावश्यक है। एक सीधी जनरेटर अभिव्यक्ति स्पष्ट है, इसलिए मैं इसके बजाय कोशिश करूंगा (मुझे लगता है कि यह मुझे एक लाइन भी बचाएगा, क्योंकि मुझे लाइनों को अलग से अलग करने की आवश्यकता नहीं होगी)। – Blckknght

+1

आप 'task_pipeline()' में स्पष्ट फॉर-लूप और 'उपज लाइन' भी लिख सकते हैं। यह सबसे पठनीय संस्करण का उत्पादन करना चाहिए। इसके अलावा फ़ाइल के रूप में खुले (फ़ाइल नाम) के साथ 'प्राकृतिक उपयोग करना स्वाभाविक है:' इस मामले में (यदि आप पेड़ में बहुत सारी खुली फाइलें "त्रुटि से बचने के लिए बड़ी संख्या में txt फ़ाइलों को शामिल करते हैं तो आप इसे चाहते हैं) – jfs

संबंधित मुद्दे