numpy에는 배열(ndarray)과 행렬(matrix) 객체가 있는데 이들 중 어느 것을 사용해야 하는지 처음 공부할 때 혼동하기 쉽다. 언뜻 보기에는 사용법에 별 차이가 없어 보이고 행렬 연산에는 무조건 matrix를 사용해야 하는 것 아닌가 짐작했었는데 다음 사이트에 잘 정리가 되어 있어서 여기에 옮겨보도록 하겠다.

              numpy for MATLAB users

1. 결론 요약

  일단 결론부터 말하자면 ndarray 를 사용하는 것이 더 효율적이라고 명시되어 있다. 개인적으로 matrix 객체는 MATLAB 사용자의 편의를 위해서 (억지로) 만들어진 것 같은 느낌이 든다. (pylab 과 같은 모듈도 비슷한 이유로 만들어졌으나 파이썬 커뮤니티 안에서는 사용이 역시 권장되지 않는다.)

 ndarray를 사용해야 하는 이유는 다음과 같이 요약할 수 있다.

  • ndarray는 numpy에서 지원하는 표준형인 벡터/행렬/텐서 를 저장한다.

  • 많은 numpy 함수가 matrix가 아니라 ndarray를 반환한다.

  • 요소 간 연산과 선형대수 연산에 대해선 명확히 구분되어 있다.

  • 표준 벡터나 열벡터/행벡터를 표현할 수 있다.

ndarray를 사용할 경우의 한 가지 단점은 행렬의 곱셈을 수행할 때 ,dot() method를 사용해야한다는 점이다. 즉, ndarray 객체 A와 B를 행렬곱하려면 A.dot(B) 와 같이 수행해야 하며 A*B는 요소간 곱셈이 된다. 반면 matrix객체 A와 B는 단순히 A*B로 행렬곱이 수행된다.

2. 좀 더 자세한 설명

 numpy는 ndarray 와 matrix 둘 다 포함한다. ndarray는 다양한 종류의 수치 연산을 위해서 고안된 범용 n차 배열이다. 반면 matrix는 선형 대수 연산을 위해서 특별히 고안된 객체이다. 실용적인 관점에서 둘 사이의 차이점은 몇 가지 안된다.

 

  • 연산자 *, dot() 그리고 multiply()

    • ndarray 는 '*'는 요소간 곱셈이다. 행렬곱을 할때는 obj.dot() 메쏘드를 사용해야 한다.

    • 반면 matrix는 '*'이 행렬곱이다. 그리고 numpy.multiply() 함수가 요소간 곱이다.

  • 벡터와 1차 배열

    • ndarray 는 벡터 1xN, Nx1, 그리고 N크기의 1차원 배열이 모두 각각 다르다. obj[:,1] 는 크기가 n인 1차 배열을 반환한다. 그리고 1차원 배열의 전치는 작동하지 않는다. (역자 주 : ndarray로 벡터를 표현할 때는 반드시 2차 배열을 이용해야 한다.)

    • 반면 matrix 객체에서 1차 배열은 모두 1xn, 혹은 nx1 행렬(2차원 배열)로 상향 변환된다. ( matrix 객체는 내부적으로 항상 2차원 배열이다. )

  • ndarray는 고차원 배열이 가능하지만 matrix는 항상 2차원 배열 이다.

  • 편리한 attribute

    • ndarray 는 전치를 해주는 a.T attribute 가 있다.

    • matrix 도 m.H, m.I, m.A attribute들이 있으며 각각 복소전치, 역행렬, ndarray로의 변환이다.

  • 편리한 생성자

    • ndarray는 중첩된 리스트로 다차원 배열을 생성한다. 예) array( [ [1,2,3].[4,5,6] ] )

    • matrix는 MATLAB 의 문법을 지원한다. 예) matrix('[1 2 3; 4 5 6]')


좀 더  부가 설명을 하자면 ndarray를 이용하여 벡터(vector)를 표현할 때는 2차 배열로 정의해야 한다. 즉, 다음 세 가지는 모두 다르며 이 중  벡터는 두 번째와 세 번째 같이 생성해야 한다. (혼동하기 참 쉽다.)


a1 = np.array( [1, 2, 3] ) #크기 (3,)인 1차원 배열
a2 = np.array( [ [1, 2, 3] ] ) #크기 (1,3)인 2차원 배열 (행벡터)
a3 = np.array( [ [1], [2], [3] ] ) #크기 (3,1)인 2차원 배열 (열벡터)


