cp2025 1.0.3
Depersonalization algorithms for course project 2025
Requires Python: >=3.10

## Общее описание

### Установка
`pip install cp2025`

### Проект состоит из следующих основных элементов:
* `cp2025`
  * `algorithms` - классы с алгоритмами обезличивания
  * `utility` - модули с функциями, использующимися несколькими алгоритмами обезличивания
  * `depersonalize.py` - файл со скриптом для быстрого обезличивания
* `data` - файлы с датасетами для тестов
* `docs` - файлы с документацией
* `tests` - тесты к классам, реализующм алгоритмы обезличивания

## Методы оценки качества обезличивания

### Методы оценки рисков деобезличивания
* оценка максимального k, при котором выполняется k-анонимность
* оценка максимального l, при котором выполняется l-разнообразие
* оценка минимального t, при котором выполняется t-близость
* adversarial knowledge gain, описанный в пункте 4.4 статьи https://desfontain.es/PDFs/PhD/TheCostOfPrivacyDestructionOfDataMiningUtilityInAnonymizedDataPublishing.pdf
* adversarial accuracy gain, описанный в пункте 4.4 статьи https://desfontain.es/PDFs/PhD/TheCostOfPrivacyDestructionOfDataMiningUtilityInAnonymizedDataPublishing.pdf
* оценка accuracy деобезличивания при помощи градиентного бустинга

### Методы оценки полезности обезличенных данных
* решение задач классификации и регрессии при помощи градиентного бустинга на изначальном и обезличенном датасетах и сравнение качества работы моделей 
* средний размер класса эквивалентности
* отношение количества уникальных записей к общему числу записей (distinctness)
* доля изменившихся отдельных значений в датасете
* неоднородная энтропия (non-uniform entropy)
* потеря информации на основе энтропии (entropy-based information loss)
* оценка расстояния между исходными и получившимися данными поэлементно (my_by_element_distance)

#### Оценка расстояния между исходными и получившимися данными поэлементно (my_by_element_distance)
* представляет собой среднее поэлементных расстояний
* поэлементное расстояние вычисляется по следующим принципам
  * вычисляется для пары элементов
  * лежит на отрезке от 0 до 1
  * если один элемент - nan, а другой нет, равно 1
  * если оба nan - 0
  * если столбец имеет тип числовых значений
    * обозначим m - минимальное значение в столбце, M - максимальное
    * обозначим первый элемент a, если он - значение и [a1, a2], если это дипапазон
    * аналогично b и [b1, b2] - для второго
    * если оба элемента значения, то расстояние вычисляется по формуле abs(a-b)/(M-m)
    * если один элемент - диапазон (пусть второй), то ((a-b1)^2+(a-b2)^2) / (2 * (M-m) * (b2 - b1))
  * если столбец имеет тип порядковых значений
    * r(x) - функцию получения ранга элемента в столбце, n - количество элементов в столбце
    * обозначим первый элемент за a, если он - значение и за [a1, a2], если это дипапазон
    * аналогично b и [b1, b2] - для второго
    * если оба элемента значения, то расстояние вычисляется по формуле abs(r(a)-r(b))/(n-1)
    * если один элемент - диапазон (пусть второй), то ((r(a)-r(b1))^2+(r(a)-r(b2))^2) / (2 * (M-m) * (r(b2) - r(b1)))
  * если столбец имеет номинальный тип, то
    * обозначим |X| мощность множества X
    * обозначим \in отношение включения
    * обозначим \union оператор объединения
    * обозначим \intersection оператор пересечения
    * обозначим первый элемент за a, а второй - за b (могут быть элементами или множествами возможных элементов)
    * если оба элемента значения, то расстояние вычисляется по формуле int(a!=b)
    * если один элемент - множество (пусть второй), то int(a /in b) / |b|
    * если оба - множества, то |a \intersection b| / |a \union b|, то есть IOU
#### Пример кода
    from cp2025.utility.metrics import my_by_element_distance_columns_ordered
    initial_qi = np.array([
                [1, 1, 1, 1],
                [1, 1, 1, 1],
                [2, 2, 2, 2],
                [2, 2, 2, 3],
    ])
    depersonalized_qi = np.array([
                [1, 1, 1, 1],
                [1, 1, 1, 1],
                [2, 2, 2, 2.5],
                [2, 2, 2, 2.5],
    ])
    my_by_element_distance_columns_ordered(initial_qi, depersonalized_qi)

