Microsegment.ru
  • Главная страница
  • О проекте
  • Портфолио
  • Блог
Системы

Кластерный анализ модели предоставления разрешений в Tessa

Кластерный анализ модели предоставления разрешений в Tessa
Системы

Для анализа существующей модели предоставления разрешений в информационной системе (далее ИС) предлагается провести кластеризацию данных, используемых для предоставления разрешений. В кластеризации может принять участие около 100 числовых и текстовых параметров, представляющих собой категориальные признаки с количеством категорий от 2 до нескольких тысяч. Далее представлена основа выполнения кластеризации данных с большим количеством текстовых и числовых показателей на Python.

В целях данного анализа совместно с DeepSeek был разработан код генерации ролевой модели и модели предоставления разрешений в Tessa.

In [3]:
# 0. Загрузка библиотек
import pandas as pd
import numpy as np
import random

from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score, silhouette_samples, confusion_matrix

import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px

Кластерный анализ тестовой модели предоставления разрешений¶

In [5]:
# 1. Загрузка и подготовка данных
data = pd.read_csv('df_permission.csv', encoding='utf-8-sig')

print('Анализ данных:')
print(f'Размер: {data.shape}')
data.head(3)
Анализ данных:
Размер: (756, 30)
Out[5]:
Unnamed: 0 Название роли Тип роли Скрытая роль Тип карточки Состояние карточки Место регистрации Создание карточки Чтение карточки Редактирование карточки … Права супермодератора Подписка на уведомления Расширенные настройки прав доступа Создание шаблона и копирование Пропуск этапов Редактирование своих сообщений Редактирование всех сообщений Добавление обсуждений Чтение обсуждений Чтение и отправка сообщений
0 0 Смирнов Мария Сотрудник 0 Входящий документ Проект Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 1 1
1 1 Смирнов Мария Сотрудник 0 Входящий документ На согласовании Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 1 1
2 2 Смирнов Мария Сотрудник 0 Входящий документ Утвержден Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 1 1

3 rows × 30 columns

In [6]:
# Преобразуем все в строки и заполняем пропуски
data = data.astype(str).fillna('Unknown')

# Убираем логически не нужные поля
#data = data.drop(['rule_id', 'rule_name', 'role_id', 'document_type_id', 'condition'], axis=1)

print(data.info())
print()
display(data.describe())
print()
data.head(5)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 756 entries, 0 to 755
Data columns (total 30 columns):
 #   Column                                      Non-Null Count  Dtype 
---  ------                                      --------------  ----- 
 0   Unnamed: 0                                  756 non-null    object
 1   Название роли                               756 non-null    object
 2   Тип роли                                    756 non-null    object
 3   Скрытая роль                                756 non-null    object
 4   Тип карточки                                756 non-null    object
 5   Состояние карточки                          756 non-null    object
 6   Место регистрации                           756 non-null    object
 7   Создание карточки                           756 non-null    object
 8   Чтение карточки                             756 non-null    object
 9   Редактирование карточки                     756 non-null    object
 10  Удаление карточки                           756 non-null    object
 11  Добавление файлов                           756 non-null    object
 12  Редактирование всех файлов                  756 non-null    object
 13  Удаление всех файлов                        756 non-null    object
 14  Редактирование собственных файлов           756 non-null    object
 15  Удаление собственных файлов                 756 non-null    object
 16  Инициация типового процесса отправки задач  756 non-null    object
 17  Ручное редактирование номера                756 non-null    object
 18  Редактирование маршрута                     756 non-null    object
 19  Полный пересчет маршрута                    756 non-null    object
 20  Права супермодератора                       756 non-null    object
 21  Подписка на уведомления                     756 non-null    object
 22  Расширенные настройки прав доступа          756 non-null    object
 23  Создание шаблона и копирование              756 non-null    object
 24  Пропуск этапов                              756 non-null    object
 25  Редактирование своих сообщений              756 non-null    object
 26  Редактирование всех сообщений               756 non-null    object
 27  Добавление обсуждений                       756 non-null    object
 28  Чтение обсуждений                           756 non-null    object
 29  Чтение и отправка сообщений                 756 non-null    object
dtypes: object(30)
memory usage: 177.3+ KB
None

Unnamed: 0 Название роли Тип роли Скрытая роль Тип карточки Состояние карточки Место регистрации Создание карточки Чтение карточки Редактирование карточки … Права супермодератора Подписка на уведомления Расширенные настройки прав доступа Создание шаблона и копирование Пропуск этапов Редактирование своих сообщений Редактирование всех сообщений Добавление обсуждений Чтение обсуждений Чтение и отправка сообщений
count 756 756 756 756 756 756 756 756 756 756 … 756 756 756 756 756 756 756 756 756 756
unique 756 40 3 1 5 4 4 2 1 2 … 2 2 2 2 2 2 2 2 2 2
top 0 Иванов Иван Сотрудник 0 Счет Проект Головной офис 1 1 1 … 0 1 0 1 0 1 0 1 1 1
freq 1 64 628 756 208 189 596 528 756 501 … 516 687 516 432 516 570 516 585 678 670

4 rows × 30 columns


Out[6]:
Unnamed: 0 Название роли Тип роли Скрытая роль Тип карточки Состояние карточки Место регистрации Создание карточки Чтение карточки Редактирование карточки … Права супермодератора Подписка на уведомления Расширенные настройки прав доступа Создание шаблона и копирование Пропуск этапов Редактирование своих сообщений Редактирование всех сообщений Добавление обсуждений Чтение обсуждений Чтение и отправка сообщений
0 0 Смирнов Мария Сотрудник 0 Входящий документ Проект Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 1 1
1 1 Смирнов Мария Сотрудник 0 Входящий документ На согласовании Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 1 1
2 2 Смирнов Мария Сотрудник 0 Входящий документ Утвержден Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 1 1
3 3 Смирнов Мария Сотрудник 0 Входящий документ Архив Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 1 1
4 4 Смирнов Мария Сотрудник 0 Договор Проект Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 1 1