여기서 a1.T 는 동작하지 않는다. 반면 a2.T 와 a3.T는 동작한다. 1차 배열은 행벡터나 열벡터 두 가지 모두로 취급되기도 한다.


 ndarray 객체를 사용하는데 있어서 장점과 단점은 다음과 같다.


  • 장점

    • 1차 배열은 행벡터나 열벡터 둘 다로 취급할 수 있다. dot(A,v) 에서 v는 열벡터로 다루어지고 dot(v,A)에서는 행벡터로 취급된다. 따라서 전치를 복잡하게 수행할 필요가 없다.

    • 요소 간 곱셈이 쉽다 (예: A*B) 사실 모든 산술 연산 (+ - * / ** 등등)이 요소 간 연산이다.

    • ndarray 는 numpy 의 가장 기본 객체이므로 연산의 속도, 효율성 그리고 numpy를 이용하는 외부 라이브러리의 반환형이 이것일 가능성이 높다.

    • 다차원 배열을 쉽게 구현한다.

    • tensor algebra 에 대한 장점이 있다.(?)

  • 단점

    • 행렬간 곱에 obj.dot() 멤버함수를 사용해야 하므로 번잡할 수 있다. 세 행렬 A, B, C의 행렬곱은 dot( dot(A,B),C) 이다


matrix 객체를 사용하는 데 있어서 장점과 단점은 다음과 같다.


  • 장점

    • MATLAB 행렬과 비슷하게 동작한다.

    • 행렬곱이 A*B 와 같이 '*' 연산자이므로 선형대수 연산이 좀 더 편하다.

  • 단점

    • matrix 의 차수는 항상 2이다. 벡터도 2차 배열로 저장된다. 3차 행렬을 구현하려면 ndarray를 이용하던가 matrix객체를 요소로 갖는 리스트(list)로 구현해야 한다.

    • ndarray 가 numpy의 표준 객체이기 때문에 어떤 함수는 인수로 matrix를 주어도 반환 객체는 ndarray 이기도 하다. numpy 에 포함된 함수는 절대 그렇지 않지만 제3자 라이브러리는 그럴 수 있다.

    • 요소간 곱은 multiply(A,B)함수를 이용해야 한다.

    • *은 행렬곱인데 반해 /는 요소간 연산이다. 즉, 연산자 오버로딩에 일관성이 결여되어 있다.


 개인적으로는 numpy를 사용하기로 결정되었다면 matlab에서의 개념은 모두 버리고 numpy에서 제공하는 객체에 빨리 익숙해져야한다는 의견이다. 그래서 matrix객체나 pylab 모듈을 사용하는 것이 권장되지 않는 것이다.




Posted by 살레시오

 MATLAB에서 제공하는 논리 연산자는 다음 표와 같다.


[표 1] 논리 연산자

논리연산자

기능

&

AND

|

OR

~

NOT


(부연하자면 C/C++의 비트연산자와 같다. MATLAB은 비트 연산자는 없다.)

 다음 예를 보자


>> A=magic(3)
A =
8 1 6
3 5 7
4 9 2

>> B=pascal(3)
B =
1 1 1
1 2 3
1 3 6

>> ~(A>B)
0 1 0
0 0 0
0 0 1

>> (A>2)&(B<3)
1 0 1
1 1 0
1 0 0


 이전 포스트에서 설명한 기본 논리/관계연산자 외에도 MATLAB은 추가적인 다양한 논리/관계 함수를 제공한다.


[표 2] 관계/논리 연산 관련 함수들 (X, Y는 함수)

함수명

함수의 기능

Z=xor(X,Y)

XOR 연산을 수행한다

Z=any(X)

벡터 X의 요소 중 하나라도 0이 아닌 요소가 있으면 1을 반환, X가 행렬일때는 열단위로 연산된다.

z=all(X)

벡터 X의 요소들 모두 0이 아니면 1을 반환

(X가 행렬일 때는 열단위로 연산된다.)

z=isempty(X)

행렬 X가 빈행렬이면 1을 반환

z=isequal(X,Y)

행렬 X와 Y가 완전히 동일하면 1을 반환

Z=isinf(X)

행렬 X의 요소 중 inf가 있는 곳에 1을 반환

Z=isnan(X)

행렬 X의 요소 중 nan이 있는 곳에 1을 반환

z=isreal(X)

행렬 X의 요소 중 복소수가 하나도 없으면 1을 반환


위의 도표에서 소문자 z가 출력변수이면 그 함수는 스칼라값(0혹은1)을 반환함을 의미하며 대문자 Z가 출력행렬이면 그 함수는 입력 파라메터와 동일한 크기를 갖는 행렬을 반환한다는 것을 의미한다.


 이 중 isequal()함수는 연산자 (==)와 같은 역할을 하는 것 아닌가 생각할수 있으나 (==)연산자는 요소간 비교이고 isequal()함수는 두 행렬 전체를 비교하는 함수이다. 다음 예에서 이를 확인해 보라.


>> A=[1 2;3 4]
A =
1 2
3 4

>> B=[1 2;3 4]
B =
1 2
3 4

>> C=[1 2; 3 5]
C =
1 2
3 5

>> A==B
1 1
1 1

>> A==C
1 1
1 0

>> isequal(A,B)
1

>> isequal(A,C)
0




Posted by 살레시오

 관계 연산이나 논리연 산에서는 C언어에서와 유사하게 MATLAB은 0을 거짓으로 0이 아닌 다른 수(보통은 1이 쓰임)를 참으로 간주한다. 모든 관계 연산과 논리 연산에서 참이면 1, 거짓이면 0을 그 결과로서 반환한다. MATLAB에서의 관계 연산자는 다음 표와 같다.


