Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

3 / main

.py
Скачиваний:
0
Добавлен:
07.04.2025
Размер:
15.15 Кб
Скачать
from pandas import read_excel

# читаем файл, получаем нужные столбцы
df = read_excel("source_data.xls", "Sheet1", skiprows=1, nrows=78, usecols="M:V")
N = len(df)
p = len(df.columns)
# 1 step - default matrix
print(f"Z - матрица данных типа объект-признак размером Nxp\nN={len(df)} p={len(df.columns)}")

print(df.to_string())
Z = df.values.tolist()


def means_dispersions():
    means = [0] * p
    for j in range(p):
        for i in range(N):
            means[j] += Z[i][j]
    for i in range(p):
        means[i] /= N
    print("\nСредние по столбцам:")
    print(means)

    dispersions = [0] * p
    for j in range(p):
        for i in range(N):
            dispersions[j] += (Z[i][j] - means[j]) ** 2
    for i in range(p):
        dispersions[i] /= N
    print("\nДисперсии по столбцам:")
    print(dispersions)

    return means, dispersions


means, dispersions = means_dispersions()


# Для устранения неоднородности в исходных данных выполнить
# нормировку (стандартизацию) данных по столбцам
def standartize_matrix():
    X = []
    for i in range(N):
        l = []
        for j in range(p):
            l.append(0)
        X.append(l)
    for j in range(p):
        for i in range(N): #Каждое значение преобразуется по формуле: (x - mean) / std
            X[i][j] = (Z[i][j] - means[j]) / (dispersions[j] ** 0.5)
    print("\nСтандартизованная матрица:")
    for j in range(N):
        for i in range(p):
            print(f"{X[j][i]:.3f}\t", end="")
        print()
    print()
    return X

X = standartize_matrix()
#Это необходимо для приведения всех признаков к одинаковому масштабу
#После стандартизации среднее = 0, дисперсия = 1 для каждого признака


#Построить матрицу ковариации. Ввиду произведенной
#стандартизации данных матрица ковариации будет корреляционной
#матрицей исходных данных R порядка pxp
def covariac_matrix():
    covar = []
    for i in range(p):
        l = []
        for j in range(p):
            l.append(0)
        covar.append(l)
    for j in range(p):
        for i in range(p):
            t = 0
            for k in range(N):
                t += (X[k][i] - means[i]) * (X[k][j] - means[j])  # means для X должны быть ~0
            covar[i][j] = t / N
    return covar

covar = covariac_matrix()

sum_disp_osn = 0

# 3 step - correl matrix
def correl_matrix():
    correl = []
    for i in range(p):
        l = []
        for j in range(p):
            l.append(0)
        correl.append(l)
    for j in range(p):
        for i in range(p):
            t = 0
            for k in range(N):
                t += X[k][i] * X[k][j]
            correl[i][j] = t / N
    print("Корелляционная матрица:")
    for j in range(p):
        for i in range(p):
            print(f"{correl[j][i]:.3f}\t", end="")
        print()
    print()
    return correl

correl = correl_matrix()
#Поскольку данные стандартизированы, ковариационная матрица = корреляционной
#Корреляционная матрица показывает взаимосвязи между признаками

#Проверка гипотезы о том, отличается ли матрица от единичной
#Если нет значимых корреляций, PCA не даст полезного результата
d = 0
for i in range(p):
    for j in range(p):
        if i == j:
            continue
        d += correl[i][j] ** 2
d *= len(Z)

d_max = 45
# x^2
if d <= d_max:
    print("Дальнейшие вычисления нецелесообразны")


def sgn(n):
    if n >= 0:
        return 1
    else:
        return -1

