【原創】python倒排索引之查找包含某主題或單詞的文件

什麼是倒排索引?java

倒排索引(英語:Inverted index),也常被稱爲反向索引、置入檔案或反向檔案,是一種索引方法,被用來存儲在全文搜索下某個單詞在一個文檔或者一組文檔中的存儲位置的映射。它是文檔檢索系統中最經常使用的數據結構。經過倒排索引,能夠根據單詞快速獲取包含這個單詞的文檔列表。倒排索引主要由兩個部分組成:「單詞詞典」和「倒排文件」。python

假設咱們如今有文件:程序員

test1.txt中存有:咱們愛天然語言處理編程

test2.txt中存有:咱們愛計算機視覺數組

正向索引:{「test1.txt」:["咱們",「愛」,"天然語言","處理"],"test2.txt":["咱們","愛","計算機","視覺"]}服務器

那麼,咱們應該如何經過正向索引找到包含某詞語的文件呢?咱們只能依次遍歷文件中的內容,從內容中找到是否有該詞語,正向查詢的效率很低。網絡

倒排索引:{"咱們":["test1.txt","test2.txt"],"愛":["test1.txt","test2.txt"],"天然語言":["test1.txt"],"處理":["test1.txt"],"計算機":["test2.txt"],"視覺":["test2.txt"]}數據結構

創建倒排索引後,咱們要想查找包含某些單詞的文件,直接從hash表中獲取,是否是就方便多了?接下來,咱們用python實現:app

如今有基本目錄:編程語言

 

python.txt

Python的設計哲學是「優雅」、「明確」、「簡單」。所以,Perl語言中「老是有多種方法來作同一件事」的理念在Python開發者中一般是難以忍受的。Python開發者的哲學是「用一種方法,最好是隻有一種方法來作一件事」。在設計Python語言時,若是面臨多種選擇,Python開發者通常會拒絕花俏的語法,而選擇明確的沒有或者不多有歧義的語法。因爲這種設計觀念的差別,Python源代碼一般被認爲比Perl具有更好的可讀性,而且可以支撐大規模的軟件開發。這些準則被稱爲Python格言。在Python解釋器內運行import this能夠得到完整的列表。
Python開發人員儘可能避開不成熟或者不重要的優化。一些針對非重要部位的加快運行速度的補丁一般不會被合併到Python內。因此不少人認爲Python很慢。不過,根據二八定律,大多數程序對速度要求不高。在某些對運行速度要求很高的狀況,Python設計師傾向於使用JIT技術,或者用使用C/C++語言改寫這部分程序。可用的JIT技術是PyPy。
Python是徹底面向對象的語言。函數、模塊、數字、字符串都是對象。而且徹底支持繼承、重載、派生、多繼承,有益於加強源代碼的複用性。Python支持重載運算符和動態類型。相對於Lisp這種傳統的函數式編程語言,Python對函數式設計只提供了有限的支持。有兩個標準庫(functools, itertools)提供了Haskell和Standard ML中久經考驗的函數式程序設計工具。

java.txt

1.簡單性
Java看起來設計得很像C++,可是爲了使語言小和容易熟悉,設計者們把C++語言中許多可用的特徵去掉了,這些特徵是通常程序員不多使用的。例如,Java不支持go to語句,代之以提供break和continue語句以及異常處理。Java還剔除了C++的操做符過載(overload)和多繼承特徵,而且不使用主文件,免去了預處理程序。由於Java沒有結構,數組和串都是對象,因此不須要指針。Java可以自動處理對象的引用和間接引用,實現自動的無用單元收集,使用戶沒必要爲存儲管理問題煩惱,能更多的時間和精力花在研發上。
2.面向對象
Java是一個面向對象的語言。對程序員來講,這意味着要注意應中的數據和操縱數據的方法(method),而不是嚴格地用過程來思考。在一個面向對象的系統中,類(class)是數據和操做數據的方法的集合。數據和方法一塊兒描述對象(object)的狀態和行爲。每一對象是其狀態和行爲的封裝。類是按必定體系和層次安排的,使得子類能夠從超類繼承行爲。在這個類層次體系中有一個根類,它是具備通常行爲的類。Java程序是用類來組織的。
Java還包括一個類的擴展集合,分別組成各類程序包(Package),用戶能夠在本身的程序中使用。例如,Java提供產生圖形用戶接口部件的類(java.awt包),這裏awt是抽象窗口工具集(abstract windowing toolkit)的縮寫,處理輸入輸出的類(java.io包)和支持網絡功能的類(java.net包)。
3.分佈性
Java設計成支持在網絡上應用,它是分佈式語言。Java既支持各類層次的網絡鏈接,又以Socket類支持可靠的流(stream)網絡鏈接,因此用戶能夠產生分佈式的客戶機和服務器。
網絡變成軟件應用的分佈運載工具。Java程序只要編寫一次,就可處處運行。