[표 1] 관계 연산자

관계 연산자

의미

<

작다.

<=

작거나 같다.

>

크다.

>=

크거나 같다.

==

같다.

~=

다르다.


위의 표에서 ‘같다’와 ‘다르다’에 주의해야 한다. C언어를 처음 배울 때도 대입연산자(=)와 관계연산자(==)를 혼동해서 논리적 오류를 범하는 경우가 많은데 여기에서도 그렇다. 두 연산자를 잘 구분해야 한다. 그리고 ‘다르다’는 (!=)가 아니고 (~=)임에 유의하자.


 관계연산자의 피연산자는 두 개인데 다음의 두 가지 경우가 있을 수 있다.

① 둘 다 행렬일 경우 - 이 경우는 두 행렬의 차수가 같아야 하고 각 행렬의 요소간의 관계가 검색된다. 결과로는 같은 차수의 행렬이 생성된다.

② 하나는 행렬 다른 하나는 스칼라인 경우 - 스칼라와 행렬의 각 요소간의 관계가 검색되며 결과로는 행렬과 같은 차수의 행렬이 생성된다.

 다음의 예를 보자.


>> A=1:4, B=4-A
A =
1 2 3 4
B =
3 2 1 0

>> C = A>2
C =
0 0 1 1

>> D = (A==B)
D =
0 1 0 0

>> E = A<=B
E =
1 1 0 0


첫 번째 예에서는 행렬 A와 스칼라 2를 비교했다(A>2). 따라서 행렬 A의 각 요소와 2를 비교해서 그 요소가 크면 1을 그렇지 않으면 0을 가지는 행렬을 만들어서 C에 대입하는 것이다. (A==B)에서는 둘 다 행렬이고 이 경우에 두 행렬의 크기는 같아야 한다. 같은 위치에 있는 요소를 비교하여 같으면 1 다르면 0을 갖는 행렬을 반환하게 된다.


 다음과 같이 관계연산자와 산술연산자를 혼용할 수도 있다.


>> B-(A>2) 󰎠
3 2 0 -1


위의 예에서 (A>2)의 결과는 0과 1로 이루어진 보통의 행렬이기 때문에 산술연산(이 경우 뺄셈)에 사용될 수 있음을 보여주는 것이다. 다음 예제를 보자.


>> x1=-1:1
x =
-1 0 1

>> x2=x1+(x1==0)*eps
x2 =
-1.0000 0.0000 1.0000


위의 예제에서 벡터 x1을 [-1 0 1]로 생성시키고 두 번째 명령에서 x1에서 0인 요소를 찾아서 0대신 eps를 대입한 후 x2에 대입한 것이다. eps는 MATLAB에서 표현할 수 있는 가장 작은 수이다. 이제 다음을 보자.


>> sin(x1)./x1
0.8415 NAN 0.8415


위에서 두 번째 요소가 NAN인데 이것은 x1의 두 번째 요소가 0이고 sin(0)도 0이기 때문에 0/0 인 결과가 되어서 그렇다. 올바른 극한값 1을 얻기 위해서는 x1의 0을 eps로 치환한 x2를 이용하면 된다.


>> sin(x2)./x2 󰎠
0.8415 1.0000 0.8415



Posted by 살레시오

 여기에서는 행렬-스칼라 그리고 행렬-행렬간의 산술 연산에 대해서 설명을 하겠다. 다음의 예를 보자.


>> G=[1 2 3 4;5 6 7 8]
G =
1 2 3 4
5 6 7 8

>> G-2
-1 0 1 2 3 4 5 6

>> -2+G
-1 0 1 2 3 4 5 6


이 예에서 변수 G는 행렬이고 2는 스칼라이다. 기본적으로 (수학적인) 행렬의 덧셈 뺄셈은 두 행렬의 차수가 같아야 하지만 한쪽이 스칼라이고 다른 한쪽이 행렬이면 행렬의 모든 요소에서 그 스칼라를 더하거나 빼준 행렬을 결과로 내보낸다.


>> 2*G-1 󰎠
1 3 5 7
9 11 13 15


스칼라와 벡터/행렬의 곱은 수학적인 의미 그대로이며 연산 우선순위도 스칼라연산과 같다. 위의 경우 곱을 먼저 행하고 뺄셈을 행하게 된다.


 행렬 간의 덧셈, 뺄셈은 두 연산자의 차수가 같아야 한다. 다음과 같이 행렬 G와 H를 입력해보자.


>> G=[1 2 3 4; 5 6 7 8; 9 10 11 12] 󰎠
G =
1 2 3 4
5 6 7 8
9 10 11 12

>> H=[1 1 1 1;2 2 2 2; 3 3 3 3] 󰎠
H =
1 1 1 1
2 2 2 2
3 3 3 3


그리고 다음과 같이 덧셈, 뺄셈을 수행해 보라.


>> G+H
2 3 4 5
7 8 9 10
12 13 14 15

>> ans-H
1 2 3 4
5 6 7 8
9 10 11 12

