2008-11-24 17 views
14

मैं एक गैर कंप्यूटर विज्ञान एक इतिहास थीसिस कि ग्रंथों की संख्या में विशिष्ट शब्दों की आवृत्ति का निर्धारण शामिल है और फिर समय के साथ इन आवृत्तियों की साजिश रचने में परिवर्तन और रुझानों को निर्धारित करने के लिए कर रही है छात्र हूँ। जबकि मुझे पता चला है कि किसी दिए गए टेक्स्ट फ़ाइल के लिए शब्द आवृत्तियों को कैसे निर्धारित किया जाए, मैं बड़ी संख्या में फाइलों (> 100) के साथ (अपेक्षाकृत, मेरे लिए) से निपट रहा हूं और स्थिरता के लिए आवृत्ति गणना में शामिल शब्दों को सीमित करना चाहता हूं मामले का एक विशिष्ट सेट (एक तरह से की तरह एक "रोक सूची" के विपरीत)निर्धारण शब्द आवृत्ति

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

मैं लिनक्स का उपयोग दिन के लिए दिन, कमांड लाइन का उपयोग कर आराम से हूँ, और एक खुला स्रोत समाधान प्यार होता (या कुछ और मैं WINE साथ चला सकते हैं)।

मैं इस समस्या को हल करने के दो तरीके देखें::

  1. पूर्व निर्धारित सूची के अलावा एक तरह से पट्टी से बाहर सब एक पाठ फ़ाइल में शब्द ढूँढें और फिर है कि एक आवश्यकता नहीं है लेकिन वहां से फ्रीक्वेंसी गिनती, या:
  2. प्री-डिफ़ाइंड सूची से केवल शब्दों का उपयोग करके आवृत्ति गणना करने का एक तरीका खोजें।

कोई विचार?

+0

मैं उत्सुक हूं, शब्दों की सूची क्या है? (और ग्रंथों की तरह) –

+0

लेख। शर्तों की सूची फ़ील्ड से महत्वपूर्ण शब्द हैं। – fdsayre

उत्तर

7

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

#!/usr/bin/perl 

use strict; 
use warnings; 

my $word_list_file = shift; 
my $process_file = shift; 

my %word_counts; 

# Open the word list file, read a line at a time, remove the newline, 
# add it to the hash of words to track, initialize the count to zero 
open(WORDS, $word_list_file) or die "Failed to open list file: $!\n"; 
while (<WORDS>) { 
    chomp; 
    # Store words in lowercase for case-insensitive match 
    $word_counts{lc($_)} = 0; 
} 
close(WORDS); 

# Read the text file one line at a time, break the text up into words 
# based on word boundaries (\b), iterate through each word incrementing 
# the word count in the word hash if the word is in the hash 
open(FILE, $process_file) or die "Failed to open process file: $!\n"; 

while (<FILE>) { 
    chomp; 
    while (/-$/) { 
    # If the line ends in a hyphen, remove the hyphen and 
    # continue reading lines until we find one that doesn't 
    chop; 
    my $next_line = <FILE>; 
    defined($next_line) ? $_ .= $next_line : last; 
    } 

    my @words = split /\b/, lc; # Split the lower-cased version of the string 
    foreach my $word (@words) { 
    $word_counts{$word}++ if exists $word_counts{$word}; 
    } 
} 
close(FILE); 

# Print each word in the hash in alphabetical order along with the 
# number of time encountered, delimited by tabs (\t) 
foreach my $word (sort keys %word_counts) 
{ 
    print "$word\t$word_counts{$word}\n" 
} 

फ़ाइल words.txt शामिल हैं:

linux 
frequencies 
science 
words 

और फ़ाइल text.txt आपकी पोस्ट के पाठ, निम्न आदेश शामिल हैं:

perl analyze.pl words.txt text.txt 

प्रिंट होगा:

frequencies  3 
linux 1 
science 1 
words 3 

ध्यान दें कि तोड़ना \ b का उपयोग कर शब्द सीमाओं पर आप सभी मामलों में जिस तरह से काम करना चाहते हैं, उदाहरण के लिए, यदि आपकी टेक्स्ट फाइलों में ऐसे शब्द होते हैं जो लाइनों पर हाइफेनेटेड होते हैं तो आपको इनसे मिलान करने के लिए थोड़ा और बुद्धिमान करने की आवश्यकता होगी। इस मामले में आप यह देखने के लिए जांच सकते हैं कि रेखा में अंतिम वर्ण एक हाइफ़न है और यदि यह है, तो हाइफ़न को हटा दें और शब्दों को पंक्ति में विभाजित करने से पहले एक और पंक्ति पढ़ें।