### Интерфейс классов обезличивания:
* параметры алгоритмов задаются в конструкторах классов
* для обезличивания датасета необходимо передать его методу depersanolize (форматы list, numpy, DataFrame)
* для того чтобы указать колонки-идентификаторы, необходимо передать в параметр identifiers_ids списток индексов этих колонок
* для того чтобы указать колонки-квазиидентификаторы, необходимо передать в параметр quasi_identifiers_ids списток индексов этих колонок
* для того чтобы указать колонки с чувствительной информацией, необходимо передать в параметр sensitives_ids списток индексов этих колонок
* для того чтобы указать, что все оставшиеся колонки относятся к данному типу, необходимо передать в соответствующий параметр значение 'left'
* по умолчанию quasi_identifiers_ids = 'left'
* только один из данных параметров может иметь значение 'left'

### Класс Depersonalizator:
* от данного класса наследуются все классы с алгоритмами обезличивания
* данный класс преобразует данные из входного формата, описанного выше, в формат, удобный для обработки (разбивает на идентификаторы, квазиидентификаторы и чувствительную информацию), а также преобразует выходные данные алгоритмов в единый датасет

### В проекте представлены следующие классы с алгоритмами (классы) обезличивания:
- `AggregationKAnonymityTimeOptimal` - агрегация с разбиением на группы с взятием k-1 ближайших к данной строке с использованием описанного ниже расстояния между элементами
- `AggregationLDiversityTimeOptimal` - алгоритм аналогичен AggregationKAnonymityTimeOptimal, но строки отбираются до тех пор, пока датасет не станет k-анонимным и l-разнообразным
- `Datafly` - реализация алгоритма Datafly (https://www.cerias.purdue.edu/assets/pdf/bibtex_archive/2005-134.pdf) с автоматически создаваемым VGH
- `DistributedDataKAnonymityDepersonalization` - алгоритм обмена данными между двумя владельцами с соблюдением k-анонимности, представленный в статье https://www.cerias.purdue.edu/assets/pdf/bibtex_archive/2005-134.pdf, но вместо DataFly используется groupjoin (см. ниже) 
- `GeneralizationGreedyByOneEqualSizedGroups` - обобщение, при котором каждый столбец делится на группы с одинаковым для каждого столбца минимальным количеством элементов в столбце
- `GeneralizationKAnonymityTimeOptimal` - обобщение с разбиением на группы с взятием k-1 ближайших к данной строке с использованием описанного ниже расстояния между элементами
- `GeneralizationLDiversityTimeOptimal` - алгоритм аналогичен GeneralizationKAnonymityTimeOptimal, но строки отбираются до тех пор, пока датасет не станет k-анонимным и l-разнообразным
- `GroupJoin` - алгоритм объединения групп строк, функция потерь для которых изменяется меньше всего, объединение происходит, пока не будет достигнута необходимая метрика (k-анонимность, l-разнообразие, t-близость)
- `TestIdentifierHasher` - алгоритм хеширования идентификаторов
- `RandomizationDepersonalizator` - добавляет к каждому значению случайную величину с распределением, задаваемым относительно исходных данных
- `Shuffler` - переставляет строки в заданных столбцах
- `ShufflerInBatches` - переставляет строки в каждой группе отдельно в заданных столбцах
- `SuppressionKAnonymityBaseline` - перебирает все варианты подавления значений в датасете, в поиске варианта, достигающего k-анонимности с наименьшим количеством подавлений
- `SuppressionLDiversityBaseline` - алгоритм, аналогичный SuppressionKAnonymityBaseline, но достигается l-разнообразие
- `SuppressionKAnonimityTimeOptimal` - алгоритм, аналогичный GeneralizationKAnonymityTimeOptimal, но используется расстояние Хэмминга и подавление
- `SuppressionLDiversityTimeOptimal` - алгоритм, аналогичный GeneralizationLDiversityTimeOptimal, но используется расстояние Хэмминга и подавление
- `SuppressionTClosenessBaseline` - алгоритм, аналогичный SuppressionKAnonymityBaseline, но достигается t-близость

### Типы столбцов:
* `real` - количественные данные
* `ordered` - порядковые данные
* `unordered` - номинальные данные

### Класс GeneralizationRange:
* класс хранит обобщение нескольких номинальных значений или отрезок порядковых или количественных значений
* для порядковых или количественных значений хранятся минимум и максимум отрезка
* для номинальных - список уникальных значений
* класс поддерживает сравнение на равенство, а для порядковых или количественных значений - отношение порядка
* предполагается, что отношение порядка применяется только к непересекающимся отрезкам, иначе - неопределённое поведение
#### Пример кода
    from cp2025.utility.GeneralizationRange import GeneralizationRange
    rng1 = GeneralizationRange(1, 4, 'real')
    rng2 = GeneralizationRange(5, 6, 'real')
    if rng1 < rng2:
        print("Correct!")
    rng3 = GeneralizationRange(column_type='unordered', column_values=np.array(['a', 'b', 'c'])])