>> 2*G-H
1 3 5 7
8 10 12 14
15 17 19 21


 곱셈의 경우 곱셈기호(*)앞의 행렬의 열수와 뒤 행렬의 행수가 같아야 계산이 수행된다. 따라서 위의 두 행렬 G와 H는 곱셈을 할 수 없다. Ht라는 행렬을 다음과 같이 정의하면


>> Ht=h'
Ht =
1 2 3
1 2 3
1 2 3
1 2 3


이제 행렬 G(차원)와 Ht(차원)간의 곱셈은 가능해진다.


>> G*Ht
10 20 30
26 52 78
42 84 126


 다른 연습 예제로서 다음과 같은 행렬 A와 B가 있을 때,

두 행렬의 곱 AB, BA, ATBT, BTAT 를 각각 구해보라.

 MATLAB에서 행렬간의 곱셈은 이와 같은 보통 행렬 간 곱셈뿐만 아니라 행렬 요소 간의 곱셈도 가능하다. 행렬 요소 간 곱셈을 행하는 연산자는 (.*)이고 이 연산자 앞뒤에 오는 피연산자들의 차원은 같아야 한다.


>> G.*H
1 2 3 4
10 12 14 16
27 30 33 36


행렬 간의 나눗셈은 무슨 의미 일까? 예를 들어서


>> A=[1 2;3 4]
A =
1 2
3 4

>> B=[5 6;7 8]
B =
5 6
7 8



와 같이 정의된 정방행렬 A와 B에 대해서 A/B 또는 B\A는 무슨 의미일까? 결과를 확인해 보자.


>> A/B
3.0000 -2.0000
2.0000 -1.0000
>> B\A
5.0000 4.0000
-4.0000 -3.0000


스칼라의 경우를 생각해 보자. 4/3은 4에게 3의 역수를 곱하는 계산 즉, 4x3-1이다. 마찬가지로 3\4은 3의 역수를 4에 곱하는 것(3-1x4)이다. 행렬의 경우도 마찬가지이다. A/B는 행렬 A에 B의 역행렬을 곱하는 것이고, B\A는 B의 역행렬에 A행렬을 곱하는 것이다. 두 결과가 같다고 생각하기 쉽지만  '행렬에서는 교환법칙이 성립하지 않는다'는 것에 유의해야 한다. 위의 결과를 보아도 서로 다르다는 것을 알 수 있다. 즉 A/B와 B\A는 A와 B가 스칼라라면 같은 수이지만 행렬이라면 서로 다른 행렬이 된다.


 MATLAB에서 역행렬을 구하는 함수는 inv()이다. (inverse 의 줄임말을 함수명으로 채용)


>> A*inv(B)
3.0000 -2.0000
2.0000 -1.0000

>> inv(B)*A
5.0000 4.0000
-4.0000 -3.0000


위의 결과와 이전의 그것과 같다는 것을 알 수 있다. 여기서는 정방행렬의 경우만 다루고 있는데 정방행렬이 아닌 행렬의 가역행렬(pseudo-inverse matrix)를 구하는 함수로 pinv()가 있다. 이에 대해서는 다음에 다루도록 한다.


 나눗셈의 경우도 행렬 요소간 나눗셈이 가능하다. 연산자(./)와 (.\)가 바로 그러한 역할을 한다.


>> A./B 󰎠
0.2000 0.3333
0.4286 0.5000

>> B.\A 󰎠
0.2000 0.3333
0.4286 0.5000


요소 간 연산인 경우에는 항상 두피연산자의 차수가 같아야 한다. 위의 결과를 보면 A./B와 B.\A의 계산결과는 같다는 것을 알 수 있다.(왜일까?)


 행렬의 거듭제곱을 구하는 데는 (^)연산자가 사용되며 행렬의 요소 각각의 거듭제곱을 구하는 데는 (.^)연산자가 사용된다.


>> A^2
7 10
15 22

>> A.^2
1 4
9 16

>> A.^-1
1.0000 0.5000
0.3333 0.2500

>> 2.^A
2 4
8 16

>> A.^B
1 64
2187 65536


이 예에서 A^2과 A.^2 그리고 2.^A의 차이를 확실히 구별해야 한다. 곱셈(*)과 나눗셈(/혹은\) 그리고 거듭제곱(^)을 수행하는 연산자 앞에 ‘점(.)이 붙으면 요소간 연산’이라는 사실을 기억해야 한다.



Posted by 살레시오

 MATLAB에서 어떤 행렬의 크기를 구하고자할 때 사용하는 함수가 size()함수와 length()함수이다.


>> A=[1 2 3 4; 5 6 7 8]
A =
1 2 3 4
5 6 7 8
>> s=size(A)
s =
2 4

출력 파라메터가 하나일 때는 size()함수는 행수와 열수를 요소로 하는 행벡터를 반환한다. 출력 파라메터가 두 개일 때는 다음과 같이 첫 번째 파라메터에는 행을 두 번째 파라메터는 열수를 담아서 반환한다.


>> [row col]=size(A)
row = 2
col = 4


만약 두 개의 입력 파라메터가 주어진다면 행수 혹은 열수를 반환한다. 다음 예제를 보면 이를 알 수 있다.


