模糊匹配地址


Fuzzy Matching Addresses

我正忙于编写一个简单的算法来模糊匹配两个数据集的地址。我正在计算两个地址之间的Levenstein距离,然后将精确匹配或最短匹配添加到匹配的数组中。

然而,这是非常缓慢的,因为在最坏的情况下,它必须将每个旧地址与每个新地址进行比较。

我目前的解决方案如下:

matches = [];
foreach ($classifications as $classification)
{
    $classification = $stringMatchingService->standardize($classification, $stringMatchingService->isClassification());
    $shortest = -1;
    $closest = '';
    $cnt = 0;
    foreach ($lines as $line)
    {
        $line = $stringMatchingService->standardize($line, $stringMatchingService->isRTT());
        if ($classification[CLASSIFICATION_POSTCODE] != $line[RTT_POSTCODE]) {
            continue;
        }
        $lev = levenshtein($classification[CLASSIFICATION_SUBURB], $line[RTT_SUBURB]);
    if ($lev == 0) {
        $matches[$classification[CLASSIFICATION_SUBURB]] = $line[RTT_SUBURB];
        $cnt++;
        break;
    }
    if ($lev <= $shortest || $shortest < 0) {
        //set the closest match and distance
        $closest = $line[RTT_SUBURB];
        $shortest = $lev;
    }
    if ($cnt == count($lines)) {
        $matches[$classification[CLASSIFICATION_SUBURB]] = $closest;
    }
    $cnt++;
}
}
print_r(count($matches));

请注意,标准化函数只是试图通过删除不相关的信息和填充邮政编码来标准化地址。

然而,我想知道如何加快速度,因为目前它非常昂贵,或者是否有更好的方法可以采取?

感谢您的帮助,

谢谢!

编辑:$classifications的大小为12000行,$lines的大小为117000行

 public function standardize($line, $dataSet)
{
    switch ($dataSet) {
        case self::CLASSIFICATIONS:
            if (!isset($line[9], $line[10]) || empty($line[9]) || empty($line[10])) {
                continue;
            }
            $suburb = $line[9];
            $suburb = strtoupper($suburb);
            $suburb = str_replace('EXT', '', $suburb);
            $suburb = str_replace('UIT', '', $suburb);
            $suburb = preg_replace('/[0-9]+/', '', $suburb);
            $postCode = $line[10];
            $postCode = str_pad($postCode, 4,'0', STR_PAD_LEFT);
            $line[9] = $suburb;
            $line[10] = $postCode;
            return $line;
        case self::RTT:
            if (!isset($line[1], $line[0]) || empty($line[1]) || empty($line[0])) {
                continue;
            }
            $suburb = $line[1];
            $suburb = strtoupper($suburb);
            $suburb = str_replace('EXT', '', $suburb);
            $suburb = str_replace('UIT', '', $suburb);
            $suburb = preg_replace('/[0-9]+/', '', $suburb);
            $postCode = $line[0];
            $postCode = str_pad($postCode, 4,'0', STR_PAD_LEFT);
            $line[1] = $suburb;
            $line[0] = $postCode;
            return $line;
    }

它只是为了适当地访问数据,删除某些关键字,并在不符合XXXX格式的情况下填充邮政编码。

这里的问题是,对于每个$classifications行,您要检查$line中是否有一行匹配。=12000*17000。。。

所以,我不知道您的数组的结构,但您可以想象使用array_filter

$matches = array_filter($classifications, function ($entry) use ($lines) {
    foreach ($lines as $line)
    {
        $lev = levenshtein($entry[CLASSIFICATION_SUBURB], $line[RTT_SUBURB]);
        // if match, return true
    }
});

CCD_ 4将是匹配线的阵列。

这取决于您的数据结构,但更好的方法是使用array_mergearray_unique

您对Levenstein距离算法使用了什么容差?根据我的经验,低于0.8会导致太多的假球。我最终对raod=road等短词进行了手动更正,否则分数将错1个字符,使其匹配率达到75%。我发现了一篇文章,共有12个测试,可以使用模糊匹配来查找地址,这对改进算法很有用。示例包括:

  1. 拼写错误
  2. 缺少空格
  3. 类型不正确(街道与道路)
  4. 边界/附近郊区
  5. 缩写
  6. 同义词:楼层与楼层
  7. 单元、公寓或公寓与信件
  8. 数字与字母
  9. 附加词(如前门、部门名称)
  10. 交换的字母
  11. 听起来像
  12. 代币化(不同的输入顺序