संपादित: अद्यतन संस्करण है कि संभालती शब्द केस-insensitively और लाइनों भर में हाइफन शब्द संभालती है।

ध्यान दें कि यदि हाइफेनेटेड शब्द हैं, जिनमें से कुछ लाइनों में टूट गए हैं और कुछ जो नहीं हैं, तो यह उन्हें सभी नहीं ढूंढ पाएगा क्योंकि यह केवल एक पंक्ति के अंत में हाइफ़न हटा देता है। इस मामले में आप हाइफ़न हटा दिए जाने के बाद सभी हाइफ़न और मिलान शब्दों को हटाना चाह सकते हैं। आप बस सही विभाजन समारोह से पहले निम्न पंक्ति जोड़कर ऐसा कर सकते हैं:

s/-//g; 
+0

संपादन के लिए धन्यवाद। मैं सोच रहा हूं कि आवृत्ति गणना करने से पहले डेटा को और अधिक सुसंगत बनाने के लिए मुझे पहले टेक्स्ट को साफ करने, हाइफेनेशन, कैप्स इत्यादि को हटाने की आवश्यकता होगी। एक आखिरी सवाल: क्या टैब को चित्रित फ़ाइल में कमांड करने का कोई तरीका है? अगर कट और पेस्ट करना आसान नहीं है। धन्यवाद। – fdsayre

+0

मैं प्रोग्राम केस को संवेदनापूर्वक संभालने के लिए प्रोग्राम संपादित करूँगा और हाइफेनेटेड शब्दों को सही तरीके से संभाल सकता हूं। आप आउटपुट को इस तरह की फ़ाइल में रीडायरेक्ट कर सकते हैं: analysis.pl file1 file2> file3। –

+0

अंतिम प्रश्न: क्या दो शब्द शर्तों से निपटने का कोई आसान तरीका है? अर्थात। यदि शब्द परिभाषा फ़ाइल में "सामान्य वक्र" वाक्यांश शामिल है, तो इसकी गणना की जाएगी। मैं बहुत प्रभावित हूं कि यह स्क्रिप्ट एक 0 देता है जब शब्द टेक्स्ट में मौजूद नहीं होता है क्योंकि यह डेटा को सुसंगत बनाए रखना आसान बनाता है। – fdsayre

1

मुझे लगता है कि समय के साथ नई फाइलें पेश की जा रही हैं, और इसी तरह चीजें बदलती हैं?

मुझे लगता है आपका सर्वश्रेष्ठ दांव अपने विकल्प 2. की तरह कुछ के साथ जाने के लिए होगा नहीं है बहुत ज्यादा नहीं बिंदु पूर्व प्रसंस्करण, फ़ाइलें अगर सभी आप क्या करना चाहते कीवर्ड की घटनाओं गिनती है। मैं बस एक बार प्रत्येक फ़ाइल के माध्यम से जाना होगा, हर बार आपकी सूची में एक शब्द गिनती है। व्यक्तिगत रूप से मैं रूबी में ऐसा करता हूं, लेकिन पर्ल या पायथन जैसी भाषा भी इस काम को काफी सरल बनाती है। उदाहरण के लिए, आप कीवर्ड के साथ एक सहयोगी सरणी का उपयोग कुंजी के रूप में कर सकते हैं, और मानों की गणना मानों के रूप में कर सकते हैं। (लेकिन यदि आप घटनाओं के बारे में अधिक जानकारी स्टोर करने की आवश्यकता है तो यह बहुत सरल हो सकता है)।

मुझे यकीन नहीं है कि क्या आप प्रति फ़ाइल जानकारी, या पूरे डेटासेट के बारे में जानकारी स्टोर करना चाहते हैं? मुझे लगता है कि शामिल करने के लिए बहुत मुश्किल नहीं होगा। यह एक स्प्रेडशीट के लिए निर्यात ठीक होगा, कि अगर आप देता है कि तुम क्या जरूरत है -

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

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