>> r=size(A,1) % 행수를 반환
r =
2
>> r=size(A,2) %열수를 반환
r =
4


크기를 구하고자 하는 것이 벡터라면 length()함수를 이용하면 된다.



>> B=pi:0.01:2*pi;
>> length(B)
315
>> size(B)
1 315


이 예에서 벡터 B는 315의 크기를 가지는 행벡터임을 알 수 있다.만약 length()함수의 입력 파라메터로 행렬이 들어간다면 행수와 열수 중에서 큰 값이 반환된다.


>> A
1 2 3 4
5 6 7 8
>> length(A)
4


즉, length(A)함수는 max(size(A))명령과 동일한 일을 수행하게 된다.



Posted by 살레시오

 행렬의 원하는 부분을 치환하기 위해서는 치환하고자 하는 행렬의 원소에 값을 입력한다. 먼저 다음과 같이 행렬 A를 생성하자


>> A = [ 1 2 3 4 5;
6 7 8 9 10;
9 9 9 9 9;
0 0 0 0 0;
1 1 1 1 1];


이제 행렬 A에 대해서 다음 예제들과 같이 조작할 수 있다.


>> A(1,2) = 100 󰎠 % (1,2) 원소가 100이 된다.
A =
1 100 3 4 5
6 7 8 9 10
9 9 9 9 9
0 0 0 0 0
1 1 1 1 1
>> A(:,2) = [1 1 1 1 1]' 󰎠 % 2열의 원소들을 치환한다.
A =
1 1 3 4 5
6 1 8 9 10
9 1 9 9 9
0 1 0 0 0
1 1 1 1 1

>> A(1:4,2) = [2 2 2 2]' 󰎠 % 1행~4행의 2열 원소들을 2로 치환
A =
1 2 3 4 5
6 2 8 9 10
9 2 9 9 9
0 2 0 0 0
1 1 1 1 1

>> A(1,2) = 100 󰎠 % (1,2) 원소가 100이 된다.
A =
1 100 3 4 5
6 7 8 9 10
9 9 9 9 9
0 0 0 0 0
1 1 1 1 1
>> A(:,2) = [1 1 1 1 1]' 󰎠 % 2열의 원소들을 치환한다.
A =
1 1 3 4 5
6 1 8 9 10
9 1 9 9 9
0 1 0 0 0
1 1 1 1 1

>> A(1:4,2) = [2 2 2 2]' 󰎠 % 1행~4행의 2열 원소들을 2로 치환
A =
1 2 3 4 5
6 2 8 9 10
9 2 9 9 9
0 2 0 0 0
1 1 1 1 1


 아직 생성되지 않은 행렬의 특정한 요소에 값을 대입할 수 있다. 이때 지정되지 않은 원소는 모두 0 으로 출력된다. 예를 들어 아직 작업 공간에 D라는 변수가 없는 경우 다음과 같이 명령을 내리면,


>> D(3,3)=3 󰎠
D =
0 0 0
0 0 0
0 0 3


와 같이 D가 새로 생성되고 (3,3)자리에 3이 대입되며 지정되지 않은 다른 자리에는 0으로 채워진다. 만약 이 상태에서 다음과 같이 추가적으로 명령을 내리면


>> D(5,5)=5 󰎠
D =
0 0 0 0 0
0 0 0 0 0
0 0 3 0 0
0 0 0 0 0
0 0 0 0 5


지정되지 않은 위치의 요소들은 0으로 초기화되는 것을 알 수 있다.


 또 다른 예로서, C라는 행렬을 새로 생성시키는데, 두 번째 행을 [1 2 3]으로 하고 싶으면 다음과 같이 한다.


>> C(2,:)=[1 2 3] 󰎠
C =
0 0 0
1 2 3


이때 지정되지 않은 첫 번째 행의 값은 0으로 초기화됨을 알 수 있다. 만약 A라는 행렬이 3X3 크기의 행렬로 작업공간에 존재한다면


>> a=A(4,4) 󰎠
??? Index exceeds matrix dimensions.


는 보다시피 에러가 발생한다. 하지만 다음 명령어는 새로운 변수가 생성되는 것과 비슷한 규칙이 적용된다.


>> A(4,4)=10 󰎠
A =
1 2 3 0
4 5 6 0
7 8 9 0
0 0 0 10


즉, 지정된 위치를 만들어 값을 대입하고 값이 없는 위치에는 0을 대입한다.




Posted by 살레시오

 여기에서는 이전 포스트에 이어서 행렬의 인덱싱에 대해서 설명하겠다. 행렬의 인덱스에 콜론(:) 연산자를 이용하여 여러 개의 행이나 열을 동시에 선택할 수도 있다.


>> A(2, 1:3) % 2행의 1열에서 3열까지의 원소
4 5 6


위의 예에서 콜론 연산자가 단독으로 쓰이는 경우와는 다르다는 것을 알 수 있다. 여기에서 콜론 연산자는 이전 포스트에서 설명한 수열을 생성하는 연산자로서 행벡터를 생성한다. 즉 위의 명령은 다음과 완전히 동일하다.