#Метод Якоби с преградами применяется для отыскания всех
#собственных значений и собственных векторов симметричной матрицы.
def yakobi(A):
    # Итеративное обнуление внедиагональных элементов
    eps = 1e-6

    # шаг 1 (Задаем t0 = E ,где E-единичная матрица порядка n)
    n = len(A)
    t = []
    for i in range(n):
        t_ = []
        for j in range(n):
            t_.append(0)
        t.append(t_)
    for i in range(n):
        t[i][i] = 1

    # шаг 2(Вычисляем первую преграду:)
    a_0 = 0
    for j in range(1, n):
        for i in range(j):
            if i == j:
                continue
            a_0 += A[i][j] ** 2
    a_0 = ((2 * a_0) ** 0.5) / n
    a_k = a_0

    while True:
        flag = True
        for j in range(1, n):
            for i in range(j):
                if A[i][j] > abs(eps * a_0):
                    flag = False
        if flag:
            break

        # шаг 3(Находим наибольший по модулю внедиагональный элемент a_pq,
        # превосходящий текущую преграду a_k, где k=0,1,2,3...
        # Если такого элемента нет, то переходим к шагу 5 алгоритма, иначе
        # идем к шагу 4.)
        a_pq = -1
        p, q = -1, -1
        for j in range(1, n):
            for i in range(j):
                if i == j:
                    continue
                if abs(A[i][j]) > a_k and abs(A[i][j]) > a_pq:
                    a_pq = abs(A[i][j])
                    p = i
                    q = j

        # шаг 4(Анализируем найденный элемент a_pq. Для этого вычисляем)
        y, x, s, c = 0, 0, 0, 0
        if p != -1:
            #y Определяет разницу между диагональными элементами. Используется для вычисления угла поворота.
            y = (A[p][p] - A[q][q]) / 2
            if y == 0:
                x = -1
            else:
                #x  параметр для вычисления угла.
                x = -sgn(y) * A[p][q] / (A[p][q] ** 2 + y ** 2) ** 0.5
            #s, c cинус и косинус угла поворота
            s = x / ((2 * (1 + (1 - x ** 2) ** 0.5)) ** 0.5)

            c = (1 - s ** 2) ** 0.5
#Преобразуем строки и столбцы матрицы A_k с номерами p и q следующим образом
            for i in range(n):
                if i != p and i != q:
                    z_1 = A[i][p]
                    z_2 = A[i][q]
                    A[q][i] = z_1 * s + z_2 * c
                    A[i][q] = A[q][i]
                    A[i][p] = z_1 * c - z_2 * s
                    A[p][i] = A[i][p]
            #При преобразовании матрицы мы не трогали элементы
            #Для них формулы преобразования выглядят следующим образом:
            z5 = s ** 2
            z6 = c ** 2
            z7 = s * c
            v1 = A[p][p]
            v2 = A[p][q]
            v3 = A[q][q]
            A[p][p] = v1 * z6 + v3 * z5 - 2 * v2 * z7
            A[q][q] = v1 * z5 + v3 * z6 + 2 * v2 * z7
            A[p][q] = (v1 - v3) * z7 + v2 * (z6 - z5)
            A[q][p] = A[p][q]
            #В результате находим матрицу A_k
            #Тогда столбцы матрицы t преобразуются следующим образом:
            for i in range(n):
                z3 = t[i][p]
                z4 = t[i][q]
                t[i][q] = z3 * s + z4 * c
                t[i][p] = z3 * c - z4 * s

        # шаг 5(Находим новую преграду и повторяем вычисления с
        # шага 3 до тех пор, пока все недиагональные элементы не станут по
        # модулю меньше числа E*a_0,где E заданная погрешность вычислений
        a_k /= n ** 2
    return t, A
#В результате, собственные значения оказываются диагональными
#элементами матрицы A_k, а собственные векторы — столбцами матрицы t.

# 4 step - calculate own numbers and own vectors
t, A = yakobi(correl)


llambda = [0] * p
for i in range(p):
    llambda[i] = correl[i][i]

# Сортировка по убыванию собственных значений
for i in range(len(llambda)):
    for j in range(i + 1, len(llambda)):
        if llambda[i] < llambda[j]:
            llambda[i], llambda[j] = llambda[j], llambda[i]
            # Меняем местами соответствующие столбцы матрицы t
            for k in range(len(t)):
                t[k][i], t[k][j] = t[k][j], t[k][i]

# транспонирование
def transposition():
    for i in range(len(t)):
        for j in range(i, len(t)):
            t[i][j], t[j][i] = t[j][i], t[i][j]
    print("Собственные числа:")
    for x in llambda:
        print(f"{x:.3f}", end="\t")
    print()
    print("Собственные вектора:")
    for i in range(p):
        for j in range(p):
            print(f"{t[i][j]:.3f}", end="\t")
        print()
    return t


t = transposition()

#Данные проецируются на новые оси (главные компоненты)

#Каждая проекция - линейная комбинация исходных признаков

#Коэффициенты - собственные векторы корреляционной матрицы