c.txt

C語言是一種結構化語言,它有着清晰的層次,可按照模塊的方式對程序進行編寫,十分有利於程序的調試,且c語言的處理和表現能力都很是的強大,依靠很是全面的運算符和多樣的數據類型,能夠輕易完成各類數據結構的構建,經過指針類型更可對內存直接尋址以及對硬件進行直接操做,所以既可以用於開發系統程序,也可用於開發應用軟件。經過對C語言進行研究分析,總結出其主要特色以下:
(1)簡潔的語言
C語言包含有各類控制語句僅有9種,關鍵字也只有32 個,程序的編寫要求不嚴格且多以小寫字母爲主,對許多沒必要要的部分進行了精簡。實際上,語句構成與硬件有關聯的較少,且C語言自己不提供與硬件相關的輸入輸出、文件管理等功能,如需此類功能,須要經過配合編譯系統所支持的各種庫進行編程,故c語言擁有很是簡潔的編譯系統。 [5]
(2)具備結構化的控制語句
C語言是一種結構化的語言,提供的控制語句具備結構化特徵,如for語句、if⋯else語句和switch語句等。能夠用於實現函數的邏輯控制,方便麪向過程的程序設計。 [5]
(3)豐富的數據類型
C語言包含的數據類型普遍,不只包含有傳統的字符型、整型、浮點型、數組類型等數據類型,還具備其餘編程語言所不具有的數據類型,其中以指針類型數據使用最爲靈活,能夠經過編程對各類數據結構進行計算。 [5]
(4)豐富的運算符
C語言包含34個運算符,它將賦值、括號等均視做運算符來操做,使C程序的表達式類型和運算符類型均很是豐富。 [5]
(5)可對物理地址進行直接操做
C語言容許對硬件內存地址進行直接讀寫,以此能夠實現彙編語言的主要功能,並可直接操做硬件。C語言不但具有高級語言所具備的良好特性,又包含了許多低級語言的優點,故在系統軟件編程領域有着普遍的應用。 [5]
(6)代碼具備較好的可移植性
C語言是面向過程的編程語言,用戶只須要關注所被解決問題的自己,而不須要花費過多的精力去了解相關硬件,且針對不一樣的硬件環境,在用C語言實現相同功能時的代碼基本一致,不需或僅需進行少許改動即可完成移植,這就意味着,對於一臺計算機編寫的C程序能夠在另外一臺計算機上輕鬆地運行,從而極大的減小了程序移植的工做強度。 [5]
(7)可生成的高質量目標代碼,高執行效率的程序

首先,咱們導入相應得寶:

#用於獲取該目錄下得全部txt文件,忽略掉文件夾及裏面的
import glob
#主要是一些路徑的操做
import os
#對句子進行分詞或關鍵詞提取
from jieba import analyse

接下來,咱們要獲取全部txt文件的絕對路徑:

#獲取當前pyhtho文件所在的目錄:當前是:C:\gongoubo\python-work\direc\files
dir_path = os.path.dirname(os.path.abspath(__file__))
print(dir_path)
#存儲txt文件的絕對路徑爲列表,同時爲每一個文件創建索引
def file_store():
    files_name =[]
    files_dict = {}
    #獲取file文件夾下全部爲txt的文件
    for i,name in enumerate(glob.glob("file/*.txt")):
        files_dict[i] = name.split('\\')[-1]
        file_name = dir_path + "\\" + name
        files_name.append(file_name)
    return files_name,files_dict

