Как применить дискретизацию энтропии к набору данных

Вопрос или проблема

У меня есть простой набор данных, к которому я хотел бы применить дискретизацию по энтропии. Программа должна дискретизировать атрибут на основе следующих критериев:

Когда истинно одно из условий “a” или “b” для раздела, этот раздел перестает делиться:

a- Число различных классов в разделе равно 1.

b- Соотношение минимальных и максимальных частот среди различных значений для атрибута Class в разделе меньше 0.5, а количество различных значений
атрибута Class в разделе равно Floor(n/2), где n – это количество различных значений в исходном наборе данных.

В качестве примера,

100 записей, это уникальные значения из них

v1 10
v2 30
v3 15
v4 20
v5 25

--- n = 5

s1

v1 10 -> 10/30 = .34   
v2 30  
v3 15

--- n1 = 3

s2
v4 20 -> 20/25 = 0.8
v5 25

--- n2 = 2

если minf(s1)/maxf(s1) < 0.5, тогда условие 1 из b выполнено, и floor(n/2) == n1, тогда условие 2 из b выполнено.
    остановить деление

в этом случае v1 – минимум в s1, а v2 – максимум. Таким образом, алгоритм должен прекратить деление.

Ожидаемый результат:
Программа должна вернуть лучшую партицию на основе максимального прироста информации.

Фактический результат:
Возвращается пустой набор данных.

Я написал реализацию.

from numpy.core.defchararray import count
import pandas as pd
import numpy as np
import numpy as np
from math import floor, log2
from sklearn.decomposition import PCA
import matplotlib.pyplot as plot

def print_full(x):
    pd.set_option('display.max_rows', len(x))
    print(x)
    pd.reset_option('display.max_rows')

def main():
    s = pd.read_csv('A1-dm.csv')
    print(s)
    print("******************************************************")
    print("Дискретизация по энтропии                         НАЧАТО")
    s = entropy_discretization(s)
    print("Дискретизация по энтропии                         ЗАВЕРШЕНО")
    print(s)
    print("******************************************************")

def entropy_discretization(s):

    I = {}
    i = 0
    n = s.nunique()['A1']
    s_temp = s
    s1 = pd.DataFrame()
    s2 = pd.DataFrame()
    while(uniqueValue(s_temp)):

        # Шаг 1: выберите порог
        threshold = s_temp['A1'].iloc[0]

        # Шаг 2: Разделите набор данных на два раздела
        s1 = s[s['A1'] < threshold]
        print("s1 после разделения")
        print(s1)
        print("******************")
        s2 = s[s['A1'] >= threshold]
        print("s2 после разделения")
        print(s2)
        print("******************")

        print("******************")
        print("расчет maxf")
        print(f" maxf {maxf(s['A1'])}")
        print("******************")

        print("******************")
        print("расчет minf")
        print(f" maxf {minf(s['A1'])}")
        print("******************")

        # print(maxf(s['A1'])/minf(s['A1']))
        if (maxf(s1['A1'])/minf(s1['A1']) < 0.5) and (s_temp.nunique()['A1'] == floor(n/2)):
            print(f"Условие b выполнено {maxf(s1['A1'])}/{minf(s1['A1'])} < {0.5} {s_temp.nunique()['A1']} == {floor(n/2)}")
            break

        # Шаг 3: вычислите прирост информации.
        informationGain = information_gain(s1,s2,s_temp)
        I.update({f'informationGain_{i}':informationGain,f'threshold_{i}': threshold})
        print(f'добавлен informationGain_{i}: {informationGain}, threshold_{i}: {threshold}')
        s_temp = s_temp[s_temp['A1'] != threshold]
        i += 1

    # Шаг 5: рассчитайте минимальный прирост информации
    n = int(((len(I)/2)-1))
    print("Расчет максимального порога")
    print("*****************************")
    maxInformationGain = 0
    maxThreshold       = 0 
    for i in range(0, n):
        if(I[f'informationGain_{i}'] > maxInformationGain):
            maxInformationGain = I[f'informationGain_{i}']
            maxThreshold       = I[f'threshold_{i}']

    print(f'maxThreshold: {maxThreshold}, maxInformationGain: {maxInformationGain}')

    s = pd.merge(s1,s2)

    # Шаг 6: сохраняем партиции S на основе значения threshold_i
    return s #maxPartition(maxInformationGain,maxThreshold,s,s1,s2)