#Функция find_projections() вычисляет проекции объектов
def find_projections():
    y = []
    for i in range(N):
        t_ = []
        for j in range(p):
            t_.append(0)
        y.append(t_)
    for i in range(p):
        yy = [0] * N
        for k in range(p):
            for j in range(N):
                yy[j] += X[j][k] * t[i][k]
        for j in range(N):
            y[j][i] = yy[j]
    print("Проекции объектов на новое пространство")
    for i in range(N):
        for j in range(p):
            print(f"{y[i][j]:.3f}", end="\t")
        print()
    return y


y = find_projections()

#средние значения по каждому столбцу матрицы y
def average(y):
    N = len(y)
    p = len(y[0])
    avg = [0] * p
    for i in range(p):
        for j in range(N):
            avg[i] += y[j][i]
        avg[i] /= N
    return avg


#вычисляет дисперсию признаков в исходных данных X и в их проекциях y, а затем сравнивает общую дисперсию.
def compute_variance(y):
    N = len(y)
    p = len(y[0])
    var = [0] * p
    avg = average(y)
    for i in range(p):
        for j in range(N):
            var[i] += (y[j][i] - avg[i]) ** 2
        var[i] /= N
    return var


var_X = compute_variance(X)
var_y = compute_variance(y)
sum_X = 0

for x in var_X:
    sum_X += x

sum_y = 0
for x in var_y:
    sum_y += x

print(f"sum_X={sum_X:.3f}, sum_y={sum_y:.3f}")

#вычисляет ковариационную матрицу для данных y

def covar(y):
    N = len(y)
    p = len(y[0])
    avg = average(y)  #среднее для каждого признака
    cor = [] #cоздаёт пустую матрицу cor размером p x p
    for i in range(p):
        row = []
        for j in range(p):
            # Ковариация между признаками i и j
            cov = sum((y[k][i] - avg[i]) * (y[k][j] - avg[j]) for k in range(N)) / N
            row.append(cov)
        cor.append(row)
    return cor

#анализирует собственные значения (λ) корреляционной матрицы, чтобы определить,
#сколько главных компонент нужно сохранить, чтобы объяснить более 95% общей дисперсии данных
def comp_ip(llambda):
    p = len(llambda)
    llambda_sum = 0
    for x in llambda:
        llambda_sum += x #сумма всех собственных значений (llambda) корреляционной матрицы.
    print(f"Сумма дисперсий главных компонент: ", llambda_sum)
    lps = 0
    for i in range(p):
        lps += llambda[i] #накопленная сумма собственных значений, начиная с наибольшего.
        print(i + 1, lps)
        # print(f"{lps:.3f}/{llambda_sum:.3f}={lps/llambda_sum}>0.95")
        if (lps) / llambda_sum > 0.95:
            return lps / llambda_sum, i + 1  # Возвращаем долю и количество компонент
            # if lps / llambda_sum > 0.95:
            # print(f"{lps:.3f}/{llambda_sum:.3f}")
            #return lps / 10, i + 1
    return 0, 0

#Определяется минимальное число компонент, объясняющих >95% дисперсии
y = covar(y)
print("Ковариационная матрицу для данных y")
for i in range(p):
	for j in range(p):
		print(f"{y[i][j]:.4f}", end="\t")
	print()
ip, count_p = comp_ip(llambda)

print(f"Minimal p' = {count_p} с I(p')={ip:.4f}\n")

print("y^1")

for i in range(10):
	if i == 9:
		print(f"{t[i][0]:.3f}*x^{i+1}", end=" ")
	else:
		print(f"{t[i][0]:.3f}*x^{i+1} +", end=" ")

print()
print("y^2")

for i in range(10):
	if i == 9:
		print(f"{t[i][1]:.3f}*x^{i+1}", end=" ")
	else:
		print(f"{t[i][1]:.3f}*x^{i+1}", end=" ")

print("\n\nTEST CASE\n\n")
p = 4
correl_test = [
	[1.00, 0.42, 0.54, 0.66],
	[0.42, 1.00, 0.32, 0.44],
	[0.54, 0.32, 1.00, 0.22],
	[0.66, 0.44, 0.22, 1.00]
]

t_test, A_test = yakobi(correl_test)
print("Проверка диагонализации:")
for row in A_test:
    print([f"{x:.3f}" for x in row])
llambda_test = [0] * p
for i in range(p):
	llambda_test[i] = correl_test[i][i]

for i in range(p):
	for j in range(p):
		print(f"{t_test[i][j]:.5f}", end="\t")
	print()
print("lambda")
print(llambda_test)



Соседние файлы в папке 3
  • #
    07.04.202515.15 Кб0main.py
  • #
    07.04.2025129.02 Кб0source_data.xls