而後,咱們讀取每一個txt文件,再對其進行關鍵詞提取,將結果存儲到新的txt中,並用原txt文件的索引命名:

#讀取每一個txt文件
def transform(files_name):
    #注意打開的時候須要申明爲utf-8編碼
    for i,j in enumerate(files_name):
        #打開文件
        tmp = open(j,'r',encoding='utf-8').read()
        #提取關鍵詞
        content = analyse.extract_tags(tmp)
     #也能夠進行分詞content=jieba.cut_for_search(tmp),關於jieba分詞,能夠看個人天然語言處理之基礎技能
#新建process文件夾 path=dir_path+'\\file\\'+'process' if not os.path.exists(path): os.makedirs(path) #爲存儲關鍵詞的txt取名,對應這每一個文件的索引 fp=open(path+'\\'+str(i)+'.txt','w',encoding='utf-8') #將關鍵詞寫入到txt中 fp.write(" ".join(content)) fp.close()

運行後,咱們會有以下目錄:其中process文件夾下的是提取關鍵詞後的結果,文件名對應索引,即{0:"c.txt",1:"java.txt",2:"python.txt"}

 

 接下來,進行倒排索引的構建:

#創建倒排索引
def invert_index():
    path=dir_path+'\\file\\'+'process'
    word_dict = {}
    # 取包含關鍵詞的txt
    for file in glob.glob(path+'/*.txt'):
        #取出txt文件名,也就是文件的索引
        index = file.split('\\')[-1][0]
        #打開文件,並將關鍵詞存儲爲列表
        with open(file,'r',encoding='utf-8') as fp:
            word_list=fp.read().split(" ")
        #創建倒排索引,若是單詞不在單詞字典中,就存儲文件的索引,不然就添加索引到索引列表後
        for word in word_list:
            if word not in word_dict:
                word_dict[word]=[index]
            else:
                word_dict[word].append(index)
    return word_dict

基本的內容咱們有了,再考慮咱們的輸入,咱們但願實如今控制檯輸入幾個單詞,找到最符合的幾個文件。咱們將輸入存儲爲單詞列表,以此判斷該單詞是否出如今文件中,若是出現了,咱們將該單詞對應的文件的索引+1,不然繼續判斷下一個單詞。以後咱們獲得了關於文件索引次數的字典,咱們按次數從大到小排列,而後取前幾個做爲咱們最後的結果。固然,咱們須要的是原始的文件名,所以,咱們還要將索引映射回文件名,相關代碼以下:

def get_topk(count,topk=None):
    print(count)
    file_index = []
    #若是topk超出了返回的數目,則有多少顯示多少
    if topk > len(count):
        for i in range(0,len(count)):
            file_index.append(int(count[i][0]))
        return file_index
    if len(count)<0:
        print("沒有找到相關的文件")
        return False
    else:
        for i in range(0,topk):
            file_index.append(int(count[i][0]))
    return file_index
#獲得文件名
def get_files(file_index,files_dict):
    res=[]
    for i in file_index:
        res.append(files_dict[i])
    return res

主函數:

def main():
    print("請輸入要查找的內容,不一樣單詞間','隔開:")
    words = input().split(',')
    #得到文件名和文件名索引字典
    files_name, files_dict = file_store()
    #提取關鍵詞或分詞
    transform(files_name)
    #倒排索引創建
    word_dict = invert_index()
    count={}
    #統計文件索引的次數
    for word in words:
        if word in word_dict:
            for file in word_dict[word]:
                if file not in count:
                    count[file]=1
                else:
                    count[file]+=1
        else:
            continue
    #按次數從大到小排列
    count=sorted(count.items(),key=lambda i:i[1],reverse=True)
    #返回前k個文件索引
    file_index=get_topk(count,topk=3)
    if file_index != False:
        print("與之描述最可能的文件是:")
        #返回文件名,並輸出結果
        res=get_files(file_index,files_dict)
        print(res)

最後,咱們運行主函數:

if __name__ == '__main__':
    main()

最終結果:

 

 咱們將topk改成3:

 

 

輸出不易,若是可以給您帶愛幫助,點個贊再走唄。

相關文章
相關標籤/搜索