倒排索引的构建与查询

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

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()
    #对于分词后序列中的每一个词term
    for term in list: 
        #将文档ID与词拼接起来,作为关键字
        key = ''.join([str(docId), ':', term]) 
        #如果该关键字已经存在于结果字典中
        if key in dic: 
            valuelist = dic.get(key)
            #则在值列表中新增1,标志词term在文档中再次出现
            valuelist.append(1) 
            dic[key] = valuelist
        else:
            #否则表明词term首次出现在文档中
            dic[key] = [1] 
    return dic


def combiner(dic):
    keys = dic.keys()
    result = {}
    result = collections.OrderedDict()
    for key in keys:
        valuelist = dic.get(key)
        count = 0
        #计算每个词在文档中出现的次数(词频)
        for i in valuelist:
            count += i
        result[key] = count
    return result

def reducer(dic):
    keys = dic.keys()
    #初始化结果字典为空
    result = {}
    result = collections.OrderedDict()
    for key in keys:
        #将输入字典中的关键字拆分为文档ID与词term
        docId, term = key.split(":")
        #将文档ID与词term的词频拼接起来作为value
        value = ''.join([docId, ':', str(dic.get(key))])
        #如果词term存在于结果字典中
        if term in result:
            valuelist = result[term]
            #则将该value加入值列表中
            valuelist.append(value)
            result[term] = valuelist
        else:
            result[term] = [value]
    return result

def get_inverted_index(filepath, stopwords):
    file = open(filepath, 'r', encoding='utf-8')
    lineNum = 0
    temp = {}
    temp = collections.OrderedDict()
    while True:
        lineNum += 1
        line = file.readline()
        if line != '':
            line = line.strip('\n').split('\t')[1]
            print (lineNum, ' ', line,)
        else:
            break
        termlist = []
        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 rdic


if __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':
            break
        else:
            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 = results
                    final_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 = results
                        final_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”,结果:“没找到”
    在这里插入图片描述
    用编辑器打开给定的中文短信数据,使用字符串搜索功能加以验证,发现上述测试用例是可以通过的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

宏景eHR FrCodeAddTreeServlet SQL注入漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

微信小程序|摇骰子

目录 简介设计与功能需求确定用户界面设计确定摇骰子动画效果确定随机数生成算法编码实现实现摇骰子动画测试与优化进行功能测试进行性能测试说明简介 制作一个摇骰子小程序是一个有趣且具有挑战性的项目。通过这个项目,你可以学习如何运用编程技术来模拟骰子的摇动和结果显示…

Linux的bash命令语法

可用点 #!/bin/bash # 文件要以上面开始,.sh结尾的文件不需要# 赋权文件可执行权限 chmod x <fileName># 获取java jar包启动的进程id ps -ef | grep *.jar | grep -v grep | awk {print $2}shell变量 变量命令规则&#xff1a; 只能包含字母、数字、下划线&#xff1…

麒麟系统—— openKylin 安装 mongodb

麒麟系统—— openKylin 安装 mongodb 一、准备工作1. 确保麒麟系统 openKylin 已经安装完毕。 二、下载解压 MongoDB二、增加环境变量三、配置MongoDB创建数据目录创建日志文件运行 四、加入到服务中 MongoDB是一款高性能、开源的NoSQL数据库&#xff0c;因其灵活的数据结构、…

基于DistFlow潮流的配电网故障重构MATLAB程序

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 简介 程序采用适用于辐射状网络的DistFlow潮流模型&#xff0c;可输入任意故障线路编号&#xff0c;得到优化重构结果。程序加入了辐射状和连续状约束&#xff0c;保证网络连通性和辐射性&#xff0c;改换成…

Linux CPU 负载说明

一、背景 工作中我们经常遇到CPU 负载高&#xff0c;CPU负载高意味着什么&#xff1f; CPU的负载是怎么计算的&#xff1f; top指令中的各个指标代表什么含义&#xff1f; 二、CPU 负载计算方法 在系统出现负载问题&#xff0c;通常会使用uptime和top确认负载&#xff0c;这两…

15、Kafka ------ SpringBoot 整合 Kafka (自动配置类 KafkaAutoConfiguration 源代码剖析)

目录 SpringBoot 整合 Kafka 的自动配置及源代码剖析Spring Boot 为 Kafka 提供的自动配置KafkaAutoConfiguration Kafka自动配置类源码解析1、自动配置类&#xff1a;KafkaAutoConfiguration 注解解析2、自动配置类&#xff1a;KafkaAutoConfiguration 配置的 bean1、KafkaTem…

2024 年, Web 前端开发趋势

希腊哲学家赫拉克利特认为&#xff0c;变化是生命中唯一不变的东西。这句话适用于我们的个人生活、行业和职业领域。 尤其是前端开发领域&#xff0c;新技术、开发趋势、库和框架不断涌现&#xff0c;变化并不陌生。最近发生的一些事件正在改变开发人员构建网站和 Web 应用的方…

