倒排索引的构建与查询

倒排索引是信息检索的重要技术,本文将基于中文短信数据(数据集可在本文所附资源处下载或点击此处链接下载),编程构建倒排索引并实现布尔查询。

1. 功能设计

  1. 用户输入查询,按下回车键,如果该查询作为单独的查询词命中,程序返回一个列表作为查询结果,列表中包含全部的命中文档。列表中的每一项的格式为:“文档ID:查询词在该文档中的词频”。例如:用户输入的查询为“蓝猫”,返回的查询结果为[‘34:1’, ‘246914:1’, ‘294413:1’, ‘404281:1’, ‘444679:1’, ‘452678:1’, ‘519311:1’, ‘660405:1’],表示“蓝猫”一词在文档ID为34、246914、294413、404281、444679、452678、519311、660405的8个文档中均出现过一次。

  2. 如果步骤1中的查询未命中,则对该查询进行分词,对分词后得到的查询词列表执行AND布尔查询。如果执行AND查询命中,程序返回同时包含全部查询词的文档ID列表。例如:用户输入的查询为“文化墙”,返回的查询结果为[‘472358’, ‘727722’, ‘560771’, ‘102998’, ‘14’],表示“文化”、“墙”两词同时出现在文档ID为472358、727722、560771、102998、14的8个文档中。

  3. 如果步骤2中的AND布尔查询未命中,则对分词后的查询词列表执行OR布尔查询。如果OR布尔查询命中,则程序返回文档ID列表。否则,程序返回“没找到”。

2. 实现思路

实现思路主要分为三个步骤:第一步,对中文文本进行分词并去除停用词;第二步,构建倒排索引;第三步,根据倒排索引完成查询。

在第一步中,中文文本分词使用结巴分词工具,停用词列表共1893个停用词,主要去除了标点符号、特殊字符、数字序号、无意义的停顿词、虚词等。

在第二步中,倒排索引的构建采用MapReduce的思想。主要分为Map、Combine、Reduce三阶段,示例如下图所示。

MapReduce流程示例

在Map阶段,对每个文档进行分词,以键值对的形式将分词结果存储在字典中输出,其中键为“文档ID:词”,值为一个元素均为1的列表,列表的长度为该词在相应文档中出现的次数。

在Combine阶段,对Map的输出结果进行一次合并归约,即对于Map阶段输出的字典中的每个键值对,将原先的元素均为1的值列表做求和操作,转换为词频。

在Reduce阶段,继续对Combine阶段的输出结果进行合并。最终输出结果字典中,每个键值对为<词, 文档ID:词频>的形式。

在第三步中,根据倒排索引进行的查询主要分为三次判断:首先判断用户输入的查询是否可以作为单独的查询词命中。若可以命中,则返回结果;否则,对查询进行分词,对分词列表做AND布尔查询。然后判断AND布尔查询是否可以命中,若可以命中,则返回结果;否则,对分词列表做OR布尔查询。然后判断OR布尔查询是否可以命中,若可以命中,则返回结果;否则,返回“没找到”。在AND布尔查询中,将每个词的文档ID列表取交集;在OR布尔查询中,将每个词的文档ID列表取并集。示例如下图所示。

根据倒排索引进行查询的判断逻辑示例

3. 代码

最终整体代码inverted_index.py完成如下所示。除主函数之外,代码中的函数主要有:stopwordslist(filepath)mapper(docId, list)combiner(dic)reducer(dic)get_inverted_index(filepath, stopwords)五个。可以看到,以下代码中并未涉及到shuffle环节,这是因为实验中选用的文档ID为短信所在行的行号,每个文档是顺序处理的且在处理后存储到有序字典中,因此省略了按文档ID排序的环节。

