다차원 배열
3+차원 배열
11차원
물리학자들은 세상에 11차원일지 모른다는 학설을 연구한다고 한다. 상당히 놀라운 일이다. 인간이 11차원을 상상할 수 있다니!
데이터 프레임 또는 2차원 배열(행렬)
인간이 받아들이는 시각 자극은 기본적으로 2차원이다. 하지만 아주 자연스럽게 3차원의 심상을 구성해낸다고 한다. 하지만 숫자로 이루어진 표를 그린다면, 3차원의 표를 그리거나 하지는 않는다.
많은 데이터는 데이터프레임의 형식(2차원의 직사각형 모양)으로 저장할 수도 있고, 다차원의 배열로 저장할 수도 있다. 예를 들어 가장 간단하게 남자 또는 여학생의 키를 측정했다고 해보자.
height = data.frame(gender=c('M','M','M', 'F','F','F'),
height = c(170,180,175, 170,166,160))
#knitr::kable(height)
height
## gender height ## 1 M 170 ## 2 M 180 ## 3 M 175 ## 4 F 170 ## 5 F 166 ## 6 F 160
이를 배열로 저장하려면 다음과 같이 할 수 있다.
우선 각 조건별로 순번을 매긴다.
library(dplyr)
df <-
height %>% group_by(gender) %>% mutate(index=row_number())
#knitr::kable(df)
df
## # A tibble: 6 x 3 ## # Groups: gender [2] ## gender height index ## <fct> <dbl> <int> ## 1 M 170 1 ## 2 M 180 2 ## 3 M 175 3 ## 4 F 170 1 ## 5 F 166 2 ## 6 F 160 3
그리고 이 순번을 활용하여 다음과 같이 배열로 바꿀 수 있다.
arr <-
array(data = df$height,
dim = c(length(unique(df$gender)),
length(unique(df$index))),
dimnames = list(unique(df$gender), unique(df$index)))
arr
## 1 2 3 ## M 170 175 166 ## F 180 170 160
2차원 배열이다! 이 배열에서 측정값 166이 남자(Male)의 세 번째 학생의 키라는 것을 알 수 있는 이유는 166이 배열 arr
의 (1,3)에 위치하고 있기 때문이다. (arr[1,3]==166
)
2차원 배열은 R이 알아서 행렬로 인식한다. (R의 2차원 배열은 행렬이다.)
class(arr)
## [1] "matrix"
3차원 배열
이번엔 조건의 갯수를 늘려보자. 성별, 학교까지 포함시키면 다음과 같다.
height = data.frame(school= c(rep('A', 6), rep('B',6), rep('C', 6)),
gender=c('M','M','M', 'F','F','F',
'M','M','M', 'F','F','F',
'M','M','M', 'F','F','F'),
height = c(170,180,175, 169,165,173,
173,181,175, 166,164,155,
172,184,176, 167,162,164))
#knitr::kable(height)
height
## school gender height ## 1 A M 170 ## 2 A M 180 ## 3 A M 175 ## 4 A F 169 ## 5 A F 165 ## 6 A F 173 ## 7 B M 173 ## 8 B M 181 ## 9 B M 175 ## 10 B F 166 ## 11 B F 164 ## 12 B F 155 ## 13 C M 172 ## 14 C M 184 ## 15 C M 176 ## 16 C F 167 ## 17 C F 162 ## 18 C F 164
이를 배열로 바꾼다면 앞서와 마찬가지로 먼저 순서를 붙이고, 배열로 변환한다.
df <-
height %>% group_by(school, gender) %>% mutate(index=row_number())
df
## # A tibble: 18 x 4 ## # Groups: school, gender [6] ## school gender height index ## <fct> <fct> <dbl> <int> ## 1 A M 170 1 ## 2 A M 180 2 ## 3 A M 175 3 ## 4 A F 169 1 ## 5 A F 165 2 ## 6 A F 173 3 ## 7 B M 173 1 ## 8 B M 181 2 ## 9 B M 175 3 ## 10 B F 166 1 ## 11 B F 164 2 ## 12 B F 155 3 ## 13 C M 172 1 ## 14 C M 184 2 ## 15 C M 176 3 ## 16 C F 167 1 ## 17 C F 162 2 ## 18 C F 164 3
arr <-
array(data = df$height,
dim = c(length(unique(df$school)),
length(unique(df$gender)),
length(unique(df$index))),
dimnames = list(unique(df$school), unique(df$gender), unique(df$index)))
arr
## , , 1 ## ## M F ## A 170 169 ## B 180 165 ## C 175 173 ## ## , , 2 ## ## M F ## A 173 166 ## B 181 164 ## C 175 155 ## ## , , 3 ## ## M F ## A 172 167 ## B 184 162 ## C 176 164
순번으로 참조
배열 arr
을 출력하면 위와 같이 다소 이해하기 힘든 방식으로 출력이 된다. 이를 조금 설명해보자.
## , , 1
##
## M F
## A 170 170
## B 180 166
## C 175 160
여기서 , , 1
의 첫 두 빈칸을 아래 행렬의 열과 행이 차지하기 된다. 따라서 아래 행렬의 (1,1) 위치 값 170
은 사실은 arr[1,1,1]
의 값이고, 행렬의 (2,1) 위치의 값 180
은 arr[2,1,1]
의 값이다.
3차원 배열은 3차원의 공간에 나타내는 것이 가장 합당하겠으나 2차원 모니터에서 출력하기 위해서는 3차원의 각 층을 행렬로 나타낸 것이라고 이해하면 된다.
만약 3차원 배열을 3차원의 공간에 나타낸다면 아래 그림의 루빅스 큐브처럼 (앞면의 숫자 때문에 가려서) 보이지 않는 부분이 생기게 된다.
3차원 배열에서 각 값이 어떤 조건에서 측정된 값인지는 그 위치를 통해 추측할 수 있다. 아래의 배열 표현을 보고 어떤 조건의 값을 나타내는지 알아보자.
arr[1,1,1]
arr[3,1,2]
arr[1,,2]
arr[,2,]
위의 표현은 모두 배열 arr
의 위치로 값을 참조하고 있다. arr[1,1,1]
은 학교 A
, 성별 남(M
), 그리고 첫 번째 학생의 키를 나타내고, arr[3,1,2]
는 학교 C
, 성별 남, 2번째 학생의 키를 나타낸다. arr[1,,2]
는 학교 A
이고 두 번째 학생의 키를 모두 나타내고(성별은 남 또는 여 모두), arr[,2,]
은 성별이 여자인 모든 학생을 나타낸다.
위치 이름으로 참조
이렇게 위치로 참조하는 것은 각 위치가 어떤 의미를 나타내는지 미리 알고 있어야 하므로 다소 불편하다. 다행히도 위에서 배열 arr
을 만들 때 dimnames=
로 각 위치에 이름을 부여하였다.
dimnames(arr)
## [[1]] ## [1] "A" "B" "C" ## ## [[2]] ## [1] "M" "F" ## ## [[3]] ## [1] "1" "2" "3"
만약 이름이 정확하게 부여되었다면, 조건을 바로 적용할 수 있다. 예를 들어 남자인 모든 학생의 키를 알고 싶다면, arr[,"M",]
, 학교 B
의 모든 학생의 키를 알고 싶다면 arr['B', , ]
, 그리고 학교 B
의 모든 남자의 키를 알고 싶다면 arr['B', 'M', ]
로 쓸 수 있다.
arr[,"M",]
## 1 2 3 ## A 170 173 172 ## B 180 181 184 ## C 175 175 176
arr['B',,]
## 1 2 3 ## M 180 181 184 ## F 165 164 162
arr['B', 'M', ]
## 1 2 3 ## 180 181 184
이 방법에서 쉼표와 정확한 위치가 정말 중요하다!
arr['B', 'M', ]
## 1 2 3 ## 180 181 184
arr[,'B', 'M']
## Error in arr[, "B", "M"]: subscript out of bounds
arr[, 'B', 'M']
은 에러를 발생시키는데, subscript out of bounds
, 다시 말해 두 번째 차원에서 'B'
에 해당하는 위치를 알 수 없고, 세 번째 차원에서 'M'
에 해당하는 위치를 알 수 없기 때문이다.
만약 위치도 신경쓰지 않고 싶다면 먼저 위치의 의미를 알려줄 필요가 있다. dimnames(arr)
는 각 차원의 위치 이름을 저장하는 리스트이고, 각 차원에 의미를 부여해 줄 수 있다.
dimnames(arr)
## [[1]] ## [1] "A" "B" "C" ## ## [[2]] ## [1] "M" "F" ## ## [[3]] ## [1] "1" "2" "3"
names(dimnames(arr)) = c('school', 'gender', 'num')
이제 다시 arr
를 출력해보면,
arr
## , , num = 1 ## ## gender ## school M F ## A 170 169 ## B 180 165 ## C 175 173 ## ## , , num = 2 ## ## gender ## school M F ## A 173 166 ## B 181 164 ## C 175 155 ## ## , , num = 3 ## ## gender ## school M F ## A 172 167 ## B 184 162 ## C 176 164
그리고 패키지 R.utils
의 extract
함수를 사용하면 다음과 같이 쓸 수 있다.
library(R.utils) # install.packages('R.utils')
extract(arr, 'school'=c('A','B'))
## , , num = 1 ## ## gender ## school M F ## A 170 169 ## B 180 165 ## ## , , num = 2 ## ## gender ## school M F ## A 173 166 ## B 181 164 ## ## , , num = 3 ## ## gender ## school M F ## A 172 167 ## B 184 162
extract(arr, gender='M', num='2', school='A')
## , , num = 2 ## ## gender ## school M ## A 173
arr[1,1,2]
## [1] 173
extract
함수는 순번으로 참조할 때에는 arr[]
과 거의 동일하게 작동한다.
extract(arr, 3,2,1)
## , , num = 1 ## ## gender ## school F ## C 173
arr[3,2,1]
## [1] 173
하지만 extract
를 사용하여 수정을 할 수는 없다.
extract(arr, 3, 2, 1)=161
## Error in extract(arr, 3, 2, 1) = 161: could not find function "extract<-"
arr[3,2,1]=161
이 글은 “[R로 하는 빅데이터 분석: 데이터 전처리와 시각화]의 일부를 발췌, 각색한 것입니다.
Dae Ho Kim
좋은 자료 감사합니다.
그런데
데이터 프레임을 행렬/배열로 변환하는데 오류가 있습니다…..
df를 배열과 행렬로 변환할 때 xtabs() 함수르 하는 것이 좋을 것 같습니다.