5 rows × 30 columns

In [7]:
# 2. Анализ уникальных значений
print("Уникальные значения по полям:")
for col in data.columns:
    unique_count = data[col].nunique()
    print(f"{col}: {unique_count} уникальных значений")
Уникальные значения по полям:
Unnamed: 0: 756 уникальных значений
Название роли: 40 уникальных значений
Тип роли: 3 уникальных значений
Скрытая роль: 1 уникальных значений
Тип карточки: 5 уникальных значений
Состояние карточки: 4 уникальных значений
Место регистрации: 4 уникальных значений
Создание карточки: 2 уникальных значений
Чтение карточки: 1 уникальных значений
Редактирование карточки: 2 уникальных значений
Удаление карточки: 2 уникальных значений
Добавление файлов: 2 уникальных значений
Редактирование всех файлов: 2 уникальных значений
Удаление всех файлов: 2 уникальных значений
Редактирование собственных файлов: 2 уникальных значений
Удаление собственных файлов: 2 уникальных значений
Инициация типового процесса отправки задач: 2 уникальных значений
Ручное редактирование номера: 2 уникальных значений
Редактирование маршрута: 2 уникальных значений
Полный пересчет маршрута: 2 уникальных значений
Права супермодератора: 2 уникальных значений
Подписка на уведомления: 2 уникальных значений
Расширенные настройки прав доступа: 2 уникальных значений
Создание шаблона и копирование: 2 уникальных значений
Пропуск этапов: 2 уникальных значений
Редактирование своих сообщений: 2 уникальных значений
Редактирование всех сообщений: 2 уникальных значений
Добавление обсуждений: 2 уникальных значений
Чтение обсуждений: 2 уникальных значений
Чтение и отправка сообщений: 2 уникальных значений
In [8]:
# 3. Упрощенное кодирование категориальных признаков
def simple_category_encoding(data):
    """Упрощенное кодирование категориальных признаков"""
    encoded_data = pd.DataFrame()
    
    for col in data.columns:
        unique_count = data[col].nunique()
        
        if unique_count <= 20:
            # One-Hot для малого количества категорий
            dummies = pd.get_dummies(data[col], prefix=col)
            encoded_data = pd.concat([encoded_data, dummies], axis=1)
        else:
            # Frequency encoding для большого количества категорий
            freq_map = data[col].value_counts().to_dict()
            encoded_data[col] = data[col].map(freq_map)
    
    return encoded_data

print("Кодирование признаков...")
encoded_data = simple_category_encoding(data)
print(f"После кодирования: {encoded_data.shape} признаков")
encoded_data.head(5)
Кодирование признаков...
После кодирования: (756, 64) признаков
Out[8]:
Unnamed: 0 Название роли Тип роли_Подразделение Тип роли_Сотрудник Тип роли_Статическая роль Скрытая роль_0 Тип карточки_Входящий документ Тип карточки_Договор Тип карточки_Приказ Тип карточки_Служебная записка … Редактирование своих сообщений_0 Редактирование своих сообщений_1 Редактирование всех сообщений_0 Редактирование всех сообщений_1 Добавление обсуждений_0 Добавление обсуждений_1 Чтение обсуждений_0 Чтение обсуждений_1 Чтение и отправка сообщений_0 Чтение и отправка сообщений_1
0 1 20 False True False True True False False False … False True False True False True False True False True
1 1 20 False True False True True False False False … False True False True False True False True False True
2 1 20 False True False True True False False False … False True False True False True False True False True
3 1 20 False True False True True False False False … False True False True False True False True False True
4 1 20 False True False True False True False False … False True False True False True False True False True

5 rows × 64 columns

In [9]:
# 4. Стандартизация
scaler = StandardScaler()
scaled_data = scaler.fit_transform(encoded_data)
scaled_data
Out[9]:
array([[ 0.        , -0.45024824, -0.37222872, ...,  0.33918173,
        -0.35827114,  0.35827114],
       [ 0.        , -0.45024824, -0.37222872, ...,  0.33918173,
        -0.35827114,  0.35827114],
       [ 0.        , -0.45024824, -0.37222872, ...,  0.33918173,
        -0.35827114,  0.35827114],
       ...,
       [ 0.        , -1.2191963 , -0.37222872, ...,  0.33918173,
         2.79118213, -2.79118213],
       [ 0.        , -1.2191963 , -0.37222872, ..., -2.94827198,
        -0.35827114,  0.35827114],
       [ 0.        , -1.2191963 , -0.37222872, ...,  0.33918173,
        -0.35827114,  0.35827114]])
In [10]:
# 5. Уменьшение размерности с проверкой
if scaled_data.shape[1] > 2:
    # Используем PCA только если много признаков
    pca = PCA(n_components=0.9)  # 90% дисперсии
    reduced_data = pca.fit_transform(scaled_data)
    print(f"После PCA: {reduced_data.shape[1]} компонент")
else:
    reduced_data = scaled_data
    print("PCA не применен - мало признаков")