# -*- coding;utf-8 -*-import os
import jieba
import re
import json
import collections
from functools import reduce
import io
#import sys
#sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')#读取停用词列表
def stopwordslist(filepath):  stopwords = [line.strip() for line in open(filepath, 'r', encoding='utf-8').readlines()]  return stopwords  def mapper(docId, list):#初始化结果字典为空dic = {} dic = collections.OrderedDict()#对于分词后序列中的每一个词termfor term in list: #将文档ID与词拼接起来,作为关键字key = ''.join([str(docId), ':', term]) #如果该关键字已经存在于结果字典中if key in dic: valuelist = dic.get(key)#则在值列表中新增1,标志词term在文档中再次出现valuelist.append(1) dic[key] = valuelistelse:#否则表明词term首次出现在文档中dic[key] = [1] return dicdef combiner(dic):keys = dic.keys()result = {}result = collections.OrderedDict()for key in keys:valuelist = dic.get(key)count = 0#计算每个词在文档中出现的次数(词频)for i in valuelist:count += iresult[key] = countreturn resultdef reducer(dic):keys = dic.keys()#初始化结果字典为空result = {}result = collections.OrderedDict()for key in keys:#将输入字典中的关键字拆分为文档ID与词termdocId, term = key.split(":")#将文档ID与词term的词频拼接起来作为valuevalue = ''.join([docId, ':', str(dic.get(key))])#如果词term存在于结果字典中if term in result:valuelist = result[term]#则将该value加入值列表中valuelist.append(value)result[term] = valuelistelse:result[term] = [value]return resultdef get_inverted_index(filepath, stopwords):file = open(filepath, 'r', encoding='utf-8')lineNum = 0temp = {}temp = collections.OrderedDict()while True:lineNum += 1line = file.readline()if line != '':line = line.strip('\n').split('\t')[1]print (lineNum, ' ', line,)else:breaktermlist = []for w in list(jieba.cut(line)):if w not in stopwords:termlist.append(w)mdic = mapper(lineNum, termlist)cdic = combiner(mdic)print(cdic)temp.update(cdic)rdic = reducer(temp)return rdicif __name__ == '__main__':stopwords = stopwordslist('stopwords.txt')try:#若倒排索引文件已经生成,则直接从文件中读取file = open('index.txt', 'r', encoding='utf-8')js = file.read()dic = json.loads(js)file.close()except:#否则生成倒排索引文件dic = get_inverted_index('data.txt', stopwords)js = json.dumps(dic)file = open('index.txt', 'w', encoding='utf-8')file.write(js)file.close()while(1):query = input('请输入你想查找的内容(输入q退出):')if query == 'q' or query == 'Q':breakelse:if query in dic:print(dic.get(query))else:termlist = []for w in list(jieba.cut(query)):termlist.append(w)results = []       for i in range(0,len(termlist)):if termlist[i] in dic:temp = []for item in dic.get(termlist[i]):docId, freq = item.split(":")temp.append(docId)results.append(temp)if results == []:print('没找到')else:#AND查询fm = lambda x,y: list(set(x).intersection(set(y))) if isinstance(x, list) and isinstance(y, list) else 'error'fn = lambda x: x[0] if len(x) == 1 else [] if  len(x) == 0 else reduce(fm,tuple(y for y in x))temp = resultsfinal_results = fn(temp)if final_results != []:print(final_results)else:#OR查询fm = lambda x,y: list(set(x).union(set(y))) if isinstance(x, list) and isinstance(y, list) else 'error'fn = lambda x: x[0] if len(x) == 1 else [] if  len(x) == 0 else reduce(fm,tuple(y for y in x))temp = resultsfinal_results = fn(temp)print(final_results)

4. 运行结果