>> A(2, [1 2 3])


다음 명령을 보자.


>> A(1:3, :) 󰎠 % 1행(전체)부터 3행(전체)까지
>> A([1 2 3], :) 󰎠


이 명령어의 행인덱스 1:3 와 열인덱스 : 에 똑같은 콜론(:)문자가 쓰였지만 전혀 다른 연산자이다. 앞에 콜론은 수열을 생성하는 연산자이고 뒤의 연산자는 인덱스로서 단독으로 쓰였기 때문에 ‘모두’의 의미를 갖는다. 즉, 둘 다 같은 명령이다.


 행렬의 마지막 요소를 지정하는 데에는 end연산자가 사용된다. 다음 예를 보자.


>> f=1:2:10 󰎠
f =
1 3 5 7 9
>> f(end)
9


위에서는 f벡터의 끝 요소 만을 참조하려면 f(5)라고 하면 되지 않겠느냐고 생각할 수도 있으나 end연산자는 참조하려는 행렬의 크기를 모르는 경우 제일 끝의 행(또는 열)만을 지정하고 싶을 때 유용하다. MATLAB 프로그래밍을 하다 보면 참조하려는 행렬의 크기가 불확실한 경우가 자주 발생하게 된다. 다음의 예제도 잘 살펴보기 바란다.


>> F=magic(5)
F =
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9

>> F(end)
9

>> F(2:end, :)
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9

>> F(1:3, 3:end)
1 8 15
7 14 16
13 20 22


위에서 magic(n)함수는 (n은 홀수) nxn 차원의 정방 행렬을 만드는데 생성된 행렬은 특정한 행(또는 열)의 모든 요소의 합이 일정하다. 대각선 방향의 합도 역시 같다. (자세한 설명은 help magic 명령으로 살펴볼 수 있다.)


 만일 어떤 행렬에서 특정한 행 또는 열을 없애려면 다음과 같이 하면 된다.


>> A=magic(3)
A =
8 1 6
3 5 7
4 9 2

>> A(2,:)=[]
A =
8 1 6
4 9 2

>> A(:,3)=[]
A =
8 1
4 9


위에서 [ ] 명령은 공행렬(empty matrix)을 생성하는데 이는 크기가 0인 행렬을 뜻한다. 예를 들어 행렬 A에서 2행을 없앤 행렬을 B에 대입하려면 다음과 같이 하면 될 것이다.


>> A=pascal(3) 󰎠
A =
1 1 1
1 2 3
1 3 6

>> B=A([1 3],:) 󰎠
B =
1 1 1
1 3 6


공행렬과 영행렬(zero matrix)은 구별해야 하는데 공행렬은 크기가 0인 행렬을 의미하며 MATLAB에서는 ‘[]’로 생성한다. 영행렬은 행렬의 모든 요소가 0인 행렬을 의미하며 zeros()함수를 이용하면 생성할 수 있다.


 find(X)함수는 입력행렬 X의 요소 중 0이 아닌 것의 인덱스를 반환한다. 즉, 행렬의 0이 아닌 요소의 인덱스를 찾는 함수가 find()함수이다. (help find󰎠명령으로 확인해 보라.) 이 함수의 반환값은 하나, 둘 그리고 세 개가 될 수 있다.


>> X=[0 1 3; 3 0 4; 0 5 0]
X =
0 1 3
3 0 4
0 5 0

>> m=find(X)
m =
2
4
6
7
8


위의 결과를 이해하려면 MATLAB에서 이차원 행렬의 요소들은 내부적으로 일차원 배열로 저장됨을 이해해야 한다. 그 일차원 배열의 0이 아닌 요소의 인덱스를 find()함수가 만약 출력변수가 한 개라면 반환하는 것이다. 위의 경우 행렬 X의 내부 인덱스를 도시하면 다음 그림과 같다.


[그림 2] X행렬의 내부 요소 인덱스


이 그림을 보면 앞의 예제에서 find()함수의 결과값이 이해가 갈 것이다.




Posted by 살레시오

 MATLAB에서 벡터의 원소를 참조하거나 벡터의 원하는 위치에 값을 설정하려고 할때는 아래와 같이 한다.


>> b = a(k)
>> a(k) = b


위에서 첫 번째 명령은 a벡터의 k번째 요소를 변수b에 저장하라는 것이고 두 번째 명령은 b변수값을 a벡터의 k번째 자리에 저장하라는 것이다. 여기서 a벡터는 행벡터이든 열벡터이든 동일하게 적용된다. 아래의 예를 보자


>> a=[8:-2:2]’
a =
8
6
4
2

>> b=a(3)
b =
4

>> a(2)=0
a =
8
0
4
2


 행렬의 한 원소에 접근하여 원하는 위치에 값을 읽어오거나 바꿀려고 할 때는 아래와 같이 괄호 안에 행 수와 열 수를 콤마(,)로 구별하여 지정한다.


>> B = A(k1,k2)
>> A(k1,k2) = B


