ElasticSearch 2 (36) - 信息聚合系列之显著项

摘要

significant_terms(SigTerms)聚合与其他聚合都不相同。目前为止我们看到的所有聚合在本质上都是简单的数学计算。将不同这些构造块相互组合在一起,我们可以创建复杂的聚合以及数据报表。

版本

elasticsearch版本: elasticsearch-2.x

内容

significant_terms(SigTerms)聚合与其他聚合都不相同。目前为止我们看到的所有聚合在本质上都是简单的数学计算。将不同这些构造块相互组合在一起,我们可以创建复杂的聚合以及数据报表。

significant_terms 有着不同的工作机制。对有些人来说,它甚至看起来有点像机器学习。significant_terms 聚合可以找到数据集中不寻常的常用项。

什么是 不普通的共同 ?这些项从统计上看是不寻常的,数据出现的频率会超过后台建议的比例。这些统计上的异常通常象征着数据里的某些有趣信息

例如,假设我们负责探测跟踪信用卡欺诈,客户打电话过来抱怨他们信用卡出现异常交易,它们的帐户遭受到危害。这些交易信息只是更严重问题的症状。在过去,商户要么不知道客户的信用卡信息被盗,要么根本就不知道它们正遭受危害。

我们的任务是找到 危害的共同点,如果我们有 100 个客户抱怨交易异常,他们很有可能都属于同一个商户,而这家商户有可能就是罪魁祸首。

当然,比仅是找到客户共同商户还能做的更好。例如,很多客户在它们近期交易历史记录中会有很大的商户如亚马逊,我们可以将亚马逊排除在外,因为有很多未受危害的信用卡的近期交易商户也都有亚马逊。

这是一个 普通的共同 商户的例子,每个人都共享这个商户,无论有没有遭受危害。我们对它并不感兴趣。

在另一个极端情况下,我们有一些很小商户比如街角的一家药店,它们属于普通但不寻常的情况,只有一两个客户有交易记录。我们同样可以将这些商户排除,因为所有受到危害的信用卡都没有与这些商户发生过交易,我们可以肯定它们不是安全漏洞的责任方。

我们真正想要的是 不普通的共同 商户。所有受到危害的信用卡都与它们发生过交易,但是在未受危害的背景噪声下,它们并不明显。这些商户属于统计异常,它们比应该出现的频率要高。这些不普通的共同商户很有可能就是需要调查的。

significant_terms 聚合就是做这些事情。它分析数据,找出那些与大量背景数据相比出现频率统计异常的项。

我们拿统计异常做什么取决于数据本身。对于信用卡数据,我们可能会想找出信用卡欺诈。对于电商数据,我们可能会想找出未被识别的人口信息,从而进行更高效的市场推广。如果我们正在分析日志,我们可能会发现一个服务器会抛出比它本应抛出的更多异常。significant_terms 的应用还远不止这些。

significant_terms 演示

因为 significant_terms 聚合是通过分析统计信息来工作的,我们需要为数据设置一个阀值让它们更有效。这也就是说我们无法通过只索引少量示例数据来展示它。

正因如此,我们准备了一个有 80,000 文档的数据集,并将它的快照保存在来一个公共演示库中。可以通过以下步骤在集群中还原这些数据:

  1. 在 elasticsearch.yml 配置文件中增加以下配置,将演示库加入到白名单中:

        repositories.url.allowed_urls: ["http://download.elastic.co/*"]
  2. 重启 Elasticsearch。

  3. 运行以下快照命令。(更多使用快照的信息,参见 备份集群(Backing Up Your Cluster))。

        PUT /_snapshot/sigterms #1
    {
    "type": "url",
    "settings": {
    "url": "http://download.elastic.co/definitiveguide/sigterms_demo/"
    }
    } GET /_snapshot/sigterms/_all #2 POST /_snapshot/sigterms/snapshot/_restore #3 GET /mlmovies,mlratings/_recovery #4

    #1 注册一个新的只读地址库,并指向演示快照。

    #2 (可选)检查库内关于快照的详细信息。

    #3 开始还原过程。会在集群中创建两个索引:mlmoviesmlratings

    #4 (可选)使用 Recovery API 监控还原过程。