首次运行inverted_index.py,将生成倒排索引文件index.txt。之后再次运行inverted_index.py,将从倒排索引文件中加载倒排索引为字典格式。倒排索引加载完毕,程序提示:“请输入你想查找的内容(输入q退出)”,用户输入查询,按下回车即可进行循环查询,输入q字符跳出循环。测试用例如下:

  1. 查询:“蓝猫”,结果:[‘34:1’, ‘246914:1’, ‘294413:1’, ‘404281:1’, ‘444679:1’, ‘452678:1’, ‘519311:1’, ‘660405:1’](属于查询词可作为单个词命中文档的情况)
    在这里插入图片描述

  2. 查询:“渤海”,结果:[‘24:2’, ‘5466:1’, ‘6478:1’, ‘10162:1’, ‘11618:1’, ‘24448:1’, ‘28510:1’, ‘30315:1’, ‘31080:1’, ‘32418:1’, ‘61972:1’, ‘70692:1’, ‘80132:2’, ‘113344:1’, ‘113581:1’, ‘116005:1’, ‘123980:2’, ‘168168:1’, ‘173460:1’, ‘176530:1’, ‘186451:2’, ‘187653:1’, ‘207964:1’, ‘209533:1’, ‘216139:1’, ‘220807:1’, ‘238442:1’, ‘241201:1’, ‘244691:1’, ‘259685:1’, ‘260014:2’, ‘268367:1’, ‘281240:2’, ‘285323:1’, ‘304731:1’, ‘314724:2’, ‘315673:1’, ‘361410:1’, ‘374756:1’, ‘385608:1’, ‘441109:1’, ‘477490:1’, ‘492804:1’, ‘493811:1’, ‘494802:1’, ‘499013:1’, ‘514353:1’, ‘529791:1’, ‘530882:1’, ‘532511:1’, ‘536790:1’, ‘539291:1’, ‘541265:1’, ‘555833:1’, ‘558951:1’, ‘566540:1’, ‘571613:1’, ‘588200:1’, ‘597989:1’, ‘616213:1’, ‘624738:1’, ‘631713:1’, ‘679088:1’, ‘686993:1’, ‘692306:1’, ‘717532:1’, ‘717598:1’, ‘749175:1’, ‘757471:1’, ‘796113:1’]
    在这里插入图片描述

  3. 查询:“南京四川”,结果:[‘500318’, ‘462582’, ‘329294’, ‘368153’, ‘260541’, ‘612586’, ‘606335’, ‘781114’, ‘507296’, ‘213024’, ‘419647’, ‘765378’, ‘148594’, ‘79034’, ‘710908’, ‘357999’, ‘27’, ‘426147’, ‘571882’, ‘476409’, ‘36435’, ‘741202’](属于AND布尔查询的情况)
    在这里插入图片描述

  4. 查询:“文化墙”,结果:[‘560771’, ‘102998’, ‘14’, ‘472358’, ‘727722’](属于AND布尔查询的情况)
    在这里插入图片描述

  5. 查询:“渤海蓝猫”,结果:[‘477490’, ‘61972’, ‘624738’, ‘30315’, ‘10162’, ‘28510’, ‘11618’, ‘32418’, ‘555833’, ‘281240’, ‘514353’, ‘260014’, ‘529791’, ‘315673’, ‘187653’, ‘113581’, ‘294413’, ‘113344’, ‘616213’, ‘207964’, ‘679088’, ‘541265’, ‘209533’, ‘24’, ‘268367’, ‘588200’, ‘5466’, ‘631713’, ‘519311’, ‘757471’, ‘216139’, ‘116005’, ‘244691’, ‘31080’, ‘404281’, ‘558951’, ‘173460’, ‘749175’, ‘304731’, ‘259685’, ‘597989’, ‘493811’, ‘220807’, ‘285323’, ‘539291’, ‘238442’, ‘374756’, ‘444679’, ‘492804’, ‘660405’, ‘6478’, ‘385608’, ‘532511’, ‘80132’, ‘186451’, ‘176530’, ‘34’, ‘314724’, ‘536790’, ‘361410’, ‘24448’, ‘70692’, ‘241201’, ‘441109’, ‘686993’, ‘499013’, ‘692306’, ‘566540’, ‘246914’, ‘494802’, ‘571613’, ‘717532’, ‘452678’, ‘530882’, ‘123980’, ‘717598’, ‘168168’, ‘796113’]
    (属于OR布尔查询的情况)
    在这里插入图片描述

  6. 查询:“english”,结果:“没找到”
    在这里插入图片描述
    用编辑器打开给定的中文短信数据,使用字符串搜索功能加以验证,发现上述测试用例是可以通过的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/652514.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mybatis-Plus入门

