2012-08-16 14 views
13

मेरे पास एक स्क्रिप्ट बनाने का एक कार्य है जो इनपुट के रूप में एक बड़ी टेक्स्ट फ़ाइल लेता है। इसके बाद सभी शब्दों और घटनाओं की संख्या को खोजने की आवश्यकता होती है और एक अद्वितीय शब्द और इसकी घटना प्रदर्शित करने वाली प्रत्येक पंक्ति के साथ एक नई फ़ाइल तैयार करने की आवश्यकता होती है।क्या यह शेल स्क्रिप्ट तेजी से बनाना संभव है?

एक उदाहरण के रूप में इस सामग्री के साथ एक फ़ाइल ले:

1 AD 
1 ADIPISICING 
1 ALIQUA 
... 
1 ALIQUIP 
1 DO 
2 DOLOR 
2 DOLORE 
... 

इसके लिए मैं tr, sort का उपयोग कर एक पटकथा लिखी और:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor 
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud 
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure 
dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt 
mollit anim id est laborum. 

मैं जो इस तरह दिखता है एक फ़ाइल बनाने की आवश्यकता uniq:

#!/bin/sh 
INPUT=$1 
OUTPUT=$2 
if [ -a $INPUT ] 
then 
    tr '[:space:][\-_?!.;\:]' '\n' < $INPUT | 
     tr -d '[:punct:][:special:][:digit:]' | 
     tr '[:lower:]' '[:upper:]' | 
     sort | 
     uniq -c > $OUTPUT 
fi 

यह क्या करता है es अंतरिक्ष को शब्दों द्वारा विभाजित करने के रूप में विभाजित करता है। यदि शब्द में -_?!.;: है तो मैं उन्हें फिर से शब्दों में तोड़ देता हूं। मैं विराम चिह्न, विशेष वर्ण और अंक हटा देता हूं और संपूर्ण स्ट्रिंग को अपरकेस में परिवर्तित करता हूं। एक बार ऐसा करने के बाद मैं इसे सॉर्ट करता हूं और इसे प्रारूप में प्राप्त करने के लिए uniq के माध्यम से पास करता हूं।

अब मैंने txt प्रारूप में बाइबल डाउनलोड की है और इसे इनपुट के रूप में उपयोग किया है। इस समय मुझे मिल गया:

import re 
from collections import Counter 
from itertools import chain 
import sys 

file = open(sys.argv[1]) 

c = Counter() 

for line in file.readlines(): 
    c.update([re.sub('[^a-zA-Z]', '', l).upper() 
      for l in chain(*[re.split('[-_?!.;:]', word) 
        for word in line.split()])]) 

file2 = open('output.txt', 'w') 
for key in sorted(c): 
    file2.write(key + ' ' + str(c[key]) + '\n') 

जब मैं स्क्रिप्ट मुझे मिल गया मार डाला:

scripts|$ time python text-to-word.py text.txt 
python text-to-word.py text.txt 7.23s user 0.04s system 97% cpu 7.456 total 

आप देख सकते हैं उस में भाग गया

scripts|$ time ./text-to-word.sh text.txt b  
./text-to-word.sh text.txt b 16.17s user 0.09s system 102% cpu 15.934 total 

मैं एक अजगर स्क्रिप्ट के साथ भी ऐसा ही किया 7.23s शेल स्क्रिप्ट की तुलना में 16.17s में चलाया गया था। मैंने बड़ी फाइलों के साथ प्रयास किया है और हमेशा पाइथन जीतने लगते हैं। मेरे ऊपर सेनेरियो के लिए कुछ प्रश्न हैं:

  1. पाइथन स्क्रिप्ट को तेजी से क्यों दिया जाता है कि खोल कमांड सी में लिखे गए हैं? मुझे एहसास है कि शेल स्क्रिप्ट इष्टतम नहीं हो सकता है।
  2. मैं शैल स्क्रिप्ट को कैसे सुधार सकता हूं?
  3. क्या मैं पाइथन लिपि में सुधार कर सकता हूं?

