使用 LINQ 实现 XML 信息检索






4.86/5 (4投票s)
我们根据 XML 语料库及其完整的词汇集,基于词项频率、逆文档频率(TF, IDF)和给定文档中的词语来描述所有概念。
引言
我们根据 XML 语料库及其完整的词汇集,基于词项频率、逆文档频率(TF, IDF)和给定文档中的词语来描述所有概念。在此项目中使用的语言集成查询(LINQ,发音为“link”)的有趣特性之一是 LINQ to XML。LINQ 是 Microsoft .NET Framework 的一个组件,它为语言添加了本地数据查询功能。对于这种方法,我们在项目中添加了 System.Linq
、System.Xml.Linq
和 System.Collections.Generic
的 using 命名空间。
使用代码
用于构建新 XML 文档的主要类是 XDocument
、 XElement
和 XAttribute
类。Elements 属性仅在被搜索的 XElement
的子节点中查找元素,而 .Descendants()
则遍历所有 XML 标记,从被搜索的 XElement
开始查找匹配的节点。
文档预处理被分为三个任务:
- 去除停用词(如冠词和连词)
- 分词
- 词干提取
这涉及到检查字符串的 Generic 列表中每个词项是否是停用词。如果词项不在列表中,则将其分割成一个词元列表,然后通过词干提取(attachment -> attach)将词元合并为其词根。对于分词,我们使用 **正则表达式命名空间** 中的 Regex
和 MatchCollection
类,并根据特殊模式(“\\w+”)匹配任何范围在 0-9、A-Z 和 a-z 之间的字符。
文档表示:所有文档中总共 N 个不同的词被称为索引词(或词汇表)。事实上,XElement.Descendants().Count()
方法返回文档的总数。
//load xml file
XElement xIr = XElement.Load(Server.MapPath("~\\IR2.xml"));
//count all doc
lblTotalDocs.Text = Convert.ToString(xIr.Descendants().Count());
// Match any character in the range 0 - 9, A - Z and a - z (equivalent of POSIX [:alnum:])
Regex regex = new Regex( "\\w+");
MatchCollection matchCollection = regex.Matches(el.Value);
词项频率 (TF):每个不同词项在文档集合中的出现次数。TF 有时也用于衡量一个词在一个特定文档中出现的频率。对于每个匹配的词项,通过考虑重复次数,将其添加到 **Generic Dictionary** 中。Dictionary Generic 类有两种类型的参数:Tkey
和 TValue
。Dictionary Generic 类提供了一个从一组键到一组值的映射。每次添加到字典中的内容包括一个值及其关联的键。为了进行枚举,字典中的每个项都被视为一个 KeyValuePair
结构,表示一个值及其键。
Dictionary<string, List<string>> docsWord = new Dictionary<string, List<string>>();
string docId = el.Attribute("ID").Value;
docsWord.Add(match.Value.ToLower(), new List<string> { docId }); </string>
齐夫定律:齐夫对自然语言词语分布的观察被称为齐夫定律。它描述了整个语料库中的词语行为。在自然语言中,有很少的非常频繁的词语和很少的非常稀有的词语。根据齐夫定律,第 i 个最频繁的词的频率与 1/i 成正比。也就是说,在一个大型语料库中,词语的频率与其排名(rank)的乘积近似恒定。如果我们计算一个语料库中词语的频率,并按频率降序排列,那么一个词的频率与其排名的乘积与另一个词的频率与其排名的乘积大致相等。因此,一个词的频率与其排名成反比。
double zipf = Math.Log(docFreqSorted.Take(200).Last().Value) + Math.Log(200);
int numWordsOccurOnly = docFreq.Where(p => p.Value.Equals(1)).Count();
lblOneDocWords.Text = Convert.ToString(numWordsOccurOnly) +
"((" + numWordsOccurOnly * 100) / corpusFreq.Count() + " %))";
查询处理:结构化检索中的查询可以是结构化的,也可以是非结构化的。从非结构化文本(我们称之为“原始”文本,没有标记)中检索信息。作为 XML 文件的 XDocument
对象,LINQ 查询使用 LINQ 表达式中的 select 子句来指示我们想要返回的数据。LINQ to XML 将返回一个 XElement
对象序列,这些对象代表我们过滤条件匹配的每个 XML 元素节点。XML 文档是一种数据库,通常以树形结构表示,并广泛用于信息检索系统。一旦这些文件被加载到 LINQ to XML API 中,你就可以在该树上编写查询。对于不经常使用 XPath 或 XQuery 的开发人员来说,查询语法比 XPath 或 XQuery 更容易。XPath 是一种语言,它使用基于文档逻辑结构或层次结构路径的寻址语法,来描述在 XML 文档中定位和处理项的方法。倒排文件包含以下形式的记录:<词语, 文档>。对于每个词项,我们定义包含该词项出现的所有文档 ID 的列表。通过将倒排文件存储在 Dictionary Generic 中并使用该类的 orderby
方法,实现了倒排文件的排序。查询处理包括两个步骤:1. 识别包含查询词项路径的文档。2. 识别包含查询词项原始文本的 XML 文档。
让我们考虑一个简单的查询,搜索包含词语“farzaneh”的文档。这个查询可以用 Lambda 表达式表示如下:
Elements ().Where (p => p.Value.ToLower().Contains ("farzaneh")).Select(p=>p.Attribute("ID").Value)
如下为特定文档 ID 的过滤词项:
xIr.Elements().SingleOrDefault(p =>
p.Attribute("ID").Value.Equals("1")).Value
Lambda 表达式类似于匿名方法。所有 Lambda 表达式都使用 Lambda 运算符 =>,读作“goes to”。Lambda 运算符的左侧指定输入参数,右侧包含表达式或语句块。它为联立或析取的布尔路径查询指定多个条件。为了选择 XML 文档的最后 N 个元素,我们使用 .Take(N)
方法。为了选择最后两个元素,使用 XElement.Reverse()
来反转序列中元素的顺序,然后使用 Take(2) 来返回第 2 个元素。
关注点
XML 检索利用 XML 标记来识别并返回最相关和最具体的文档,以回答查询。