### Описание скрипта для быстрого обезличивания
#### Запуск скрипта
    depersonalize [-h] -i INPUT [-f] [-o OUTPUT] [-a ALGORITHM]
                  [-m {k,l,t}] [-r {s,g,a}] [-k K] [-l L] [-t T]
                  [--qi_ids QI_IDS] [--i_ids I_IDS] [--s_ids S_IDS]
                  [--types TYPES] [--s_types S_TYPES] [-s SEED]
                  [--k_suppressed_lines K_SUPPRESSED_LINES]
                  [--scale SCALE] [--cols2shuffle COLS2SHUFFLE]

#### Флаги
    -h, --help - справка
    -i INPUT, --input INPUT - входной csv файл с датасетом
    -f, --header - входной файл содержит шапку
    -o OUTPUT, --output OUTPUT - выходной csv файл (по умолчанию в консоль)
    -a ALGORITHM, --algorithm ALGORITHM - название алгоритма из: baseline, time_optimal, datafly, greedy, group_join, hasher, randomization, shuffler, batch_shuffler (по умолчанию group_join)
    -m {k,l,t}, --metric {k,l,t} - метрика (k, l, t) соответствующая k-анонимности, l-разнообразнию, t-близости (k-анонимность по умолчанию)
    -r {s,g,a}, --method {s,g,a} - метод обезличивания (s, g, f) соответствующий подавлению, обобщению, агрегации (подавление по умолчанию)
    -k K - значение k для k-анонимности
    -l L - значение l для l-разнообразия
    -t T - значение t для t-близости 
    --qi_ids QI_IDS - номера столбцов c квазиидентификаторами, разделённые запятыми ("1,3,5") или "left"
    --i_ids I_IDS - номера столбцов c идентификаторами, разделённые запятыми ("1,3,5") или "left"
    --s_ids S_IDS - номера столбцов c чувствительными значениями, разделённые запятыми ("1,3,5") или "left"
    --types TYPES - типы столбцов с квазиидентификаторами, например: "rour" для "real, ordered, unordered, real"
    --s_types S_TYPES - типы столбцов c чувствительными значениями, например: "rour" для "real, ordered, unordered, real" (используется в алгоритмах t-близости)
    -s SEED, --seed SEED - сид для генератора случайных чисел  
    --k_suppressed_lines K_SUPPRESSED_LINES - максимальное число подавленных строк для алгоритма Datafly
    --scale SCALE - параметр scale для алгоритма рандомизации
    --cols2shuffle COLS2SHUFFLE - номера столбцов, которые необходимо перемешать, разделённые запятой ("1,3,5")


## Описание алгоритмов обезличивания

### AggregationKAnonymityTimeOptimal
#### Краткое описание алгоритма
Агрегация с разбиением на группы с взятием k-1 ближайших к данной строке с использованием описанного ниже расстояния между элементами
#### Параметры алгоритма
* k - параметр k-анонимности (целое число больше либо равное 1)
* quasi_identifiers_types - тип каждого столбца (массив размера равного количеству квазиидентификаторов со значениями 'real', 'ordered', 'unordered'); по умолчанию значения считаются номинальными
#### Результат работы алгоритма
* K-анонимный датасет
* количество значений-квазиидентификаторов K-анонимного датасета, отличных от соответствующих значений изначального датасета
#### Принцип работы алгоритма
* все идентификаторы подавляются
* все действия далее проводятся над квазиидентификаторами
* если размер датасета меньше k, но не 0 возвращается None, то есть невозможность провести k-анонимизацию 
* прозводится расчёт расстояния для всех пар строк по следующему принципу:
  * расстояние между строками представляет собой сумму расстояний между соответствующими элементами
  * расстояние между двумя значениями лежит на отрезке от 0 до 1
  * расстояние между двумя номинальными значениями равно 0, если они равны, и 1 иначе
  * расстояние между двумя порядковыми значениями равно модулю разности их рангов среди всех элементов столбца, делённому на количество строк
  * расстояние между двумя вещественными значениями равно 0, если они равны, или модулю их разности, делённому на разность максимального и минимального значений, в ином случае