def maxf(s):
    return s.max()

def minf(s):
    return s.min()

def uniqueValue(s):
    # одинаковы ли записи в s? вернуть истину
    if s.nunique()['A1'] == 1:
        return False
    # иначе ложь 
    else:
        return True

def maxPartition(maxInformationGain,maxThreshold,s,s1,s2):
    print(f'informationGain: {maxInformationGain}, threshold: {maxThreshold}')
    merged_partitions =  pd.merge(s1,s2)
    merged_partitions =  pd.merge(merged_partitions,s)
    print("Лучшая партиция")
    print("***************")
    print(merged_partitions)
    print("***************")
    return merged_partitions

def information_gain(s1, s2, s):
    # вычислите кардинальность для s1
    cardinalityS1 = len(pd.Index(s1['A1']).value_counts())
    print(f'Кардинальность s1: {cardinalityS1}')
    # вычислите кардинальность для s2
    cardinalityS2 = len(pd.Index(s2['A1']).value_counts())
    print(f'Кардинальность s2: {cardinalityS2}')
    # вычислите кардинальность для s
    cardinalityS = len(pd.Index(s['A1']).value_counts())
    print(f'Кардинальность s: {cardinalityS}')
    # вычислите прирост информации
    informationGain = (cardinalityS1/cardinalityS) * entropy(s1) + (cardinalityS2/cardinalityS) * entropy(s2)
    print(f'Общий прирост информации: {informationGain}')
    return informationGain

def entropy(s):
    print("расчет энтропии для s")
    print("*****************************")
    print(s)
    print("*****************************")

    # инициализируйте ent
    ent = 0

    # вычислите количество классов в s
    numberOfClasses = s['Class'].nunique()
    print(f'Количество классов для набора данных: {numberOfClasses}')
    value_counts = s['Class'].value_counts()
    p = []
    for i in range(0,numberOfClasses):
        n = s['Class'].count()
        # вычислите частоту class_i в S1
        print(f'p{i} {value_counts.iloc[i]}/{n}')
        f = value_counts.iloc[i]
        pi = f/n
        p.append(pi)

    print(p)

    for pi in p:
        ent += -pi*log2(pi)

    return ent

main()

Набор выборочных данных выглядит следующим образом

A1,A2,A3,Class
2,0.4631338,1.5,3
8,0.7460648,3.0,3
6,0.264391038,2.5,2
5,0.4406713,2.3,1
2,0.410438159,1.5,3
2,0.302901816,1.5,2
6,0.275869396,2.5,3

Любая помощь в понимании причин, по которым метод возвращает пустой набор данных, будет очень признательна.

Я пытался создать процедуру для этого, которая разделяет данные на два раздела, но я буду признателен за отзывы о том, правильно ли я реализовал.

from numpy.core.defchararray import count
import pandas as pd
import numpy as np
import numpy as np
from math import floor, log2
from sklearn.decomposition import PCA
import matplotlib.pyplot as plot

def print_full(x):
    pd.set_option('display.max_rows', len(x))
    print(x)
    pd.reset_option('display.max_rows')

def main():
    s = pd.read_csv('A1-dm.csv')
    print("******************************************************")
    print("Дискретизация по энтропии                         НАЧАТО")
    s = entropy_discretization(s)
    print("Дискретизация по энтропии                         ЗАВЕРШЕНО")

# Этот метод дискретизирует атрибут A1
# Если прирост информации равен 0, т.е. количество 
# различных классов равно 1 или
# Если min f/ max f < 0.5 и количество различных значений равно floor(n/2)
# Тогда этот раздел перестает разделяться.
# Этот метод дискретизирует s A1
# Если прирост информации равен 0, т.е. количество 
# различных классов равно 1 или
# Если min f/ max f < 0.5 и количество различных значений равно floor(n/2)
# Тогда этот раздел перестает разделяться.
def entropy_discretization(s):

    I = {}
    i = 0
    n = s.nunique()['Class']
    s1 = pd.DataFrame()
    s2 = pd.DataFrame()
    distinct_values = s['Class'].value_counts().index
    information_gain_indicies = []
    print(f'Уникальные значения для набора данных s["Class"]: {distinct_values}')
    for i in distinct_values:

        # Шаг 1: выберите порог
        threshold = i
        print(f'Используя порог {threshold}')

        # Шаг 2: Разделите набор данных на два раздела
        s1 = s[s['Class'] < threshold]
        print("s1 после разделения")
        print(s1)
        print("******************")
        s2 = s[s['Class'] >= threshold]
        print("s2 после разделения")
        print(s2)
        print("******************")

        print("******************")
        print("расчет maxf")
        print(f" maxf {maxf(s['Class'])}")
        print("******************")

        print("******************")
        print("расчет minf")
        print(f" maxf {minf(s['Class'])}")
        print("******************")

        print(f"Проверка условия a: {s1.nunique()['Class']} == {1}")
        if (s1.nunique()['Class'] == 1):
            break

        print(f"Проверка условия b:  {maxf(s1['Class'])}/{minf(s1['Class'])} < {0.5} {s1.nunique()['Class']} == {floor(n/2)}")
        if (maxf(s1['Class'])/minf(s1['Class']) < 0.5) and (s1.nunique()['Class'] == floor(n/2)):
            print(f"Условие b выполнено: {maxf(s1['Class'])}/{minf(s1['Class'])} < {0.5} {s1.nunique()['Class']} == {floor(n/2)}")
            break

        # Шаг 3: вычислите прирост информации.
        informationGain = information_gain(s1,s2,s)
        I.update({f'informationGain_{i}':informationGain,f'threshold_{i}': threshold})
        print(f'добавлен informationGain_{i}: {informationGain}, threshold_{i}: {threshold}')
        information_gain_indicies.append(i)

    # Шаг 5: рассчитайте минимальный прирост информации
    n = int(((len(I)/2)-1))
    print("Расчет максимального порога")
    print("*****************************")
    maxInformationGain = 0
    maxThreshold       = 0 
    for i in information_gain_indicies:
        if(I[f'informationGain_{i}'] > maxInformationGain):
            maxInformationGain = I[f'informationGain_{i}']
            maxThreshold       = I[f'threshold_{i}']

    print(f'maxThreshold: {maxThreshold}, maxInformationGain: {maxInformationGain}')

    partitions = [s1,s2]
    s = pd.concat(partitions)

    # Шаг 6: сохраняем партиции S на основе значения threshold_i
    return s #maxPartition(maxInformationGain,maxThreshold,s,s1,s2)

def maxf(s):
    return s.max()

def minf(s):
    return s.min()

def uniqueValue(s):
    # одинаковы ли записи в s? вернуть истину
    if s.nunique()['Class'] == 1:
        return False
    # иначе ложь 
    else:
        return True

def maxPartition(maxInformationGain,maxThreshold,s,s1,s2):
    print(f'informationGain: {maxInformationGain}, threshold: {maxThreshold}')
    merged_partitions =  pd.merge(s1,s2)
    merged_partitions =  pd.merge(merged_partitions,s)
    print("Лучшая партиция")
    print("***************")
    print(merged_partitions)
    print("***************")
    return merged_partitions

def information_gain(s1, s2, s):
    # вычислите кардинальность для s1
    cardinalityS1 = len(pd.Index(s1['Class']).value_counts())
    print(f'Кардинальность s1: {cardinalityS1}')
    # вычислите кардинальность для s2
    cardinalityS2 = len(pd.Index(s2['Class']).value_counts())
    print(f'Кардинальность s2: {cardinalityS2}')
    # вычислите кардинальность для s
    cardinalityS = len(pd.Index(s['Class']).value_counts())
    print(f'Кардинальность s: {cardinalityS}')
    # вычислите прирост информации
    informationGain = (cardinalityS1/cardinalityS) * entropy(s1) + (cardinalityS2/cardinalityS) * entropy(s2)
    print(f'Общий прирост информации: {informationGain}')
    return informationGain

def entropy(s):
    print("расчет энтропии для s")
    print("*****************************")
    print(s)
    print("*****************************")

    # инициализируйте ent
    ent = 0

    # вычислите количество классов в s
    numberOfClasses = s['Class'].nunique()
    print(f'Количество классов для набора данных: {numberOfClasses}')
    value_counts = s['Class'].value_counts()
    p = []
    for i in range(0,numberOfClasses):
        n = s['Class'].count()
        # вычислите частоту class_i в S1
        print(f'p{i} {value_counts.iloc[i]}/{n}')
        f = value_counts.iloc[i]
        pi = f/n
        p.append(pi)

    print(p)

    for pi in p:
        ent += -pi*log2(pi)

    return ent

Ответ или решение

Как применить дискретизацию энтропии к набору данных

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

Задача

Ваша задача – дискретизировать признак в наборе данных, основываясь на следующих условиях:

  1. Условие "а": Если число различных классов в определенной группе равно 1, то дискретизация прекращается.
  2. Условие "б": Если отношение минимальной к максимальной частоте среди различных значений атрибута "Class" в группе меньше 0.5, и количество уникальных значений среди "Class" в группе равно floor(n/2) (где n — количество уникальных классов в исходном наборе данных).

Пример

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

v1: 10
v2: 30
v3: 15
v4: 20
v5: 25

Здесь n = 5. Если вы разделите данные, например, на s1 и s2, и в одной из групп вы окажетесь в ситуации, когда выполняется хотя бы одно из условий, процесс дискретизации должен остановиться.

Реализация

Для реализации дискретизации с использованием Python, следуйте следующему алгоритму:

1. Импорт необходимых библиотек

import pandas as pd
import numpy as np
from math import floor, log2

2. Функция для вычисления энтропии

def calculate_entropy(s):
    p = s['Class'].value_counts(normalize=True)
    return -sum(p * np.log2(p + 1e-10))  # Добавление маленького числа для избежания деления на ноль

3. Основная функция дискретизации

def entropy_discretization(dataset):
    unique_classes = dataset['Class'].nunique()
    best_gain = -1  # Изначально нет информации
    best_partition = None

    thresholds = np.sort(dataset['A1'].unique())

    for threshold in thresholds:
        s1 = dataset[dataset['A1'] < threshold]
        s2 = dataset[dataset['A1'] >= threshold]

        if len(s1) == 0 or len(s2) == 0:  # Проверка на пустые группы
            continue

        # Условия остановки
        if s1['Class'].nunique() == 1:  # Условие "а"
            break

        min_freq = s1['Class'].value_counts().min()
        max_freq = s1['Class'].value_counts().max()

        if (min_freq / max_freq < 0.5) and (s1['Class'].nunique() == floor(unique_classes / 2)):  # Условие "б"
            break

        # Вычисление информации
        total_entropy = calculate_entropy(dataset)
        gain = total_entropy - (len(s1) / len(dataset)) * calculate_entropy(s1) - (len(s2) / len(dataset)) * calculate_entropy(s2)

        if gain > best_gain:
            best_gain = gain
            best_partition = (s1, s2)

    return best_partition

4. Основная функция для выполнения кода

if __name__ == '__main__':
    dataset = pd.read_csv('A1-dm.csv')
    best_partition = entropy_discretization(dataset)
    print("Лучшие разделения:", best_partition)

Заключение

Таким образом, вы можете применить дискретизацию на основе энтропии к вашему набору данных, следуя описанным шагам и критериям. Убедитесь, что вы учитываете условия остановки, чтобы избежать излишнего разделения данных. С помощью этого подхода вы сможете повысить качество анализа данных и улучшить предсказательные способности моделей машинного обучения.

Оцените материал
Добавить комментарий

Капча загружается...