print(reduced_data.shape)
reduced_data
После PCA: 16 компонент
(756, 16)
Out[10]:
array([[-6.38461387, -2.07976516,  0.27327496, ..., -0.69588419,
         0.17315388,  0.1680276 ],
       [-6.3707872 , -2.05304326,  0.27375451, ..., -0.97059083,
         0.30411373,  0.19108113],
       [-6.36915588, -2.05302299,  0.22194772, ..., -0.88420406,
         0.21788984,  0.01218988],
       ...,
       [ 5.35477888, -0.37243403,  5.07032899, ...,  0.33104472,
        -3.83735829, -0.55014153],
       [ 6.08172755, -1.42192222,  2.06117267, ...,  0.85109714,
        -1.09269228,  4.60214457],
       [ 4.22651852,  1.15695471,  0.96363363, ..., -2.37967269,
        -2.03487862, -0.94841155]])
In [11]:
def find_optimal_clusters(data, max_k=8):
    """Подбор оптимального числа кластеров с анализом 'локтя' и методом силуэта"""
    best_score = -1
    best_k = 2
    wcss = []  # Within-cluster sum of squares

    # Зависимость количества кластеров 
    # от количества объектов в датафрейме
    if len(data) <= 10:
        max_k = int(len(data) / 2)
    elif 10 < len(data) <= 100:
        max_k = int(len(data) / 10)
    elif 100 < len(data) <= 1000:
        max_k = 10 + int(len(data) / 100)
    elif 1000 < len(data):
        max_k = 20 + int(len(data) / 10000)
        if max_k > 50: max_k = 50

    for k in range(2, max_k + 1):
        kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
        labels = kmeans.fit_predict(data)
        score = silhouette_score(data, labels)
        silhouette_vals = silhouette_samples(data, labels)

        # Рассчитываем сумму квадратов расстояний
        wcss.append(kmeans.inertia_)

        # Вычисляем статистику для значений силуэта
        min_val = np.min(silhouette_vals)
        mean_val = np.mean(silhouette_vals)
        median_val = np.median(silhouette_vals)
        max_val = np.max(silhouette_vals)

        print(f'Кластеров: {k}. Score: {score:.3f}')
        print(f'Silhouette values statistics:')
        #print(f"  Min: {min_val:.3f}")
        #print(f"  Mean: {mean_val:.3f}")
        #print(f"  Median: {median_val:.3f}")
        #print(f"  Max: {max_val:.3f}")
        display(pd.DataFrame({'Min':[min_val], 'Mean':[mean_val], 'Median':[median_val], 'Max':[max_val]}).round(3))

        if score > best_score:
            best_score = score
            best_k = k
            
            best_min_val = min_val
            best_mean_val = mean_val
            best_median_val = median_val
            best_max_val = max_val

    # Выводим оптимальное число кластеров по методу силуэта
    print(f'\nОптимальное число кластеров, выбранное "методом cилуэта": {best_k}. Score: {best_score:.3f}')
    print(f'Silhouette values statistics:')
    display(pd.DataFrame({'Min':[best_min_val], 'Mean':[best_mean_val], 'Median':[best_median_val], 'Max':[best_max_val]}).round(3))

    # Строим график для анализа 'локтя'
    print('\nВизуальная проверка выбранного количества кластеров "методом локтя" (Elbow Method):')
    plt.figure(figsize=(10, 6))
    plt.plot(range(2, max_k + 1), wcss, marker='o')
    plt.title('Метод локтя (Elbow Method)')
    plt.xlabel('Количество кластеров')
    plt.ylabel('Сумма квадратов расстояний (WCSS)')
    plt.grid(True)
    plt.show()

    return best_k

