Нечёткая текстовая кластеризация

Представьте на секунду, что вам в качестве исходных данных достался вот такой список:

Исходные грязные данные

Если присмотреться, то легко уловить, что на самом деле в нём фигурируют всего 4 города (Москва, Санкт-Петербург, Новосибирск и Тамбов), написанные в разных вариантах - с ошибками и без, с "г" и без "г", с пробелом и без него и т.д. В реальной практике такой "разношёрстный зоопарк" встречается весьма часто, когда данные собираются из нескольких источников или в файл заносят информацию разные люди и делают это творчески.

Оставим пока за скобками глобальную неправильность этой ситуации и то, что её надо принципиально решать на другом, более высоком уровне, и сосредоточимся на следующей задаче: можно ли, имея вот такие корявые текстовые данные, автоматически сгруппировать их в наборы (т.е. 4 кучки в данном случае) по максимальной похожести? Чтобы получить на выходе упорядоченный список, с которым уже можно дальше хоть как-то работать? Примерно так:

Результат кластеризации

Думаю, очевидно, что делать это вручную при большом количестве исходных данных - адская задача.

В науке анализа данных подобную процедуру называют нечёткой текстовой кластеризацией (fuzzy text clustering). В Microsoft Excel решить её можно с помощью Power Query - надстройки для импорта и очистки данных. Впервые она появилась для Excel 2010 (как отдельная вкладка), а начиная с версии Excel 2016 уже стала неотъемлемой его частью на вкладке Данные (Data). Под капотом у этой надстройки работает внутренний язык программирования "М", движок которого и реализует все преобразования данных.

В последних версиях Excel в этот язык добавили новую функцию Table.AddFuzzyClusterColumn, которая как раз и предназначена для решения нашей задачи - группировки неточно совпадающего текста в группы (кластеры). Нюанс же в том, что в интерфейсе Power Query пока нет кнопок или меню, где можно было бы вызвать эту функцию - применить её можно только введя соответствующий М-код вручную.

Давайте попробуем это сделать.

Конвертируем наш исходный "грязный" список в динамическую "умную" таблицу с помощью команды Главная - Форматировать как таблицу (Home - Format as Table) или сочетанием клавиш Ctrl+T. Затем загрузим её в Power Query стандартным образом - кнопкой Из таблицы/диапазона на вкладке Данные (Data - From Table/Range).

Нажмём на кнопку со значком fx, чтобы добавить новый шаг преобразований. В строке формул будет видно, что, по умолчанию, этот шаг просто ссылается на предыдущий (скорее всего он будет у вас называться "Измененный тип" или "Changed type" - это не принципиально).

Теперь главное - введём нашу функцию нечёткой текстовой кластеризации из языка М:

Выполняем кластеризацию

= Table.AddFuzzyClusterColumn( 
    #"Измененный тип",
    "Город",
    "Кластер",
    [ Threshold=0.4, IgnoreCase=true, IgnoreSpace=true, SimilarityColumnName="Подобие" ] )

