Я занят написанием простого алгоритма для нечетких совпадений адресов из двух наборов данных. Я вычисляю расстояние Левенштейна между двумя адресами, а затем добавляю точное совпадение или самое короткое совпадение к сопоставленному массиву.
Однако это очень медленно, так как в худшем случае приходится сравнивать каждый старый адрес с каждым новым адресом.
Мое текущее решение заключается в следующем:
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));
Обратите внимание, что функция стандартизации просто пытается стандартизировать адреса, удаляя ненужную информацию и добавляя почтовые индексы.
Мне, однако, интересно, как это ускорить, так как в настоящий момент это очень дорого или, наоборот, есть ли лучший подход?
Любая помощь приветствуется,
Спасибо!
РЕДАКТИРОВАТЬ:
Размер $ классификации 12000 строк и размер $ строк 17000 строк. Функция стандартизации выглядит следующим образом:
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
}
});
$matches
будет массив совпавших строк.
Это зависит от вашей структуры данных, но лучше использовать array_merge
в сочетании с array_unique
Какой допуск вы использовали для алгоритма расстояния Левенштейна? По моему опыту, менее 0,8 вернуло бы слишком много ложных совпадений. В итоге я использовал ручные поправки для коротких слов, таких как raod = road, иначе оценка была бы неправильной на 1 символ, что соответствовало 75%. Я нашел статью для 12 тестов для поиска адресов с использованием нечеткого сопоставления это может быть полезно для улучшения вашего алгоритма. Примеры включают в себя: