ML707 — ПОДГОТОВКА ДАННЫХ
Подготовка данных, также известная как предварительная обработка данных, является важным этапом рабочего процесса машинного обучения, который включает очистку, преобразование и подготовку данных для использования алгоритмами машинного обучения. Вот некоторые распространенные методы, используемые при подготовке данных:
- Очистка данных. Очистка данных включает выявление и обработку отсутствующих, противоречивых или ошибочных данных в наборе данных. Некоторые распространенные методы, используемые при очистке данных, включают:
- 1.1. Вменение: заполнение пропущенных значений средним значением, медианой или модой соответствующего признака.
- 1.2. Удаление дубликатов: выявление и удаление повторяющихся записей в наборе данных.
- 1.3. Обработка выбросов: выявление и обработка выбросов путем их удаления или преобразования.
- 1.4. Обработка несоответствий: выявление и обработка несоответствий в данных, таких как неверные значения, единицы измерения или форматирование.
2. Преобразование данных.Преобразование данных включает преобразование данных в формат, подходящий для использования алгоритмами машинного обучения. Некоторые распространенные методы, используемые при преобразовании данных, включают:
- 2.1. Масштабирование характеристик: масштабирование числовых характеристик для получения одного и того же диапазона, например, масштабирование минимум-максимум или нормализация Z-показателя.
- 2.2. Кодирование функций: преобразование категориальных функций в числовые функции, например горячее кодирование или кодирование меток.
- 2.3. Выбор функций: выбор подмножества соответствующих функций для повышения производительности модели и снижения вычислительной сложности.
- 2.4. Уменьшение размерности: сокращение количества функций при сохранении наиболее важной информации, такой как анализ главных компонентов (PCA) или линейный дискриминантный анализ (LDA).
3. Интеграция данных. Интеграция данных включает в себя объединение нескольких наборов данных в один набор данных для использования алгоритмами машинного обучения. Это может быть сложно из-за различий в форматах данных, качестве данных и структуре данных. Некоторые распространенные методы, используемые при интеграции данных, включают:
- 3.1. Соединения: объединение двух или более наборов данных на основе общего атрибута или ключа.
- 3.2. Изменение формы: изменение формата или структуры данных для облегчения интеграции, например сведение или группирование.
4. Сокращение данных. Сокращение данных включает уменьшение размера или сложности набора данных для повышения производительности модели и снижения вычислительной сложности. Некоторые распространенные методы, используемые при сокращении данных, включают:
- 4.1. Выборка: выбор подмножества данных для уменьшения размера набора данных, например случайная выборка или стратифицированная выборка.
- 4.2. Сжатие: уменьшение размера набора данных за счет сжатия данных, например с использованием методов сжатия с потерями или без потерь.
- 4.3. Кластеризация: группировка похожих точек данных для создания кластеров, которые можно использовать для более компактного представления данных.
В целом подготовка данных является важным этапом рабочего процесса машинного обучения, который может существенно повлиять на производительность моделей машинного обучения. Правильная подготовка данных может помочь повысить точность, эффективность и интерпретируемость моделей машинного обучения.
1.1. Вменение:
- из sklearn.impute import SimpleImputer:в этом коде используется класс SimpleImputer из scikit-learn для вменения пропущенных значений. Параметр стратегии имеет значение «среднее», что заменяет отсутствующие значения средним значением каждого столбца.
from sklearn.impute import SimpleImputer import numpy as np # create a sample dataset with missing values data = np.array([[1, 2, np.nan], [4, np.nan, 6], [7, 8, 9]]) # create an imputer object with strategy='mean' imputer = SimpleImputer(missing_values=np.nan, strategy='mean') # fit the imputer to the data imputer.fit(data) # transform the data using the imputer data_imputed = imputer.transform(data) # print the imputed dataset print(data_imputed)
- from sklearn.impute import KNNImputer: этот код использует класс KNN из библиотеки fancyimpute для вменения пропущенных значений с помощью вменения KNN. Параметр k указывает количество ближайших соседей, которые следует использовать для вменения.
import matplotlib.pyplot as plt from sklearn.impute import KNNImputer import numpy as np # create a sample dataset with missing values data = np.array([[1, 2], 4, np.nan],[8, 9],[11, 3], [6, np.nan],[2, 5],[np.nan, 9], [11, 3],[8, 9], [np.nan, 3]]) # impute missing values using KNN imputation imputer = KNNImputer(n_neighbors=3) data_imputed = imputer.fit_transform(data) # plot the imputation steps using a heatmap plt.figure(figsize=(15, 8)) plt.subplot(121) plt.title("Original Data") plt.imshow(data, cmap='coolwarm') for i in range(data.shape[0]): for j in range(data.shape[1]): plt.text(j, i, data[i, j], ha='center', va='center', color='k') plt.subplot(122) plt.title("Imputed Data") plt.imshow(data_imputed, cmap='coolwarm') for i in range(data_imputed.shape[0]): for j in range(data_imputed.shape[1]): plt.text(j, i, round(data_imputed[i, j], 2), ha='center', va='center', color='k') plt.tight_layout() plt.show()
- from fancyimpute import IterativeImputer: этот код использует класс IterativeImputer из библиотеки fancyimpute для выполнения множественного вменения. Множественное вменение генерирует несколько импутированных наборов данных и объединяет их для создания одного импутированного набора данных.
from fancyimpute import IterativeImputer import numpy as np # create a sample dataset with missing values data = np.array([[1, 2], 4, np.nan],[8, 9],[11, 3], [6, np.nan],[2, 5],[np.nan, 9], [11, 3],[8, 9], [np.nan, 3]]) # impute missing values using iterative imputation imputer = IterativeImputer() data_imputed = imputer.fit_transform(data) # plot the imputation steps using a heatmap plt.figure(figsize=(15, 8)) plt.subplot(121) plt.title("Original Data") plt.imshow(data, cmap='coolwarm') for i in range(data.shape[0]): for j in range(data.shape[1]): plt.text(j, i, data[i, j], ha='center', va='center', color='k') plt.subplot(122) plt.title("Imputed Data") plt.imshow(data_imputed, cmap='coolwarm') for i in range(data_imputed.shape[0]): for j in range(data_imputed.shape[1]): plt.text(j, i, round(data_imputed[i, j], 2), ha='center', va='center', color='k') plt.tight_layout() plt.show()
- импорт statsmodels.api как sm: этот код использует класс OLS (Обычные наименьшие квадраты) из библиотеки statsmodels для выполнения линейной регрессии и вменения пропущенных значений. Отсутствующие значения заменяются предсказанными значениями из модели линейной регрессии. ОТМЕТЬТЕ, ЧТО этот метод требует указания статистической модели для импутации, что не всегда может быть осуществимо или уместно в зависимости от данных. Я не буду вдаваться в подробности на этом уровне.
KNNImputer() VS IterativeImputer()
KNNImputer() — это метод вменения на основе ближайших соседей, который вменяет пропущенные значения, используя среднее значение K-ближайших соседей пропущенного значения. Он работает, сначала идентифицируя K ближайших соседей каждого отсутствующего значения, а затем вычисляя среднее значение этих соседей как условное значение для отсутствующего значения.
С другой стороны, IterativeImputer() — это метод многомерного вменения, который вменяет пропущенные значения с помощью моделей регрессии. Он работает путем итеративной подгонки регрессионной модели к каждому признаку с отсутствующими значениями, а затем использует эту модель для прогнозирования отсутствующих значений. Вмененные значения затем используются в следующей итерации для обновления моделей регрессии, и процесс повторяется до сходимости.
В целом, KNNImputer() быстрее и проще в использовании, чем IterativeImputer(), НО он может плохо работать с многомерными данными или данными со сложными отношениями между функциями. IterativeImputer() является более универсальным и может обрабатывать более широкий диапазон типов данных и взаимосвязей между функциями, но может потребовать больше вычислительных ресурсов, и его может быть сложнее настроить для достижения оптимальной производительности.
1.2. Удаление дубликатов
Чтобы удалить дубликаты из списка в Python, вы можете использовать встроенную функцию set(). Однако это будет работать только в том случае, если порядок элементов в списке не имеет значения, поскольку наборы неупорядочены.
Если вам нужно сохранить порядок списка при удалении дубликатов, вы можете использовать цикл for для создания нового списка только с уникальными элементами.
- Из списка с помощью цикла For:
#VERSION 1: original_list = [1, 2, 2, 3, 4, 4, 5] # Remove duplicates while preserving order new_list = [] for element in original_list: if element not in new_list: new_list.append(element) print(new_list) # Output: [1, 2, 3, 4, 5] #VERSION 2: original_list = [1, 2, 2, 3, 4, 4, 5] # Remove duplicates while preserving order using a list comprehension new_list = [] for i,element in enumerate(original_list): if element not in original_list[:i]: new_list.append(element) print(new_list) # Output: [1, 2, 3, 4, 5]
- Из списка со встроенными функциями:
# VERSION 1: my_list = [1, 2, 3, 2, 4, 1, 5] unique_list = list(set(my_list)) print(unique_list) # Output: [1, 2, 3, 4, 5] # VERSION 2: # or using dict.fromkeys() unique_list = list(dict.fromkeys(my_list)) print(unique_list) # Output: [1, 2, 3, 4, 5]
- Из DataFrames со встроенными функциями:drop_duplicates() вызывается без каких-либо аргументов, что удаляет все строки, которые являются точными дубликатами (т. е. имеют одинаковые значения во всех столбцах). drop_duplicates() вызывается с аргументом подмножества, установленным на «A», который удаляет все строки, содержащие повторяющиеся значения в столбце «A».
import pandas as pd # create a sample dataframe with duplicates df = pd.DataFrame({'A': [1, 2, 2, 3, 4], 'B': [4, 5, 6, 7, 8]}) # remove duplicates based on ALL columns df = df.drop_duplicates() # remove duplicates based on a specific column df = df.drop_duplicates(subset='A')
1.3. Обработка выбросов:
На данный момент необходимо принять некоторые решения. Определенные выбросы можно удалить из набора данных или, в некоторых случаях, изменить и оставить в наборе данных. Например, приравнивание значения выброса к верхнему пределу может помешать вам удалить некоторые значения, необходимые для набора данных, с которым вы будете работать. Ниже вы увидите две разные формулы. Они отличаются в вопросе, упомянутом выше.
Объяснение формулы handle_outliers:
- data: массив 1D numpy или список точек данных, содержащих выбросы.
- Q1 и Q3: первый и третий квартили данных соответственно.
- IQR: межквартильный размах, представляющий собой разницу между Q3 и Q1.
- lower_bound и upper_bound: нижняя и верхняя границы для обнаружения выбросов с использованием метода IQR.
- filtered_data: новый массив numpy, содержащий только точки данных в пределах нижней и верхней границ.
def handle_outliers(data): Q1 = np.percentile(data, 25) Q3 = np.percentile(data, 75) IQR = Q3 - Q1 lower_bound = Q1 - 1.5 * IQR upper_bound = Q3 + 1.5 * IQR filtered_data = data[(data >= lower_bound) & (data <= upper_bound)] return filtered_data
Объяснение формулы handle_outliers_equalizing: первая часть функции такая же, как и раньше, вычисляет квартили и межквартильный диапазон.
- filtered_data теперь инициализируется как копия исходного массива данных. Мы используем логическое индексирование numpy для замены значений за пределами нижней и верхней границ на соответствующее связанное значение.
def handle_outliers_equalizing(data): Q1 = np.percentile(data, 25) Q3 = np.percentile(data, 75) IQR = Q3 - Q1 lower_bound = Q1 - 1.5 * IQR upper_bound = Q3 + 1.5 * IQR filtered_data = data.copy() # create a copy of data to avoid modifying the original array filtered_data[filtered_data < lower_bound] = lower_bound filtered_data[filtered_data > upper_bound] = upper_bound return filtered_data
1.4. Устранение несоответствий. Это название может быть полностью сформировано в соответствии с имеющимся у вас набором данных. Например, если вы столкнулись со столбцом, который не должен содержать отрицательных значений в анализе набора данных, если в столбце есть словесные выражения, которые должны быть числовыми в наборе данных, если в столбце даты есть неисторические значения, вам необходимо обнаружить их и принять меры для них. Я делюсь формулой ниже в качестве примера. Вы можете сделать эту формулу более полной.
def handle_inconsistencies(df): # Step 1: Find negative values in the DataFrame negative_values = df < 0 # Step 2: Replace negative values with NaNs df = df.where(~negative_values, np.nan) # Step 3: Find missing values in the DataFrame missing_values = df.isnull() # Step 4: Fill missing values with the median of the corresponding column median_values = df.median() df = df.fillna(median_values) # Step 5: Return the cleaned DataFrame return df
2.1. Масштабирование характеристик. Масштабирование характеристик — это процесс преобразования входных характеристик, чтобы они имели одинаковый масштаб или диапазон. Обычно это делается для повышения производительности алгоритмов машинного обучения, чувствительных к масштабу входных признаков. Существует несколько типов масштабирования функций (MinMaxScaler, StandardScaler, MaxAbsScaler и RobustScaler), в том числе:
Минимально-максимальное масштабирование (нормализация).Этот метод масштабирует входные объекты в указанном диапазоне, обычно от 0 до 1. Формула для мин-макс масштабирования:
- X_norm = (X — X_min) / (X_max — X_min)
from sklearn.preprocessing import MinMaxScaler, StandardScaler import numpy as np # Create a sample dataset with 3 features X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # Create a MinMaxScaler object and apply it to the dataset scaler = MinMaxScaler() X_minmax = scaler.fit_transform(X) print("Min-Max Scaling:\n", X_minmax)
Стандартизация (нормализация Z-показателя). Этот метод масштабирует входные объекты, чтобы иметь нулевое среднее значение и единичную дисперсию. Формула стандартизации:
- X_std = (X — X_mean) / X_std
from sklearn.preprocessing import MinMaxScaler, StandardScaler import numpy as np # Create a sample dataset with 3 features X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # Create a StandardScaler object and apply it to the dataset scaler = StandardScaler() X_std = scaler.fit_transform(X) print("Standardization:\n", X_std)
MaxAbsScaler: это метод масштабирования в scikit-learn, который масштабирует каждую функцию по ее максимальному абсолютному значению. Он работает аналогично MinMaxScaler, но вместо масштабирования признаков до фиксированного диапазона [0, 1] он масштабирует каждый признак до диапазона [-1, 1]. MaxAbsScaler полезен, когда распределение функций искажено или содержит выбросы, и мы хотим масштабировать функции без изменения их относительного порядка. Его можно использовать как для плотных, так и для разреженных данных. Формула для масштабирования каждой функции с помощью MaxAbsScaler:
- X_scaled = X/max(abs(X))
from sklearn.preprocessing import MaxAbsScaler import pandas as pd # Create a sample dataset with 3 features X = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) # Initialize MaxAbsScaler scaler = MaxAbsScaler() # Fit and transform the data X_std= scaler.fit_transform(X) print("Standardization:\n", X_std)
RobustScaler: это масштабатор, устойчивый к выбросам. Он масштабирует данные в соответствии с межквартильным диапазоном (IQR) вместо среднего и стандартного отклонения, как StandardScaler. Это больше подходит для данных с большим количеством выбросов.
from sklearn.preprocessing import RobustScaler import pandas as pd # Load dataset df = pd.read_csv('data.csv') # Separate features from target X = df.drop('target', axis=1) y = df['target'] # Scale the features using RobustScaler scaler = RobustScaler() X_scaled = scaler.fit_transform(X) # Use the scaled features for model training model.fit(X_scaled, y)
Из вышеперечисленных методов чаще всего используются MinMaxScaler и StandardScaler. Выбор между использованием MinMaxScaler и StandardScaler зависит от характеристик данных и требований алгоритма машинного обучения.
MinMaxScaler полезен, когда известен диапазон входных объектов, и мы хотим масштабировать объекты в фиксированном диапазоне, обычно от 0 до 1. Этот метод масштабирования особенно полезен, когда распределение данных не является гауссовым и когда данные не имеют значительные выбросы. MinMaxScaler сохраняет форму исходного распределения, но на преобразованные значения могут влиять выбросы.
StandardScaler полезен, когда мы хотим преобразовать входные объекты, чтобы они имели нулевое среднее значение и единичную дисперсию. Этот метод масштабирования предполагает, что распределение данных является гауссовым или приблизительно гауссовым. StandardScaler менее чувствителен к наличию выбросов по сравнению с MinMaxScaler, поскольку он использует среднее значение и стандартное отклонение для масштабирования признаков. StandardScaler также может обрабатывать отрицательные значения, в отличие от MinMaxScaler.
В целом, если известно, что распределение данных не является гауссовым, MinMaxScaler может быть более подходящим. С другой стороны, если распределение данных является гауссовским или приблизительно гауссовым, StandardScaler может быть более подходящим. Однако всегда полезно попробовать оба метода масштабирования и сравнить их влияние на производительность машины. алгоритм обучения.
2.2. Кодирование функций. Кодирование функций — это процесс преобразования категориальных переменных (признаков) в числовые переменные, которые могут использоваться алгоритмами машинного обучения. Существуют различные типы методов кодирования признаков, в том числе:
Горячее кодирование. Этот метод создает двоичную переменную для каждой категории функции. Например, функция с категориями «красный», «зеленый» и «синий» будет закодирована в 3 бинарных переменных, по одной для каждой категории. Если образец относится к «красной» категории, его соответствующая бинарная переменная будет установлена в 1, а остальные в 0.
import pandas as pd from sklearn.preprocessing import LabelEncoder import pandas as pd import category_encoders as ce # Create a sample dataframe with a categorical feature df = pd.DataFrame({ 'color': ['red', 'blue', 'green', 'green', 'red', 'blue', 'green'] }) # Perform One-Hot Encoding one_hot_encoded = pd.get_dummies(df['color']) one_hot_encoded
Кодировка метки. Этот метод присваивает уникальное целочисленное значение каждой категории в функции. Например, функция с категориями «красный», «зеленый» и «синий» будет закодирована в целые числа 0, 1 и 2 соответственно.
# Perform Label Encoding label_encoder = LabelEncoder() label_encoded = pd.DataFrame(label_encoder.fit_transform(df['color'])) label_encoded
Двоичное кодирование. Этот метод кодирует категории в двоичные числа. Каждой категории присваивается уникальный двоичный код, и коды объединяются для создания окончательной кодировки. Например, объект с категориями «красный», «зеленый» и «синий» будет закодирован следующим образом: «красный» = 00, «зеленый» = 01 и «синий» = 10. В этом примере цвет Функция содержит 3 категории: «красный», «синий» и «зеленый». BinaryEncoder преобразует каждую категорию в двоичное представление ее целочисленной метки. Результирующий кадр данных содержит три новых столбца, по одному для каждого бита в двоичном кодировании. Обратите внимание, что первый бит всегда равен 0, что соответствует общему среднему значению признака. При желании этот бит можно сбросить, установив для параметра drop_first значение True при создании экземпляра BinaryEncoder.
# Create an instance of BinaryEncoder and fit the dataframe encoder = ce.BinaryEncoder(cols=['color']) df_encoded = encoder.fit_transform(df) print(df_encoded)
Целевое кодирование. Этот метод заменяет каждую категорию средним значением целевой переменной для этой категории. Этот метод может быть полезен для задач классификации, но может привести к систематической ошибке, если данные не сбалансированы должным образом.
# Create a sample dataframe with a categorical feature and a target variable df = pd.DataFrame({ 'color': ['red', 'green', 'blue', 'red', 'red', 'green', 'red', 'red', 'green'], 'target': [1, 0, 1, 0, 1, 1, 0, 0, 0] }) # Create an instance of TargetEncoder and fit the dataframe encoder = ce.TargetEncoder(cols=['color']) df_encoded = encoder.fit_transform(df['color'], df['target']) # Add the encoded feature to the original dataframe df['color_encoded'] = df_encoded print(df)
В целом выбор метода кодирования признаков зависит от конкретной задачи и характеристик данных. Прежде всего, нам нужно интерпретировать, существует ли естественный порядок в переменной. Например, если значения переменной «маленькие», «средние» и «большие», существует естественное упорядочение по размеру этой категории.
- Горячее кодирование полезно, когда категории не упорядочены и между ними нет внутренней связи. Его можно использовать как с линейными, так и с нелинейными алгоритмами.
- Кодирование меток полезно, когда категории имеют неотъемлемый порядок или взаимосвязь. Если неотъемлемый порядок не сохраняется, это может привести к потере информации и снижению производительности модели машинного обучения. Его можно использовать как с линейными, так и с нелинейными алгоритмами.
- Двоичное кодирование полезно, когда количество категорий велико и желательно уменьшить размерность. Его можно использовать как с линейными, так и с нелинейными алгоритмами.
- Целевое кодирование полезно, когда существует связь между категориями и целевой переменной. Двоичное кодирование полезно, когда количество категорий велико и желательно уменьшить размерность. Целевое кодирование полезно, когда существует связь между категориями и целевой переменной.
В этом примере функция цвета содержит 3 категории: «красный», «зеленый» и «синий». TargetEncoder преобразует каждую категорию в среднее значение целевой переменной (0 или 1) для этой категории. Полученная закодированная функция добавляется в исходный фрейм данных в виде нового столбца с именем color_encoded. Обратите внимание, что в этом примере мы кодируем только цветовой признак, но на практике мы обычно кодируем все категориальные признаки в наборе данных.
Вы можете использовать целевое кодирование, когда в вашем наборе данных есть как категориальные, так и числовые переменные. Однако обычно вы применяете целевое кодирование только к категориальным переменным и оставляете числовые переменные такими, какие они есть, или применяете другой тип кодирования/нормализации, специально разработанный для числовых данных.
Например, вы можете использовать Target Encoding для кодирования категориальных переменных, а затем применить MinMaxScaler или StandardScaler для нормализации числовых переменных. Важно убедиться, что каждый тип переменной преобразуется соответствующим образом для текущей задачи моделирования.
Стоит отметить, что при использовании Target Encodingважно использовать перекрестную проверку, чтобы избежать переобучения. Это означает, что вместо кодирования всего набора обучающих данных вы будете кодировать каждую складку данных отдельно, основываясь только на обучающих данных в этой свертке. Это помогает гарантировать, что кодирование является обобщающим, а не специфичным для какого-либо конкретного подмножества данных.
2.3. Выбор функций.Выбор функций — это процесс выбора подмножества соответствующих функций из большего набора функций в наборе данных. Цель выбора функций — повысить точность и эффективность модели машинного обучения. за счет уменьшения размерности входных данных, удаления ненужных или избыточных функций и улучшения интерпретируемости модели. Существует несколько типов методов выбора признаков, в том числе:
- Методы фильтрации. Методы фильтрации оценивают релевантность каждой функции независимо от других и выбирают лучшие функции на основе статистических показателей, таких как корреляция, хи-квадрат, взаимная информация или ANOVA. Эти методы, как правило, менее затратны в вычислительном отношении, чем методы-оболочки и встроенные методы, и их можно применять до процесса обучения модели. ОДНАКО, они не всегда могут привести к наиболее оптимальному подмножеству функций и могут упустить важные взаимодействия функций.
import pandas as pd import numpy as np from sklearn.feature_selection import SelectKBest, chi2, f_regression # Create sample dataframe df = pd.DataFrame({ 'Category1': ['A', 'B', 'C', 'A', 'C', 'B', 'B', 'C'], 'Category2': ['X', 'Y', 'Z', 'X', 'Y', 'Z', 'Z', 'X'], 'Category3': ['M', 'N', 'O', 'O', 'N', 'M', 'O', 'M'], 'Category4': ['G', 'H', 'G', 'H', 'G', 'H', 'H', 'G'], 'Numeric1': [10, 20, 30, 40, 50, 60, 70, 80], 'Numeric2': [1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5], 'Numeric3': [100, 200, 300, 400, 500, 600, 700, 800], 'Numeric4': [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8], 'Numeric5': [25, 30, 35, 40, 45, 50, 55, 60], 'Numeric6': [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5], 'Numeric7': [12, 14, 16, 18, 20, 22, 24, 26], 'Numeric8': [0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2], }) # Split the dataframe into features and target X = df.drop(columns=['Category1', 'Category2', 'Category3', 'Category4']) y = df['Category1'] # Apply feature selection using SelectKBest with chi2 as the score function selector = SelectKBest(chi2, k=5) X_new = selector.fit_transform(X, y) # Get the selected feature columns selected_columns = X.columns[selector.get_support(indices=True)].tolist() # Print the selected columns print("Selected columns:", selected_columns)
- Методы-оболочки.Методы-оболочки оценивают производительность модели машинного обучения, используя подмножества функций, и выбирают наилучшее подмножество на основе точности модели. Эти методы могут быть дорогостоящими в вычислительном отношении, поскольку они требуют многократного запуска модели с различными подмножествами функций. Однако они могут привести к повышению производительности, поскольку учитывают взаимодействие функций и могут привести к более оптимальному подмножеству функций.
import pandas as pd from sklearn.feature_selection import RFE from sklearn.linear_model import LogisticRegression # Create sample dataframe df = pd.DataFrame({ 'Category1': ['A', 'B', 'C', 'A', 'C', 'B', 'B', 'C'], 'Category2': ['X', 'Y', 'Z', 'X', 'Y', 'Z', 'Z', 'X'], 'Category3': ['M', 'N', 'O', 'O', 'N', 'M', 'O', 'M'], 'Category4': ['G', 'H', 'G', 'H', 'G', 'H', 'H', 'G'], 'Numeric1': [10, 20, 30, 40, 50, 60, 70, 80], 'Numeric2': [1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5], 'Numeric3': [100, 200, 300, 400, 500, 600, 700, 800], 'Numeric4': [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8], 'Numeric5': [25, 30, 35, 40, 45, 50, 55, 60], 'Numeric6': [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5], 'Numeric7': [124, 14, 106, 180, 20, 202, 240, 26], 'Numeric8': [0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2], 'target': [1, 0, 1, 0, 1, 1, 0, 0] }) # Define X and y X = df.drop(['Category1', 'Category2', 'Category3', 'Category4','target'], axis=1) y = df['target'] # Create the logistic regression model model = LogisticRegression(max_iter=10000) #solver='lbfgs',class_weight='balanced', # Create the RFE object and fit the model rfe = RFE(model, n_features_to_select=5) rfe.fit(X, y) # Print the selected features print("Selected Features: ") for i in range(len(X.columns)): if rfe.support_[i]: print(X.columns[i])
- В этом примере категориальные переменные были удалены, потому что используемый здесь метод RFE (рекурсивное исключение признаков) предназначен для выбора признаков с числовыми входными данными и не может обрабатывать категориальные переменные напрямую.
- Если вы хотите включить категориальные переменные в выбор объектов с помощью встроенных методов, вы можете использовать такие методы, как однократное кодирование, чтобы преобразовать их в числовые функции. После того, как категориальные переменные закодированы, вы можете использовать тот же подход, что и показанный выше, для числовых признаков.
# Import required libraries import pandas as pd import numpy as np from sklearn.feature_selection import RFE from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import LabelEncoder # Create sample dataframe df = pd.DataFrame({ 'Category1': ['A', 'B', 'C', 'A', 'C', 'B', 'B', 'C'], 'Category2': ['X', 'X', 'Z', 'X', 'Y', 'Z', 'Z', 'X'], 'Category3': ['M', 'M', 'O', 'O', 'N', 'M', 'O', 'M'], 'Category4': ['G', 'H', 'G', 'H', 'G', 'H', 'H', 'G'], 'Numeric1': [10, 80, 30, 40, 80, 60, 70, 40], 'Numeric2': [1.5, 2.5, 3.5, 4.5, 8.5, 6.5, 7.5, 4.5], 'Numeric3': [100, 400, 300, 480, 500, 600, 780, 840], 'Numeric4': [0.1, 0.4, 0.3, 0.4, 0.54, 0.64, 0.7, 0.8], 'Numeric5': [25, 30, 35, 40, 45, 50, 55, 60], 'Numeric6': [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5], 'Numeric7': [124, 14, 106, 180, 20, 202, 240, 26], 'Numeric8': [0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2], 'target': [1, 0, 1, 0, 1, 1, 0, 0] }) # Encode categorical features with LabelEncoder categorical_features = ['Category1', 'Category2', 'Category3','Category4'] for feature in categorical_features: le = LabelEncoder() df[feature] = le.fit_transform(df[feature]) # Combine encoded categorical features and numeric features X = df.drop('target', axis=1) X # Define target variable Y = df['target'] # Create the logistic regression model model = LogisticRegression(max_iter=10000) #solver='lbfgs',class_weight='balanced', # Create the RFE object and fit the model rfe = RFE(model, n_features_to_select=5) rfe.fit(X, y) # Print the selected features print("Selected Features: ") for i in range(len(X.columns)): if rfe.support_[i]: print(X.columns[i])
- Встроенные методы. Встроенные методы включают выбор признаков непосредственно в алгоритм машинного обучения, например методы регуляризации, такие как регрессия Лассо и Риджа, или методы на основе дерева решений, такие как Random Forest и Gradient Boosting. Эти методы эффективны в вычислительном отношении и могут привести к более оптимальному подмножеству функций. Однако их не всегда можно интерпретировать так же, как методы фильтра или оболочки.
from sklearn.preprocessing import LabelEncoder from sklearn.linear_model import Lasso # Create sample dataframe df = pd.DataFrame({ 'Category1': ['A', 'B', 'C', 'A', 'C', 'B', 'B', 'C'], 'Category2': ['X', 'Y', 'Z', 'X', 'Y', 'Z', 'Z', 'X'], 'Category3': ['M', 'N', 'O', 'O', 'N', 'M', 'O', 'M'], 'Category4': ['G', 'H', 'G', 'H', 'G', 'H', 'H', 'G'], 'Numeric1': [10, 20, 30, 40, 50, 60, 70, 80], 'Numeric2': [1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5], 'Numeric3': [100, 200, 300, 400, 500, 600, 700, 800], 'Numeric4': [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8], 'Numeric5': [25, 30, 35, 40, 45, 50, 55, 60], 'Numeric6': [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5], 'Numeric7': [12, 14, 16, 18, 20, 22, 24, 26], 'Numeric8': [0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2], 'target': [1, 0, 1, 0, 1, 1, 0, 0] }) # Encode categorical features with LabelEncoder categorical_features = ['Category1', 'Category2', 'Category3','Category4'] for feature in categorical_features: le = LabelEncoder() df[feature] = le.fit_transform(df[feature]) # Combine encoded categorical features and numeric features X = df.drop('target', axis=1) # Define target variable Y = df['target'] # Initialize Lasso model lasso = Lasso(alpha=0.1) # Fit Lasso to the data lasso.fit(X, Y) # Get feature coefficients feature_coefs = pd.Series(lasso.coef_, index=X.columns) # Get selected features selected_features = feature_coefs[feature_coefs != 0].index.tolist() # Print selected features print('Selected Features:', selected_features)
Как правило, выбор метода выбора признаков зависит от размера набора данных, количества признаков, сложности модели и конкретной проблемы. Например, если у вас есть большой набор данных со многими функциями и вы используете линейную модель, хорошим выбором могут быть методы на основе фильтров, такие как оценка взаимной информации или корреляция. С другой стороны, если у вас есть небольшой набор данных и вы используете более сложную модель, такую как нейронная сеть, более подходящими могут быть методы-оболочки, такие как рекурсивное исключение признаков или генетические алгоритмы.
2.4. Уменьшение размерности:количество объектов или переменных в наборе данных при сохранении наиболее важной информации или закономерностей, присутствующих в данных. Основная цель уменьшения размерности — повысить эффективность алгоритмов машинного обучения и снизить риск переобучения за счет удаления нерелевантных или избыточных функций.
Извлечение признаков: включает в себя преобразование исходных признаков в пространство меньшего размера с сохранением наиболее важной информации или закономерностей в данных. Методы извлечения признаков включают анализ основных компонентов (PCA), линейный дискриминантный анализ (LDA), неотрицательную матричную факторизацию (NMF), t-распределенное стохастическое встраивание соседей (t-SNE) и многие другие.
Извлечение признаков необходимо, когда у нас есть многомерные данные, а это означает, что количество признаков велико по сравнению с количеством выборок. В таких случаях модель может страдать от проклятия размерности, которое связано с тем, что производительность многих алгоритмов машинного обучения ухудшается по мере увеличения количества признаков.
import pandas as pd from sklearn.decomposition import PCA from sklearn.preprocessing import LabelEncoder # Create sample dataframe df = pd.DataFrame({ 'Category1': ['A', 'B', 'C', 'A', 'C', 'B', 'B', 'C'], 'Category2': ['X', 'Y', 'Z', 'X', 'Y', 'Z', 'Z', 'X'], 'Category3': ['M', 'N', 'O', 'O', 'N', 'M', 'O', 'M'], 'Category4': ['G', 'H', 'G', 'H', 'G', 'H', 'H', 'G'], 'Numeric1': [10, 20, 30, 40, 50, 60, 70, 80], 'Numeric2': [1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5], 'Numeric3': [100, 200, 300, 400, 500, 600, 700, 800], 'Numeric4': [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8], 'Numeric5': [25, 30, 35, 40, 45, 50, 55, 60], 'Numeric6': [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5], 'Numeric7': [12, 14, 16, 18, 20, 22, 24, 26], 'Numeric8': [0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2], 'target': [1, 0, 1, 0, 1, 1, 0, 0] }) # Encode categorical features using LabelEncoder encoder = LabelEncoder() for col in ['Category1', 'Category2', 'Category3', 'Category4']: df[col] = encoder.fit_transform(df[col]) # Separate features from target X = df.drop('target', axis=1) y = df['target'] # Perform PCA pca = PCA(n_components=3) X_pca = pca.fit_transform(X) # Print explained variance ratio print('Explained variance ratio:', pca.explained_variance_ratio_) # Visualize PCA results import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.scatter(X_pca[:,0], X_pca[:,1], X_pca[:,2], c='red') ax.set_xlabel('Principal Component 1') ax.set_ylabel('Principal Component 2') ax.set_zlabel('Principal Component 3') plt.show()
Затем мы применяем PCA к функциям, указывая, что мы хотим уменьшить количество измерений до 3. Мы печатаем объясненный коэффициент дисперсии полученных основных компонентов, который говорит нам, какая часть дисперсии исходных данных объясняется каждым основным компонентом. компонент. Наконец, мы визуализируем результаты PCA с помощью точечной диаграммы, где цвет каждой точки соответствует целевой переменной.
Извлечение функций VS Выбор функций
Мы выбираем извлечение признаков вместо выбора признаков, когда хотим преобразовать данные высокой размерности в пространство меньшей размерности, сохраняя при этом важную информацию в данных. Методы извлечения признаков особенно полезны, когда исходные признаки не могут быть интерпретированы напрямую или когда мы подозреваем, что могут быть скрытые переменные, отражающие базовую структуру данных.
Напротив, выбор признаков включает в себя выбор подмножества исходных признаков, наиболее релевантных целевой переменной. Выбор функций полезен, когда исходные функции можно интерпретировать напрямую, и мы хотим уменьшить вычислительную сложность модели, удалив ненужные функции.
Выбор между извлечением признаков и выбором признаков зависит от конкретных характеристик набора данных и решаемой проблемы. В общем, если исходные функции поддаются прямой интерпретации, и мы заинтересованы в понимании взаимосвязей между функциями и целевой переменной, мы можем выбрать выбор функций. Если исходные признаки не поддаются интерпретации или мы подозреваем, что могут быть скрытые переменные, объясняющие структуру данных, мы можем выбрать извлечение признаков.
Интеграция данных — это процесс объединения данных из разных источников и представления их в едином виде. Это может включать очистку, преобразование и обогащение данных для создания единого, согласованного и точного представления информации. Существует несколько типов интеграции данных, в том числе:
3.1. Присоединяется:
- Вертикальная интеграция. Под вертикальной интеграцией понимается объединение двух или более наборов данных с одной и той же схемой, но разными наблюдениями.
- Горизонтальная интеграция. Горизонтальная интеграция — это процесс объединения двух или более таблиц с одинаковыми столбцами в одну таблицу.
- Интеграция со схемой-звездой. Интеграция со схемой-звездой — это распространенный подход, используемый при хранении данных. Он включает в себя создание центральной таблицы фактов и окружение ее несколькими таблицами измерений.
Интеграция схемы Star — это тип интеграции данных, при котором данные из нескольких источников объединяются в центральную базу данных. Он включает в себя создание центральной таблицы, известной как таблица фактов, которая содержит метрики или измерения, и несколько таблиц измерений, содержащих атрибуты, связанные с метриками.
Вертикальная интеграция предполагает объединение данных из разных источников с одинаковой структурой, а горизонтальная интеграция предполагает объединение данных из разных источников с разной структурой.
# HORIZONTALLY & VERTICALLY INTERGRATION import pandas as pd # Create first dataset df1 = pd.DataFrame({ 'id': [1, 2, 3], 'name': ['Alice', 'Bob', 'Charlie'], 'age': [25, 30, 35] }) # Create second dataset df2 = pd.DataFrame({ 'id': [4, 5, 6], 'name': ['David', 'Eve', 'Frank'], 'age': [40, 45, 50] }) # Concatenate the datasets vertically df_vertically = pd.concat([df1, df2]) # Combine dataframes horizontally df_horizontally = pd.concat([df1, df2], axis=1) # Print the concatenated dataset print(df_vertically) print() print(df_horizontally) # STAR SCHEMA INTERGRATION import pandas as pd # Read the fact table fact_table = pd.read_csv('fact_table.csv') # Read the dimension tables dim_table1 = pd.read_csv('dim_table1.csv') dim_table2 = pd.read_csv('dim_table2.csv') dim_table3 = pd.read_csv('dim_table3.csv') # Join the dimension tables with the fact table using common keys fact_table = pd.merge(fact_table, dim_table1, on='key1', how='left') fact_table = pd.merge(fact_table, dim_table2, on='key2', how='left') fact_table = pd.merge(fact_table, dim_table3, on='key3', how='left') # Set the index to the primary key in the fact table fact_table.set_index('fact_table_key', inplace=True) # Save the integrated data as a CSV file fact_table.to_csv('star_schema_integration.csv')
Интеграция схемы Star — это тип интеграции данных, который включает создание центральной таблицы, также известной как таблица фактов, которая содержит общую информацию из всех источников данных. Эта таблица фактов окружена таблицами измерений, которые содержат дополнительную информацию, связанную с таблицей фактов. Таблицы измерений обычно меньше и более целенаправленны, чем таблица фактов, что позволяет выполнять более быстрые и эффективные запросы.
Интеграция со звездообразной схемой особенно полезна при работе с большими объемами данных из нескольких источников, поскольку она позволяет эффективно запрашивать и анализировать эти данные. Он обычно используется в хранилищах данных и приложениях бизнес-аналитики, где он может помочь повысить скорость и точность принятия решений.
Основное различие между вертикальной и горизонтальной интеграцией заключается в том, что вертикальная интеграция предполагает объединение данных из разных источников в одну таблицу, а горизонтальная интеграция предполагает объединение данных из схожих источников в несколько таблиц. Вертикальная интеграция часто используется в ситуациях, когда источники данных различаются, тогда как горизонтальная интеграция используется, когда источники данных похожи, но содержат разную информацию.
Напротив, интеграция схемы Star включает создание центральной таблицы фактов, которая содержит общую информацию из всех источников данных, и окружение этой таблицы меньшими, более целенаправленными таблицами измерений. Этот подход позволяет более эффективно запрашивать и анализировать данные, поскольку таблицы параметров предоставляют дополнительный контекст и помогают упростить запросы, необходимые для извлечения информации из данных.
3.2. Изменение формы
Интеграция данных с изменением формы — это процесс объединения данных из разных источников и преобразования их в единый формат, пригодный для анализа. Изменение формы включает в себя изменение структуры данных из широкого формата в длинный формат или наоборот. Это делается для того, чтобы сделать данные пригодными для различных методов анализа, таких как визуализация, моделирование или статистический анализ.
Существует два типа переоформления:
- Преобразование от широкого к длинному — pd.melt(): этот тип преобразования используется, когда данные представлены в расширенном формате, где каждая переменная представлена отдельным столбцом. При преобразовании широкого формата в длинный данные преобразуются в длинный формат, где каждое наблюдение представлено отдельной строкой. Изменение формы от широкой к длинной в пандах выполняется с помощью функции плавления. Функция плавления используется для преобразования широкого кадра данных в длинный кадр данных. Это делается путем разворота фрейма данных и объединения столбцов в один столбец.
import pandas as pd data = { 'id': [1, 2, 3], 'name': ['Alice', 'Bob', 'Charlie'], 'day1_score': [90, 80, 85], 'day2_score': [95, 75, 90], 'day3_score': [80, 85, 95] } df = pd.DataFrame(data) melted_df = pd.melt(df, id_vars=['id', 'name'], value_vars=['day1_score', 'day2_score', 'day3_score'], var_name='day', value_name='score') print(df) print() print(melted_df)
- Преобразование от длинного к широкому — pd.pivot():этот тип преобразования используется, когда данные представлены в длинном формате, где каждое наблюдение представлено отдельной строкой. При преобразовании от длинного к широкому данные преобразуются в широкий формат, где каждая переменная представлена отдельным столбцом.
import pandas as pd # Create sample dataframe df = pd.DataFrame({ 'Quarter': ['Q1', 'Q1', 'Q1', 'Q1', 'Q2', 'Q2', 'Q2', 'Q2', 'Q3', 'Q3', 'Q3', 'Q3', 'Q4', 'Q4', 'Q4', 'Q4'], 'Salesperson': ['Alice', 'Bob', 'Charlie', 'Dave', 'Alice', 'Bob', 'Charlie', 'Dave', 'Alice', 'Bob', 'Charlie', 'Dave', 'Alice', 'Bob', 'Charlie', 'Dave'], 'Product': ['A', 'A', 'B', 'B', 'A', 'A', 'B', 'B', 'A', 'A', 'B', 'B', 'A', 'A', 'B', 'B'], 'Sales': [100, 200, 150, 250, 300, 400, 350, 450, 500, 600, 550, 650, 700, 800, 750, 850] }) # Reshape from long to wide format based on 'Quarter' column df_wide = df.pivot(index='Salesperson', columns='Quarter', values='Sales') # View new wide format dataframe print('Wide Format:\n', df_wide)
Изменение формы обычно используется, когда формат данных не соответствует используемому методу анализа. Например, методы визуализации, такие как диаграммы разброса и точечные диаграммы, требуют данных в длинном формате, в то время как для регрессионного анализа и алгоритмов машинного обучения часто требуются данные в широком формате.
Сокращение данных с выборкой — это метод, используемый для уменьшения размера набора данных путем случайного выбора подмножества точек данных. Это может быть полезно при работе с большими наборами данных, обработка которых трудна или требует много времени. Существует несколько типов редукции данных с выборкой, в том числе:
- Простая случайная выборка. Это включает в себя случайный выбор точек данных из набора данных без какой-либо систематической ошибки.
- Стратифицированная выборка. Она включает в себя разделение набора данных на страты на основе некоторых критериев, а затем случайный выбор точек данных из каждой страты.
import numpy as np import pandas as pd from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D # Generate sample data X, y = make_blobs(n_samples=2000, centers=10, n_features=3, random_state=42) # Perform simple random sampling X_train_simple, X_test_simple, y_train_simple, y_test_simple = train_test_split(X, y, test_size=0.4) # Perform stratified sampling X_train_strat, X_test_strat, y_train_strat, y_test_strat = train_test_split(X, y, test_size=0.4, stratify=y) # Plot the simple random sample fig = plt.figure(figsize=(30,16)) ax2 = fig.add_subplot(131, projection='3d') ax2.scatter(X_train_simple[:, 0], X_train_simple[:, 1], X_train_simple[:, 2], c=y_train_simple, cmap='tab10') ax2.set_title('Simple Random Sample') # Plot the stratified sample ax3 = fig.add_subplot(132, projection='3d') ax3.scatter(X_train_strat[:, 0], X_train_strat[:, 1], X_train_strat[:, 2], c=y_train_strat, cmap='tab10') ax3.set_title('Stratified Sample') plt.show()
- Кластерная выборка: набор данных делится на кластеры, а затем случайным образом выбираются целые кластеры для включения в выборку.
import pandas as pd import matplotlib.pyplot as plt from sklearn.cluster import KMeans from sklearn.datasets import make_blobs # Generate sample data X, y = make_blobs(n_samples=5000, centers=10, n_features=5, random_state=42) # Perform KMeans clustering to create clusters kmeans = KMeans(n_clusters=9, random_state=42) kmeans.fit(X) # Get cluster labels cluster_labels = kmeans.labels_ # Create clusters dataframe clusters_df = pd.DataFrame(X, columns=['Feature1', 'Feature2', 'Feature3', 'Feature4', 'Feature5']) clusters_df['Cluster'] = cluster_labels # Create cluster sampling function def cluster_sampling(df, cluster_col, n_clusters, sample_size): # Create a dictionary to store the sampled dataframes sampled_clusters = {} # Loop through each cluster and sample data for i in range(n_clusters): cluster_data = df[df[cluster_col] == i] sampled_cluster = cluster_data.sample(n=sample_size, replace=True) sampled_clusters[i] = sampled_cluster # Concatenate the sampled dataframes into one dataframe sampled_df = pd.concat(sampled_clusters.values()) return sampled_df # Sample data from clusters sampled_data = cluster_sampling(clusters_df, 'Cluster', 9, 50) # Plot the original data and the sampled data fig = plt.figure(figsize=(30,16)) # Plot the original data in 3D ax1 = fig.add_subplot(121, projection='3d') ax1.scatter(X[:, 0], X[:, 1], X[:, 2], c=cluster_labels, cmap='tab10') ax1.set_title('Original Data') # Plot the sampled data in 3D ax2 = fig.add_subplot(122, projection='3d') ax2.scatter(sampled_data['Feature1'], sampled_data['Feature2'], sampled_data['Feature3'], c=sampled_data['Cluster'], cmap='tab10') ax2.set_title('Sampled Data') plt.show()
- Систематическая выборка. Сюда входит выбор точек данных через равные промежутки времени из набора данных. Систематическая выборка — это метод статистической выборки, при котором из совокупности выбирается случайная начальная точка, а затем отбирается каждый n-й член совокупности. быть частью выборки. Это означает, что выбор каждого члена выборки основан на определенном заранее определенном шаблоне, который может быть либо каждым k-м элементом, либо каждым элементом с определенным количеством элементов, пропущенных между ними. Систематическая выборка часто используется, когда совокупность слишком велика для выборка каждого члена или когда случайная выборка невозможна или нецелесообразна. Однако важно отметить, что систематическая выборка может привести к систематической ошибке в выборке, если в генеральной совокупности есть повторяющийся шаблон или периодичность, которые совпадают с интервалом выборки. Поэтому важно тщательно продумать интервал выборки, чтобы убедиться, что выборка репрезентативна для генеральной совокупности.
import random # Define the population population = list(range(1, 101)) # Define the sample size and sampling interval sample_size = 20 sampling_interval = int(len(population) / sample_size) # Define the starting point of the sample starting_point = random.randint(0, sampling_interval) # Perform systematic sampling sample = [] for i in range(starting_point, len(population), sampling_interval): sample.append(population[i]) # Print the sample print("Systematic Sample:", sample)
Сокращение данных с помощью выборки может быть полезно при работе с большими наборами данных, которые слишком велики для эффективной обработки. Уменьшая размер набора данных, мы можем ускорить время обработки и уменьшить требования к памяти для наших алгоритмов.
Github: фатихозгул