optimal_clusters = find_optimal_clusters(reduced_data)
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 2. Score: 0.365
Silhouette values statistics:
Min Mean Median Max
0 0.034 0.365 0.279 0.694
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 3. Score: 0.395
Silhouette values statistics:
Min Mean Median Max
0 -0.12 0.395 0.432 0.624
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 4. Score: 0.429
Silhouette values statistics:
Min Mean Median Max
0 -0.192 0.429 0.479 0.636
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 5. Score: 0.421
Silhouette values statistics:
Min Mean Median Max
0 -0.198 0.421 0.493 0.608
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 6. Score: 0.425
Silhouette values statistics:
Min Mean Median Max
0 -0.171 0.425 0.491 0.621
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 7. Score: 0.390
Silhouette values statistics:
Min Mean Median Max
0 -0.172 0.39 0.565 0.621
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 8. Score: 0.400
Silhouette values statistics:
Min Mean Median Max
0 -0.143 0.4 0.564 0.62
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 9. Score: 0.266
Silhouette values statistics:
Min Mean Median Max
0 -0.143 0.266 0.226 0.62
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 10. Score: 0.414
Silhouette values statistics:
Min Mean Median Max
0 -0.12 0.414 0.546 0.62
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 11. Score: 0.268
Silhouette values statistics:
Min Mean Median Max
0 -0.123 0.268 0.207 0.62
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 12. Score: 0.280
Silhouette values statistics:
Min Mean Median Max
0 -0.099 0.28 0.218 0.614
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 13. Score: 0.328
Silhouette values statistics:
Min Mean Median Max
0 -0.173 0.328 0.322 0.622
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 14. Score: 0.306
Silhouette values statistics:
Min Mean Median Max
0 -0.142 0.306 0.264 0.596
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 15. Score: 0.312
Silhouette values statistics:
Min Mean Median Max
0 -0.086 0.312 0.286 0.619
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 16. Score: 0.325
Silhouette values statistics:
Min Mean Median Max
0 -0.196 0.325 0.299 0.611
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
Кластеров: 17. Score: 0.335
Silhouette values statistics:
Min Mean Median Max
0 -0.17 0.335 0.269 0.593
Оптимальное число кластеров, выбранное "методом cилуэта": 4. Score: 0.429
Silhouette values statistics:
Min Mean Median Max
0 -0.192 0.429 0.479 0.636
Визуальная проверка выбранного количества кластеров "методом локтя" (Elbow Method):
No description has been provided for this image
In [12]:
# 7. Финальная кластеризация
kmeans = KMeans(n_clusters=optimal_clusters, random_state=42, n_init=10)
clusters = kmeans.fit_predict(reduced_data)
silhouette_vals = silhouette_samples(reduced_data, clusters)
C:\Users\featu\anaconda3\Lib\site-packages\sklearn\cluster\_kmeans.py:1436: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3.
  warnings.warn(
In [13]:
# Предварительный просмотр результатов кластерного анализа
data_with_clusters = data.copy()
data_with_clusters['cluster'] = clusters
data_with_clusters['silhouette_vals'] = silhouette_vals

data_with_clusters.to_excel('data_with_clusters.xlsx')

print('Предварительный просмотр результатов кластерного анализа:')
#pd.DataFrame({'clusters': clusters, 'silhouette_vals': silhouette_vals})
data_with_clusters.head(5)
Предварительный просмотр результатов кластерного анализа:
Out[13]:
Unnamed: 0 Название роли Тип роли Скрытая роль Тип карточки Состояние карточки Место регистрации Создание карточки Чтение карточки Редактирование карточки … Расширенные настройки прав доступа Создание шаблона и копирование Пропуск этапов Редактирование своих сообщений Редактирование всех сообщений Добавление обсуждений Чтение обсуждений Чтение и отправка сообщений cluster silhouette_vals
0 0 Смирнов Мария Сотрудник 0 Входящий документ Проект Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 2 0.609365
1 1 Смирнов Мария Сотрудник 0 Входящий документ На согласовании Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 2 0.607169
2 2 Смирнов Мария Сотрудник 0 Входящий документ Утвержден Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 2 0.603517
3 3 Смирнов Мария Сотрудник 0 Входящий документ Архив Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 2 0.614648
4 4 Смирнов Мария Сотрудник 0 Договор Проект Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 2 0.617297

5 rows × 32 columns

In [14]:
# 8. Визуализация результатов
plt.figure(figsize=(15, 5))

# Визуализация кластеров (первые 2 компоненты)
plt.subplot(1, 3, 1)
if reduced_data.shape[1] >= 2:
    plt.scatter(reduced_data[:, 0], reduced_data[:, 1], c=clusters, cmap='tab10', alpha=0.6)
    plt.xlabel('Компонента 1')
    plt.ylabel('Компонента 2')
else:
    # Если одна компонента - гистограмма
    for cluster in range(optimal_clusters):
        cluster_data = reduced_data[clusters == cluster, 0]
        plt.hist(cluster_data, alpha=0.6, label=f'Cluster {cluster}')
    plt.xlabel('Значение компоненты')
    plt.ylabel('Частота')
    plt.legend()

plt.title('Визуализация кластеров')

# Размеры кластеров
plt.subplot(1, 3, 2)
cluster_sizes = pd.Series(clusters).value_counts().sort_index()
plt.bar(cluster_sizes.index, cluster_sizes.values)
plt.xlabel('Кластер')
plt.ylabel('Количество объектов')
plt.title('Размеры кластеров')

for i, size in enumerate(cluster_sizes.values):
    plt.text(i, size, str(size), ha='center', va='bottom')

# Качество кластеризации по кластерам
plt.subplot(1, 3, 3)
from sklearn.metrics import silhouette_samples
silhouette_vals = silhouette_samples(reduced_data, clusters)

y_lower = 10
for i in range(optimal_clusters):
    cluster_silhouette_vals = silhouette_vals[clusters == i]
    cluster_silhouette_vals.sort()
    
    plt.barh(range(y_lower, y_lower + len(cluster_silhouette_vals)),
             cluster_silhouette_vals, height=1)
    plt.text(-0.05, y_lower + len(cluster_silhouette_vals) // 2, str(i))
    y_lower += len(cluster_silhouette_vals) + 10

plt.xlabel('Silhouette Score')
plt.ylabel('Кластер')
plt.title('Silhouette Analysis')

plt.tight_layout()
plt.show()
No description has been provided for this image
In [15]:
# Переименование положительных значений 
# для лучшей визуализации и нейменга кластеров
def permission_naming(df, cols, vals, names):
    ''' Переименование значений для лучшей визуализации и нейменга кластеров '''
    # Пока только для замены по одному значению параметра за итерацию
    n = 0
    for c in cols:
        df.loc[df[c] == vals[n], c] = names[n]
        n = n + 1
    return df
In [16]:
# Анализ параметров кластеров
def analyzing_cluster_parameters(data_with_clusters, cluster_nums, minimum_cluster_share):
    ''' Анализ параметров кластеров '''
    # data_with_clusters - датафрейм с указанием принадлежности к кластерам
    # cluster_nums - функция типа range(optimal_clusters) 
    #                для задания номеров кластеров для их перебора циклом
    # minimum_cluster_share - минимальная доля категории внутри кластера для ее учета
    
    analysis_cols = []
    for col in data.columns:
        if 2 <= data[col].nunique() <= 50:
            analysis_cols.append(col)
        if len(analysis_cols) >= 30: 
            break
    
    if not analysis_cols:
        # Если не нашли подходящие, берем первые 3 с наименьшим количеством категорий
        col_stats = [(col, data[col].nunique()) for col in data.columns]
        col_stats.sort(key=lambda x: x[1])
        analysis_cols = [col for col, count in col_stats[:3]]
    
    for cluster_num in cluster_nums:
        cluster_data = data_with_clusters[data_with_clusters['cluster'] == cluster_num]
        print(f"\n\n--- Кластер {cluster_num} ({len(cluster_data)} объектов, {len(cluster_data)/len(data_with_clusters):.1%}) ---")
        
        print('\nКачество объектов кластера:')
        display(pd.DataFrame({'Min':[data_with_clusters.loc[data_with_clusters['cluster'] == cluster_num, 'silhouette_vals'].min()], 
                              'Mean':[data_with_clusters.loc[data_with_clusters['cluster'] == cluster_num, 'silhouette_vals'].mean()], 
                              'Median':[data_with_clusters.loc[data_with_clusters['cluster'] == cluster_num, 'silhouette_vals'].median()], 
                              'Max':[data_with_clusters.loc[data_with_clusters['cluster'] == cluster_num, 'silhouette_vals'].max()]}).round(3))
        
        col = []
        value = []
        count = []
        percentage = []
        for c in analysis_cols:
            top_value = cluster_data[c].value_counts().head(1)
            if len(top_value) > 0:
                col.append(c)
                value.append(top_value.index[0])
                count.append(top_value.values[0])
                #percentage.append(f'{count[-1] / len(cluster_data):.1%}')
                percentage.append(count[-1] / len(cluster_data))
                #print(f"{col}:\t'{value}'\t({percentage:.1%})")
        #display(pd.DataFrame({'Параметр':col, 'Модальное значение':value, 'Количество':count, 'Доля в кластере':percentage}))
        print('\nКлючевые параметры, характеризующие кластер:')
        display_claster = pd.DataFrame({'Параметр':col, 'Модальное значение':value, 'Количество':count, 'Доля в кластере':percentage})
        display(display_claster[display_claster['Доля в кластере'] > minimum_cluster_share])
    
        print('\nПримеры объектов кластера:')
        display(data_with_clusters[data_with_clusters['cluster'] == cluster_num].head(5))

analyzing_cluster_parameters(data_with_clusters, range(optimal_clusters), .75)

--- Кластер 0 (256 объектов, 33.9%) ---

Качество объектов кластера:
Min Mean Median Max
0 0.175 0.405 0.462 0.482
Ключевые параметры, характеризующие кластер:
Параметр Модальное значение Количество Доля в кластере
1 Тип роли Сотрудник 240 0.9375
4 Место регистрации Головной офис 256 1.0000
5 Создание карточки 1 208 0.8125
8 Добавление файлов 1 256 1.0000
10 Удаление всех файлов 0 256 1.0000
11 Редактирование собственных файлов 1 256 1.0000
12 Удаление собственных файлов 1 256 1.0000
13 Инициация типового процесса отправки задач 1 256 1.0000
14 Ручное редактирование номера 0 256 1.0000
16 Полный пересчет маршрута 0 256 1.0000
17 Права супермодератора 0 256 1.0000
18 Подписка на уведомления 1 256 1.0000
19 Расширенные настройки прав доступа 0 256 1.0000
21 Пропуск этапов 0 256 1.0000
22 Редактирование своих сообщений 1 256 1.0000
23 Редактирование всех сообщений 0 256 1.0000
24 Добавление обсуждений 1 256 1.0000
25 Чтение обсуждений 1 256 1.0000
26 Чтение и отправка сообщений 1 256 1.0000
Примеры объектов кластера:
Unnamed: 0 Название роли Тип роли Скрытая роль Тип карточки Состояние карточки Место регистрации Создание карточки Чтение карточки Редактирование карточки … Расширенные настройки прав доступа Создание шаблона и копирование Пропуск этапов Редактирование своих сообщений Редактирование всех сообщений Добавление обсуждений Чтение обсуждений Чтение и отправка сообщений cluster silhouette_vals
180 180 Обработчик входящих документов Статическая роль 0 Входящий документ Проект Головной офис 1 1 1 … 0 0 0 1 0 1 1 1 0 0.245946
181 181 Обработчик входящих документов Статическая роль 0 Входящий документ На согласовании Головной офис 0 1 1 … 0 0 0 1 0 1 1 1 0 0.205732
182 182 Обработчик входящих документов Статическая роль 0 Входящий документ Утвержден Головной офис 0 1 0 … 0 0 0 1 0 1 1 1 0 0.178360
183 183 Обработчик входящих документов Статическая роль 0 Входящий документ Архив Головной офис 0 1 0 … 0 0 0 1 0 1 1 1 0 0.174734
184 184 Обработчик входящих документов Статическая роль 0 Служебная записка Проект Головной офис 1 1 1 … 0 0 0 1 0 1 1 1 0 0.243527

5 rows × 32 columns


--- Кластер 1 (103 объектов, 13.6%) ---

Качество объектов кластера:
Min Mean Median Max
0 0.18 0.603 0.613 0.636
Ключевые параметры, характеризующие кластер:
Параметр Модальное значение Количество Доля в кластере
1 Тип роли Сотрудник 102 0.990291
4 Место регистрации Головной офис 100 0.970874
5 Создание карточки 0 103 1.000000
6 Редактирование карточки 0 101 0.980583
7 Удаление карточки 0 103 1.000000
8 Добавление файлов 0 103 1.000000
9 Редактирование всех файлов 0 103 1.000000
10 Удаление всех файлов 0 103 1.000000
11 Редактирование собственных файлов 0 103 1.000000
12 Удаление собственных файлов 0 102 0.990291
13 Инициация типового процесса отправки задач 0 102 0.990291
14 Ручное редактирование номера 0 103 1.000000
15 Редактирование маршрута 0 103 1.000000
16 Полный пересчет маршрута 0 103 1.000000
17 Права супермодератора 0 103 1.000000
18 Подписка на уведомления 1 103 1.000000
19 Расширенные настройки прав доступа 0 103 1.000000
20 Создание шаблона и копирование 0 103 1.000000
21 Пропуск этапов 0 103 1.000000
22 Редактирование своих сообщений 0 103 1.000000
23 Редактирование всех сообщений 0 103 1.000000
24 Добавление обсуждений 0 102 0.990291
25 Чтение обсуждений 1 103 1.000000
26 Чтение и отправка сообщений 1 102 0.990291
Примеры объектов кластера:
Unnamed: 0 Название роли Тип роли Скрытая роль Тип карточки Состояние карточки Место регистрации Создание карточки Чтение карточки Редактирование карточки … Расширенные настройки прав доступа Создание шаблона и копирование Пропуск этапов Редактирование своих сообщений Редактирование всех сообщений Добавление обсуждений Чтение обсуждений Чтение и отправка сообщений cluster silhouette_vals
496 496 Сидоров Сергей Сотрудник 0 Входящий документ Проект Головной офис 0 1 0 … 0 0 0 0 0 0 1 1 1 0.615982
497 497 Сидоров Сергей Сотрудник 0 Входящий документ На согласовании Головной офис 0 1 0 … 0 0 0 0 0 0 1 1 1 0.616705
498 498 Сидоров Сергей Сотрудник 0 Входящий документ Утвержден Головной офис 0 1 0 … 0 0 0 0 0 0 1 1 1 0.610507
499 499 Сидоров Сергей Сотрудник 0 Входящий документ Архив Головной офис 0 1 0 … 0 0 0 0 0 0 1 1 1 0.621624
500 500 Сидоров Сергей Сотрудник 0 Договор Проект Головной офис 0 1 0 … 0 0 0 0 0 0 1 1 1 0.635285

5 rows × 32 columns


--- Кластер 2 (240 объектов, 31.7%) ---

Качество объектов кластера:
Min Mean Median Max
0 0.585 0.61 0.609 0.623
Ключевые параметры, характеризующие кластер:
Параметр Модальное значение Количество Доля в кластере
1 Тип роли Сотрудник 240 1.0
4 Место регистрации Головной офис 240 1.0
5 Создание карточки 1 240 1.0
6 Редактирование карточки 1 240 1.0
7 Удаление карточки 1 240 1.0
8 Добавление файлов 1 240 1.0
9 Редактирование всех файлов 1 240 1.0
10 Удаление всех файлов 1 240 1.0
11 Редактирование собственных файлов 1 240 1.0
12 Удаление собственных файлов 1 240 1.0
13 Инициация типового процесса отправки задач 1 240 1.0
14 Ручное редактирование номера 1 240 1.0
15 Редактирование маршрута 1 240 1.0
16 Полный пересчет маршрута 1 240 1.0
17 Права супермодератора 1 240 1.0
18 Подписка на уведомления 1 240 1.0
19 Расширенные настройки прав доступа 1 240 1.0
20 Создание шаблона и копирование 1 240 1.0
21 Пропуск этапов 1 240 1.0
22 Редактирование своих сообщений 1 240 1.0
23 Редактирование всех сообщений 1 240 1.0
24 Добавление обсуждений 1 240 1.0
25 Чтение обсуждений 1 240 1.0
26 Чтение и отправка сообщений 1 240 1.0
Примеры объектов кластера:
Unnamed: 0 Название роли Тип роли Скрытая роль Тип карточки Состояние карточки Место регистрации Создание карточки Чтение карточки Редактирование карточки … Расширенные настройки прав доступа Создание шаблона и копирование Пропуск этапов Редактирование своих сообщений Редактирование всех сообщений Добавление обсуждений Чтение обсуждений Чтение и отправка сообщений cluster silhouette_vals
0 0 Смирнов Мария Сотрудник 0 Входящий документ Проект Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 2 0.609365
1 1 Смирнов Мария Сотрудник 0 Входящий документ На согласовании Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 2 0.607169
2 2 Смирнов Мария Сотрудник 0 Входящий документ Утвержден Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 2 0.603517
3 3 Смирнов Мария Сотрудник 0 Входящий документ Архив Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 2 0.614648
4 4 Смирнов Мария Сотрудник 0 Договор Проект Головной офис 1 1 1 … 1 1 1 1 1 1 1 1 2 0.617297

5 rows × 32 columns


--- Кластер 3 (157 объектов, 20.8%) ---

Качество объектов кластера:
Min Mean Median Max
0 -0.192 0.076 0.096 0.279
Ключевые параметры, характеризующие кластер:
Параметр Модальное значение Количество Доля в кластере
7 Удаление карточки 0 157 1.0
9 Редактирование всех файлов 0 157 1.0
10 Удаление всех файлов 0 157 1.0
14 Ручное редактирование номера 0 157 1.0
15 Редактирование маршрута 0 157 1.0
16 Полный пересчет маршрута 0 157 1.0
17 Права супермодератора 0 157 1.0
19 Расширенные настройки прав доступа 0 157 1.0
20 Создание шаблона и копирование 0 157 1.0
21 Пропуск этапов 0 157 1.0
23 Редактирование всех сообщений 0 157 1.0
Примеры объектов кластера:
Unnamed: 0 Название роли Тип роли Скрытая роль Тип карточки Состояние карточки Место регистрации Создание карточки Чтение карточки Редактирование карточки … Расширенные настройки прав доступа Создание шаблона и копирование Пропуск этапов Редактирование своих сообщений Редактирование всех сообщений Добавление обсуждений Чтение обсуждений Чтение и отправка сообщений cluster silhouette_vals
596 596 Руководство Подразделение 0 Счет Проект Санкт-Петербург 1 1 1 … 0 0 0 0 0 1 0 0 3 0.278785
597 597 Руководство Подразделение 0 Счет На согласовании nan 0 1 0 … 0 0 0 0 0 1 1 0 3 0.044825
598 598 Руководство Подразделение 0 Счет Утвержден nan 0 1 0 … 0 0 0 0 0 1 1 0 3 0.113414
599 599 Руководство Подразделение 0 Счет Архив Санкт-Петербург 1 1 1 … 0 0 0 1 0 0 1 1 3 -0.037894
600 600 Руководство Подразделение 0 Служебная записка Проект Москва 0 1 0 … 0 0 0 0 0 0 1 0 3 -0.002771

5 rows × 32 columns

In [17]:
# Табличное представление параметров кластеров
cluster_parameters = data_with_clusters.groupby(['cluster']).agg(lambda x: x.mode().iloc[0]).reset_index()
#cluster_parameters = data_with_clusters.groupby(['cluster']).agg(lambda x: x.mode()).reset_index()
cluster_parameters['silhouette_vals'] = data_with_clusters.groupby(['cluster'])['silhouette_vals'].mean()
cluster_parameters['cluster_size'] = data_with_clusters.groupby(['cluster']).size()
cluster_parameters['percentage_cluster_size'] = cluster_parameters['cluster_size'] / len(data_with_clusters)

cluster_parameters.to_excel('cluster_parameters.xlsx')

print('Табличное представление медианных значений ключевых параметров кластеров:')
cluster_parameters
Табличное представление медианных значений ключевых параметров кластеров:
Out[17]:
cluster Unnamed: 0 Название роли Тип роли Скрытая роль Тип карточки Состояние карточки Место регистрации Создание карточки Чтение карточки … Создание шаблона и копирование Пропуск этапов Редактирование своих сообщений Редактирование всех сообщений Добавление обсуждений Чтение обсуждений Чтение и отправка сообщений silhouette_vals cluster_size percentage_cluster_size
0 0 180 Смирнов Петр Сотрудник 0 Договор Архив Головной офис 1 1 … 1 0 1 0 1 1 1 0.405181 256 0.338624
1 1 496 Иванов Иван Сотрудник 0 Договор На согласовании Головной офис 0 1 … 0 0 0 0 0 1 1 0.603262 103 0.136243
2 2 0 Кузнецов Алексей Сотрудник 0 Входящий документ Архив Головной офис 1 1 … 1 1 1 1 1 1 1 0.609590 240 0.317460
3 3 596 Смирнов Алексей Подразделение 0 Счет Архив Санкт-Петербург 1 1 … 0 0 0 0 1 1 0 0.076172 157 0.207672

4 rows × 34 columns

In [18]:
# 10. Итоговая информация
silhouette_avg = silhouette_score(reduced_data, clusters)
print(f"\n=== ИТОГИ ===")
print(f"Исходные данные: {data.shape[1]} категориальных признаков")
print(f"После кодирования: {encoded_data.shape[1]} признаков")
print(f"Для кластеризации: {reduced_data.shape[1]} компонент")
print(f"Число кластеров: {optimal_clusters}")
print(f"Общее качество (silhouette): {silhouette_avg:.3f}")
print(f"Распределение по кластерам:")
for cluster_num in range(optimal_clusters):
    count = (clusters == cluster_num).sum()
    print(f"  Кластер {cluster_num}: {count} объектов ({count/len(clusters):.1%})")
=== ИТОГИ ===
Исходные данные: 30 категориальных признаков
После кодирования: 64 признаков
Для кластеризации: 16 компонент
Число кластеров: 4
Общее качество (silhouette): 0.429
Распределение по кластерам:
  Кластер 0: 256 объектов (33.9%)
  Кластер 1: 103 объектов (13.6%)
  Кластер 2: 240 объектов (31.7%)
  Кластер 3: 157 объектов (20.8%)

Анализ выбросов¶

In [20]:
# Анализ выбросов по сущствующим кластерам
analyzing_cluster_parameters(data_with_clusters[data_with_clusters['silhouette_vals'] < 0], 
                             data_with_clusters[data_with_clusters['silhouette_vals'] < 0]['cluster'].unique(), 
                             .75)

--- Кластер 3 (40 объектов, 100.0%) ---

Качество объектов кластера:
Min Mean Median Max
0 -0.192 -0.07 -0.065 -0.003
Ключевые параметры, характеризующие кластер:
Параметр Модальное значение Количество Доля в кластере
7 Удаление карточки 0 40 1.00
9 Редактирование всех файлов 0 40 1.00
10 Удаление всех файлов 0 40 1.00
14 Ручное редактирование номера 0 40 1.00
15 Редактирование маршрута 0 40 1.00
16 Полный пересчет маршрута 0 40 1.00
17 Права супермодератора 0 40 1.00
18 Подписка на уведомления 1 34 0.85
19 Расширенные настройки прав доступа 0 40 1.00
20 Создание шаблона и копирование 0 40 1.00
21 Пропуск этапов 0 40 1.00
23 Редактирование всех сообщений 0 40 1.00
Примеры объектов кластера:
Unnamed: 0 Название роли Тип роли Скрытая роль Тип карточки Состояние карточки Место регистрации Создание карточки Чтение карточки Редактирование карточки … Расширенные настройки прав доступа Создание шаблона и копирование Пропуск этапов Редактирование своих сообщений Редактирование всех сообщений Добавление обсуждений Чтение обсуждений Чтение и отправка сообщений cluster silhouette_vals
599 599 Руководство Подразделение 0 Счет Архив Санкт-Петербург 1 1 1 … 0 0 0 1 0 0 1 1 3 -0.037894
600 600 Руководство Подразделение 0 Служебная записка Проект Москва 0 1 0 … 0 0 0 0 0 0 1 0 3 -0.002771
616 616 IT-отдел Подразделение 0 Счет Проект nan 0 1 1 … 0 0 0 0 0 0 0 1 3 -0.041938
645 645 Юридический отдел Подразделение 0 Входящий документ На согласовании nan 1 1 1 … 0 0 0 1 0 0 1 1 3 -0.074432
654 654 Юридический отдел Подразделение 0 Счет Утвержден nan 1 1 1 … 0 0 0 1 0 0 1 1 3 -0.032847

5 rows × 32 columns

In [21]:
# Анализ выбросов с объединением выбросов в отдельный кластер

cluster_data = data_with_clusters[data_with_clusters['silhouette_vals'] < 0]
print(f"\n\n--- Кластер выбросов ({len(cluster_data)} объектов, {len(cluster_data)/len(data_with_clusters):.1%}) ---")

analysis_cols = []
for col in data.columns:
    if 2 <= data[col].nunique() <= 50:
        analysis_cols.append(col)
    if len(analysis_cols) >= 30: 
        break

if not analysis_cols:
    # Если не нашли подходящие, берем первые 3 с наименьшим количеством категорий
    col_stats = [(col, data[col].nunique()) for col in data.columns]
    col_stats.sort(key=lambda x: x[1])
    analysis_cols = [col for col, count in col_stats[:3]]

print('\nКачество объектов кластера:')
display(pd.DataFrame({'Min':[cluster_data['silhouette_vals'].min()], 
                      'Mean':[cluster_data['silhouette_vals'].mean()], 
                      'Median':[cluster_data['silhouette_vals'].median()], 
                      'Max':[cluster_data['silhouette_vals'].max()]}).round(3))

col = []
value = []
count = []
percentage = []
for c in analysis_cols:
    top_value = cluster_data[c].value_counts().head(1)
    if len(top_value) > 0:
        col.append(c)
        value.append(top_value.index[0])
        count.append(top_value.values[0])
        percentage.append(count[-1] / len(cluster_data))
print('\nКлючевые параметры, характеризующие кластер:')
display_claster = pd.DataFrame({'Параметр':col, 'Модальное значение':value, 'Количество':count, 'Доля в кластере':percentage})
display(display_claster[display_claster['Доля в кластере'] > .75])

print('\nПримеры объектов кластера:')
display(cluster_data.head(5))

--- Кластер выбросов (40 объектов, 5.3%) ---

Качество объектов кластера:
Min Mean Median Max
0 -0.192 -0.07 -0.065 -0.003
Ключевые параметры, характеризующие кластер:
Параметр Модальное значение Количество Доля в кластере
7 Удаление карточки 0 40 1.00
9 Редактирование всех файлов 0 40 1.00
10 Удаление всех файлов 0 40 1.00
14 Ручное редактирование номера 0 40 1.00
15 Редактирование маршрута 0 40 1.00
16 Полный пересчет маршрута 0 40 1.00
17 Права супермодератора 0 40 1.00
18 Подписка на уведомления 1 34 0.85
19 Расширенные настройки прав доступа 0 40 1.00
20 Создание шаблона и копирование 0 40 1.00
21 Пропуск этапов 0 40 1.00
23 Редактирование всех сообщений 0 40 1.00
Примеры объектов кластера:
Unnamed: 0 Название роли Тип роли Скрытая роль Тип карточки Состояние карточки Место регистрации Создание карточки Чтение карточки Редактирование карточки … Расширенные настройки прав доступа Создание шаблона и копирование Пропуск этапов Редактирование своих сообщений Редактирование всех сообщений Добавление обсуждений Чтение обсуждений Чтение и отправка сообщений cluster silhouette_vals
599 599 Руководство Подразделение 0 Счет Архив Санкт-Петербург 1 1 1 … 0 0 0 1 0 0 1 1 3 -0.037894
600 600 Руководство Подразделение 0 Служебная записка Проект Москва 0 1 0 … 0 0 0 0 0 0 1 0 3 -0.002771
616 616 IT-отдел Подразделение 0 Счет Проект nan 0 1 1 … 0 0 0 0 0 0 0 1 3 -0.041938
645 645 Юридический отдел Подразделение 0 Входящий документ На согласовании nan 1 1 1 … 0 0 0 1 0 0 1 1 3 -0.074432
654 654 Юридический отдел Подразделение 0 Счет Утвержден nan 1 1 1 … 0 0 0 1 0 0 1 1 3 -0.032847

5 rows × 32 columns

In [ ]:
 

Python анализ информационная система корпоративная информационная система практика

Предыдущая статьяАнализ ошибок в Tessa (Тесса)Следующая статья DeepSeek сгенерировал ролевую модель предоставления разрешений в Tessa

Рубрики

Метки

abc abcd excel ms sql Python sql tessa VBA xyz Комбинаторика Математика Теория вероятностей анализ виртуальный помощник данные знания информационная система информация искусственный интеллект кластерный анализ компетенции корпоративная информационная система маркетинг мудрость о проекте оптимизация ошибка практика программное обеспечение пэст ролевая модель теория тесса юмор языки программирования

Политика конфиденциальности

Продолжая использовать данный сайт вы подтверждаете свое согласие с условиями его политики конфиденциальности. Подробнее…




Администрация и владельцы данного информационного ресурса не несут ответственности за возможные последствия, связанные с использованием информации, размещенной на нем.


Все права защищены. При копировании материалов сайта обязательно указывать ссылку на © Microsegment.ru (2020-2025)