Mybatis-Plus入门 MyBatis-Plus 官网&#xff1a;https://mp.baomidou.com/ 1、简介 MyBatis-Plus (简称 MP) 是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、 提高效率而生。 https://github.com/baomidou/mybatis-p…

【Java】Spring注解开发

一、Spring注解开发 1 注解开发定义Bean对象【重点】 目的&#xff1a;xml配置Bean对象有些繁琐&#xff0c;使用注解简化Bean对象的定义 问题导入 问题1&#xff1a;使用什么标签进行Spring注解包扫描&#xff1f; 问题2&#xff1a;Component注解和Controller、Service、R…

Blender教程(基础)-初始用户界面-01

开始第一天的Blender学习、也是业余学习。希望记录下这一份学习的过程、并且分享给大家。今天带大家认识Blender这一款软件&#xff0c;先说说我为什么选择了Blender&#xff0c;我在软件市场找了好久&#xff0c;市场上其他雷同软件都是要么收费要么不好用&#xff0c;最终决定…

【Vitest】 Vitest测试框架的简单使用

简言 在了解vue源码的时候接触到了vitest测试框架,它的官网语言有中文&#xff0c;所以本篇只作简单的使用介绍。 Vitest官网 Vitest 旨在将自己定位为 Vite 项目的首选测试框架&#xff0c;即使对于不使用 Vite 的项目也是一个可靠的替代方案。它本身也兼容一些Jest的API用法…

寒假思维训练计划day16 A. Did We Get Everything Covered?

今天更新一道1月27号晚上div2的C题作为素材&#xff0c;感觉用到了我的构造题总结模型&#xff0c;我总结了一系列的模型和例题。 摘要&#xff1a; Part1 定义"边界贪心法" Part2 题意 Part3 题解 Part4 代码 Part5 思维构造题模型和例题 Part1 边界贪心…

【服务器Midjourney】创建部署Midjourney网站

目录 🌺【前言】 🌺【准备】 🌺【宝塔搭建MJ】 🌼1. 给服务器添加端口 🌼2. 使用Xshell连接服务器 🌼3. 安装docker 🌼4. 安装Midjourney程序 🌼5. 绑定域名+申请SSL证书 🌼6. 更新网站

全球唯一使用Python生成双色球和大乐透

生成双色球和大乐透代码&#xff1a; import randomdef gen_union_lotto(nums: int):"""随机生成N个双色球:param nums::return:"""union_list []for i in range(0, nums):data []for data_ in range(0, 6):random_num random.randint(1, 33…

电脑 文件夹内 显示是 文件在一起 ,实际存储硬盘的不同地方?

是的&#xff0c;在电脑上&#xff0c;文件夹内显示在一起的文件可能实际上存储在硬盘的不同物理位置。这是因为现代操作系统使用的是文件系统来管理磁盘上的数据&#xff0c;常见的如NTFS&#xff08;Windows&#xff09;、HFS&#xff08;旧版Mac&#xff09;或APFS&#xff…

【GitHub项目推荐--如何构建项目】【转载】

这是一个 138K Star 的开源项目&#xff0c;这个仓库汇集了诸多优质资源&#xff0c;教你如何构建一些属于自己的东西&#xff0c;内容主要分为增强现实、区块链、机器人、编辑器、命令行工具、神经网络、操作系统等几大类别。 开源地址&#xff1a;https://github.com/danist…

【贪吃蛇:C语言实现】

文章目录 前言1.了解Win32API相关知识1.1什么是Win32API1.2设置控制台的大小、名称1.3控制台上的光标1.4 GetStdHandle&#xff08;获得控制台信息&#xff09;1.5 SetConsoleCursorPosition&#xff08;设置光标位置&#xff09;1.6 GetConsoleCursorInfo&#xff08;获得光标…

滴水逆向三期笔记与作业——02C语言——10 Switch语句反汇编

滴水逆向三期笔记与作业——02C语言——10 Switch语句反汇编 一、Switch语句1、switch语句 是if语句的简写2、break加与不加有什么特点?default语句可以省略吗&#xff1f;3、游戏中的switch语句&#xff08;示例&#xff09;4、添加case后面的值&#xff0c;一个一个增加&…

