如何对互联网上挖掘到的POI数据进行去重


如何对互联网上挖掘到的POI数据进行去重

一、需求

随着互联网的发展,电子地图被越来越多地使用。在制作电子点图时,需要使用大量的POI数据(POI,Point of Interest,兴趣点),这些地址信息小则一家店铺、一个公交站等,大则一座公园、住宅小区等。目前这些POI数据的来源主要有两种渠道,一种渠道是一些政府机构、商业地图服务厂商去现场实地勘测,记录下各个兴趣点以及他们的经纬度等信息。另一种渠道来源于从海量的互联网数据中挖掘。本发明就是针对后一种,当互联网中挖掘出海量的POI数据后,这些数据因为来源不同、更新时间不通、格式不同等,因此需要对其进行去重,以得到一份最有价值的POI数据,以补充到被绘制的地图中。

二、技术方案

2.1 当前问题

我以一个住宅小区(“杭州市上城区的向阳新村”)为例,来详细分析目前的问题。目前我们采集到的POI数据非常杂,总共有68条数据:

POI名称经度维度采集时间
向阳新村(东门)120.16317630.26000172020
向阳新村3幢-4单元120.16234330.25969952016
向阳新村120.16269330.25977282019
向阳新村2号楼120.1630530.2599052021
向阳新村3幢-1单元120.16255230.25993842018
长生路小区向阳新村1栋120.1629130.25962442018
向阳新村3幢-3单元120.16233530.25987852018
向阳新村3幢-3单元120.16233530.25987052016
向阳新村3幢-2单元120.16243530.25991052016
向阳新村3幢-4单元120.16234630.25970152021
向阳新村2号楼120.16314730.2598622015
向阳新村3幢-1单元120.16256630.25994652020
向阳新村120.16312830.2598642022
向阳新村3号楼120.16246530.25961342016
向阳新村3幢-1单元120.16255230.25992942017
向阳新村3幢-2单元120.16250230.25961142018
向阳新村3号楼120.16254530.25961642018
向阳新村3号楼120.16255930.25962452020
向阳新村3号楼120.16254530.25961642018
向阳新村3幢-4单元120.1623630.25971122020
向阳新村3号楼120.16246530.25961342015
向阳新村120.16269330.25977282019
向阳新村3幢-4单元120.16234630.25969952017
长生路小区向阳新村1栋120.16290930.25959642016
向阳新村(西门)120.16243130.25960232020
向阳新村3号楼120.16246230.25961242017
长生路小区向阳新村1栋120.16291230.25959542017
向阳新村3幢-1单元120.16255230.25993842021
向阳新村2号楼120.16286330.25991842016
向阳新村3号楼120.16241230.25965552015
长生路小区向阳新村2-3栋120.16242230.25977232019
向阳新村3幢-1单元120.16250230.25961142018
向阳新村120.16272630.2597922021
长生路小区向阳新村1幢120.16281730.2596292021
长生路小区向阳新村2-3栋120.16240630.25972752021
向阳新村2号楼120.16317130.25998272020
向阳新村3幢-2单元120.16243530.25991952018
向阳新村3幢-3单元120.16250230.25961142018
向阳新村-西门120.16243230.25960232019
向阳新村3号楼120.16236830.25967552021
向阳新村3幢-2单元120.16243530.25991952021
向阳新村3幢-3单元120.16233530.25987052015
向阳新村-东门120.16317430.26000372019
向阳新村3幢120.16246230.25978242019
向阳新村3幢-4单元120.16250230.25961142018
长生路小区向阳新村1栋120.16290930.25959642015
向阳新村2号楼120.16314830.2598622017
向阳新村3幢-1单元120.1625530.25993042016
向阳新村3幢-2单元120.16243530.25991052015
向阳新村2号楼120.16314730.2598622016
向阳新村3幢-3单元120.16234930.25988812020
向阳新村3幢-2单元120.16243530.25991152017
向阳新村3幢-3单元120.16233530.25987052017
长生路小区向阳新村2-3栋120.16240430.25972752016
长生路小区向阳新村2-3栋120.1624230.25973732020
向阳新村3幢-1单元120.1625530.25993042015
向阳新村3号楼120.16241230.25965552016
向阳新村3幢-2单元120.16244930.25992932020
长生路小区向阳新村2-3栋120.16240630.25972752017
向阳新村2幢120.16290430.25990322019
向阳新村3幢-3单元120.16233530.25987852021
长生路小区向阳新村2-3栋120.16240430.25972752015
向阳新村2号楼120.16286330.25991842015
长生路小区向阳新村1栋120.16297430.25975332019
向阳新村(西门)120.16241730.25959252018
向阳新村3幢-4单元120.16234330.25969952015
长生路小区向阳新村1栋120.16292530.25963322020
向阳新村3幢-4单元120.16234630.25970152018

分析上述数据,我们可以看出几个问题,这些问题就是我们核心要去解决的:

  • 问题一:相同名称的POI数据非常多,比如有多条:“向阳新村3号楼”,但他们的经纬度数据却不同:
POI名称经度维度采集时间
向阳新村3号楼120.162464830.259613362016
向阳新村3号楼120.16254530.259616362018
向阳新村3号楼120.162558730.259624552020
向阳新村3号楼120.16254530.259616362018
向阳新村3号楼120.162464830.259613362015
向阳新村3号楼120.162461830.259612362017
向阳新村3号楼120.162412430.259655532015
向阳新村3号楼120.162368430.259675532021
向阳新村3号楼120.162412430.259655532016
  • 问题二:对于地图渲染而言,尤其是缩放级数没有那么高的场景(比如缩放级数不超过20级),精确到某幢楼的某个单元其实没啥必要。比如上述数据中的“向阳新村3幢-1单元”、“向阳新村3幢-2单元”、“向阳新村3幢-3单元”、“向阳新村3幢-4单元”都可以归一到“向阳新村3幢”。

