R에서 행렬(matrix)와 배열(array)를 쓰는 이유는 연산 속도의 사유로 많이 사용하게 된다. dataframe으로 된 데이터 셋을 연산하게 되면 속도가 매우 느린 현상이 발생 하게 된다. R에서 멀티코어 코딩을 하려면, 반드시 알고 넘어가야 하는 것이 행렬(matrix)와 배열(array)이다.
행렬과 배열
배열은 다차원 개체다. 배열의 차원은 dim 속성을 사용해 각 차원에 대한 최대값으로 설정 합니다.. 따라서 1부터 24까지 24개의 숫자로 구성된 3차원 배월 (2X3X3)에 대해 다음과 같이 명령을 작성 합니다.
y<-1:24
dim(y) <- c(2,4,3)
y
## , , 1
##
## [,1] [,2] [,3] [,4]
## [1,] 1 3 5 7
## [2,] 2 4 6 8
##
## , , 2
##
## [,1] [,2] [,3] [,4]
## [1,] 9 11 13 15
## [2,] 10 12 14 16
##
## , , 3
##
## [,1] [,2] [,3] [,4]
## [1,] 17 19 21 23
## [2,] 18 20 22 24
세번째 차원의 값이 3이므로 명령을 실행하면 3개의 2차원 테이블을 얻게 됩니다. 차원을 변경 하면 다음과 같습니다.
dim(y)<-c(3,2,4)
y
## , , 1
##
## [,1] [,2]
## [1,] 1 4
## [2,] 2 5
## [3,] 3 6
##
## , , 2
##
## [,1] [,2]
## [1,] 7 10
## [2,] 8 11
## [3,] 9 12
##
## , , 3
##
## [,1] [,2]
## [1,] 13 16
## [2,] 14 17
## [3,] 15 18
##
## , , 4
##
## [,1] [,2]
## [1,] 19 22
## [2,] 20 23
## [3,] 21 24
변경 결과 3개의 행과 2개의 열을 갖는 이차원 테이블 4개를 얻을 수 있습니다. 두 가지 예를 살펴 보고 이를 정확히 이해해 봅시다. 행렬은 숫자를 내용으로 하는 이차원 배열 입니다. 데이터 프레임은 서로 다른 열에 숫자나 텍스트, 논리 변수가 포함된 데이터 유형을 갖고 있는 이차원 리스트 입니다. 행렬이나 데이터 프레임같은 개체에 대해 2개의 첨자[5,3]이 있는 경우, 첫 번째 첨자는 행의 번호를 의미하고, 두번째 첨자를 열의 번호를 의미합니다. 따라서 [5,3]에서 5는 행을 의미하고 3은 열을 의미합니다. R에서 가장 중요하고 강력한 규책으로 공백 첨자는 모든 데이터
를 의미 합니다. 따라서 다음과 같습니다.
- [.4]는 4열의 모든 행을 의미 합니다.
- [2.]는 2행의 모든 열을 의미 합니다.
2.8.1 행렬
행렬을 만들기 위한 방법에는 여러가지가 있으며 다음과 같이 직접 만들수 있습니다.
X<-matrix(c(1,0,0,0,1,0,0,0,1), nrow = 3)
X
## [,1] [,2] [,3]
## [1,] 1 0 0
## [2,] 0 1 0
## [3,] 0 0 1
기본 설정 사애에서 숫자는 열방향으로 입력이 됩니다.. X의 클래스와 속성은 dim의 속성값인 3개의 행과 3개의 열로 구성되어 있으며 이값은 dim 함수의 속성값이 됩니다.
class(X)
## [1] "matrix" "array"
attributes(X)
## $dim
## [1] 3 3
다음 예제에서 벡터 테이터는 행방향으로 표시하기 위해 byrow의 속성값을 T 설정 하였습니다.
vector<-c(1,2,3,4,4,3,2,1)
V<-matrix(vector, byrow = T, nrow = 2)
V
## [,1] [,2] [,3] [,4]
## [1,] 1 2 3 4
## [2,] 4 3 2 1
벡터를 행렬로 변환하는 또다른 방법은 다음과 같이 대상 벡터 개체에 대해 dim 함수를 이용해 이차원 행과 열의 값을 설정 하는 것입니다.
dim(vector)<-c(4,2)
이제 벡터는 행렬이 되었습니다.
is.matrix(vector)
## [1] TRUE
행렬로 변환하는 과정에서 데이터는 입력되지 않았으므로 주의하도록 합니다.
vector
## [,1] [,2]
## [1,] 1 4
## [2,] 2 3
## [3,] 3 2
## [4,] 4 1
행과 열 변환을 위해 전치행렬 t를 사용합니다.
(vector<-t(vector))
## [,1] [,2] [,3] [,4]
## [1,] 1 2 3 4
## [2,] 4 3 2 1
2.8.2 행렬의 행과 열에 이름 지정
앞의 실행 결과처럼 행렬은 행과 열(위 참조)에 대해 숫자로 이름을 붙이기로 하였습니다. 다음은 평균 1.5인 포아송 분포에서 추출한 임의의 정수로 구성된 4X5 행렬 입니다.
X <- matrix(rpois(20,1.5), nrow = 4)
X
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 4 2 0 2
## [2,] 0 4 2 1 1
## [3,] 1 2 1 2 1
## [4,] 0 1 6 3 1
행은 4개의 다른 시도를 표현한다고 가정 합시다. 그리고 행을 시도 횟수 데이터라고 하며 Traial.1로 호칭 한다고 합시다. 이를 위해서는 rownames 함수를 사용합니다.
rownames(X) <- rownames(X,do.NULL=FALSE, prefix = "Trial.")
X
## [,1] [,2] [,3] [,4] [,5]
## Trial.1 1 4 2 0 2
## Trial.2 0 4 2 1 1
## Trial.3 1 2 1 2 1
## Trial.4 0 1 6 3 1
특정 시도에 관련된 5개의 의학품 이름 벡터를 생성한 후 이값을 colnames(x)에 설정 합니다.
drug.names <- c("aspirin","paracetamol", "nerofen", "hedex", "placebo")
colnames(X) <- drug.names
X
## aspirin paracetamol nerofen hedex placebo
## Trial.1 1 4 2 0 2
## Trial.2 0 4 2 1 1
## Trial.3 1 2 1 2 1
## Trial.4 0 1 6 3 1
또는 dimnames 함수를 사용해 행렬의 행과 열의 이름을 설정 할 수 있습니다. 이번 예제에서 행에는 표시를 붙이지 않고 열에는 drug.1, drug.2 등의 형태를 갖도록 합니다. dimnames에 대한 인자는 예제에서와 같이 길이 4와 5를 갖는 리스트 요소로 구성된 리스트(일반적으로 행이 첫번째, 열이 두 번째)여야 합니다.
dimnames(X)<-list(NULL,paste("drug.", 1:5, sep = ""))
X
## drug.1 drug.2 drug.3 drug.4 drug.5
## [1,] 1 4 2 0 2
## [2,] 0 4 2 1 1
## [3,] 1 2 1 2 1
## [4,] 0 1 6 3 1
dimnames(X) <- list()
2.8.3 행렬의 행 또는 열 계산
모든 행
또는 모든 열
을 갖고 있는 행렬의 일부를 선택하기 위해 첨자를 사용할 수 있습니다. 다음은 최우측 열(숫자 5)에 대한 평균이며, 전체 행에 대해서 연산 작업을 실행 합니다. 다음은 최우측 열 (숫자 5)에 대한 평균이며, 전체 행에 대해서 연산 작업을 실행 합니다. 다음 코드를 보면 공백 다음에 쉼표가 있을을 알아야 합니다.
# 5 번째 열에 대한 평균계산
mean(X[,5])
## [1] 1.25
또는 최하위 행의 모든 열에 대한 분산을 계산할 수 있다. 두 번째 위치에 공백이 있습니다.
var(X[4,])
## [1] 5.7
행렬에 대한 요약통계를 계산하기 위한 몇 개의 특정 함수는 다음과 같습니다.
rowSums(X)
## [1] 9 8 7 11
colSums(X)
## [1] 2 11 11 6 5
rowMeans(X)
## [1] 1.8 1.6 1.4 2.2
colMeans(X)
## [1] 0.50 2.75 2.75 1.50 1.25
이 함수들은 빠른 실행 속도를 위해 개발 되었습니다. NA 또는 NaN을 처리하는 미묘한 차이의 일부를 구별하기 힘들게 합니다. 이러한 미묘한 차이가 문제가 된다면, apply를 대산 적용합니다. apply의 margin의 설정값이 2이면 열이고, 1이면 행을 의미 한다는 것에 주의 해야 합니다.
apply(X,2, mean)
## [1] 0.50 2.75 2.75 1.50 1.25
열 내부에 있는 여러 행에 대한 더하기 연산을 하려고 할 수 도 있습니다. rowsum 함수는 이러한 기능을 실행하는 효율적인 함수이며 rowsum 함수는 rowSums와는 달리 단일 함수이고 모두 소문자입니다. 이 예제에서는 1행과 4행을 그룹 A로, 2행과 3행을 그룹 B로 그룹화 한다고 합시다. 그룹화 벡터는 행의 수와 동일한 길이를 갖고 있다는 점에 주의 해야 합니다.
group=c("A", "B", "B", "A")
rowsum(X, group)
## [,1] [,2] [,3] [,4] [,5]
## A 1 5 8 3 3
## B 1 6 3 3 2
다음과 같이 tapply 또는 aggregate로 동일한 결과를 얻을 수 있지만, 처리 속도는 약간 느립니다.
tapply(X, list(group[row(X)], col(X)), sum)
## 1 2 3 4 5
## A 1 5 8 3 3
## B 1 6 3 3 2
tapply 에서 row(x)와 col(x)를 사용했으므로, group의 첨자로 row(x)를 설정 했습니다.
aggregate(X, list(group), sum)
## Group.1 V1 V2 V3 V4 V5
## 1 A 1 5 8 3 3
## 2 B 1 6 3 3 2
행렬의 각 열의 요소를 독립적으로 섞는 다고 가장 합니다. 다음과 같이 각 열(margin 값 2이다.)에 sample 함수를 적용할 수 있으며 필요한 만큼의 섞인 샘플을 만들 수 있습니다.
apply(X, 2, sample)
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 4 1 0 2
## [2,] 0 1 2 2 1
## [3,] 0 2 2 1 1
## [4,] 1 4 6 3 1
apply(X,2, sample)
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 1 1 3 1
## [2,] 0 4 2 1 2
## [3,] 1 2 2 2 1
## [4,] 0 4 6 0 1
2.8.4 행렬에 행과 열 추가
특정한 경우에 행렬 아래에 한 행을 추가해 평균을 보여주고 오른쪽에 열을 추가하여 행의 분산을 계산 합니다.
X<-rbind(X,apply(X,2,mean))
X<-cbind(X,apply(X,1,var))
X
## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 1.0 4.00 2.00 0.0 2.00 2.20000
## [2,] 0.0 4.00 2.00 1.0 1.00 2.30000
## [3,] 1.0 2.00 1.00 2.0 1.00 0.30000
## [4,] 0.0 1.00 6.00 3.0 1.00 5.70000
## [5,] 0.5 2.75 2.75 1.5 1.25 0.96875
소수 자릿수 1열과 2열에서는 1개, 3열과 4열에서는 2개 5열에서는 0개(정수)이며 6열에서는 5개로, 열에 따라 다른 점에 주의 하십시오. R의 기본 설정값은 전체 열의 소수점 중에서 최소개수를 기준으로 출력 합니다. 다음으로 여섯번째 열을 분산(variance)으로 다섯번째 행 평균(mean)으로 표시한다.
colnames(X)<-c(1:5, "variance")
rownames(X)<-c(1:4, "mean")
X
## 1 2 3 4 5 variance
## 1 1.0 4.00 2.00 0.0 2.00 2.20000
## 2 0.0 4.00 2.00 1.0 1.00 2.30000
## 3 1.0 2.00 1.00 2.0 1.00 0.30000
## 4 0.0 1.00 6.00 3.0 1.00 5.70000
## mean 0.5 2.75 2.75 1.5 1.25 0.96875
2.8.5 sweep 함수
sweep 함수는 벡터, 행렬, 배열 데이터 프레임에서 배열 요약을 일괄차감 sweep out 하기 위해 사용한다. 데이터를 대상으로 개별 열 평균에서 벗어난 정도를 행렬로 표현합니다.
matdata <- read.table("https://drive.google.com/u/0/uc?id=1amZ1xnQ6Ts37DpRwGHzK7dL0z_4mjNl_&export=download")
우선 행렬에서 일괄차감하려는 인자를 포함한 벡터를 만듭니다. 예제에서는 matdata에 대한 4개 열 평균을 계산하여 cols에 할당 한다.
(cols<-apply(matdata, 2, mean))
## V1 V2 V3 V4
## 4.60 13.30 0.44 151.60
sweep 함수에서 matdata 데이터를 대상으로 margin을 2로 설정한 다음 cols를 사용하면 matdata의 모든 데이터에 대해 해당 열 평균에서 벗어난 정도를 간단히 계산 할 수 있습니다.
sweep(matdata,2,cols)
## V1 V2 V3 V4
## 1 -1.6 -1.3 -0.04 -26.6
## 2 0.4 -1.3 0.26 14.4
## 3 2.4 1.7 0.36 22.4
## 4 2.4 0.7 0.26 -23.6
## 5 0.4 4.7 -0.14 -15.6
## 6 4.4 -0.3 -0.24 3.4
## 7 2.4 1.7 0.06 -36.6
## 8 -2.6 -0.3 0.06 17.4
## 9 -3.6 -3.3 -0.34 30.4
## 10 -4.6 -2.3 -0.24 14.4
이전 sweep 명령에서 두 번째 인자인 margin 값을 2로 설정하면 열 기준으로 sweep 함수를 실행 한다는 점에 유의 하십시오. 각 데이터에 대해 열 평균과의 차이를 계산하는 기능과 관련된 함수에는 scale이 있으며, 이 함수는 표준편차를 이용해 데이터를 정규화 합니다.
예제 1
2022년 1월 부터 5월까지의 부동산 실거래가 셋이 있다. 이건에 대해서, 지역별 월별 데이터 추이를 나타내고, 그것에 대한 지역별 월별, 거래 횟수 평균과 합계 분산을 구하여라.
library(httr)
library(readxl)
# 부동산 데이터 셋 가져오는 함수 만들기
real_estate_fun <- function() {
# 2022년도 데이터 가져오기
url <- "https://drive.google.com/u/0/uc?id=1JC6JPtA4nK5ZRyii64NDO98LyU38kL0R&export=download"
GET(url, write_disk(tf <- tempfile(fileext = ".xlsx")))
apt_2022 <- read_excel(tf, 1L)
df <- rbind(apt_2022)
return(df)
}
# 부동산 셋 데이터 가져오기
system.time(
df <- real_estate_fun()
)
## 사용자 시스템 elapsed
## 1.346 0.276 6.415
위와 같이 부동산 데이터 셋을 가져 온다음 strsplit를 활용 하여, 행정구역을 가져 옵니다.
# map 함수를 돌리기 위해 purrr을 사용한다. map 함수는 속도가 매우 빠르다.
library(purrr)
library(dplyr)
##
## 다음의 패키지를 부착합니다: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(stringr)
library(tidyr)
# word를 나누는 함수를 구한다. 여기에서 strsplit 함수가 쓰인다.
word_fun <- function(i){
split <- unlist(strsplit(df$시군구[i]," "))
word <- data.frame(
행정구역 = split[1]
)
return(word)
}
# 아파트 데이터 프레임의 갯수를 df로 한다.
x <- 1:nrow(df)
# map 함수로 list를 구한다.
system.time(
mylist <- x %>%
map(word_fun)
)
## 사용자 시스템 elapsed
## 11.815 0.000 11.815
# list 파일을 데이터 프레임으로 바꾼다.
apt_split <- as.data.frame(do.call(rbind,mylist))
df <- cbind(apt_split, df)
그리고, 월별 행정 구역별로 데이터를 행렬 표를 만들기 위한 데이터 프레임을 구성 합니다.
# 데이터 그룹와 월별 행정구열별 거래량
mt <-df %>%
group_by(행정구역, 계약년월) %>%
summarise(거래량 = n())
## `summarise()` has grouped output by '행정구역'. You can override using the
## `.groups` argument.
mt <- mt %>%
spread(key= 계약년월, value = "거래량")
mt
## # A tibble: 17 × 6
## # Groups: 행정구역 [17]
## 행정구역 `202201` `202202` `202203` `202204` `202205`
## <chr> <int> <int> <int> <int> <int>
## 1 강원도 1316 1670 1995 1764 1460
## 2 경기도 3570 3991 5993 4960 3768
## 3 경상남도 2812 3182 3603 3386 2380
## 4 경상북도 1941 1990 2398 2170 1757
## 5 광주광역시 1122 1125 1435 1510 1228
## 6 대구광역시 781 804 953 710 692
## 7 대전광역시 560 563 693 499 392
## 8 부산광역시 1330 1427 1914 1692 1493
## 9 서울특별시 1119 831 1462 1001 1096
## 10 세종특별자치시 174 216 243 194 166
## 11 울산광역시 771 736 893 995 754
## 12 인천광역시 791 893 1212 1023 877
## 13 전라남도 1157 1225 1500 1221 1100
## 14 전라북도 1564 1752 2381 1915 1541
## 15 제주특별자치도 231 242 249 189 183
## 16 충청남도 1825 1991 2415 2199 1800
## 17 충청북도 1312 1594 1875 1779 1540
그리고 아래와 같이 matrix 데이터를 구성 합니다.
# 매트릭스 데이터 셋으로 구성합니다.
MT <- as.matrix(mt[2:6])
# 데이터 행정구역을 rowname으로 구성 합니다.
rownames(MT) <- mt$행정구역
MT
## 202201 202202 202203 202204 202205
## 강원도 1316 1670 1995 1764 1460
## 경기도 3570 3991 5993 4960 3768
## 경상남도 2812 3182 3603 3386 2380
## 경상북도 1941 1990 2398 2170 1757
## 광주광역시 1122 1125 1435 1510 1228
## 대구광역시 781 804 953 710 692
## 대전광역시 560 563 693 499 392
## 부산광역시 1330 1427 1914 1692 1493
## 서울특별시 1119 831 1462 1001 1096
## 세종특별자치시 174 216 243 194 166
## 울산광역시 771 736 893 995 754
## 인천광역시 791 893 1212 1023 877
## 전라남도 1157 1225 1500 1221 1100
## 전라북도 1564 1752 2381 1915 1541
## 제주특별자치도 231 242 249 189 183
## 충청남도 1825 1991 2415 2199 1800
## 충청북도 1312 1594 1875 1779 1540
월별 총거래량을 구합니다.
# # 월별 총거래량
colSums(MT)
## 202201 202202 202203 202204 202205
## 22376 24232 31214 27207 22227
행정구역별 월단위 평균 거래량을 구합니다.
colMeans(MT)
## 202201 202202 202203 202204 202205
## 1316.235 1425.412 1836.118 1600.412 1307.471
시도별 누적 거래량을 구합니다.
rowSums(MT)
## 강원도 경기도 경상남도 경상북도 광주광역시
## 8205 22282 15363 10256 6420
## 대구광역시 대전광역시 부산광역시 서울특별시 세종특별자치시
## 3940 2707 7856 5509 993
## 울산광역시 인천광역시 전라남도 전라북도 제주특별자치도
## 4149 4796 6203 9153 1094
## 충청남도 충청북도
## 10230 8100
시도별 평균 거래량을 구합니다.
rowMeans(MT)
## 강원도 경기도 경상남도 경상북도 광주광역시
## 1641.0 4456.4 3072.6 2051.2 1284.0
## 대구광역시 대전광역시 부산광역시 서울특별시 세종특별자치시
## 788.0 541.4 1571.2 1101.8 198.6
## 울산광역시 인천광역시 전라남도 전라북도 제주특별자치도
## 829.8 959.2 1240.6 1830.6 218.8
## 충청남도 충청북도
## 2046.0 1620.0
시도별 거래량 표준편차를 구합니다.
apply(MT, 1, sd)
## 강원도 경기도 경상남도 경상북도 광주광역시
## 264.42012 1011.30475 484.54081 243.32427 179.26098
## 대구광역시 대전광역시 부산광역시 서울특별시 세종특별자치시
## 103.47705 109.44542 233.01867 231.06644 31.47698
## 울산광역시 인천광역시 전라남도 전라북도 제주특별자치도
## 111.00315 163.86641 153.84505 343.21466 30.69528
## 충청남도 충청북도
## 260.74509 219.22933
댓글 없음:
댓글 쓰기