स्पष्ट होने के लिए मैं पाइथन को शैल स्क्रिप्ट पर तुलना नहीं कर रहा हूं। मैं लौ युद्ध शुरू करने की कोशिश नहीं कर रहा हूं या किसी अन्य भाषा में उत्तर की आवश्यकता नहीं है जो खुद को तेजी से तुलना कर रहा है। कार्य करने के लिए छोटे आदेशों को पाइप करने के यूनिक्स दर्शन का उपयोग करके, मैं शेल स्क्रिप्ट को तेज़ी से कैसे बना सकता हूं?

+5

मैं की तरह कुछ करने के लिए शीर्षक बदलने सुझाव देंगे " क्या इस शेल स्क्रिप्ट को तेज़ी से बनाना संभव है? ", इतनी अलग पायथन लिपि ऑनल का उपयोग करना तुलना बिंदु के रूप में वाई। यह अजगर और खोल के बीच मतभेदों के बारे में बेकार और ऑफ-विषय चर्चाओं के जोखिम को खत्म कर देगा। –

+5

मुझे नहीं लगता कि \ N निक्स दर्शन एक छोटे से काम करने वाले कई छोटे आदेशों का उपयोग करने के लिए है क्योंकि यह सबसे अधिक * कुशल * है। इसका कारण यह है कि हमारे पास मौजूद टूल के साथ, आप बहुत कुछ हासिल कर सकते हैं और अपने आप को एक साधारण कार्य के लिए एक नया कार्यक्रम विकसित करने में काफी समय बचा सकते हैं। – mgilson

+1

पायथन भी सी में लिखा गया है। "सी में लिखित" होने के कारण चीजों को तेजी से बनाने के लिए पर्याप्त नहीं है - परतों को हस्तक्षेप करना (और पाइपलाइनों से और पढ़ने के लिए सभी पढ़ने/लिखना) ऊपर की ओर है। –

उत्तर

7

यहां एक महत्वपूर्ण बिंदु शायद इंटर-प्रोसेस I/O है। पायथन स्क्रिप्ट में स्मृति में सभी डेटा हैं, इसलिए डेटा को संसाधित करते समय कोई I/O नहीं होता है।

यह भी ध्यान दें कि पाइथन इस तरह धीमा नहीं है। पायथन में अधिकांश कार्यक्षमता सी

खोल स्क्रिप्ट को 5 प्रक्रियाएं शुरू करनी होंगी और उनमें से प्रत्येक को stdin से पूरा टेक्स्ट पढ़ना होगा और पूरे पाठ को stdout पर चार बार लिखना होगा।

थोड़ा तेजी से अजगर स्क्रिप्ट बनाने के लिए एक तरह से हो सकती है: आप एक एकल स्ट्रिंग में पूरे पाठ को पढ़ने तो सभी विराम, विभाजन शब्द निकालें और फिर कर सकते हैं उन्हें गिनती:

text = file.read() 
text = re.sub(r'[.,:;-_]', '', text) 
text = text.upper() 
words = re.split(r'\\s+', text) 
c = Counter() 
c.update(words) 

यही होगा कई नेस्टेड loops के ऊपरी भाग से बचें।

शैल स्क्रिप्ट के लिए: आपको प्रक्रियाओं की संख्या को कम करने की कोशिश करनी चाहिए। तीन tr प्रक्रियाओं को शायद एक कॉल के साथ sed पर प्रतिस्थापित किया जा सकता है।

+0

मेरा अनुमान है कि सबसे महत्वपूर्ण कारक कई उपप्रोसेसरों को लॉन्च करने का उपर है। –

+1

@ स्वेनमार्कैच: नहीं; कुल में शामिल केवल पांच प्रक्रियाएं हैं। उन्हें शुरू करने से 1 से भी कम समय लगेगा और उनकी स्क्रिप्ट 16 के लिए चलती हैं। –

+0

हां, आप सही हैं। (मैं पहले से ही पहले से ऊपर उठाया गया है।) –

