레이블이 행렬인 게시물을 표시합니다. 모든 게시물 표시
레이블이 행렬인 게시물을 표시합니다. 모든 게시물 표시

2015년 7월 6일 월요일

넘파이(numpy) 배열(ndarray)와 행렬(matrix)객체 비교

  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 모듈을 사용하는 것이 권장되지 않는 것이다.


2015년 6월 27일 토요일

MATLAB의 행렬 간 논리 연산과 관련 함수

 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


MATLAB의 행렬 간 관계 연산

 관계 연산이나 논리연 산에서는 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