Первый её аргумент (#"Измененный тип") - исходная таблица, т.е. имя переменной, хранящей данные с предыдущего шага (если имя содержит пробелы, то оно по правилам синтаксиса языка М берётся в кавычки и перед ним ставится решётка).

Второй ("Город") - имя столбца с разношёрстными текстовыми значениями, который мы анализируем.

Третий аргумент ("Кластер") - имя создаваемого столбца, где будут храниться обобщенные значения.

Четвёртый аргумент представляет собой запись (record), где в квадратных скобках через запятую перечисляются параметры кластеризации:

  • Threshold - порог подобия (похожести). Это дробное число от 0 до 1, определяющее требуемую похожесть текста внутри каждого кластера. Чем ближе это число к 1, тем меньше ошибок и различий эта функция будет "прощать".
  • IgnoreCase - логический параметр (true или false), определяющий надо ли игнорировать регистр (обычно - да, надо).
  • IgnoreSpace - логический параметр, определяющий надо ли игнорировать пробелы в тексте (если они расставлены как попало, то надо).
  • SimilarityColumnName - имя дополнительного столбца, где будет выведено числовое значение коэффициента подобия (полезная штука для последующего анализа и уточнения). Если его не задать, то такой столбец создаваться не будет.

После нажатия на Enter в конце формулы (или галочки в строке формул рядом с fx) мы получим два новых столбца - с кластером, к которому Power Query отнёс каждое значение, и коэффициентом подобия исходного и обобщенного значений. 

Само собой, имеет смысл отсортировать всю эту историю (с помощью кнопок фильтра в шапке таблицы) - сначала по кластерам, а потом по убыванию подобия внутри каждого кластера:

Сортировка результатов

Останется выгрузить результаты в Excel стандартным образом - с помощью кнопки Закрыть и загрузить на вкладке Главная (Home - Close & Load).

Бонусом можно добавить разделительную линию между кластерами, чтобы их лучше было видно. Для этого выделяем всю полученную таблицу (кроме шапки) и используем команду Главная - Условное форматирование - Создать правило - Использовать формулу для определения форматируемых ячеек (Home - Conditional formatting - New rule - Use formula to detect which cells to format):

Условное форматирование

Логика тут простая: если значение в текущей строке столбца Кластер не равно значению в следующей, то мы добавляем ячейке нижнюю границу.

Ещё одно усовершенствование, которое можно сделать - это брать значение коэффициента подобия для параметра Threshold из какой-нибудь определённой ячейки на листе. Для удобства можно даже привязать к этой ячейке полосу прокрутки, чтобы динамически задавать коэффициент прямо мышью.

Чтобы реализовать такое, делаем следующее:

  1. Добавляем на лист полосу прокрутки, используя команду Вставить - Элементы управления формы - Полоса прокрутки на вкладке Разработчик (Developer - Insert):

    Вставляем полосу прокрутки для порога подобия

  2. Щёлкаем по нарисованной полосе правой кнопкой мыши, выбираем команду Формат элемента управления (Format control) и задаем в качестве Связанной ячейки (Linked cell) любую подходящую пустую ячейку, например H2. В этой ячейке, при перемещении мышью ползунка полосы прокрутки, будет выводиться число от 0 до 100.
  3. В любой соседней ячейке (например, H3) вводим формулу для формирования коэффициента как дробного числа: =H2/100
  4. Превращаем эту ячейку в именованный диапазон с именем, например, Порог на вкладке Формулы - Диспетчер имён (Formulas - Name manager).
  5. Загружаем эту именованную ячейку в Power Query, используя команды Данные - Из таблицы/диапазона (Data - From Table/Range).
  6. Удаляем шаг Измененный тип (Changed type), если он есть, и проваливаемся внутрь единственной ячейки этой таблицы непосредственно к числовому значению коэффициента, щёлкая по ячейке правой кнопкой мыши - Детализация (Drill down).
  7. Выходим обратно в Excel, используя команду Главная - Закрыть и загрузить в - Только создать подключение (Home - Close & Load to - Only create connection).
После этого останется лишь вернуться к нашему первому запросу на шаг, где мы делали кластеризацию, и заменить в функции Table.AddFuzzyClusterColumn значение коэффициента в параметре Threshold на имя нашего второго запроса-параметра - Порог:

Подставляем порог в формулу

Теперь можно прямо мышью динамически корректировать порог подобия на листе с помощью полосы прокрутки, нажимать кнопку Обновить всё на вкладке Данные (Data - Refresh all) - и тут же получать обновлённые сгруппированные результаты.

По сути, мы тут сделали универсальный инструмент для нечёткой текстовой кластеризации чего угодно. Удобная штука :)

P.S.

Если после вставки имени второго запроса "Порог" в код нашей функции вы получаете ошибку Formula.Firewall, то отключите проверку уровней конфиденциальности при слиянии запросов. Это можно сделать в окне Power Query командой Файл - Параметры и настройки - Параметры запроса - Конфиденциальность - Всегда игнорировать параметры уровней конфиденциальности (File - Options & Settings - Query settings - Privacy - Always ignore privacy levels).

Ссылки по теме




20.06.2025 14:10:01
День добрый, для получения нужного с точки зрения написания результата лучше в исходную таблицу добавить эталонное написание городов или догрузить их из таблицы эталона (только в начало). иначе PQ  берет первое по порядку написание кластера, а оно может быть некорректно записано.
23.06.2025 15:21:41
Отличо! однако
правда в том, сто при наличии таблиц из 100 млн строк например подбирать коэффициэнт при при помощи визуальнго контроля ещё та работа)))
24.06.2025 20:02:39
При наличии в таблице 100 млн. строк эту задачу надо решать вообще не в Excel :D
11.07.2025 14:34:16
Добрый день. Вы не знаете, публиковала ли Майкрософт алгоритм этой функции? Достаточно подробно, чтобы можно было спрограммировать ее самостоятельно, напр. на другом языке.

Поиск с интернете не дает ничего вразумительного. Пишут, например, что как-то используется для сравнения коэф-т Жаккара. Но ведь он не учитывает порядок букв ("Токио" и "Киото" для него идентичны).
Наверх