인덱스 표현식
인덱스 표현식
행렬이나 배열에서 원하는 원소만 골라내는 작업은 Octave를 쓰면서 가장 자주 하는 일이에요. 괄호 안에 인덱스를 넣는 '인덱스 표현식'이 바로 그 방법이에요. 성분 인덱싱, 선형 인덱싱, 논리 인덱싱 세 가지 방식을 알아두면 대부분의 상황을 처리할 수 있어요.
본문
인덱스 표현식은 벡터, 행렬(2차원), 또는 더 고차원 배열에서 선택한 원소를 참조하거나 추출할 수 있게 해줘요. 배열은 세 가지 방식으로 인덱싱할 수 있어요: 성분 인덱싱(Component Indexing), 선형 인덱싱(Linear Indexing), 논리 인덱싱(Logical Indexing).
성분 인덱싱 (Component Indexing)
성분 인덱스는 스칼라, 벡터, 범위, 또는 특별한 연산자 :일 수 있어요. :는 전체 행, 열, 또는 고차원 슬라이스를 선택해요.
성분 인덱스 표현식은 쉼표로 구분된 M개의 표현식을 둘러싼 괄호 집합이에요. 각각의 개별 인덱스 값, 즉 성분(component)은 그것이 적용되는 대상의 각 차원에 쓰여요. 다시 말해 첫 번째 인덱스 성분은 대상의 첫 번째 차원(행)에, 두 번째 인덱스 성분은 두 번째 차원(열)에, 이런 식으로 적용돼요. 인덱스 성분의 수 M이 인덱스 표현식의 차원 수를 정의해요. 성분이 두 개인 인덱스를 2차원 인덱스라고 부르는 이유예요.
가장 단순한 경우는 1) 모든 성분이 스칼라이고, 2) 인덱스 표현식의 차원 수 M이 대상의 차원 수와 같은 경우예요. 예를 들어,
A = reshape (1:8, 2, 2, 2) # Create 3-D array
A =
ans(:,:,1) =
1 3
2 4
ans(:,:,2) =
5 7
6 8
A(2, 1, 2) # second row, first column of second slice
# in third dimension: ans = 6
특정 차원에서 반환되는 객체의 크기는 인덱스 표현식의 해당 성분에 있는 원소의 수와 같아요. 모든 성분이 스칼라면 결과는 단일 출력 값이에요. 하지만 어떤 성분이 벡터나 범위라면, 반환되는 값은 각 차원의 인덱스들의 곱집합(Cartesian product)이에요. 예를 들어:
A([1, 2], 1, 2) ≡ [A(1,1,2); A(2,1,2)]
⇒
ans =
5
6
반환되는 값의 총 개수는 각 인덱스 성분이 반환하는 원소 수의 곱이에요. 위 예에서 총 211 = 2개 원소예요.
주어진 차원에서 반환되는 객체의 크기가 그 차원의 인덱스 표현식에 있는 원소 수와 같다는 점을 주목하세요. 위 코드에서 첫 번째 인덱스 성분([1, 2])은 행 벡터로 지정했지만 그 형태는 중요하지 않아요. 중요한 사실은 성분이 두 개의 값을 지정했으므로 결과가 첫 번째 차원에서 크기가 2여야 한다는 것이고, 첫 번째 차원이 행에 대응하므로 전체 결과는 열 벡터라는 점이에요.
A(1, [2, 1, 1], 1) # result is a row vector: ans = [3, 1, 1]
A(ones (2, 2), 1, 1) # result is a column vector: ans = [1; 1; 1; 1]
첫 번째 줄은 주어진 차원의 출력 크기가 해당 인덱싱 성분의 원소 수와 같다는 것을 다시 보여줘요. 이 경우 출력은 두 번째 차원(열에 대응)에 세 개의 원소가 있으므로 행 벡터예요. 이 예는 인덱스 표현식에서 반복되는 항목을 써서 출력에서 원소를 복제할 수 있음을 보여주기도 해요. 마지막 예는 인덱싱 성분의 형태는 무관하고 원소 수(2x2 = 4)만 중요하다는 것을 더 증명해요.
위 규칙은 인덱스 표현식의 차원 수가 1보다 클 때(M > 1) 적용돼요. 그러나 1차원 인덱스 표현식에는 특별한 규칙이 적용되어 출력의 형태는 인덱싱 성분의 형태에 의해 결정돼요. 예를 들어:
A([1, 2]) # result is a row vector: ans = [1, 2]
A([1; 2]) # result is a column vector: ans = [1; 2]
A(P)의 형태 규칙은 다음과 같아요.
A또는P중 적어도 하나가 2차원 이상이면,A(P)는P의 형태를 취해요. 이는 적어도 하나의 변수가 2차원 행렬이나 N차원 배열일 때 일어나요.A와P가 모두 1차원 벡터이면,A(P)는A자신의 형태를 취해요. 특히A가 행 벡터이면P의 형태와 무관하게A(P)도 행 벡터예요.A가 열 벡터인 경우도 유사해요.
콜론(:)을 인덱스 성분으로 사용하면 지정된 차원의 모든 원소를 선택할 수 있어요. 행렬
A = [1, 2; 3, 4]
이 주어지면 다음 표현식은 모두 동등하며 행렬의 첫 번째 행을 선택해요.
A(1, [1, 2]) # row 1, columns 1 and 2
A(1, 1:2) # row 1, columns in range 1-2
A(1, :) # row 1, all columns
콜론이 1차원 인덱싱의 특별한 경우에 쓰이면 결과는 항상 열 벡터예요. 콜론 인덱스로 열 벡터를 만드는 것은 매우 자주 만나는 코드 관용구이며, 이 경우 reshape를 호출하는 것보다 빠르고 일반적으로 더 명확해요.
A(:) # result is column vector: ans = [1; 2; 3; 4]
A(:)' # result is row vector: ans = [1, 2, 3, 4]
인덱스 표현식에서 end 키워드는 특정 차원의 마지막 항목을 자동으로 가리켜요. 이 마법 인덱스는 범위에서도 쓸 수 있으며, 인덱싱 전에 배열 경계를 얻기 위해 size나 length를 호출할 필요를 없애줘요. 예를 들어:
A(1:end/2) # first half of A => [1, 2]
A(end + 1) = 5; # append element
A(end) = []; # delete element
A(1:2:end) # odd elements of A => [1, 3]
A(2:2:end) # even elements of A => [2, 4]
A(end:-1:1) # reversal of A => [4, 3, 2, 1]
자세한 내용은 "end" 키워드 문서를 참고하세요.
선형 인덱싱 (Linear Indexing)
다차원 객체에 1차원 인덱스를 사용하는 것도 허용돼요. 이를 선형 인덱싱(linear indexing)이라고 해요. 이 경우 다차원 배열의 원소는 Fortran처럼 열 우선(colomn-first) 순서로 취해져요. 즉 배열의 열들이 서로 위에 쌓여 열 벡터를 형성한다고 상상하고, 그 벡터에 단일 선형 인덱스를 적용해요.
A = [1, 2, 3; 4, 5, 6; 7, 8, 9];
A(4) # linear index of 4th element in 2-D array: ans = 2
A(3:5) # result has shape of index component: ans = [7, 2, 5]
A([1, 2, 2, 1]) # result includes repeated elements: ans = [1, 4, 4, 1]
논리 인덱싱 (Logical Indexing)
논리 값으로도 행렬과 셀 배열을 인덱싱할 수 있어요. 논리 배열로 인덱싱하면 결과는 논리 배열의 참(true) 부분에 대응하는 값을 담은 벡터가 돼요. 다음 예가 이를 보여줘요.
data = [ 1, 2; 3, 4 ];
idx = [true, false; false true];
data(idx)
⇒ ans = [ 1; 4 ]
idx = (data <= 2);
data(idx)
⇒ ans = [ 1; 2 ]
idx 배열을 만드는 대신 위 코드에서 data(idx)를 data( data <= 2 )로 바꿀 수도 있어요.
논리 인덱스 표현식의 크기는 보통 인덱싱되는 배열의 크기와 같지만, 필수 조건은 아니에요. 논리 인덱스가 배열과 크기가 다르면, 선형 인덱싱과 마찬가지로 배열의 원소가 선형 순서에 따라 논리 인덱스의 원소와 대응돼요.
data = [ 1, 2, 3; 4, 5, 6 ];
idx = [ true, false, false, true ];
data(idx)
⇒ ans = [ 1 5 ] # idx selected the 1st and 4th position elements
논리 인덱스가 배열보다 크면, 어떤 참 값이 배열의 원소 수보다 큰 선형 위치를 선택하려고 할 때 범위 초과(out of bounds) 오류가 발생해요.
idx = [ true, true, false; false, true, false; true; false; false ];
data(idx)
⇒ ans = [ 1; 2; 5; 3 ] # returns positions 1, 3, 4, 5 in a column
idx = [ true, true, false; false, true, false; true; false; true ];
data(idx)
⇒ error: a(9): out of bound 6 (dimensions are 2x3)
배열 크기 밖에 있는 논리 인덱스의 거짓(false) 원소는 무시되지만, 논리 인덱스의 아홉 번째 원소가 참이면 존재하지 않는 배열의 9번째 원소를 선택하려 하므로 오류가 발생해요.
더 알아보기
- 인덱스 표현식은 함수 호출(Calling Functions) 문서에서 계속 이어져요.
end키워드와 벡터화, 브로드캐스팅 문서도 함께 보면 좋아요.