
Добавил:
eeeikh
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:3 / main
.pyfrom 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)