작성자: admin 작성일시: 2016-09-22 14:05:43 조회수: 313 다운로드: 27
카테고리: R 태그목록:

R에서 벡터와 행렬 사용하기

데이터는 수많은 숫자들로 이루어져 있다. 이 많은 숫자들을 효율적으로 계산하기 위해서는 관련된 데이터를 모두 하나의 변수에 넣고 처리해야 한다. 하나의 변수에 여러 개의 데이터를 넣는 방법으로 리스트를 사용할 수도 있지만 리스트는 속도가 느리고 메모리를 많이 차지하는 단점이 있다. 더 적은 메모리를 사용해서 빠르게 데이터를 처리하려면 배열(array)을 사용해야 한다.

배열은 같은 자료형의 데이터를 정해진 갯수만큼 모아놓은 것이다. 배열은 다음과 같은 점에서 리스트와 다르다.

  1. 모든 원소가 같은 자료형이어야 한다.
  2. 원소의 갯수를 바꿀 수 없다.

1차원 배열(벡터) 만들기

1차원 배열을 만들기 위해서는 c()라는 함수를 사용한다. 이 함수의 이름은 concatenate(붙이기)란 단의 앞글자를 따서 만들어졌다.

In [1]:
a <- c(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
a
  1. 0
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. 8
  10. 9

R에서 1차원 배열은 벡터(vector)라고 불린다. 하지만 자료형이나 클래스는 벡터라는 표시를 하지 않는다. 그 이유는 사실 R에서는 모든 단일값은 크기가 1인 벡터이기 때문이다.

In [2]:
typeof(a)
'double'
In [3]:
class(a)
'numeric'

벡터화 연산

벡터는 배열의 각 원소에 대한 연산을 한 번에 처리하는 벡터화 연산(vectorized operation)을 지원한다. 예를 들어 데이터를 모두 2배 해야 하는 경우를 생각하자.

for 반복문을 사용하면 다음과 같이 구현할 수 있다.

In [4]:
b <- 0
i <- 1
for (ai in a) {
    b[[i]] <- ai * 2
    i <- i + 1
}
In [5]:
b
  1. 0
  2. 2
  3. 4
  4. 6
  5. 8
  6. 10
  7. 12
  8. 14
  9. 16
  10. 18

하지만 벡터화 연산을 사용하면 다음과 같이 for 반복문이 없이 곱하기 연산을 통째로 할 수 있다. 계산 속도도 반복문을 사용할 때 보다 훨씬 빠르다.

In [6]:
a * 2
  1. 0
  2. 2
  3. 4
  4. 6
  5. 8
  6. 10
  7. 12
  8. 14
  9. 16
  10. 18

벡터화 연산은 모든 종류의 수학 연산에 대해 적용된다.

In [7]:
a <- c(1, 2, 3)
b <- c(10, 20, 30)
In [8]:
2 * a + b
  1. 12
  2. 24
  3. 36
In [9]:
exp(a)
  1. 2.71828182845905
  2. 7.38905609893065
  3. 20.0855369231877
In [10]:
log(b)
  1. 2.30258509299405
  2. 2.99573227355399
  3. 3.40119738166216
In [11]:
sin(a)
  1. 0.841470984807897
  2. 0.909297426825682
  3. 0.141120008059867

2차원 배열(행렬) 만들기

ndarray 는 N-dimensional Array의 약자이다. 이름 그대로 배열 객체는 단순 리스트와 유사한 1차원 배열 이외에도 2차원 배열, 3차원 배열 등의 다차원 배열 자료 구조를 지원한다.

R에서 배열을 만드는 간단한 명령어로는 다음과 같은 것들이 있다.

  • matrix: 긴 벡터를 잘라서 행렬로 변환
  • cbind: 여러개의 벡터를 좌우로 붙여서 행렬로 변환
  • rbind: 여러개의 벡터를 위아래로 붙여서 행렬로 변환

2차원 배열은 행렬(matrix)이라고도 하는데 행렬에서는 가로줄을 (row)이라고 하고 세로줄을 (column)이라고 한다.

matrix 명령은 행렬 전체의 원소의 갯수와 같은 길이를 가진 긴 벡터를 잘라 행렬을 만든다. 값은 가장 윗줄, 가장 왼쪽 원소부터 아래로 채워진다. 한 열(column)이 다 채워지면 다음 열로 이동한다.

이 때 크기를 지정하기 위해 nrow 또는 ncol 인수를 사용해야 한다.

  • nrow: 행(row)의 갯수를 지정한다.
  • ncol: 열(column)의 갯수를 지정한다.

둘 중 하나의 인수만 지정해도 나머지는 원래 벡터의 길이에서 결정된다. 예를 들어 원래 벡터의 길이가 6 이고 행의 수가 2, 즉 nrow=2 이면 열의 수는 6 / 2 = 3 이 된다.

만약 가장 왼쪽 원소부터 오른쪽으로 먼저 채우고 한 행을 다 채운다음 다음 행을 채우도록 하려면 byrow 인수를 TRUE로 설정하면 된다.

In [12]:
b <- matrix(1:6, nrow=2)
In [13]:
matrix(1:6, ncol=3)
135
246
In [14]:
b <- matrix(1:6, ncol=3, byrow=TRUE)
b
123
456

cbind, rbind 명령을 다음과 같이 사용한다.

In [15]:
cbind(c(1, 2), c(3, 4), c(5, 6))
135
246
In [16]:
rbind(c(1, 2, 3), c(4, 5, 6))
123
456

연습 문제 2

다음과 같은 행렬을 만든다.

10 20 30 40
50 60 70 80

벡터, 행렬의 차원과 크기 알아내기

R에서 어떤 변수가 벡터인지 행렬인지 알기 위해서는 length 명령어와 dim 명령어를 사용한다.

In [17]:
a
  1. 1
  2. 2
  3. 3
In [18]:
b
123
456

length 명령을 실행하면 벡터이든 행렬이든 전체 원소의 갯수를 출력한다.

In [19]:
length(a)
3
In [20]:
length(b)
6

dim 명령은 벡터의 경우에는 NULL 이고 행렬의 경우에는 크기를 나타내는 벡터이다.

In [21]:
dim(a)
NULL
In [22]:
dim(b)
  1. 2
  2. 3

벡터의 인덱싱과 서브셋팅

벡터의 인덱싱(indexing)이나 서브셋팅(subsetting)은 원소의 이름이 없다는 점만 제외하고는 리스트와 같다. [[]] 연산 기호로 인덱싱하고 [] 연산기호로 서브셋팅한다.

In [23]:
a[[1]]
1
In [24]:
a[1:2]
  1. 1
  2. 2

행렬의 인덱싱과 서브셋팅

행렬을 인덱싱할 때는 연산 기호는 같지만 두 인덱스사이를 쉼표(, comma)로 분리한다.. 행(row)이 먼저 오고 열(column)이 나중에 온다.

In [25]:
a <- rbind(0:3, 4:7)
a
0123
4567
In [26]:
a[[1, 1]]
0
In [27]:
a[1,]   # 첫번째 행 전체
  1. 0
  2. 1
  3. 2
  4. 3
In [28]:
a[, 2]  # 두번째 열 전체
  1. 1
  2. 5
In [29]:
a[2, 2:4]  # 두번째 행의 두번째 열부터 끝열까지
  1. 5
  2. 6
  3. 7
In [30]:
a[1:2, 1:2]
01
45

연습 문제 2

다음 행렬과 같은 행렬이 있다.

In [1]:
m <- matrix(0:14, nrow=3, byrow=T)
m
0 1 2 3 4
5 6 7 8 9
1011121314
  1. 이 행렬에서 값 7 을 인덱싱한다.
  2. 이 행렬에서 값 14 을 인덱싱한다.
  3. 이 행렬에서 배열 [6, 7] 을 서브셋팅한다.
  4. 이 행렬에서 배열 [7, 12] 을 서브셋팅한다.
  5. 이 행렬에서 배열 [[3, 4], [8, 9]] 을 서브셋팅한다.

음수 인덱스

R에서는 음수를 인덱스로 쓸 수도 있다. 이 경우에는 전체 인덱스에서 음수의 절대값에 해당하는 인덱스만 제외한 나머지 인덱스를 의미한다. 따라서 하나의 원소를 인덱싱할 때는 사용할 수 없고 서브셋팅의 경우에만 쓸 수 있다.

In [1]:
a  <- c(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

이 벡터의 첫번째 원소를 제외한 나머지 벡터를 서브셋팅하러면 다음과 같다.

In [6]:
a[-1]
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9

반대로 마지막 두 개의 원소를 제외하고 나머지 벡터를 서브셋팅하는 것은 다음과 같다.

In [9]:
a[-9:-10]
  1. 0
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7

배열 인덱싱

벡터나 행렬의 또다른 강력한 기능은 팬시 인덱싱(fancy indexing)이라고도 부르는 배열 인덱싱(array indexing) 방법이다. 인덱싱이라는 이름이 붙었지만 사실은 데이터베이스의 질의(Query) 기능을 수행한다.

배열 인덱싱에서는 대괄호(Bracket, [])안의 인덱스 정보로 숫자나 슬라이스가 아니라 위치 정보를 나타내는 또 다른 배열을 받을 수 있다. 여기에서는 이 배열을 편의상 인덱스 배열이라고 부르겠다. 배열 인덱싱의 방식에은 불리안(Boolean) 배열 방식과 정수 배열 방식 두가지가 있다.

먼저 불리안 배열 인덱싱 방식은 인덱스 배열의 원소가 TRUE, FALSE 두 값으로만 구성되며 인덱스 배열의 크기가 원래 ndarray 객체의 크기와 같아야 한다. TRUE, FALSE 대신 T, F라고 써도 된다.

예를 들어 다음과 같은 1차원 벡터에서 홀수인 원소만 골라내려면 홀수인 원소에 대응하는 인덱스 값이 T 이고 짝수인 원소에 대응하는 인덱스 값이 F인 인덱스 벡터를 넣으면 된다.

In [31]:
a  <- c(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
idx <- c(T, F, T, F, T, F, T, F, T, F)
a[idx]
  1. 0
  2. 2
  3. 4
  4. 6
  5. 8

조건문 연산을 사용하면 다음과 같이 간단하게 쓸 수 있다.

In [32]:
a[a %% 2 == 0]
  1. 0
  2. 2
  3. 4
  4. 6
  5. 8

정수 배열 인덱싱에서는 인덱스 배열의 원소 각각이 원소 하나를 가리키는 인덱스 정수이여야 한다. 예를 들어 벡터에서 홀수번째 원소만 골라내는 것은 다음과 같다

In [33]:
a <- c(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) * 10
idx = c(1, 3, 5, 7, 9)
a[idx]
  1. 0
  2. 20
  3. 40
  4. 60
  5. 80

이 때 정수 배열 인덱스의 크기는 원래의 배열 크기와 달라도 상관없다. 같은 원소를 반복해서 가리키는 경우에는 원래의 배열보다 더 커지기도 한다.

In [34]:
a <- c(0, 1, 2, 3) * 10
idx = c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3)
a[idx]
  1. 0
  2. 0
  3. 0
  4. 0
  5. 0
  6. 10
  7. 10
  8. 10
  9. 10
  10. 10
  11. 20
  12. 20
  13. 20
  14. 20
  15. 20
  • 배열 인덱싱

    • 불리안(Boolean) 방식 배열 인덱싱

      • True인 원소만 선택
      • 인덱스의 크기가 배열의 크기와 같아야 한다.
    • 위치 지정 방식 배열 인덱싱

      • 지정된 위치의 원소만 선택
      • 인덱스의 크기가 배열의 크기와 달라도 된다.

질문/덧글

아직 질문이나 덧글이 없습니다. 첫번째 글을 남겨주세요!