注意

数据集有 50 MB 会需要一些时间下载。

在演示中,我们会看看 MovieLens 里面用户对电影的评分。在 MovieLens 里,用户可以推荐电影并评分,这样其他用户也可以找到新的电影。在本演示中,我们会基于输入的电影采用 significant_terms 对电影进行推荐。

让我们看看示例中的数据,感受一下我们要处理的内容。本数据集有两个索引,mlmoviesmlratings。让我们首先查看 mlmovies

  GET mlmovies/_search

  {
"took": 4,
"timed_out": false,
"_shards": {...},
"hits": {
"total": 10681,
"max_score": 1,
"hits": [
{
"_index": "mlmovies",
"_type": "mlmovie",
"_id": "2",
"_score": 1,
"_source": {
"offset": 2,
"bytes": 34,
"title": "Jumanji (1995)"
}
},
....

mlmovies 里的每个文档表示一个电影,数据有两个重要字段:电影ID _id 和电影名 title 。 可以忽略 offsetbytes ,它们是用来将数据从原始 CSV 文件提取出来的构件。数据集中有 10,681 部影片。

现在来看看 mlratings

  GET mlratings/_search
{
"took": 3,
"timed_out": false,
"_shards": {...},
"hits": {
"total": 69796,
"max_score": 1,
"hits": [
{
"_index": "mlratings",
"_type": "mlrating",
"_id": "00IC-2jDQFiQkpD6vhbFYA",
"_score": 1,
"_source": {
"offset": 1,
"bytes": 108,
"movie": [122,185,231,292,
316,329,355,356,362,364,370,377,420,
466,480,520,539,586,588,589,594,616
],
"user": 1
}
},
...

这样我们可以看到每个用户的推荐信息。每个文档表示一个用户,用 ID 字段 user 来表示,movie 字段维护一个用户观看过和用户推荐的影片列表。

基于受欢迎度推荐(Recommending Based on Popularity)

我们可以采取的首个策略就是基于受欢迎度向用户推荐影片。对于某部影片,我们可以找到所有推荐过它的用户,然后我们可以将他们的推荐进行聚合并获得推荐中最受欢迎的五部。

我们可以很容易的通过一个 terms 聚合以及一些过滤来表示它,让我们看看 Talladega Nights(塔拉迪加之夜) 这部影片,它是 Will Ferrell 主演的一部关于全国运动汽车竞赛(NASCAR racing)的喜剧。在理想情况下,我们的推荐应该找到类似风格的喜剧(很有可能也是 Will Ferrell 主演的)。

首先我们需要找到影片 Talladega Nights 的 ID :

  GET mlmovies/_search
{
"query": {
"match": {
"title": "Talladega Nights"
}
}
} ...
"hits": [
{
"_index": "mlmovies",
"_type": "mlmovie",
"_id": "46970", #1
"_score": 3.658795,
"_source": {
"offset": 9575,
"bytes": 74,
"title": "Talladega Nights: The Ballad of Ricky Bobby (2006)"
}
},
...

#1 Talladega Nights 的 ID 是 46970

有了ID,我们可以过滤评分,再应用 terms 聚合从喜欢 Talladega Nights 的用户中找到最受欢迎的影片:

  GET mlratings/_search
{
"size" : 0, #1
"query": {
"filtered": {
"filter": {
"term": {
"movie": 46970 #2
}
}
}
},
"aggs": {
"most_popular": {
"terms": {
"field": "movie", #3
"size": 6
}
}
}
}

#1 这次我们查询 mlratings ,将结果内容大小设置为 0 因为我们只对聚合的结果感兴趣。

#2 对影片 Talladega Nights 的 ID 使用过滤器。

#3 最后,使用 terms 桶找到最受欢迎的影片。

mlratings 索引下搜索,然后对影片 Talladega Nights 的 ID 使用过滤器。由于聚合是针对查询范围进行操作的,它可以有效的过滤聚合结果从而得到那些只推荐 Talladega Nights 的用户。最后,我们执行 terms 聚合得到最受欢迎的影片。我们请求排名最前的六个结果,因为 Talladega Nights 本身很有可能就是其中一个结果(我们并不想重复推荐它)。

结果如下:

  {
...
"aggregations": {
"most_popular": {
"buckets": [
{
"key": 46970,
"key_as_string": "46970",
"doc_count": 271
},
{
"key": 2571,
"key_as_string": "2571",
"doc_count": 197
},
{
"key": 318,
"key_as_string": "318",
"doc_count": 196
},
{
"key": 296,
"key_as_string": "296",
"doc_count": 183
},
{
"key": 2959,
"key_as_string": "2959",
"doc_count": 183
},
{
"key": 260,
"key_as_string": "260",
"doc_count": 90
}
]
}
}
...

我们需要将得到结果转换成它们原始影片名,可以通过一个简单的过滤器查询获得:

  GET mlmovies/_search
{
"query": {
"filtered": {
"filter": {
"ids": {
"values": [2571,318,296,2959,260]
}
}
}
}
}

最后获取以下列表:

  1. Matrix, The(黑客帝国)
  2. Shawshank Redemption(肖申克的救赎)
  3. Pulp Fiction(低俗小说)
  4. Fight Club(搏击俱乐部)
  5. Star Wars Episode IV: A New Hope(星球大战 IV:曙光乍现)

好吧,这肯定不是一个好的列表!我喜欢所有这些影片。但问题是:几乎每个人都喜欢它们。这些影片本来就受大众欢迎,也就是说它们出现在每个人的推荐中都会受欢迎。这其实是一个受欢迎影片推荐列表,而不是和影片 Talladega Nights 相关的推荐。

可以通过再次运行聚合轻松验证,而不需要对影片 Talladega Nights 进行过滤。会提供最受欢迎的影片的前五名列表:

  GET mlratings/_search
{
"size" : 0,
"aggs": {
"most_popular": {
"terms": {
"field": "movie",
"size": 5
}
}
}
}

返回列表非常相似:

  1. Shawshank Redemption(肖申克的救赎)
  2. Silence of the Lambs, The(沉默的羔羊)
  3. Pulp Fiction(低俗小说)
  4. Forrest Gump(阿甘正传)
  5. Star Wars Episode IV: A New Hope(星球大战 IV:曙光乍现)

显然,只是检查最受欢迎影片是不能足以创建一个良好而又具鉴别能力的推荐器的。

基于统计的推荐(Recommending Based on Statistics)

现在场景已经设定好,让我们使用 significant_termssignificant_terms 会分析喜欢影片 Talladega Nights 的用户组(前端用户组),并且确定最受欢迎的电影,然后为每个用户(后端用户)构造一个流行影片列表,最后将两者进行比较。

统计异常就是与统计背景相比在前景特征组中过度展现的那些影片。理论上讲,它应该是一组喜剧,因为喜欢 Will Ferrell 喜剧的人给这些影片的评分会比一般人高。

让我们试一下:

  GET mlratings/_search
{
"size" : 0,
"query": {
"filtered": {
"filter": {
"term": {
"movie": 46970
}
}
}
},
"aggs": {
"most_sig": {
"significant_terms": { #1
"field": "movie",
"size": 6
}
}
}
}

#1 设置几乎一模一样,只是用 significant_terms 替代了 terms

正如我们所见,查询也几乎是一样的。我们过滤出喜欢影片 Talladega Nights 的用户,他们组成了前景特征用户组。默认情况下,significant_terms 会使用整个索引里的数据作为统计背景,所以我们不需要特别的处理。

terms 类似,结果返回了一组桶,不过有更多的元数据信息:

  ...
"aggregations": {
"most_sig": {
"doc_count": 271, #1
"buckets": [
{
"key": 46970,
"key_as_string": "46970",
"doc_count": 271,
"score": 256.549815498155,
"bg_count": 271
},
{
"key": 52245, #2
"key_as_string": "52245",
"doc_count": 59, #3
"score": 17.66462367106966,
"bg_count": 185 #4
},
{
"key": 8641,
"key_as_string": "8641",
"doc_count": 107,
"score": 13.884387742677438,
"bg_count": 762
},
{
"key": 58156,
"key_as_string": "58156",
"doc_count": 17,
"score": 9.746428133759462,
"bg_count": 28
},
{
"key": 52973,
"key_as_string": "52973",
"doc_count": 95,
"score": 9.65770100311672,
"bg_count": 857
},
{
"key": 35836,
"key_as_string": "35836",
"doc_count": 128,
"score": 9.199001116457955,
"bg_count": 1610
}
]
...

#1 顶层 doc_count 展现了前景特征组里文档的数量。

#2 每个桶里面列出了聚合的键值(例如,影片的ID)。

#3 桶内文档的数量 doc_count

#4 背景文档的数量,表示该值在整个统计背景里出现的频度。

可以看到我们获得的第一个桶是 Talladega Nights 。它可以在所有 271 个文档中找到,这并不意外。让我们看下一个桶:键值 52245

这个 ID 对应影片 Blades of Glory(荣誉之刃) ,它是一部关于男子学习滑冰的喜剧,也是由 Will Ferrell 主演。我们可以看到喜欢 Talladega Nights 的用户对它的推荐是 59 次。这也意味着 21% 的前景特征用户组推荐了影片 Blades of Glory59 / 271 = 0.2177)。

形成对比的是,Blades of Glory 在整个数据集合中仅被推荐了 185 次,只占 0.26% (185 / 69796 = 0.00265)。因此 Blades of Glory 是一个统计异常:它在喜欢 Talladega Nights 的用户中是不普通的共同。这样我们就找到了一个好的推荐!。

如果我们看完整的列表,它们都是好的喜剧推荐(其中很多也是由 Will Ferrell 主演):

  1. Blades of Glory(荣誉之刃)
  2. Anchorman: The Legend of Ron Burgundy(王牌播音员)
  3. Semi-Pro(半职业选手)
  4. Knocked Up(一夜大肚)
  5. 40-Year-Old Virgin, The(四十岁的老处男)

这只是 significant_terms 它强大的一个示例,一旦开始使用 significant_terms,我们可能碰到这样的情况,我们不想要最受欢迎的,而想要不普通的共同。这个简单的聚合可以为我们揭示出一些数据里出人意料的复杂趋势。

参考

elastic.co:

Significant Terms

ElasticSearch 2 (36) - 信息聚合系列之显著项的更多相关文章

  1. ElasticSearch 2 (30) - 信息聚合系列之条形图

    ElasticSearch 2 (30) - 信息聚合系列之条形图 摘要 版本 elasticsearch版本: elasticsearch-2.x 内容 聚合还有一个令人激动的特性就是能够十分容易地 ...

  2. ElasticSearch 2 (37) - 信息聚合系列之内存与延时

    ElasticSearch 2 (37) - 信息聚合系列之内存与延时 摘要 控制内存使用与延时 版本 elasticsearch版本: elasticsearch-2.x 内容 Fielddata ...

  3. ElasticSearch 2 (38) - 信息聚合系列之结束与思考

    ElasticSearch 2 (38) - 信息聚合系列之结束与思考 摘要 版本 elasticsearch版本: elasticsearch-2.x 内容 本小节涵盖了许多基本理论以及很多深入的技 ...

  4. ElasticSearch 2 (35) - 信息聚合系列之近似聚合

    ElasticSearch 2 (35) - 信息聚合系列之近似聚合 摘要 如果所有的数据都在一台机器上,那么生活会容易许多,CS201 课商教的经典算法就足够应付这些问题.但如果所有的数据都在一台机 ...

  5. ElasticSearch 2 (34) - 信息聚合系列之多值排序

    ElasticSearch 2 (34) - 信息聚合系列之多值排序 摘要 多值桶(terms.histogram 和 date_histogram)动态生成很多桶,Elasticsearch 是如何 ...

  6. ElasticSearch 2 (33) - 信息聚合系列之聚合过滤

    ElasticSearch 2 (33) - 信息聚合系列之聚合过滤 摘要 聚合范围限定还有一个自然的扩展就是过滤.因为聚合是在查询结果范围内操作的,任何可以适用于查询的过滤器也可以应用在聚合上. 版 ...

  7. ElasticSearch 2 (32) - 信息聚合系列之范围限定

    ElasticSearch 2 (32) - 信息聚合系列之范围限定 摘要 到目前为止我们看到的所有聚合的例子都省略了搜索请求,完整的请求就是聚合本身. 聚合与搜索请求同时执行,但是我们需要理解一个新 ...

  8. ElasticSearch 2 (31) - 信息聚合系列之时间处理

    ElasticSearch 2 (31) - 信息聚合系列之时间处理 摘要 如果说搜索是 Elasticsearch 里最受欢迎的功能,那么按时间创建直方图一定排在第二位.为什么需要使用时间直方图? ...

  9. ElasticSearch 2 (29) - 信息聚合系列之测试驱动

    ElasticSearch 2 (29) - 信息聚合系列之测试驱动 摘要 我们可以用以下几页定义不同的聚合和它们的语法,但学习聚合的最佳途径就是用实例来说明.一旦我们获得了聚合的思想,以及如何合理地 ...

随机推荐

  1. 死磕nginx系列-nginx日志配置

    nginx access日志配置 access_log日志配置 access_log用来定义日志级别,日志位置.语法如下: 日志级别: debug > info > notice > ...

  2. BZOJ3578:GTY的人类基因组计划2(集合hash,STL)

    Description GTY召唤了n个人来做实验,GTY家的房子很大,有m个房间一开始所有人都在1号房间里,GTY会命令某人去某个房间等待做实验,或者命令一段区间的房间开始实验,实验会获得一些实验信 ...

  3. docker devicemapper 问题

    DOCKER_OPTS= "--storage-driver=devicemapper  --storage-opt  dm.basesize=50G --storage-opt dm.da ...

  4. 1、JUC--volatile 关键字-内存可见性

    Java JUC简介 在 Java 5.0 提供了 java.util.concurrent (简称JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线 ...

  5. 20165302 程上杰 Exp2 后门原理与实践

    一,后门概念 后门就是不经过正常认证流程而访问系统的通道. 二,后门工具 1.netcat(nc.ncat) 是一个底层工具,进行基本的TCP UDP数据收发.常被与其他工具结合使用,起到后门的作用. ...

  6. [转]改善C#程序的建议4:C#中标准Dispose模式的实现

    需要明确一下C#程序(或者说.NET)中的资源.简单的说来,C#中的每一个类型都代表一种资源,而资源又分为两类: 托管资源:由CLR管理分配和释放的资源,即由CLR里new出来的对象: 非托管资源:不 ...

  7. leetcode18—4Sum

    Given an array nums of n integers and an integer target, are there elements a, b, c, and d in nums s ...

  8. Oracle 解决【ORA-01704:字符串文字太长】

    错误提示:oracle在toad中执行一段sql语句时,出现错误‘ORA-01704:字符串文字太长’.如下图: 原因:一般为包含有对CLOB字段的数据操作.如果CLOB字段的内容非常大的时候,会导致 ...

  9. 关于docker构建镜像

    今天正好看到这一块了,记录一下,希望可以帮助到大家. 构建Dockerfile 先来看一个示例: --------------------------------------------------- ...

  10. CODE[VS] 1159 最大全0子矩阵

    写一道CODEVS的题目 其实我还是很喜欢CODEVS的界面的 主要是系统地学习一下悬线法这个看似十分简单,实际就是十分简单的算法 对于一些详细的东西参考dalao's blog,不喜勿喷 对于悬线法 ...