什么是倒排索引?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:
输出不易,若是可以给您带爱帮助,点个赞再走呗。