लगता है जैसे मजेदार परियोजना - शुभकामनाएँ!

बेन

2

सबसे पहले अपने आप को शाब्दिक विश्लेषण और कैसे एक स्कैनर जनरेटर विनिर्देश लिखने के साथ परिचित। YACC, Lex, Bison, या मेरे व्यक्तिगत पसंदीदा, JFlex जैसे टूल का उपयोग करने के लिए परिचय पढ़ें। यहां आप परिभाषित करते हैं कि टोकन का गठन क्या होता है। यह वह जगह है जहां आप टोकननाइज़र बनाने के तरीके के बारे में जानेंगे।

अगला आपके पास बीज सूची कहा जाता है। स्टॉप सूची के विपरीत आमतौर पर प्रारंभ सूची या सीमित लेक्सिकॉन के रूप में जाना जाता है। लेक्सिकॉन भी सीखने के लिए एक अच्छी बात होगी। ऐप के भाग को स्टार्ट सूची को मेमोरी में लोड करने की ज़रूरत है ताकि इसे जल्दी से पूछताछ की जा सके। स्टोर करने का सामान्य तरीका एक फ़ाइल प्रति पंक्ति वाला एक फ़ाइल है, फिर इसे मानचित्र की तरह कुछ में, ऐप की शुरुआत में पढ़ें। आप हैशिंग की अवधारणा के बारे में जानना चाहेंगे।

यहां से आप मूल एल्गोरिदम और परिणाम संग्रहित करने के लिए आवश्यक डेटा संरचनाओं के बारे में सोचना चाहते हैं। एक वितरण को आसानी से दो आयामी स्पैस सरणी के रूप में दर्शाया जाता है। एक स्पैर मैट्रिक्स की मूल बातें जानें। यह समझने के लिए आपको 6 महीने के रैखिक बीजगणित की आवश्यकता नहीं है।

क्योंकि आप बड़ी फ़ाइलों के साथ काम कर रहे हैं, मैं एक स्ट्रीम-आधारित दृष्टिकोण की वकालत करता हूं। पूरी फाइल में स्मृति में मत पढ़ो। इसे टोकननाइज़र में एक धारा के रूप में पढ़ें जो टोकन की धारा उत्पन्न करता है।

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

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

तो, पहले एक खाली स्पैर मैट्रिक्स बनाएं। फिर पार्सिंग के दौरान नए पाए गए टोकन को सम्मिलित करने के बारे में सोचें। जब ऐसा होता है, तो इसकी गणना बढ़ जाती है यदि सूची में है या अन्यथा 1 की गिनती के साथ एक नया टोकन डालें। इस बार, फ़ाइल को पार्स करने के अंत में, आपके पास अलग टोकन की एक सूची है, प्रत्येक कम से कम आवृत्ति के साथ 1.

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

उस मामले के लिए, गैर-वाणिज्यिक उत्पाद "गेट" कहा जाता है या http://textanalysis.info

+0

आपने यूनिकोड वर्णों के कैननिकल अपघटन के बारे में महत्वपूर्ण जानकारी छोड़ी। : p – erickson

1