즉, 행렬 A의 k1행 k2열 원소를 참조하고자 할 때 A(k1,k2)로 입력한다.


>> A = [ 1 2 3; 4 5 6; 7 8 9 ]
A =
1 2 3
4 5 6
7 8 9
>> A(1,2) 󰎠 % 1행 2열 원소
2
>> A(1,3) 󰎠 % 1행 3열 원소
3
>> A(3,3) 󰎠 % 3행 3열 원소
9


만약 어떤 행이나 어떤 열의 모든 원소를 선택하고자 하면, 선택하고자 하는 해당 열이나 행을 지정할 때 콜론(:)을 단독으로 사용한다. 행렬의 지정자로 콜론(:)이 단독으로 쓰이면 ‘전부(all)’라는 의미를 갖는다. 다음 예를 보자.


>> A(1,:) 󰎠 % 1행의 모든 원소
1 2 3

>> A(:,3) 󰎠 % 3열의 모든 원소
ans =
3
6
9


여기서 A(1,:)는 ‘1행, 전체’를 가리키고 A(:,3)은 ‘3열,전체’를 가리킨다.


행이나 열의 특정한 부분만 선택하고자 하면 선택하고자 하는 해당 열이나 행를 벡터로 지정해주면 된다. 예를 들어


>> A([1 3], :) %1행과 3행만 선택
1 2 3
7 8 9
>> A(:, [2 3]) %2열과 3열만 선텍
2 3
5 6
8 9
>> A([1 3], [2 3])
2 3
8 9


위에서 마지막 예제의 결과는 다음 그림을 보면 쉽게 이해할 수 있다.


[그림 1] 선이 겹치는 부분이 선택된다.

A행렬에서 지정한 행과 열이 겹치는 곳에 위치한 요소만 선택이 되어진다는 것을 [그림 1]을 보면 쉽게 이해할 수 있다. 즉 [그림 1]에서 1행과 3행에 가로선을 그리고 2열과 3열에 가로선을 그어보면 선들이 겹치는 부분만 선택되는 것이다.


 A([1 3], :) 명령어를 읽을 때 ‘A행렬의 1행 전체, 3행 전체’라고 읽으면 정확하고 결과값도 1행 밑에 3행이 위치한다. 그런데 다음 예제롤 하나 더 보고 넘어가자.


>> A([3 1], :) %3행과 1행만 선택
7 8 9
1 2 3
>> A(:, [3 2]) %3열과 2열만 선텍
3 2
6 5
7 8


A([3 1], :)명령어와 그 결과를 보면 ‘3행 먼저, 그 다음 1행’순으로 인덱스 벡터에 써준 순서대로 요소를 뽑아낸다는 것을 알 수 있다. A(:, [3 2])도 마찬가지로 ‘3열 먼저. 그 옆에 2열’이라는 것을 알 수 있다.




Posted by 살레시오

 여기에서는 이전 포스트에 이어서 기본적인 행렬 생성의 기본적인 내용을 설명하겠다. 행렬 X가 다음과 같이 복소행렬(행렬의 요소 중 하나 이상이 복소수인 행렬)이라고 하자.

아래와 같이 입력하면 화면에 행렬 X가 나타난다. j 대신 i가 쓰일 수도 있다.


>> X=[1 j;-5j 2]
X =
  1 + 0i   0 + 1i
 -0 - 5i   2 + 0i


위와 같이 MATLAB에서 허수를 나타내기 위해서 ‘i’ 혹은 ‘j'를 사용한다. 그러므로 변수 이름으로 ‘i’와 ‘j'를 사용하지 않아야 한다.


>> i
0.0000 + 1.0000i
>> a = 2 + 3i
a =
2.0000 + 3.0000i


수식 입력의 편의를 위해서 복소수를 입력할 때에는 위에서와 같이 ‘3i’를 입력해도 가능하다. 그러나 i 가 허수단위가 아닐 때에는 불가능하며 앞에서도 설명한 바 있듯이 'i3'으로 입력하면 안된다는 것이 유의하자. i3은 변수명으로 해석하기 때문이다.

 만약 ‘i’를 행렬 이름으로 (즉, 변수로) 사용했을 때는 그 행렬이 존재하는 한 ‘i’는 행렬 이름이며 허수가 아니다.


>> i = [1 2 3] 󰎠
i =
1 2 3
>> a = 10 + i 󰎠
a =
11 12 13


‘i’를 다음과 같이 지우면 다시 허수로 사용할 수 있다.


>> clear i 󰎠
>> i 󰎠
0.0000 + 1.0000i


여기서 clear 명령어는 그 뒤에 오는 변수를 메모리에서 지우는 일을 수행한다. 만약  ‘i’가 행렬 변수로 사용되고 있을 때는 ‘sqrt(-1)’을 이용하여 허수를 만들 수 있다.


>> i = [1 2 3]
i =
1 2 3

>> i
1 2 3

>> a = [1+i 1-i] % 'i'는 행렬로 인식된다.
a =
2 3 4 0 -1 -2

>> a = [1+sqrt(-1) 1-sqrt(-1)]
a =
1.0000 + 1.0000i 1.0000 - 1.0000i