* затем производится группировка значений по следующему принципу
  * берётся первая по списку не отнесённая ни к какой группе строка
  * она группируется с k-1 самыми близкими к ней несгруппированными строками
  * эти действия повторяются пока не останется менее k строк
  * оставшиеся строки присоединяются к последней группе
* для каждого столбца в каждой группе считается агрегированное значение
  * для столбцов с номинальными значениями - мода
  * для столбцов с порядковыми значениями - медиана 
  * для столбцов с числовыми значениями - среднее
* из агрегированных значений составляются агрегированные строки
* каждая строка группы заменяется на агрегированную строку
#### Пример кода
    from cp2025.algorithms.AggregationKAnonymityTimeOptimal import AggregationKAnonymityTimeOptimal
    df = [
      [1, 1, 1, 1],
      [1, 1, 1, 1],
      [2, 2, 2, 2],
      [2, 2, 2, 3],
    ]
    k = 2
    k_anonymus_df, k_replaced = AggregationKAnonymityTimeOptimal(k, ['real']*4).depersonalize(df)

### AggregationLDiversityTimeOptimal
#### Краткое описание алгоритма
Алгоритм аналогичен AggregationKAnonymityTimeOptimal, но строки отбираются до тех пор, пока датасет не станет k-анонимным и l-разнообразным
#### Параметры алгоритма
* k - параметр k-анонимности (целое число больше либо равное 1)
* l - параметр l-разнообразия (целое число больше либо равное 1)
* quasi_identifiers_types - тип каждого столбца (массив размера равного количеству квазиидентификаторов со значениями 'real', 'ordered', 'unordered'); по умолчанию значения считаются номинальными
#### Результат работы алгоритма
* L-разнообразный датасет
* количество значений-квазиидентификаторов K-анонимного датасета, отличных от соответствующих значений изначального датасета
#### Принцип работы алгоритма
* все идентификаторы подавляются
* все действия далее проводятся над квазиидентификаторами
* если размер датасета меньше k, но не 0 возвращается None, то есть невозможность провести k-анонимизацию 
* прозводится расчёт расстояния для всех пар строк по следующему принципу:
  * расстояние между строками представляет собой сумму расстояний между соответствующими элементами
  * расстояние между двумя значениями лежит на отрезке от 0 до 1
  * расстояние между двумя номинальными значениями равно 0, если они равны, и 1 иначе
  * расстояние между двумя порядковыми значениями равно модулю разности их рангов среди всех элементов столбца, делённому на количество строк
  * расстояние между двумя вещественными значениями равно 0, если они равны, или модулю их разности, делённому на разность максимального и минимального значений, в ином случае
* затем производится группировка значений по следующему принципу
  * берётся первая по списку не отнесённая ни к какой группе строка
  * она группируется с необходимым количеством самых близких к ней несгруппированных строк для того, чтобы группа стала содержать хотя бы k строк и для каждого чувствительного столбца - l различных значений
  * эти действия повторяются пока не останется менее k строк
  * оставшиеся строки присоединяются к последней группе
* для каждого столбца в каждой группе считается агрегированное значение
  * для столбцов с номинальными значениями - мода
  * для столбцов с порядковыми значениями - медиана 
  * для столбцов с числовыми значениями - среднее
* из агрегированных значений составляются агрегированные строки
* каждая строка группы заменяется на агрегированную строку
#### Пример кода
    from cp2025.algorithms.AggregationLDiversityTimeOptimal import AggregationLDiversityTimeOptimal
    df = [
      [1, 1, 1, 1, 1],
      [1, 1, 1, 1, 2],
      [2, 2, 2, 2, 2],
      [2, 2, 2, 3, 1],
    ]
    k = 2
    l = 2
    l_diverse_df, k_replaced = AggregationLDiversityTimeOptimal(k, l, ['real']*4).depersonalize(df, sensitives_ids=[4])

