利用PowerBI从500多个地址中匹配poi点(100000+个)

一、需求

手头有500多个地址信息,另外有十万个poi点的详情信息(包括poi点的详细地址)。
现在需要判断这10万个poi点是否覆盖了这500多个地址。

二、分析

可以很轻松地得出选点思路:
1.看地址信息中是否包含poi点信息;
2.看地址信息是否与poi详细地址相同。

为什么不直接比对两边的地址呢?
原因在于,同一个地址,可能有多种表示方式,有的用“xx省xx市xx区xx路2296号”,有的直接用“xx省xx市xx区xx小区”表示。

三、 手工匹配【不推荐】

手工匹配是最直接的方法,而且手工匹配最可靠。
但是,手工比对最慢。
这种大批量的比对,只有脑袋被门夹了,才会想都不想就用手工开干。
所以,手工比对,只有在万不得已的情况下采用。

四、利用地图慧匹配【不推荐】

由于地图慧可以把地址信息在地图上显示出来,所以,理论上,我们只要分别把poi的详细地址和手头的地址导入地图慧,那么就可以通过技术手段筛选出在地图上重合的点。

但是,地图慧免费版不能超过1000个点,每增加1000个点需要缴200元费用(使用一年),而poi点位是百万级,这将是一笔巨大开支。并且判断重合的点的技术手段需要研发(或寻找)。

所以,这种方式也不推荐。

五、利用powerbi来选点【推荐】

先来看看基本思路。

(一)基本思路

利用powerbi来选点的基本思路是:

首先判断客户地址中是否包含poi点名称(同一个poi点可能出现在不同的省市,所以还需对匹配结果进行筛选,剔除省市不一致的结果)。如果包含,那么选点成功,同时把匹配失败的客户地址单独剔除出来。

把匹配失败的客户地址信息,与poi点的详细地址进行比对。如果两者比对成功,那么,标记为选点成功;如果比对失败,再次剔除出来。

对两次比对都失败的点位进行人工处理:要么手工匹配,要么忽略掉这一部分地址。

(二)具体操作

由于匹配要求PowerBI在每一条具体地址信息中,去10万个poi点中寻找匹配项。
所以,我们可以有两种思路:
一是把地址信息进行分词,然后把分词结果和poi点进行比对。但是PowerBI并不能进行有效的自动分词,而python我又不会,所以,放弃。
二是把每一个poi点名称拿来和地址信息比对,看地址中是否包含poi点名称。这个方法比较简单,直接用PowerBI就可以实现。所以采用此方法。

由于要在10万个poi点中去寻找匹配信息,所以必须要用到函数。该函数的功能是检查每一个poi点是否出现在地址中,如果包含,则返回地址信息,如果不包含,则返回空。

说人话就是:在地址信息中筛选poi点名称,据此建立函数。只不过理论上,我要拿十万个词去地址中筛选十万次,然后返回每一次筛选的结果。

这里就有两种方法,一种是雷公子提供的比较快速的方法,一种是我自己想到的笨办法。
一一列举如下。

1.雷公子的快速方法

这个方法是我参考雷公子的一篇文章《PowerBI中如何实现高性能的模糊词根匹配》
,照猫画虎改写的(连有些名字都没改):