mysql中的锁

按照锁的粒度分 表锁 表级锁&#xff0c;增删改操作时&#xff0c;会给正张表加锁&#xff1b; myisam支持表级锁&#xff0c;innodb中默认没有使用表锁&#xff0c; 特点&#xff1a;虽然加锁的开销小&#xff0c;但是并发性能低。 间隙锁 满足某些条件&#xff0c;获取…

Selenium教程11:模拟账号密码,自动登入qq空间

Python爬虫教程30&#xff1a;Selenium网页元素&#xff0c;定位的8种方法&#xff01; Selenium自动化教程02&#xff1a;浏览器options配置及常用的操作方法 Selenium自动化教程03&#xff1a;延时等待的3种方式 Selenium自动化教程04&#xff1a;鼠标键盘网页的模拟操作 …

AJAX进阶(重点)

目录 ◆ 同步代码和异步代码 ◆ 回调函数地狱和 Promise 链式调用 什么是回调函数地狱&#xff1f; Promise - 链式调用 什么是Promise链式调用&#xff1f; Promise 链式应用 &#xff08;重点&#xff09; ◆ async 和 await 使用 async函数和await_捕获错误 ◆ 事…

算法设计与分析实验:滑动窗口与二分查找

目录 一、寻找两个正序数组的中位数 1.1 具体思路 1.2 流程展示 1.3 代码实现 1.4 代码复杂度分析 1.5 运行结果 二、X的平方根 2.1 具体思路 2.2 流程展示 2.3 代码实现 2.4 代码复杂度分析 2.5 运行结果 三、两数之和 II-输入有序数组 3.1 采用二分查找的思想 …

ES6详解

一 let 和 const ES6中可以使用let和const声明变量&#xff0c;用法类似于var const声明的为常量&#xff0c;不可修改&#xff08;但声明对象&#xff0c;对象中的属性可以修改&#xff09;&#xff0c;由于这个特性&#xff0c;它需要在声明的同时就赋值&#xff0c;否则报错…

SpringBoot+SqlServer查询接口

SpringBootSqlServer查询接口 文章目录 SpringBootSqlServer查询接口1. pom环境配置2. common工具包3. 实体类接口映射4. Service层Controller层 需求&#xff1a;根据站号查询前一个小时的所有数据&#xff0c;将数据返回格式为Map<String,List<Map<String,String>…

双非本科准备秋招(9.2)——力扣哈希

1、383. 赎金信 跟昨天的题大同小异&#xff0c;因为只有26个字母&#xff0c;所以可以建个有26个坑位的数组。 做完昨天的题目&#xff0c;这个题没啥新意。 class Solution {public boolean canConstruct(String ransomNote, String magazine) {int[] hashTable new int[…

语言模型大战:GPT、Bard与文心一言,谁才是王者?

如何对GPT-3.5、GPT-4、Bard、文心一言、通义千问的水平进行排序&#xff1f; 在聊技术原理之前我们来先看看几个产品的团队背景 一、团队背景 1.1、ChatGPT ChatGPT团队的成员大多具有计算机科学、人工智能、自然语言处理、机器学习等相关领域的高等教育背景&#xff0c;有…

十分钟发布自己的NFT

概述 本文将以一个例子来说明如何在opensea快速发布自己的NFT智能合约&#xff08;ERC721&#xff09;。本着DRY&#xff08;Dont Repeat Yourself&#xff09;原则&#xff0c;我们需要站在巨人的肩膀上来搭建自己的应用&#xff0c;使用经过社区审计和实践检验的代码可以有效…

【Netty技术专题】「原理分析系列」Netty强大特性之ByteBuf零拷贝技术原理分析

零拷贝Zero-Copy 我们先来看下它的定义&#xff1a; “Zero-copy” describes computer operations in which the CPU does not perform the task of copying data from one memory area to another. This is frequently used to save CPU cycles and memory bandwidth when t…

Cubase 13.0下载安装教程,附安装包和工具,轻松解决安装问题

前言 Cubase是一款专业级的高级音乐创作软件&#xff0c;凭借其无与伦比的灵活工具&#xff0c;您可以快速和直观地创造任何类型的音&#xff0c;充满了各种各样的虚拟仪器、效果和数干种声音。 准备工作 1、Win7及以上系统 2、提前准备好 Cubase 13.0 安装包 没有的可以参…

Kafka高级_生产者ACk机制数据一致性问题

Kafka高级_生产者ACk机制&数据一致性问题 目录需求&#xff1a; 设计思路实现思路分析1.Kafka高级_生产者ACk机制2.Kafka高级数据一致性问题 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c…