3

यह एक भाषा बनाम किसी अन्य भाषा का विषय नहीं है। आपका दृष्टिकोण अलग है।

पायथन में, आप प्रत्येक शब्द के लिए एक काउंटर बढ़ा रहे हैं, और फिर उत्पादन का उत्पादन करने के लिए अपने काउंटर को फिर से चालू कर रहे हैं। यह ओ (एन) होने जा रहा है।

बाश में, आप अपने सभी शब्दों को व्यक्तिगत रूप से एक लंबे टुपल में डाल रहे हैं, टुपल को सॉर्ट कर रहे हैं, फिर उदाहरणों की गिनती कर रहे हैं। यह संभवतः इस तरह के लिए ओ (nlogn) होने जा रहा है।

sed 's/[^a-zA-Z][^a-zA-Z]*/\'$'\n/g' <$INPUT | sort -f -u >$OUTPUT 

लेकिन आपके सवाल का कम और सही जवाब है:

+3

'काउंटर' अभी भी सॉर्ट किया गया है जो 'ओ (एन * लॉग (एन))' – mgilson

+0

काउंटर का एन लंबे टुपल के एन से कम है क्योंकि कई डुप्लीकेट –

+0

* आप दोनों में यह गलत है । पायथन डॉक्स से: * ए काउंटर हैशबल ऑब्जेक्ट्स गिनने के लिए एक नियम उप-वर्ग है। यह एक अनियंत्रित संग्रह है जहां तत्वों को शब्दकोश कुंजी के रूप में संग्रहीत किया जाता है और उनकी गणना शब्द मानों के रूप में संग्रहीत की जाती है। * काउंटर का समय आदेश अभी भी एन है क्योंकि आपको प्रत्येक की गणना करने के लिए सभी एन तत्वों का निरीक्षण करना होगा। आप सही हैं कि काउंटर का मेमोरी ऑर्डर है जहां के यूनिक्स की संख्या है। –

1

आप अपने bash स्क्रिप्ट सुधार कर सकते हैं क्योंकि आप पूरी तरह से अलग एल्गोरिदम का उपयोग करते रहे हैं।

+0

धन्यवाद लेकिन आपकी स्क्रिप्ट मुझे घटनाएं नहीं देती है और यह धीमी गति से चलती है। लेकिन आप एल्गोरिदम में अंतर को इंगित करने में सही हैं। – satran

0

आप इस कोशिश कर सकते हैं:

इनपुट फ़ाइल को ध्यान में रखते होने की Input.txt

बैश स्क्रिप्ट

cat Input.txt | tr [:space:] '\n' | grep -v "^\s*$" | sort | uniq -c | sort -bnr | tr [:lower:] [:upper:] 
0

एक तरह से GNU awk का उपयोग कर:

WHINY_USERS=1 awk '{ for (i=1; i<=NF; i++) { sub("[,.]","",$i); array[toupper($i)]++ } } END { for (j in array) print array[j], j }' file.txt 

,210

स्यूडोकोड/स्पष्टीकरण:

## WHINY_USERS=1 enables sorting by keys. A bit of a trick. 
## Now loop through each word on each line, removing commas, full-stops, 
## adding each word in uppercase to an array. 
## Loop through the array printing vals and keys 

YMMV

0

एक बैश समाधान

#!/bin/bash 
IFS=' -_?!.;\:,' 
while read -r line; do 
    for word in $line; do 
    word=${word//[^[:alpha:]]/} 
    [ $word ] || continue 
    word=$(tr '[:lower:]' '[:upper:]' <<<"$word") 
    ((_w_$word++)) 
    done 
done <"$INPUT" 
IFS=' ' 
for wword in ${!_w_*}; do echo "${!wword} ${wword#_w_}"; done > $OUTPUT.v1 

एक पर्ल गोल्फ समाधान

perl -nle '$h{uc()}++for/(\w+)/g}{print"$h{$_} $_"for sort keys%h' $INPUT > $OUTPUT.v2 
संबंधित मुद्दे