= [数据 = List.Buffer(Table.ToRecords(源)), 
地域词根 = List.Buffer(Table.ToRecords(#"全国点位表 (2)")), 
result = List.Transform( 数据,(x)=>[ 地址=x[地址], 地域词根 = List.Select(地域词根, each Text.Contains(x[地址],_[poi名称])){0}?] ), 
table = Table.FromRecords(result) ][table]

这个公式非常吓人。虽然我能勉强解释清楚,但是自己绝对没本事独立写出这个公式来。
首先【[]】在powerquery中,可以理解为record这种数据格式,所以:

[数据 = List.Buffer(Table.ToRecords(源)), 
地域词根 = List.Buffer(Table.ToRecords(#"全国点位表 (2)")), 
result = List.Transform( 数据,(x)=>[ 地址=x[地址], 地域词根 = List.Select(地域词根, each Text.Contains(x[地址],_[poi名称])){0}?] ), 
table = Table.FromRecords(result) ]

表示一个巨大无比的record。这个record有五个字段,分别是:数据、地域词根、result、地域词根和table。
其次,【[]】在powerquery中,可以理解为一种record操作符——它能获取到record中的字段名称。所以[record][table]表示获取[record]这个record里“table”这个字段的值,它是一个包含匹配结果(为record数据格式)的table。
而在result这一步里,又把一个包含函数的record作为list.transform()函数的第二参数嵌入进去了。
?表示容错,具体怎么个容错法,我自己还没搞清楚。

用这种方式可以在5分钟内获取结果,而且地址和poi匹配成功的结果,是一个record,匹配失败的显示为null。

2.我自己找到的不优雅方法

这种方法比较直观,很好理解。
首先基于地址信息制作一个函数,这个函数接受poi点作为参数,结果是把包含这个poi点的地址筛选出来。

let
源 = (word as text) => let
    源 = Excel.Workbook(File.Contents(path), null, true),
    Sheet1_Sheet = 源{[Item="Sheet1",Kind="Sheet"]}[Data],
    更改的类型 = Table.TransformColumnTypes(Sheet1_Sheet,{{"Column1", type text}}),
    提升的标题 = Table.PromoteHeaders(更改的类型, [PromoteAllScalars=true]),
    更改的类型1 = Table.TransformColumnTypes(提升的标题,{{"地址", type text}}),
    删除的副本 = Table.Distinct(更改的类型1),
    筛选的行 = Table.SelectRows(删除的副本, each Text.Contains([地址], word)),//这是函数的主体部分,这一行之后的几个自定义不是必须的
    已添加自定义 = Table.AddColumn(筛选的行, "省", each Text.Start([地址],Text.PositionOfAny([地址],{"省","市","区"},Occurrence.First)+1)),
    已添加自定义1 = Table.AddColumn(已添加自定义, "市", each Text.Start([地址],Text.PositionOfAny([地址],{"市"},Occurrence.Last)+1)),
    自定义1 = Table.Buffer(已添加自定义1)
    in
        自定义1
in
  源

接下来,在引入poi点源的查询里添加一个新的列,引用该函数,参数是每一行的poi名称。

= Table.AddColumn(自定义1, "自定义", each fnadd([poi名称]))

由于数据量巨大,所以运行一次要耗时十分钟左右。

接下来处理没有匹配上的结果,也就是在地址信息中过滤掉匹配出来的结果,然后再对这些没匹配出来的结果添加一个新的列,函数还是fnadd(),只不过参数变成了poi信息中的“详细地址”列。

再接下来是合并两次匹配到的结果,把第二次仍未匹配到的结果筛选出来进行手工匹配。具体过程略。

六、总结

用powerbi选点的好处是:

  • 没有数据量的限制,powerbi可以轻松处理百万级数据;
  • 处理速度能接受,在10万个poi中匹配500多个地址,耗费十分钟左右;
  • 只要客户地址所在的Excel表格格式不变,模型可以反复使用,跟具体的地址信息无关;
  • 免费。

目前powerbi选点的不足在于:

  • 做匹配工作的人必须熟悉powerbi;
  • 处理速度跟计算机硬件配置关系很大(8核、32G内存,处理500多个客户地址占用内存超过3G,耗时十分钟左右)。

此外,PowerBI提供了模糊合并的功能,我直觉它应该可以处理这个问题,但是我试验了一下,结果不太理想,也许是我的阈值没设置对或者别的什么原因。

不知有没有大神研究过这类批量匹配的问题。

你可能感兴趣的:(利用PowerBI从500多个地址中匹配poi点(100000+个))