में सूचीबद्ध TextAnalyst या उत्पादों की तरह एक व्यावसायिक उत्पाद मैं फ़ाइलों पर एक "grep" करना चाहते हैं सभी को खोजने के लिए पर एक नज़र लाइनें जिनमें आपके मुख्य शब्द होते हैं। (Grep -f को खोजने के लिए शब्दों की एक इनपुट फ़ाइल निर्दिष्ट करने के लिए इस्तेमाल किया जा सकता है (grep के आउटपुट को फ़ाइल में पाइप करें)। इससे आपको लाइनों की एक सूची मिल जाएगी जिसमें आपके शब्दों के उदाहरण होंगे। फिर "sed" अपनी शब्द विभाजक (सबसे अधिक संभावना रिक्त स्थान) को नई लाइनों के साथ प्रतिस्थापित करें, आपको अलग-अलग शब्दों की एक फ़ाइल (प्रति पंक्ति एक शब्द) देने के लिए। अब एक ही शब्द सूची के साथ, grep के माध्यम से फिर से चलाएं, इस समय को छोड़कर -c (गणना करने के लिए निर्दिष्ट शब्दों के साथ लाइनों की, यानी मूल फ़ाइल में शब्द की घटनाओं की गणना)

दो-पास विधि बस "sed" के लिए जीवन को आसान बनाता है; पहले grep को कई पंक्तियों को खत्म करना चाहिए

आप इसे मूल लिनक्स कमांड लाइन कमांड में कर सकते हैं। एक बार जब आप प्रक्रिया के साथ सहज महसूस कर लेंगे, तो आप इसे सभी को sh में डाल सकते हैं बहुत आसानी से ell स्क्रिप्ट।

4

मैं निम्नलिखित की तरह एक स्क्रिप्ट के साथ बात की इस तरह करते हैं (बैश वाक्य रचना में):

for file in *.txt 
do 
    sed -r 's/([^ ]+) +/\1\n/g' "$file" \ 
    | grep -F -f 'go-words' \ 
    | sort | uniq -c > "${file}.frq" 
done 

आप रेगुलर एक्सप्रेशन से आप अलग-अलग शब्दों परिसीमित करने के लिए का उपयोग ठीक कर सकते हैं; उदाहरण में मैं सिर्फ व्हाइटसाइट को डेलीमीटर के रूप में मानता हूं। Grep के लिए -f तर्क एक फ़ाइल है जिसमें आपकी रुचि के शब्दों, प्रति पंक्ति एक है।

1

एक और पर्ल प्रयास:

#!/usr/bin/perl -w 
use strict; 

use File::Slurp; 
use Tie::File; 

# Usage: 
# 
# $ perl WordCount.pl <Files> 
# 
# Example: 
# 
# $ perl WordCount.pl *.text 
# 
# Counts words in all files given as arguments. 
# The words are taken from the file "WordList". 
# The output is appended to the file "WordCount.out" in the format implied in the 
# following example: 
# 
# File,Word1,Word2,Word3,... 
# File1,0,5,3,... 
# File2,6,3,4,... 
# . 
# . 
# . 
# 

### Configuration 

my $CaseSensitive = 1;  # 0 or 1 
my $OutputSeparator = ","; # another option might be "\t" (TAB) 
my $RemoveHyphenation = 0; # 0 or 1. Careful, may be too greedy. 

### 

my @WordList = read_file("WordList"); 
chomp @WordList; 

tie (my @Output, 'Tie::File', "WordCount.out"); 
push (@Output, join ($OutputSeparator, "File", @WordList)); 

for my $InFile (@ARGV) 
    { my $Text = read_file($InFile); 
     if ($RemoveHyphenation) { $Text =~ s/-\n//g; }; 
     my %Count; 
     for my $Word (@WordList) 
      { if ($CaseSensitive) 
       { $Count{$Word} = ($Text =~ s/(\b$Word\b)/$1/g); } 
       else 
       { $Count{$Word} = ($Text =~ s/(\b$Word\b)/$1/gi); }; }; 
     my $OutputLine = "$InFile"; 
     for my $Word (@WordList) 
      { if ($Count{$Word}) 
       { $OutputLine .= $OutputSeparator . $Count{$Word}; } 
       else 
       { $OutputLine .= $OutputSeparator . "0"; }; }; 
     push (@Output, $OutputLine); }; 

untie @Output; 

जब मैं wc-ans-test में फ़ाइल wc-test और रॉबर्ट गैंबल के जवाब में अपने प्रश्न रखा, आउटपुट फ़ाइल इस तरह दिखता है:

File,linux,frequencies,science,words 
wc-ans-test,2,2,2,12 
wc-test,1,3,1,3 

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

1

बड़ी लिपियों के साथ नरक में।आप सभी शब्द हड़पने के लिए तैयार हैं, तो इस खोल फू का प्रयास करें:

cat *.txt | tr A-Z a-z | tr -cs a-z '\n' | sort | uniq -c | sort -rn | 
sed '/[0-9] /&, /' 

कि (परीक्षण किया) आप सीएसवी प्रारूप, आसानी से अपने पसंदीदा स्प्रेडशीट द्वारा आयात में आवृत्ति के अनुसार क्रमबद्ध सभी शब्दों की एक सूची दे देंगे। यदि आपके पास स्टॉप शब्द होना चाहिए तो पाइपलाइन (परीक्षण नहीं) में grep -w -F -f stopwords.txt डालने का प्रयास करें।

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