深兰科技入选亿欧《“制”敬不凡先锋榜·智能机器人Top10》榜单

日前&#xff0c;由亿欧协办的2023工博会工业智能化发展高峰论坛于上海成功举办&#xff0c;会上发布了《2023智能制造&#xff1a;“制”敬不凡先锋者》系列名单。深兰科技凭借在智能机器人开发中的技术创新和模式应用&#xff0c;入选《“制”敬不凡先锋榜——智能机器人Top1…

前端大厂面试题探索编辑部——第二期

目录 题目 单选题1 题解 关于TCP 关于UDP 单选题2 题解 A选项的HTTP是否是无状态协议 B选项的HTTP支持的方法 C选项的关于HTTP的状态码 D选项HTTP协议的传输格式 题目 单选题1 1.以下哪个描述是关于 TCP 和 UDP 的区别&#xff08;&#xff09; A. TCP 是无连接的…

行测-资料:3. 比重、平均数

1、比重 1.1 现期比重★★★ C A&#xff0c;16.63%≈1/6 B C&#xff0c;拆成 50% 和 6.6% ≈ 1/15。 C D 1.2 基期比重★ 数学推导&#xff0c;A&#xff0c;B&#xff0c;A/(1 a)&#xff0c;B / (1 b) A&#xff0c;4 / 9&#xff0c;12 / 27 x 1.14 / 1.18&#xff0c;看…

【大数据】Flink 架构(四):状态管理

Flink 架构&#xff08;四&#xff09;&#xff1a;状态管理 1.算子状态2.键值分区状态3.状态后端4.有状态算子的扩缩容4.1 带有键值分区状态的算子4.2 带有算子列表状态的算子4.3 带有算子联合列表状态的算子4.4 带有算子广播状态的算子 在前面的博客中我们指出&#xff0c;大…

WinRAR压缩包高级技巧:永久设置压缩包单个或批量单独压缩成包并且不内嵌文件夹,解压保留原始时间设置

目录点击跳转&#xff1a;WinRAR压缩包高级技巧&#xff1a;永久设置压缩包单个或批量单独压缩成包并且不内嵌文件夹&#xff0c;解压保留原始时间设置 解压永久设置1 解压保存原始时间 压缩永久设置1 默认压缩成zip手机电脑都通用的格式2 默认压缩文件不多额外嵌套一层文件夹&…

【新书推荐】3.1节 布尔运算

本节内容&#xff1a;布尔运算&#xff0c;又称为逻辑运算或位运算。 ■布尔代数&#xff1a;and与、or或、not非、xor异或&#xff0c;按位运算。 3.1.1 布尔代数 ■布尔代数与二进制的关系 乔治布尔是一位英国小学数学老师&#xff0c;19世纪最重要的数学家之一。出版了《…

《HTML 简易速速上手小册》第2章:HTML 的标签和元素(2024 最新版)

文章目录 2.1 文本格式化标签&#xff08;&#x1f3a9;✨&#x1f4dc; 网页的“时尚搭配师”&#xff09;2.1.1 基础示例&#xff1a;一篇博客的格式化2.1.2 案例扩展一&#xff1a;产品介绍页面2.1.3 案例扩展二&#xff1a;个人简历 2.2 链接和锚点&#xff08;&#x1f6a…

matplotlib实现动画效果

实现正弦波动画 import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation import numpy as np# 创建图像和轴 fig, ax plt.subplots()# 生成平均分布在0~2*pi之间的100个坐标点 x_data np.linspace(0, 2 * np.pi, 100) # 画出初始图 line, ax.plo…

【漏洞复现】中移铁通禹路由器信息泄露漏洞

Nx01 产品简介 中移禹路由器支持宽带拨号、动态IP和静态IP三种上网模式,一般中国移动宽带的光猫都是智能光猫也就是光猫带路由器功能,中移禹路由器作为二级路由使用。 Nx02 漏洞描述 中移禹路由器ExportSettings处存在信息泄露漏洞&#xff0c;攻击者可以获取后台权限。 Nx03…