2008-11-24 62 views
14

我是一名非計算機科學專業的學生,​​撰寫了一篇歷史論文,其中涉及確定若干文本中特定術語的頻率,然後隨時間繪製這些頻率以確定變化和趨勢。雖然我已經想出瞭如何確定給定文本文件的詞頻,但我正在處理一個(對我來說)大量文件(> 100),爲了保持一致性,我想限制包含在頻率計數中的詞到一組特定的術語(有點像「停止列表」的反面)確定特定術語的詞頻

這應該保持非常簡單。最後,我需要的是我處理的每個文本文件的特定單詞的頻率,最好以電子表格格式(選項卡描述文件),以便我可以使用該數據創建圖表和可視化。

我每天都在使用Linux,我很喜歡使用命令行,並且會喜歡開源解決方案(或者我可以用WINE運行的東西)。這不是然而一個要求:

我看到兩個辦法來解決這個問題:

  1. 查找文本文件的方式帶出所有的話,除了預先定義的列表,然後執行頻率計數,或:
  2. 找到一種方法來使用預定義列表中的術語來進行頻率計數。

任何想法?

+0

我很好奇,單詞列表是什麼? (和文本的種類) – 2008-11-24 23:19:06

+0

文章。術語列表是該領域的關鍵詞。 – fdsayre 2008-11-24 23:33:20

回答

7

我會去第二個想法。這是一個簡單的Perl程序,它將從第一個提供的文件中讀取單詞列表,並以製表符分隔格式提供的第二個文件列出列表中每個單詞的計數。第一個文件中的單詞列表應該每行提供一個。

#!/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的單詞邊界上,在所有情況下,您可能無法按照您希望的方式工作,例如,如果您的文本文件包含跨行連字符的單詞,則需要做一些更加智能的匹配操作。在這種情況下,您可以檢查一行中的最後一個字符是否爲連字符,如果是,則只需刪除連字符並在將行分割成單詞之前再讀取另一行。

編輯:更新後的版本,它不區分大小寫地處理單詞,並處理跨行的帶連字符的單詞。

請注意,如果存在帶連字符的單詞,其中某些單詞在行中被打破,有些則不會被全部找到,因爲它只會在行尾刪除連字符。在這種情況下,您可能希望刪除連字符後刪除所有連字符和匹配詞。您可以通過在分割函數之前簡單添加以下行來執行此操作:

s/-//g; 
+0

感謝您的編輯。我想我需要首先清理文本,刪除連字符,大寫等,以便在進行頻率計數之前使數據更加一致。最後一個問題:有沒有辦法將命令輸出到標籤劃定的文件?如果不容易剪切和粘貼。謝謝。 – fdsayre 2008-11-24 23:50:12

1

我猜新文件會隨着時間的推移而被引入,這就是事情的改變?

我認爲你最好的選擇是使用類似於你的選項的東西2.如果你想要做的只是計算關鍵字的出現次數,那麼預處理文件沒有太多的意義。我只需通過每個文件一次,計算每次出現列表中的單詞時。就我個人而言,我會用Ruby來做,但像Perl或Python這樣的語言也會使這個任務變得非常簡單。例如,你可以使用關鍵字作爲關鍵字的關聯數組,以及出現次數作爲值。 (但是,如果你需要存儲關於事件的更多信息,這可能太簡單了)。

我不確定是否要存儲每個文件或整個數據集的信息?我想這不會太難納入。