2.2 步骤一,粗略去重:对于同一较大区域内的所有POI数据,按照POI名称来去重

还是以上述问题一为例

POI名称经度维度采集时间
向阳新村3号楼120.162464830.259613362016
向阳新村3号楼120.16254530.259616362018
向阳新村3号楼120.162558730.259624552020
向阳新村3号楼120.16254530.259616362018
向阳新村3号楼120.162464830.259613362015
向阳新村3号楼120.162461830.259612362017
向阳新村3号楼120.162412430.259655532015
向阳新村3号楼120.162368430.259675532021
向阳新村3号楼120.162412430.259655532016

针对这一问题,我们可以把整个世界平铺成一个平面,然后把平铺后的世界地图按照一定长宽值切割成诸多矩形。然后看这些POI数据的经纬度落在哪些个矩形中,然后在同一矩形中,按POI数据的名称进行去重。 还是以“向阳新村”为例,下图是我们按照一定的长宽值将世界切成很多矩形后,“向阳新村”周边的四个矩形:分别标记是A、B、C、D。 然后对上述的“向阳新村3号楼”的经纬度映射他们的落位信息,发现它们都是落在了区域B,那么我们就可以对该区域中所有的相同名称的POI数据去重。至于去重后“向阳新村3号楼”的具体经纬度取值很简单,对进度和维度取个平均值就行。

当然,在这一步骤中,把平铺后的世界切割按照一定的长宽值切割后矩形,这个长宽值的选择粒度很重要。如果粒度太大,长10公里、宽5公里,这么一个区域内相同的名称的POI数据会很多,比如:肯德基、兰州拉面这些。如果粒度太小,比如长5米、宽5米的,那么又起不到去重的效果。 此外,我们在具体工程实现的上,还会做如下一些优化:

  • 对平铺的世界地图的切割,直接采用了geohash6算法,geohash6的尺寸是:1.2km x 609.4 km,这个效果相对最佳。
  • 在对POI数据去重时,会对POI数据的名称做一些清洗,比如去重标点符号,例如:“向阳新村(东门)”和“向阳新村-东门”都被清洗成“向阳新村东门”。
  • 此外,还可以将POI数据中的电话号码作为分组字段,甚至可以优先电话号码。

2.3 步骤二,更小范围的去重:对于同一较小区域内的所有POI数据,取中心点的数据

我们还是以“向阳新村”为例,经过步骤一的补充,目前的数据情况如下:

POI名称经度维度采集时间
向阳新村120.16312830.2598642022
向阳新村(西门)120.16243130.2596022020
向阳新村2号楼120.1630530.2599052021
向阳新村3幢2单元120.16243530.259922021
向阳新村(东门)120.16317630.2600022020
长生路小区向阳新村1幢120.16281730.2596292021
长生路小区向阳新村2-3栋120.16240630.2597282021
向阳新村3幢120.16246230.2597822019
向阳新村3幢4单元120.16234630.2597022021
向阳新村3幢1单元120.16255230.2599382021
向阳新村2幢120.16290430.2599032019
长生路小区向阳新村1栋120.16292530.2596332020
向阳新村3号楼120.16236830.2596762021
向阳新村3幢3单元120.16233530.2598792021

从上述数据中我们就可以看出,目前就是前文所提的问题二的情况。 这里我们采用更小区域粒度内找中心点的方案。 如果你咨询AI的话,它往往会告诉你使用KMeans等方案来实现,但据我各种尝试效果并没有预期得那么那么好,并且因为使用了KMeans等机器学习的方法后,效率太挺慢。最终我取巧:直接在以GeoHash8粒度来去重,保留名称最小的那条数据。

2.4 最终代码

最终代码就很简单了,一条SQL搞定:

WITH ranked_poi AS (
    SELECT *,
           ROW_NUMBER() OVER (
               PARTITION BY 
                   CASE WHEN is_not_blank(tel) THEN tel 
                   ELSE CONCAT(clean_all_punctuation(name), geohash(longitude, latitude, 6)) 
                   END
               ORDER BY ds DESC
           ) AS rn
    FROM poi_china_detail_merged
    WHERE 
        is_not_blank(name) 
        AND longitude BETWEEN 72.004 AND 137.8347
        AND latitude BETWEEN 0.8293 AND 55.8271
),
filtered_poi AS (
    SELECT *,
           geohash(longitude, latitude, 8) AS geohash8,
           ROW_NUMBER() OVER (
               PARTITION BY geohash(longitude, latitude, 8)
               ORDER BY LENGTH(name)
           ) AS georank
    FROM ranked_poi
    WHERE rn = 1
)
INSERT OVERWRITE TABLE poi_china_detail
SELECT 
    clean_poi_name(name) as name, 
    clean_poi_name(address) as address, 
    admin1, 
    admin2, 
    admin3, 
    ROUND(longitude, 6) as longitude, 
    ROUND(latitude, 6) as latitude, 
    zip_code, 
    tel, 
    big_category, 
    mid_category, 
    sub_category, 
    ds
FROM filtered_poi
WHERE georank = 1
;

undefined: wuliang142857