하지만 ‘i’ 나 ‘j’는 MATLAB에서는 일종의 예약어(keyword)이므로 변수명으로 사용하는 것은 피하는 것이 바람직하다.


 MATLAB에서 복소수를 취급하는 기본적인 함수들은 다음 표와 같다.


[표 1] 복소수 관련 내장 함수들

함수명

기능

실행 예

(a=3+4j 일 경우)

결과

real()

복소수의 실수부를 구한다

real(a)

3

imag()

복소수의 허수부를 구한다

imag(a)

4

abs()

복소수의 크기를 구한다

abs(a)

5

angle()

복소수의 각(라디안)을 구한다

angle(a)

0.9273

conj()

켤레복수소를 구한다

conj(a)

3-i4


 만약 각도의 결과를 라디안에서 도(degree)로 바꾸고 싶다면 angle()함수의 결과값에 360/2π 를 곱하면 된다.


>> angle(3+4i)
ans =
0.9273
>> ans*360/(2*pi)
ans =
53.1301


 복소행렬의 전치를 구할 때는 약간의 주의가 필요하다. 복소전치행렬(complex conjugate transpose)을 구할 수도 있고 그냥 전치행렬을 구할 수도 있다. 복소전치행렬은 각 행렬 요소들의 공액 복소수(complex conjugate, 허수부의 부호만 바뀐 복소수)를 구한 다음 전치를 하는 것이다. 이전 포스트에서 설명한 (‘)연산자가 실은 복소전치행렬을 구하는 연산자이다. 다음 예에서 이것을 확인할 수 있다.


>> X=[1 j;-5j 2]
X =
  1 + 0i   0 + 1i
 -0 - 5i   2 + 0i

>> Y=X'
Y =
  1 - 0i  -0 + 5i
  0 - 1i   2 - 0i


이와 다르게 행렬의 복소수 요소의 공액복소수를 취하지 않고 단순히 비대각요소의 위치만 전치하고 싶다면 (.‘)연산자를 사용한다.


>> Z=X.'
Z =
  1 + 0i  -0 - 5i
  0 + 1i   2 + 0i


 두  전치연산자 (‘)와 (.’)의 차이에 주의해야 한다.



Posted by 살레시오

 여기에서는 이전 포스트에 이어서 기본적인 행렬 생성의 기본적인 내용을 설명하겠다.

함수의 행렬

 행렬을 입력하는 또 다른 예로, 아래와 같이 주어진 행렬 C를 생각해보자.

위 행렬은 다음과 같이 입력될 수 있다.


>> C = [1 exp(-0.02); sqrt(2) 3]; 󰎠
>> C 󰎠
C =
1.0000 0.9802
1.4142 3.0000


여기서 exp(x)함수는 ex 값(exponetial)을 구하는 함수이고, sqrt(x)함수는 제곱근(square root)을 구하는 함수이다. exp(x)함수나 sqrt(x)함수의 용법이 궁금하다면 help 명령어를 사용하면 된다. help exp 명령을 내리면 exp()함수에 대한 기본 설명, 유사한 함수들 그리고 오버로드(overload)된 함수들에 대한 설명이 나오는 것을 알 수 있다. 유사한 함수들의 목록에 expm()함수가 있는데 함수명 끝의 m은 이 함수가 행렬연산을 한다는 것을 나타내며 스칼라 연산 함수들은 보통 대응하는 행렬 연산 함수를 가지고 있다.


※ 스칼라 연산이란 예를 들어 A=[1 2; 3 4]이고 exp(A)를 수행하면 결과값이 [exp(1) exp(2); exp(3) exp(4)]와 같이 행렬 각 요소에다가 함수값을 취하는 겻을 의미한다. 이와 달리 행렬 연산 함수 expm(A)는 다음과 같은 Taylor급수 정의식에 의한 결과값을 계산하게 된다.


[그림 1] Octave 4.0에서 help exp 명령을 수행한 결과


전치 행렬 (transpose)

 어떤 행렬의 전치행렬(transpose)을 만드는 방법은 행렬의 끝에 작은따옴표 (‘)를 붙이는 것이다. 예를 들어서 C행렬의 전치행렬을 만들어 D에 입력하고 싶다면 다음과 같이 한다.


>> D=C'
D =
1.0000 1.4142
0.9802 3.0000


어떤 행렬의 대각 요소(diagonal element)란 행 인덱스와 열 인덱스가 같은 요소를 그리고 비대각요소(off-diagonal element)는 같지 않은 요소를 의미한다. 위의 예에서 행렬 C의 비대각요소의 위치가 대칭적으로 바뀌어서 D에 저장되었음을 알 수 있다.

 또는 행렬을 입력할 때 마지막에 (‘)을 추가하면 입력되는 행렬의 전치행렬이 바로 구해진다.


>> D=[1 exp(-0.02); sqrt(2) 3]'
D =
1.0000 1.4142
0.9802 3.0000


이와 같이 전치 행렬은 행렬의 끝에 작은 따옴표로 쉽게 구해진다.



Posted by 살레시오