R의 특징과 자료 구조 - 2 Matrix ( 요소 연산과 행렬곱 연산 )
title: R의 특징과 자료 구조 - 2 Matrix ( 요소 연산과 행렬곱 연산 ) date: 2025-01-28 01:28:30 +09:00 categories: [R, 기본] tags: [R, R Studion, 행열, 행열분해, matrix, 벡터] —
안녕하세요, 오늘은 Matrix의 요소연산과 행렬곱연산에 대해 알아보도록 하겠습니다.
R의 특징과 자료구조
1. R: 선형대수
아래부턴 구어체로 노트 필기한 것을 정리한 것입니다.
1-1: 선행대수
선형대수
크기: \(\sqrt{a_1^2 + a_2^2 + \cdots + a_n^2}\)
방향: \((\frac{x}{크기}, \frac{y}{크기}, \frac{z}{크기} \ldots)\) (즉, 크기가 1로 표현된 벡터)
normalize: 방향값으로 표현 (크기가 1)
두 점 거리 : \(\sqrt{(a_1 - b_1)^2 + (a_2 - b_2)^2 + \cdots + (a_n - b_n)^2}\)
두 점 방향: 내적 : \(a_1 \cdot b_1 + a_2 \cdot b_2 + \cdots + a_n \cdot b_n\) 을 통해 유사도를 구할 수 있음.
내적을 통해 차원을 축소하고, 특성을 추출
각도가 0°일 때의 코사인값은 1이며, 다른 모든 각도의 코사인값은 1보다 작다.
따라서 이 값은 벡터의 크기가 아닌 방향의 유사도를 판단하는 목적으로 사용된다.
두 벡터의 방향이 완전히 같을 경우 1, 90°의 각을 이룰 경우 0(직교)
180°로 완전히 반대 방향인 경우 -1의 값을 갖는다.
즉, 코사인1일 때 아주 유사(추천), 0일땐 직교
Dot(A⋅B)=∥A∥∥B∥cos(각도)
𝐴⋅𝐵/∥𝐴∥∥𝐵∥ = cos(각도)
Acos(𝐴⋅𝐵/∥𝐴∥∥𝐵∥) = 각도
1-2: 다중공선성
회귀분석에서 설명변수들 사이에 선형적이거나 또는 거의 선형적인 종속관계에 있는 경우를 다중공선성이라고 한다.
(a가 b에 영향처럼) 서로 상관 관계가 있어 발생하며, 이는 데이터를 오판하게 만들기 때문에 직교를 통해 해결한다.
1-3: Matrix
vector(배열) -> matrixt(행열 연산) : data.frame, list, array, matrix
R에서 열(column)에서는 features, atrribute, 행(row)에는 recod, data point 
2. 행열 연산
2.1: 행열의 종류
대각행렬: i=j인 주대각 성분들만 0이 아닌 정방행렬, 대각선
-> 크기에 영향전치행렬 : 행과 열 바꿈, 정방행렬의 행과 열의 요소값을 바꾸어 만든 행렬
역행렬(inverse matrix) : 행열은 나눗셈이 없어서 역행렬을 만들어서 곱해줌, 정방행렬만 역행렬이 가능
단위행렬(항등행렬) : 주대각 성분이 모두 1인 행렬 (곱해도 자기 자신)
희소행렬 : 대부분이 0인 행렬 (내적의 의미가 거의 없음)
정방행렬(square matrix) : m(가로) = n(세로)인 행렬
대칭행렬 : 상삼각행렬과 하삼각행렬이 일치하는 경우
단위행렬(Identity) or 항등행렬 : 주대각 성분이 모두 1인 행렬, 변환을 일으키지 않는 행렬, 곱해도 자기 자신(초기화)
희소행렬 : 대부분이 0인 행렬 (내적의 의미가 거의 없음)
| 특징 | 공분산행렬 | 상관계수행렬 |
|---|---|---|
| 값의 범위 | 데이터의 크기에 따라 달라짐 | -1 ~ 1 |
| 해석의 용이성 | 크기에 따라 해석이 어려움 | 해석이 더 쉬움 |
| 데이터 스케일 영향 | 데이터 크기(단위)에 민감 | 단위와 무관 |
| 용도 | 데이터의 분포와 관계를 정량적으로 분석 | 변수 간의 상관성을 비교할 때 유용 |
<![endif]–>
상관계수행렬에서
대각선: 항상 1(자기 자신과의 상관계수)
비대각선: 서로 다른 변수 간의 상관계수
2.2: 행렬곱 연산
: 특징을 추출 : 내적 연산의 연속
=> 앞 행과 뒤 열 수가 일치해야 함(전치), 뒤에 있는 행렬의 열 수만큼 특징을 추출
3. 행열 분해
행렬 분해 : 크기와 방향으로 분해 가능
3.1: 고유값
고유벡터(Eigenvector) : 행렬의 변환 후에도 방향이 변하지 않는 벡터
행렬 A가 벡터 x에 작용했을 때, x의 방향은 바뀌지 않고 크기만 변하는 벡터를 고유벡터라고 한다. 고유값(Eigenvalue) : 고유값은 고유벡터의 크기가 얼마나 변했는지를 나타내는 값
고유값 분해(eigen)
정방 : ex) 3by3 일 경우 크기(고유치) 값(3by1, 이 크기 값이 큰 것만 뽑은 것이 주성분 분석)과 방향 값(고유벡터, 원래 데이터의 열 수가 결정)(크기가 1인 방향) 3by3(내적을 하면 0 : 서로 직교 행렬)이 나옴 => 정직교하는 데이터 축에 표현하는 것으로 nosie 해결 및 다중공선성 해결.
3.2: 특이 행렬 분해(Singular Value Decomposition, SVD)
SVD는임의의 행렬 A를 세 개의 행렬로 분해하는 방법 A = UΣVᵀ 여기서,
- U: A의 열 방향 정보를 담은 직교 행렬(왼쪽 특이벡터)
- Σ: 특이값(Singular Values)을 대각선에 가지는 대각행렬
- Vᵀ: A의 행 방향 정보를 담는 직교 행렬(오른쪽 특이벡터의 전치 행렬)
특이 행렬 분해(SVD)
비정방 : 자연어 처리, 연관 분석(추천), 차원 축소, 이미지 압축에 사용
3.3 고유값 분해 vs 특이값 분해
| 특징 | 고유값 분해 | 특이값 분해 (SVD) |
|---|---|---|
| 행렬 크기 조건 | 정방행렬(정사각형)만 가능 | 모든 형태의 행렬에 적용 가능 |
| 분해 형태 | A = QΛQ⁻¹ | A = UΣVᵀ |
| 주요 활용 | 행렬의 성질 분석, PCA | 차원 축소, 데이터 압축, 추천 시스템 등 |
| 계산의 복잡도 | 상대적으로 간단 | 조금 더 복잡하지만 더 일반적 |
3. 간단한 예제
이론만 보았을 때 이해가 쉽지 않을 수도 있습니다. R을 통해 한번 확인해 봅시다.
벡터화 연산
a<-c(1,2,4,1,2,4,3,6,7,2,0) b<-c(1,3,2,3,2,2,1,2,1,1,1) #내적 sum(a*b) (dotresult <- a%*%b) #문제 : 벡터의 크기 anorm <- sqrt(sum(a^2)) bnorm <- sqrt(sum(b^2)) costheta <- dotresult / (anorm*bnorm) randiangle <- acos(costheta) # 라디안 install.packages("NISTunits", dependencies = TRUE) NISTradianTOdeg( randiangle ) 행렬
(a=matrix(1:8, nrow = 3, ncol= 3, byrow=TRUE)) # recycling으로 빈 곳 채워줌 matrix(1:12, nrow = 3) # ncol은 자동으로 계산, byrow=FALSE는 디폴트 class(a) attributes(a) # 차수 3*3 dim(a) 행과 열
x = matrix(1:9,nrow= 3, byrow=TRUE) colnames(x) colnames(x) <- c("c1","c2","c3") rownames(x) <- c("R1","R2","R3") x x["R1","c1"] # 행, 열 순서로 입력해야 함 x["R1",] # 행, 열 다줘야 하기에 , 로 행을 다 출력 (x <- matrix(1:9, nrow=3, dimnames = list(c("x","y","z"), c("a","b","c")))), 행렬 다른 이빠진 것은 list로 넘겨야 함. y<-matrix(nrow=2, ncol=2) # 빈행렬 y[1,2] # NA 결측치 y[1,1]<-1 y[2,1]<-2 y[1,2]<-3 y[2,2]<-4 y[10,10] <- 100 # 정해진 사이즈에만 들어감 mode(y) NROW(c(1,2,3)) # 열로 표현 cbind(c(1,2,3),c(4,5,6)) # column rbind(c(1,2,3),c(4,5,6)) # row (x <- c(1,2,3,4,5,6)) dim(x) (dim(x)<-c(2,3)) # dim <- c(열,행) x (x=matrix(1:9, nrow=3, byrow=TRUE)) x[c(1,2),c(2,3)] x[c(2,3),] x[,] x[-1,] #R에서 -는 빼고 실제 데이터가 메모리에 저장된 형태는 1,4,7,2,5,8.... x[1:4] # vector만 들어 온 경우 x[c(3,5,7)] (x=matrix(1:9, nrow=3, byrow=TRUE)) x[c(TRUE,FALSE,TRUE), c(TRUE,TRUE,FALSE)] # 열,행 x[c(TRUE,FALSE),c(2,3)] x[c(TRUE,FALSE)] # recycling x[x>5] x[x%%2 == 0] 특징 추출
(x=matrix(1:12, nrow=3, ncol=4)) # 3*4 4*3 => 3*3 뒤에 있는 열수 만큼 특징을 추출 xt <- t(x) # 전치 x %*%t(x) x[1, ] xt[,1] x[1,]*xt[,1] sum(x[1,]*xt[,1]) # 내적 요소연산 _ 브로드 캐스팅
t(x)[,c(1,2,3)] x[1,] * t(x)[,c(1,2,3)] # 4by1 # 4by3 => broadcasting colSums(x[1,] * t(x)[,c(1,2,3)]) # 4*3 열 수인 3개만큼 나옴 (mdat <- matrix(seq(20,4,-2), nrow=3, ncol=3, byrow=TRUE, dimnames=list(c('a','b','c'), c('x','y','z')))) t(mdat) nrow(mdat) ncol(mdat) dim(mdat) rowSums(mdat) rowMeans(mdat) colSums(mdat) colMeans(mdat) diag(mdat) # 대각요소를 추출 diag(diag(mdat)) # 대각행렬 생성 고유값 분해 (정방행열/대칭행렬)
(mdat2 <- mdat%*%t(mdat)) (mdatEigen<-eigen(mdat2)) #R에서 사이즈 다르면 list => 앞에 $ 붙음 mdatEigen$vectors # 3by3 => 정규화 (1), 내적 = 0 정직교축 sqrt(sum(mdatEigen$vectors[1,]^2)) mdatEigen$vectors[2,] mdatEigen$vectors[3,] mdatEigen$vectors[1,] %*% mdatEigen$vectors[2,] # 0이라고 표현 못하기에 이해 mdatEigen$vectors[1,] %*% mdatEigen$vectors[3,] mdatEigen$vectors[2,] %*% mdatEigen$vectors[3,] mdatEigen$values # 고유값 : 큰 것이 중요한 값(주성분) singular value decomposition(특이행렬분해)
비정방행렬에 대해서 크기와 방향값으로 분해 x<-matrix(1:12, nrow=3, ncol=4) # **3*4 비정방** svd_result <- svd(x) U <- svd_result$u # 3*3 # $u : 열방향 정보를 담은 직교 행렬 (행수) Sigma <- diag(svd_result$d) # $d : 특이값 -> 대각 Vt <- t(svd_result$v) # 3*4 : 원래 전치되어 있기 때문에(**4*3**) **다시 전치(3*4**), 행방향 직교 print(U) print(Sigma) print(Vt) 3*3 3*3 3*4 U %*% Sigma %*% Vt 결과 3*3 => 3*4 원래로 돌아감 noise 제거
주성분 분석으로 2개의 변수만 사용 (주성분을 2개 -> noise 제거) k <- 2 (Sigma_k <- Sigma[1:k, 1:k]) # 2*2 U_k <- U[, 1:k] # 3*2 Vt_k <- Vt[1:k, ] #2*4 U_k %*% Sigma_k %*% Vt_k # 곱하는 순서 중요!! 연립방정식 풀이
y =10x 이 때 y값이 20이라면 x값은 solve(10,20) 2X + 3Y = 5 3X + 5Y = 6 2 3 X 3 5 Y 5 6 값 (mdat <-matrix(c(2,3,3,5), nrow=2, ncol=2, byrow=TRUE)) (c=c(5,6)) solve(mdat,c) # 역행렬 apply(X,2,sum) # 행과 열 apply(x,2,mean) # 열 방향 apply(x,1,sum) # 행방향 apply(x,1,mean) Array (행, 열, 면 으로 지정)
vector1 <- c(5,9,3) vector2 <- c(10,11,12,13,14) result <- array(c(vector1,vector2),dim=c(4,3,2)) # 4by3이 2개 나옴, recyle print(result) 다차원 배열
column.names <- c("COL1","COL2","COL3") row.names <- c("ROW1","ROW2","ROW3") matrix.names <- c("Matrix1","Matrix2", "m3", "m4", "m5") result <- array(c(11:19, 21:29, 31:39, 41:49, 51:59), dim = c(3,3,5), dimnames = list(row.names,column.names,matrix.names)) print(result) print(result[3,,2]) data.frame
열내 동질, 열간 이질적
열방향 (vector: 동질적, vector 사이는 달라도 됨)
행렬, 데이터 프레임, 배열 : 사각형을 이뤄야 함 (이빨 빠지면 x)
x <- c(10,20,30,40) y<-c(6,7,8,9) data <- data.frame(width=x, height=y) data str(data) # 설명을 해줌 data$width data["width"] data[,1] # $처럼 출력 head(data, 3) # 지정 안하면 4개까지 보여줌 tail(data,2) # 뒤에서부터 출력 data$delinetr = c(30,40,50,60) # 실시간으로 추가 가능 데이터 요소 삭제는 없음 d.f<-data.frame() d.f<-edit(d.f) # 데이터 입력 창 d.f (a<-data.frame(id=c("01","02","03","04","05"), x=c(1,NA,3,4,5), y=c("a","b","c","d",NA))) print(a) L3 = LETTERS[1:3] # R의 알파벳 상수(대문자) d<-data.frame(cbind(x=1, y=1:10), #브로드 캐스팅 fac=sample(L3,10,replace = TRUE)) # replace : 복원추출 파생변수
d d$fac names(d) (d$yhat <-d$fac) # 파생변수 str(d) head(d) # 기본 6개 출력 d$fac = factor(d$fac) # 범주형 데이터 : 질적 데이터의 종류를 나타날 때 rownames(d) = ("일","이","삼","사","오","육","칠","팔","구","십") d str(d) class(d$fac) 문자열을 자동으로 factor으로 변환
x<-data.frame("SN" = 1:2, "Age"=c(21,15), "Name"=c("에이콘", "삼성SDS"), stringsAsFactors = T) str(x) 분위수
library(help="datasets") data() **# 최소값, 1사분위수 median(이상치에 영향 받지 않기 위해),4사분위수 최대값** str(trees) head(trees, n=3) tail(trees, n=3) trees[2:3,] trees[trees$Height> 82,] trees[10:12,2] trees[10:12,2,drop=FALSE] # 열특성을 제거하지 말고 summary(trees) boxplot(trees) pairs(trees) # 상관도 IQR = 3사분위수 - 1사분위수
상한값 : 3사분위수 + IQR*1.5 하한값 : 1사분위수 - IQR*1.5 데이터가 있는 것가지만 보여줌 (RA가 다르게 보임) getwd() (data <- read.csv("input.csv", header=T, encoding="UTF-8", sep="," , fileEncoding="UTF-8")) #csv는 , 로 나눠지는 sep="," 해준것임 colnames(data) colnames(data)[1]<-"id" print(data) print(is.data.frame(data)) print(ncol(data)) print(nrow(data)) print(length(data)) sal <- max(data$salary) print(sal) str(data) retval <- subset(data, salary == max(salary)) # 원하는 조건 검색 print(retval) install.packages("stringr") library(stringr) info <- subset(data, salary >=600 & str_trim(dept) == "IT") print(info) retval <- subset(data, as.Date(start_date) > as.Date("2014-01-01")) print(retval) write.csv(retval,"output.csv") newdata <- read.csv("output.csv") # R에서 저장해서 인코딩 필요x print(newdata) data frame 예제
d.f <- data.frame() d.f <- edit(d.f) colnmaes(d.f) <- c("영어점수", "등급") rownames(d.f) <- c("퀴즈", "중간", "기말") d.f eng<-c(67,92,89) grade<-c("C","A","B") name <- c("퀴즈", "중간", "기말") (data<-data.frame("영어"=eng, "등급"=grade, row.names = name)) data$"수학" <- c(50,100,80) data 보충이라는 이름으로 dataframe을 만들어 rbind da <- data.frame("영어"=c(90), "등급"=c('A'), "수학"=c(100), row.names = "보충") (data2 <- rbind(data, da)) data frame 수정
영어의 중간점수를 100점으로 수정
data["중간","영어"]=100 data 열별합계 (colSums)
(hab <- colSums(as.matrix(cbind(data2$"영어", data2$"수학")))) da = data.frame("영어"=hab[1], "등급"=0, "수학"=hab[2], row.names = "합계") (data2 <- rbind(data2,da)) 정규분포
random nrmal distribution (x <- rnorm(400, mean=50, sd=10)) hist(x) 균등분포
(x<-floor(runif(400, min=0, max=100))) # 균등분포 plot(x) # 산포도 : 인덱스(x), y축 : 0~100 사이값 x <-runif(400, min=0, max=100) # 구간범주화하고 카운트를 해서 도수를 시각화 hist(x)