### Datafly
#### Краткое описание алгоритма
Реализация алгоритма Datafly (https://www.cerias.purdue.edu/assets/pdf/bibtex_archive/2005-134.pdf) с автоматически создаваемым VGH
#### Параметры алгоритма
* k - параметр k-анонимности (целое число больше либо равное 1)
* quasi_identifiers_types - тип каждого столбца (массив размера равного количеству квазиидентификаторов со значениями 'real', 'ordered', 'unordered'); по умолчанию значения считаются номинальными
* k_suppressed_lines - максимальное количество строк, которые можно подавить (в Datafly, описанном в статье = k-1), по умолчанию = k-1
#### Результат работы алгоритма
* K-анонимный датасет
* количество значений-квазиидентификаторов K-анонимного датасета, отличных от соответствующих значений изначального датасета
#### Принцип работы алгоритма
* все идентификаторы подавляются
* все действия далее проводятся над квазиидентификаторами
* если размер датасета меньше k, но не 0 возвращается None, то есть невозможность провести k-анонимизацию 
* выбирается столбец с наибольшим количеством различных значений
* для этого столбца производится обобщение по следующим принципам
  * если значения столбца номинальные, то 2 наиболее редких значения объединяются в одно при помощи класса GeneralizationRange
  * если значения столбца порядковые или числовые, то наиболее редкое значение объединяется с наиболее редким из соседних с ним по порядку при помощи класса GeneralizationRange
* данные действия повторяются до тех пор, пока число строк, которые не относятся ни к одной группе из равных строк из хотя бы k элементов, не станет меньше k_suppressed_lines
* оставшиеся строки подавляются
#### Пример кода
    from cp2025.algorithms.Datafly import Datafly
    df = [
      [1, 1, 1, 1],
      [1, 1, 1, 1],
      [2, 2, 2, 2],
      [2, 2, 2, 3],
    ]
    k = 2
    k_anonymus_df, k_changes = Datafly(k, ['real']*4).depersonalize(df)

### DistributedDataKAnonymityDepersonalization
#### Краткое описание алгоритма
Алгоритм обмена данными между двумя владельцами с соблюдением k-анонимности, представленный в статье https://www.cerias.purdue.edu/assets/pdf/bibtex_archive/2005-134.pdf, но вместо DataFly используется groupjoin (см. ниже)
#### Параметры алгоритма
* Алгоритм не наследуется от Depersonalizer
* Алгоритм запускается методом exchange_data
* Для запуска алгоритма необходимо создать 2 объекта класса DistributedDataOwnerKAnonymityDepersonalizator, представляющие собой двух владельцев данных, и сообщить им друг о друге при помощи метода set_other_data_owner
* Конструктор класса имеет следующие параметры:
  * quasi_identifiers - квадиидентификаторы данного владельца (матрица numpy)
  * sensitives - чувствительные столбы этого владельца (матрица numpy)
  * k - параметр k-анонимности
  * encryption_key - секретный и отдельный для каждого ключ шифрования
  * quasi_identifiers_types - тип каждого столбца матрицы квазиидентификаторов (массив размера равного количеству квазиидентификаторов со значениями 'real', 'ordered', 'unordered'); по умолчанию значения считаются номинальными
  * seed - сид генератора случайных чисел (по умолчанию не задаётся)
#### Результат работы алгоритма
Объединённый массив, из которого каждый из владельцев может узнать не больше, чем позволяет k-анонимность. Порядок объединения следующий: 
  * квадиидентификаторы первого (того, у кого запускается метод exchange_data)
  * чувствительные значения первого
  * квадиидентификаторы второго
  * чувствительные значения второго

Содержится в атрибуте joined_data каждого владельца
#### Принцип работы алгоритма
* изначально данные обезличиваются локально (у каждого владельца отдельно до k-анонимности)
* вычисляются параметры шифрования (публичный ключ - точка на эллиптической кривой)
* каждый вычисляет статистики датасетов для удобства обезличивания при помощи groupjoin
* обмен информацией о группах, на которые производится обезличивание, осуществляется следующим образом:
  * все индексы групп шифруются алгоритмом передающего владельца
  * зашифрованные индексы групп ещё раз шифруются алгоритмом принимающего владельца
  * в результате, если индексы равны, их итоговое представление у обоих владельцев будет одинаковым, так как используется коммутативное шифрование на эллиптических кривых
* на каждом шаге (цикл):
  * производится обмен информацией о группах
  * производится проверка на "равенство" по следующему принципу: если у первого и у второго существует хотя бы по одной группе, мощность пересечения которых не равна нулю, но меньше k, то разбиения на группы не равны, иначе - равны
  * если равны - завершение цикла
  * если нет - обезличивание на 1 шаг при помощи groupjoin (см. ниже)
* как только разбиения равны, можно производить обмен обезличенными датасетами, что соответственно и происходит, и объединённый датасет становится значением атрибута joined_data каждого владельца
#### Пример кода
    from cp2025.algorithms.DistributedDataKAnonymityDepersonalization import DistributedDataOwnerKAnonymityDepersonalizator
    qi1= np.array([
        [1, 1, 1],
        [1, 1, 2],
        [3, 3, 3],
        [3, 3, 4],
        [4, 4, 5],
        [4, 4, 4],
    ], dtype=object)
    qi2 = np.array([
        [1, 1, 1],
        [1, 1, 2],
        [3, 3, 3],
        [4, 4, 5],
        [3, 3, 4],
        [4, 4, 4],
    ], dtype=object)
    s1= np.array([])
    s2= np.array([])
    dep1 = DistributedDataOwnerKAnonymityDepersonalizator(qi1,s1,2, 7,seed=7,quasi_identifiers_types=['real']*qi1.shape[1])
    dep2 = DistributedDataOwnerKAnonymityDepersonalizator(qi2,s2,2, 8, seed=7,quasi_identifiers_types=['real']*qi2.shape[1])
    dep1.set_other_data_owner(dep2)
    dep2.set_other_data_owner(dep1)
    dep1.exchange_data()
    print(dep1.joined_data)


### GeneralizationGreedyByOneEqualSizedGroups
#### Краткое описание алгоритма
Обобщение, при котором каждый столбец делится на группы с одинаковым для каждого столбца минимальным количеством элементов в столбце
#### Параметры алгоритма
* k - параметр k-анонимности (целое число больше либо равное 1)
* quasi_identifiers_types - тип каждого столбца (массив размера равного количеству квазиидентификаторов со значениями 'real', 'ordered', 'unordered'); по умолчанию значения считаются номинальными
#### Результат работы алгоритма
* K-анонимный датасет
* минимальный размер группы (смотри принцип работы алгоритма)
#### Принцип работы алгоритма
* все идентификаторы подавляются
* все действия далее проводятся над квазиидентификаторами
* если размер датасета меньше k, но не 0 возвращается None, то есть невозможность провести k-анонимизацию 
* производится перебор минимального размера группы (group_size) от k до размера датасета 
* group_size одинаково для каждого столбца
* для каждого размера группы строится обобщённый датасет:
  * для количественных и порядковых столбцов от начала отсортированного столбца берутся по group_size значений
  * для номинальных столбцов в случайном порядке набираются уникальные значения, пока общее количество взятых элементов столбца не достигнет k
  * если после данных k значений идут значения равные последнему взятому значению, они тоже берутся в группу
  * в случае, если в конце остаётся менее k значений, они добавляются в последней сформированной группе
* перебор останавливается, как только обобщённый датасет стал анонимным
* анонимность всегда достижима, так как при group_size, равном размеру датасета, он будет k-анонимным
* бинарный поиск не используется, так как не гарантировано, что для всех group_size больших подходящего group_size обобщённый датасет k-анонимен
#### Пример кода
    from cp2025.algorithms.GeneralizationGreedyByOneEqualSizedGroups import GeneralizationGreedyByOneEqualSizedGroups
    df = [
      [1, 1, 1, 1],
      [1, 1, 1, 1],
      [2, 2, 2, 2],
      [2, 2, 2, 3],
    ]
    k = 2
    k_anonymus_df, group_size = GeneralizationGreedyByOneEqualSizedGroups(k, ['real'] * 4).depersonalize(df)

### GeneralizationKAnonymityTimeOptimal
#### Краткое описание алгоритма
Обобщение с разбиением на группы с взятием k-1 ближайших к данной строке с использованием описанного ниже расстояния между элементами
#### Параметры алгоритма
* k - параметр k-анонимности (целое число больше либо равное 1)
* quasi_identifiers_types - тип каждого столбца (массив размера равного количеству квазиидентификаторов со значениями 'real', 'ordered', 'unordered'); по умолчанию значения считаются номинальными
#### Результат работы алгоритма
* K-анонимный датасет
* число обобщений
#### Принцип работы алгоритма
* все идентификаторы подавляются
* все действия далее проводятся над квазиидентификаторами
* если размер датасета меньше k, но не 0 возвращается None, то есть невозможность провести k-анонимизацию 
* прозводится расчёт расстояния для всех пар строк по следующему принципу:
  * расстояние между строками представляет собой сумму расстояний между соответствующими элементами
  * расстояние между двумя значениями лежит на отрезке о