我不知道如何處理這些數據 - 將其導出到電子表格中即可,如果這樣可以滿足您的需求。或者您可能會發現,從長遠來看,只需編寫一些額外的代碼,可以很好地爲您顯示數據,就會更輕鬆。取決於你想要對數據做什麼(例如,如果你想在練習結束時只生成幾個圖表並將它們放入報告中,那麼導出爲CSV可能是最有意義的,而如果要生成一年中每天都有一套新的數據,然後建立一個工具來自動完成這項工作幾乎肯定是最好的想法。

編輯:我剛剛發現,既然你正在學習歷史,那麼你的文檔可能不是隨着時間的推移而發生變化,而是反映了一系列已經發生的變化,對於誤解這一點抱歉,無論如何,我認爲上面所說的幾乎所有東西都適用,但我想你會傾向於導出爲CSV或者你有什麼而不是自動顯示器。

聽起來像一個有趣的項目 - 祝你好運!

2

首先熟悉詞法分析以及如何編寫掃描生成器規範。閱讀使用YACC,Lex,Bison或我個人最喜歡的JFlex等工具的介紹。在這裏您可以定義構成令牌的內容。這是您瞭解如何創建標記器的地方。

接下來你有什麼叫做種子列表。停止列表的反面通常被稱爲起始列表或有限詞典。詞彙也是一件很好的事情要了解。部分應用程序需要將啓動列表加載到內存中,以便快速查詢。典型的存儲方式是每行包含一個單詞的文件,然後在應用程序的開始處將其讀入一次,如同地圖一樣。您可能想了解哈希的概念。

從這裏您想要考慮存儲結果所需的基本算法和數據結構。分佈很容易表示爲二維稀疏陣列。學習稀疏矩陣的基礎知識。你不需要6個月的線性代數來理解它的作用。

因爲您正在處理較大的文件,我會主張基於流的方法。不要將整個文件讀入內存。將它作爲一個流讀入標記生成器,生成一個標記流。

在算法的下一部分中,考慮如何將令牌列表轉換爲僅包含所需單詞的列表。如果你仔細想想,這個列表在內存中並且可能非常大,所以最好在開始時過濾掉非開始的單詞。因此,在從令牌生成器獲取新令牌並將其添加到令牌列表之前的臨界點,請在內存中的開始字列表中進行查找,以查看該字是否爲開始字。如果是這樣,請將其保存在輸出令牌列表中。否則,忽略它並移動到下一個標記,直到讀取完整個文件。

現在您只有感興趣的令牌列表。問題是,你沒有看到其他索引指標,如位置,案例和上下文。因此,你真的不需要所有令牌的列表。你真的只想要一個稀疏矩陣的不同的標記與相關的計數。

因此,首先創建一個空的稀疏矩陣。然後考慮在解析過程中插入新發現的標記。當它發生時,如果其在列表中增加它的計數,或者插入一個計數爲1的新標記。這次,在解析文件結束時,你有一個不同標記的列表,每個標記的頻率至少爲1.

該列表現在是在內存中,你可以做任何你想要的。將它轉儲到CSV文件將是一個迭代過程,並重復每個條目的每個條目並記錄它的數量。

對於這個問題,看看或所謂的「門」的非商業產品商業產品一樣TextAnalyst或產品在http://textanalysis.info

1

上市我會做對文件的「grep」找到所有包含關鍵詞的行。 (grep -f可以用來指定要搜索的文件的輸入文件(將grep的輸出管道輸出到一個文件中),這會給你一個包含你單詞實例的行列表,然後執行一個「sed」用換行符替換你的單詞分隔符(最有可能的空格),給你一個單獨的單詞文件(每行一個單詞)。現在再次運行grep,使用相同的單詞列表,除了這次指定-c(得到一個計數與指定的單詞的行數;即在原始文件中出現單詞的次數)

雙通道方法只是簡單地使「sed」的生活更輕鬆;第一個grep應該消除很多行。

你可以在基本的linux命令行命令中完成這一切,一旦你熟悉了這個過程,你可以把它全部放入sh橢圓腳本很容易。

4

我做這樣的事情有像下面的腳本(在bash語法):

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

另一個Perl的嘗試:

#!/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-test和羅伯特寶潔的答案爲wc-ans-test,輸出文件看起來是這樣的:

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

這是逗號分隔值(csv)文件(但您可以更改腳本中的分隔符)。它應該對任何電子表格應用程序都是可讀的。對於繪製圖表,我會推薦gnuplot,它可以完全編寫腳本,因此您可以獨立於輸入數據調整輸出。

1

大地獄。如果你願意抓住所有話,試試這個殼復:

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

這(測試)會給你通過CSV格式,您最喜愛的電子表格輕鬆導入頻率排序的所有單詞的列表。如果您必須有停用詞,則嘗試將grep -w -F -f stopwords.txt插入管線(未測試)。