넘파이(numpy)를 사용하기 위해서는 다음과 같이 import 해야 한다. winpython, python(x,y)에는 기본적으로 포함되어 있으므로 바로 사용할 수 있다. import 명령을 이용하면 되는데 다음과 같이 몇 가지 방법이 있다.


>>> import numpy # 넘파이의 모든 객체를 numpy.obj 형식으로 불러 사용할 수 있다.
>>> import numpy as np  # 넘파이의 모든 객체를 np.obj 형식으로 불러 사용할 수 있다.
>>> from numpy import * # 넘파이의 모든 객체를 내장 함수 (객체) 처럼 사용 가능.

 

넘파이는 ndarray (n-dimensional array 의 약어, 이후 배열) 라는 객체를 사용하는데 이는 리스트와 유사하지만 다차원 데이터의 연산에 좀 더 효율적이라고 한다. 리스트는 모든 형식의 객체를 저장할 수 있지만 배열은 한 행의 요소는 모두 같은 자료형어야 한다.

배열 생성

 배열을 입력하는 가장 기본적인 함수는 array() 이다. 첫 번째 인자로 리스트를 받는데 이것으로 array객체를 생성한다.

>>> np.array( [1, 2, 3] ) #1차 배열 (벡터) 생성
>>> np.array( [ [1, 2, 3], [4, 5, 6] ) # 2x3 크기의 2차원 배열 (행렬) 생성

일반적으로 3차원, 4차원, ... , n차원 배열도 중첩리스트를 이용하여 유사하게 생성할 수 잇다.

 파이썬의 내장 함수인 range()와 유사하게 배열 객체를 반환해주는 arange() 함수가 있다.

>>> np.arange(10) # 1차원 배얼 [0,1,2, ... ,9] 생성


그리고 시작점과 끝점을 균일 간격으로 나눈 점들을 생성해주는 linspace()함수도 있다.


>>> c = np.linspace(0, 1, 6)   # start, end, num-points
>>> c
    array([ 0. , 0.2, 0.4, 0.6, 0.8, 1. ])

>>> d = np.linspace(0, 1, 5, endpoint=False)
>>> d
    array([ 0. , 0.2, 0.4, 0.6, 0.8])

생성된 배열은 reshape() 함수를 이용하여 행수와 열수를 조절할 수 있다.

>>> a = np.arange(10) # 1차원 배열 [0,1,2, ... ,9] 생성
>>> a = a.reshape(2,5) # 같은 요소를 가지고 2x5 배열로 변형


특수 행렬 생성

 넘파이에는 특수 행렬을 생성하기 위한 함수들이 있다. zeros(), ones() 라는 함수들은 각각 0과 1로 채원진 배열을 생성하는데 차수를 정수 혹은 튜플로 받는다. 예를 들어서 0으로 채워진 5x5 배열을 만들고 싶다면 다음과 같이 한다.

>>> b1 = np.ones( 10 ) # 1로 채원진 10 크기의 (1차원) 배열 생성
>>> b2 = np.zeros( (5,5) ) # 0으로 채원진 5x5 크기의 배열 생성


대각행렬을 생성하기 위한 eye() 함수와 diag() 함수도 있다. eye() 함수는 항등행렬을 생성하고 diag()는 주어진 정방행렬에서 대각요소만 뽑아내서 벡터를 만들거나 반대로 벡터요소를 대각요소로 하는 정방행렬을 만들어 준다..

  

         정의 : eye(N, M=, k=, dtype=), M은 열 수, k는 대각 위치(대각일 때가 0), dtype은 데이터형


>>> np.eye(2, dtype=int)
    array([[1, 0],
           [0, 1]])

>>> np.eye(3, k=1)
    array([[ 0.,  1.,  0.],
           [ 0.,  0.,  1.],
           [ 0.,  0.,  0.]])


         정의:  diag(v, k=0), v는 array_like, k는 대각 위치


>>> x = np.arange(9).reshape((3,3))
>>> x
    array([[0, 1, 2],
           [3, 4, 5],
           [6, 7, 8]])
>>> np.diag(x)
    array([0, 4, 8])
>>> np.diag(x, k=1)
    array([1, 5])
>>> np.diag(x, k=-1)
    array([3, 7])
>>> np.diag(np.diag(x))
    array([[0, 0, 0],
           [0, 4, 0],
           [0, 0, 8]])

 

이에 반해서 empty() 라는 함수는 크기만 지정해 두고 각각의 요소는 초기화 시키지 않는다. 따라서 이 함수로 생성된 배열의 요소는 가비지값이 채워져 있다.


>>> c = np.empty( (2,2) ) # 가비지 값으로 채원진 2x2 크기의 배열 생성



Posted by 살레시오

댓글을 달아 주세요

  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 살레시오

댓글을 달아 주세요

 넘파이에는 ndarray 라는 객체가 있는데 레퍼런스에는 그냥 배열(array)로 지칭한다. 아마 파이썬의 기본 객체인 리스트, 튜플, 딕셔너리 와 이름이 겹치지 않으므로 혼동의 여지가 없어서 그런 것 같다. 앞으로도 그냥 배열이라고 하면 numpy.ndarray 객체를 지칭하는 것으로 하겠다.


>>> import numpy as np

이 객체를 생성하는 대표적인 함수가 array() 함수인데 첫 번째 입력인수가 array-like 객체이다. array-like 객체는 다음과 같은 것들이 있다.

 

  • 리스트 (list)

  • 튜플 (tuple)

  • 배열 (ndarray) - 배열 자체도 array()함수의 첫 번째 인자로 올 수 있다.

  • 행렬 (matrix)


예를 들면 다음과 같다.


>>> np.array([1, 2, 3])
     array([1, 2, 3])

>>> np.array([1, 2, 3.0]) # upcasting
    array([ 1.,  2.,  3.])

>>> np.array([[1, 2], [3, 4]]) #More than one dimension:
    array([[1, 2],
          [3, 4]])
>>> np.array([1, 2, 3], ndmin=2) # Minimum dimensions 2
    array([[1, 2, 3]])

>>> np.array([1, 2, 3], dtype=complex) # Type provided:
    array([ 1.+0.j,  2.+0.j,  3.+0.j])

>>> np.array(np.mat('1 2; 3 4')) # Creating an array from sub-classes:
    array([[1, 2],
           [3, 4]])

>>> np.array(np.mat('1 2; 3 4'), subok=True)
    matrix([[1, 2],
            [3, 4]])

 넘파이로 코딩하면서 직접 확인한 몇 가지 사실들을 아래에 기록해 보았다. (혹시 오류가 있다면 댓글을 남져주시길 부탁드립니다.)


스칼라 상수(변수)로 생성된 배열은 0차 배열 이다.

스칼라로 배열을 생성할 경우 배열 객체의 차수는 0차이다. 즉, 스칼라는 0차 배열이다. shape은 empty tuple을 가지고 size는 1이다.


>>> x= 10
>>> a = np.array(x)
>>> a.ndim, a.shape, a.size
     (0, (), 1)


0차 배열이라는 용어가 생소하겠지만 배열에 스칼라도 포함된다는 것이 더 논리적인 것 같다. 만약 단일 수에서 1차 배열을 생성하려면 다음과 같이 리스트(혹은 튜플)로 만들어서 넘겨야 한다.

>>> a = np.array( [x] ) # 혹은 a = np.array( (x,) )
>>> a.ndim, a.shape, a.size
     (1, (1,), 1)


이것은 요소가 한 개인 1차 배열이 된다. 즉 np.array(10) 과 np.array([10])은 엄연히 구별된다. 전자는 0차 배열, 후자는 1차 배열이다.


2차 배열에서 크기가 다른 행은 리스트다.

이것은 예를 들어서 다음과 같은 배열 a와 b를 고려해 보자.


>>> a = np.array( [ [1,2], [3,4] ] )
>>> b = np.array( [ [1,2], [3,4,5] ] )


두 변수는 모두 배열이다. 하지만 배열 a의 각 요소 a[0] 과 a[1] 은 type 이 배열인 반면 b[0], b[1] 은 리스트이다. 즉, 변수 a 는 array of array 이고 변수 b는 array of list 이다. 따라서 a[0] 은 배열의 모든 속성을 사용할 수 있지만 b[0]는 리스트이기 때문에 그럴 수 없다.


 만약 크기가 다른 행들도 모두 배열로 만들고 싶다면 다음과 같이 하면 된다.


>>> a = np.array( [ np.array([1,2]) , np.array([1,2,3]) ] )


이것은 array of array 가 된다.


리스트와 배열의 연산 결과는 배열이다.

 리스트끼리의 산술 연산은 정의되어 있지 않으나 리스트와 배열 상호간 연산은 요소간 연산이 적용된다. 그리고 그 결과도 배열이다.


>>> [1,2,3]/2 # 에러가 발생한다.
>>> [1,2,3]/np.asarray([2])
    array([ 0.5,  1. ,  1.5])


위의 두 번째 명령의 결과를 보면 연산 결과도 배열임을 알 수 있다.



Posted by 살레시오

댓글을 달아 주세요

 넘파이의 ndarray(이하 배열)을 슬라이싱할 때 파이썬의 리스트(list)와 다르게 원본의 참조가 생성되는데 이를 뷰(view)라고 한다. 예를 들어서 다음과 같이 2차 배열 arr을 고려하자.


>>> arr=np.arange(8).reshape(2,4)
>>> arr
array([[0, 1, 2, 3],
      [4, 5, 6, 7]])


단순 대입문을 사용하여 arr2 = arr 이라는 명령은 arr 의 참조본이 arr2에 저장된다. 따라서 arr이 수정되면 arr2 도 같이 바뀐다. 이것은 리스트의 동작방식과 같다.

 그런데 arr[0] 라고 슬라이싱하면 첫 번째 행 전체가 선택된다.


>>> b=arr[0]
>>> b
array([0, 1, 2, 3])


여기서도 마찬가지로 b 배열은 arr 배열의 첫 행에 대한 복사가 아니고 뷰(참조의 개념)이기 때문에 원본이 바뀌면 이 참조본도 따라서 바뀌게 된다.

>>> arr[0]=10
>>> arr
array([[10, 10, 10, 10],
      [ 4,  5,  6,  7]])
>>> b
Out[127]: array([10, 10, 10, 10])


이 예에서 보면 arr 배열의 첫 행을 모두 10으로 바꾸었다. 배열의 슬라이싱에 스칼라를 대입하면 그 범위의 전체 요소에 대입된다. 그런데  b 배열도 같이 바뀐 것을 알 수 있다. 이는 리스트의 슬라이싱에서는 복사본이 생성되는 것과는 다른 동작이기 때문에 주의가 필요하다.  numpy는 대용량 데이터 처리를 염두에 두고 설계되었다. 대용량 데이터의 슬라이싱이 빈번하게 일어나는 복잡한 코드를 실행시키는데 있어서 복사가 남발되면 메모리 문제를 일으킬 소지가 많기 때문에 이렇게 설계된 것으로 짐작된다.


  만약 슬라이싱의 복사본을 생성하고 싶다면 copy() 속성을 이용하면 된다.


>>> b=arr[0].copy()
>>> b
array([0, 1, 2, 3])

이제 b 배열은 원본인 arr 과 독립적인 복사본이므로 원본이 바뀌어도 아무런 영향을 받지 않는다.

  비슷한 차이가 numpy.asarray() 함수와 numpy.array() 함수에도 존재하는데 두 함수의 동작은 거의 유사하다. 가장 큰 차이점은 copy 옵션의 초기값이 다르다는 것이다. 사실 asarray()는 내부적으로 다음과 같이 정의되어 있다.

def asarray(a, dtype=None, order=None):
   return array(a, dtype, copy=False, order=order)


참고로 array() 함수의 정의부는 다음과 같다.


numpy.array(object, dtype=None, copy=True, order=None, subok=False, ndmin=0)


따라서 만약 배열이나 행렬객체를 이용하여 다시 생성된 배열은 numpy.array()함수를 이용하였다면 복사본이, numpy.asarray()함수를 이용하였다면 참조본(전체 뷰)이 생성된다.


>>> arr = np.ones((3,4))
>>> arr
array([[ 1.,  1.,  1.,  1.],
      [ 1.,  1.,  1.,  1.],
      [ 1.,  1.,  1.,  1.]])
>>> arrB = np.asarray(arr) # 참조본 생성
>>> arrC = np.array(arr) #복사본 생성
>> arr[1]=np.pi # 원본 변경
>>> arr
array([[ 1.        ,  1.        ,  1.        ,  1.        ],
      [ 3.14159265,  3.14159265,  3.14159265,  3.14159265],
      [ 1.        ,  1.        ,  1.        ,  1.        ]])
>>> arrB # 참조본은 자동으로 변경된다.
array([[ 1.        ,  1.        ,  1.        ,  1.        ],
      [ 3.14159265,  3.14159265,  3.14159265,  3.14159265],
      [ 1.        ,  1.        ,  1.        ,  1.        ]])
>>> arrC # 복사본은 변경되지 않는다.
array([[ 1.,  1.,  1.,  1.],
      [ 1.,  1.,  1.,  1.],
      [ 1.,  1.,  1.,  1.]])



Posted by 살레시오

댓글을 달아 주세요

 넘파이 배열(ndarray) 의 팬시 색인은 정수 리스트(혹은 배열)를 이용하여 여러 개를 동시에 선택하는 방식이다. 인덱싱은 항상 0으로 시작한다는 것에 유의하자. MATLAB/SCILAB/OCTAVE 에 익숙한 사용자는 인덱스가 0에서 시작된다는 것에 주의해야 한다. 이후 예제에서는 넘파이가 다음과 같이 임포트되었다고 가정한다.

>>> import numpy as np

 예를 들어서 다음과 같은 a 행렬을 고려하자.


>>> a=np.arange(24).reshape((6,4))
>>> a
array([[ 0,  1,  2,  3],
      [ 4,  5,  6,  7],
      [ 8,  9, 10, 11],
      [12, 13, 14, 15],
      [16, 17, 18, 19],
      [20, 21, 22, 23]])

여기서 특정한 행들을 선택하고 싶다면 원하는 순서대로 행번호를 적은 리스트나 배열로 인덱싱하면 된다.

>>> a[[3,0,5]]
array([[12, 13, 14, 15],
      [ 0,  1,  2,  3],
      [20, 21, 22, 23]])

>>> a[[-1,-3,2]]
array([[20, 21, 22, 23],
      [12, 13, 14, 15],
      [ 8,  9, 10, 11]])


 한 가지 주의할 점은 슬라이싱과 다르게 이런 식으로 반환되는 배열은 복사된 것이라는 점이다. 즉, 원본과 연결되지 않은 복사 객체가 반환된다. 따라서 원본의 갱신과 무관하게 된다.

 또 한 가지 유의할 점이 있는데 다음과 같이 다중으로 인덱싱을 할 경우이다.

>>> a[:,[2,3]] #(1)
array([[ 2,  3],
      [ 6,  7],
      [10, 11],
      [14, 15],
      [18, 19],
      [22, 23]])

>>>  a[[0,1],[2,3]] # (2)
array([2, 7])


여기서 (1)에서와 같이 한 쪽이 모든 요소를 나타내는 콜론(:) 이 쓰인 경우는 해석이 쉽다. 즉, a 배열의 0번 열과 1번 열을 골라내는 것이다. 그런데 (2)의 경우는 MATLAB에 익숙하던 사용자라면 좀 의아할 것이다. 다음 표와 같이 0행, 1행과 2열, 3열이 겹치는 부분인 2x2 행렬이 반환된다고 오해하기 쉽기 때문이다.

2열

3열

0행

0

1

2

3

1행

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

실제 동작은 (0,2) 요소와 (1,3)요소 두 개를 반환한다.

 MATLAB과 같이 그리드 점에 있는 요소들의 집합인 2x2 행렬을 반환하게 하려면 np.ix_() 함수에 팬시 색인을 넘겨주어야 한다.

>>> a[np.ix_([0,1],[2,3])]
array([[2, 3],
      [6, 7]])


이것은 사실 a[ [0,1] ][ :, [2,3] ] 과 같은 동작을 취한다.

 그리고 a[1,2] 와 a[1][2] 는 같은 요소를 가리킨다.

>>>  a[1,2]
6

>> a[1][2]
6


하지만 이것으로 두 인덱싱이 같은 표현법이라고 오해하면 안 된다. 사실은 전혀 다르기 때문이다. 예를 들면 다음과 같다.

>>> a[:,2] # 2번 열만 뽑아낸다.
array([ 2,  6, 10, 14, 18, 22])

>>> a[:][2] # 이것은 a[2]와 같다.
Out[41]: array([ 8,  9, 10, 11])

>>> a[ [-1,-2], 1 ]
array([21, 17])

>>> a[[-1,-2]][1]
array([16, 17, 18, 19])


마지막 예에서 a[[-1,-2]][1] 은 a[[-1,-2]] 행렬의 1번째 행을 뽑아내는 것이다. a[[-1,-2], 1] 과는 완전히 다르다.




Posted by 살레시오

댓글을 달아 주세요

  라즈베리파이(RPi)에서 수치 해석툴을 적용하려고 scilab 과 octave의 장점을 검색해 보았더니 둘 다 장점이 비슷한 것 같다. 어떤 논문에서는 octave를 최고로 언급하기도 한다.


"요약하면, Octave가 가장 지속 가능한 Matlab의 대안이라고 결론을 내렸다. 그 이유는 테스트 결과 (문법적인 측면에서) Matlab과 완전히 호환되며 성능 또한 상당히 좋기 때문이다"


"In summary, we conclude that Octave is the best viable alternative to Matlab because it was not only fully compatible (in terms of syntax) with Matlab in our tests, but it also performed very well."



 그런데 수치해석 툭들을 검색 하는 중에 의외의 대안이 눈에 띄었다. 바로


  • python + numpi / scipy + matplotlib


이다. 어떤 교수가 이렇게 언급해 놓았다.


Francesca Mazzia · Università degli Studi di Bari Aldo Moro


"내 수업(이탈리아 Bari 대학교)에서  Octave, Scilab, R 을 모두 사용해 보았다지만 지금은 파이썬의 넘파이(numpy)와 사이파이(scipy)를 사용한다. 과학 계산에서 pythonxy(윈도우)와 spyder(리눅스)는 훌륭한 통합개발환경이다. 나는 내 포트란 90 코드와의 인터페이스를 개바랬는데 Matlab보다 훨씬 더 쉬었다. 아직도 연구를 위해서 Matlab을 사용하기는 하지만 파이썬은 훌륭한 대안이다. 내 학생들도 좋아한다..." 2013년 1월 31일


"I used in my courses (University of Bari, Italy), Octave, Scilab and R, but now I'm using Python, with numpy and scipy. Pythonxy in Windows and spyder for Linux are good ide for scientific computing. I developed some Python interface for my FORTRAN 90 codes and was easier than in matlab. I still use Matlab for research, but I think Python is a good alternative, my students like it ...." Jan 31, 2013


  이후에 관심이 생겨서 더 조사해 보았는데 python이 과학계산 분야에서 의외로 광범위하게 사용되고 있음을 알게 되었다. Matlab은 앞으로는 어떻게 될 지 모르겠지만(뭐 윈도우도 무료가 되느니 마느니 하는 시대인데) 아직은 고가라 학부 수업에서 사용하기에는 부적합하다.


 개인적으로 scilab은 익히는데 상당히 시간이 많이 걸렸다. (한마디로 제대로 사용하려면 어렵다.) 하지만 python은 그 특유의 쉽고 간결한 문법과 많은 리소스들, 다양한 모듈들로 인해서 훨씬 익히기가 용이했다.


  단적인 예를 들어서 아두이노로 시리얼 통신을 하는 프로그램(모터의 지령지를 준다든가 센서값을 읽어들인다든가 하는 일들을 하기 위해서)을 작성하기 위해서는 시리얼통신을 수행하는 모듈이 있어야되는데 scilab은 마땅한게 없다. (matlab도 어렵기는 마찬가지임.) 하지만 파이썬은 잘 동작하는 모듈이 있어서 바로 사용하는데 아무런 문제가 없었다. 이 정도로 막강하다. 더군다나 numpy같은 수치해석 모듈과 scipy 같은 과학계산 모듈을 모두 무료로 아무 제한 없이 사용할 수 있으니 더 이상 큰 장점이 없다.


  만약 수치해석 툴이 필요하다면 파이썬을 공부해 볼 것을 강력히 추천한다.

[#00089]


Posted by 살레시오

댓글을 달아 주세요

 파이썬에 대해서 웹서핑을 하다가 파이썬으로 과학 계산을 하는 분야에 대한 좋은 발표 형식의 글이 있어서 번역해 보았다. 단순히 파이썬을 쉽고 배우기 쉬운 범용 프로그래밍 언어라고만 알고 있다면 흥미로운 주제글일 것이다.(원문)


 과학 계산에 사용되는 툴(소프트웨어)와 작업 흐름. 왜 파이썬인가?


1. 과학자(공학자도 포함)의 요구

  • (모의실험, 실제 실험 제어에서 얻어진) 데이터 취득

  • 데이터의 조작, 가공

  • (작업 결과의 이해를 돕기 위한) 시각화

  • 보고서, 출판, 발표물 제작등의 의사소통 결과물들


2, 조금 더 구체적인 요구사항들 (역자 주: 원필자가 다분히 파이썬을 의식하고 나열한 것 같음.)


  • 고전적인 수치 해석 방법이나 기본적인 기능에 대한 풍부한 라이브러리: 그래프를 그리거나 푸리에변환이나 보간법 알고리듬 같은 것을 다시 직접 짤 필요가 없다. 바퀴를 다시 발명할 필요가 없듯이.

  • 배우기 쉬워야 한다 : 전산이나 컴퓨터 과학을 전공하지 않은 사람이라도 몇 분안에 그래프를 그리거나 신호를 필터링하거나 푸리에 변환 결과를 볼 수 있어야 한다.

  • 동 료, 학생, 고객와 의사소통이 쉬워야 한다. 즉 범용성이 확보되어야 한다. (역자 주:내가 작성한 코드를 그들도 알고 있거나 단기간에 배워서 쓸 수 있어야 한다.) 프로그래밍 언어 자체가 가능하면 적은 문법 기호와 불필요한 루틴을 가지지 말아야 한다. 그래서 코드를 읽어나가는데 수학적/과학적인 부분에 집중할 수 있어야 한다.

  • 신속하게 실행 가능해야 하며 기왕이면 고속으로 동작해야 한다. (...)

  • 가능하다면 거의 모든 경우에 있어서 단일 환경/단일 언어를 사용해야 한다. 문제가 달라질 때 새로운 언어나 툴을 다시 익혀야 한다면 곤란한다.


현존하는 해결책들 (프로그램들) : 과학자들은 어떤 툴로 작업하는가?


1. 컴파일러 언어: C, C++, Fortran, 등등

  • 장점

    • 컴파일러가 최적화되어 있으며 매우 빠르다. 계산량이 매우 많을 때 이러한 언어들의 성능을 능가하는 대안이 없음.

    • 고도로 최적화된 과학 라이브러리 (역자 주:주로 행렬연산 등 수치해석에 사용되는) 가 존재함. 예를 들어서 BLAS 등

  • 단점

    • 사용하기 매우 어렵다. (역자 주: 이게 가장 큰 장애물임. C/C++를 웬만큼 안다고 해도 이러한 라이브러를 익숙하게 사용하기는 어려움) 특히나 전산 전공이 아닌 사람들에게는 이러한 언어들로는 답이 없음.

 

2. 상용 스크립팅 언어 : 매트랩(Matlab), 매쓰매티카(mathematica), 매이플(maple) 등

  • 장점

    • 다양한 영역의 수많은 알고리듬을 구현한 풍부한 라이브러리. 보통 이런 라이브러리는 컴파일되어 있기 때문에 실행속도도 빠르다.

    • 잘 갖춰진 개발환경 : 잘 정리된 도움말, 편리한 IDE 등등

    • 상업적인 지원이 가능함.

  • 단점

    • 기본적인 언어가 다소 부실하다.(역자 주: 문법 자체가 잘 정돈되어 있지 않음에도 불구하고 하위 호환서을 유지하기 위해서 개선이 되지 않고 있음.)

    • 비싸다. (역자 주: 가장 큰 단점임. 특히 학생들에게)


3. 대안 스크립팅 언어들 : Scilab, Octave, R, Igor, IDL, etc.


  • 장점

    • 오픈소스, 대부분 무료이거나 matlab보다는 저가임.(역자주: Igor와 IDL이 상용프로그램임. Igor는 그래픽 성능이 뛰어나며 IDL은 천문학과 의학 영상 분야에서 많이 사용된다고 함.)

    • 어떤 기능은 매우 특화되어 있음 : 예를 들어서 R의 통계분석, Igor의 그래프 기능 등. (역자 주 : Scilab은 제어분야와 수치해석에, Octave는 matlab과 거의 유사한 문법을 가지는 특징이 있다.)

  • 단점

    • matlab 보다는 (당연히) 라이브러리가 빈약함. 사용되는 언어 자체도 크게 발전적이지 않음.

    • 어 떤 소프트웨어는 한 분야에만 사용 가능함 : 예를 들어서 gnuplot 이나 xmgrace 는 매우 강력한 기능을 가졌지만 그래프 기능으로만 한정되어 있다. (역자 주: 많은 오픈소스 수치 해석 프로그램이 gnuplot 을 채용하지만 scilab은 matlab과 유사한 독자적인 그래프 기능을 가지고 있다.)


4. 파이썬(python)의 경우는 어떠한가?

  • 일반적인 특성

    • 파이썬은 일반적이고 현대적인 컴퓨팅 언어.

    • 표준 라이브러리 모듈

    • 파이썬으로 작성된 많은 수의 특화된 모듈이나 어플리케이션 : 웹 분야 등등 그리고 과학 계산 분야

    • 개발 도구들 (자동 시험, 문서 생성기)Python language: data types (string, int), flow control, data collections (lists, dictionaries), patterns, etc.

  • 장점

    • (matlab보다는 부족하지만) 매우 풍부한 과학 계산 라이브러리들

    • 잘 고안된 언어, 가독성이 뛰어나고 구조화가 잘 되어 있어서 "생각한 대로 코딩"할 수 있다.

    • 과학 계산 이외 영역에도 다양한 라이브러리가 있다. : 웹서버 관리, 시리얼포트 접근 등등

    • 무료이고 오픈소스 언어이다. 광범위하게 사용되고 활발한 사용자그룹들이 있다.

  • 단점

    • (matlab에 비하면) 다소 불편한 개발 환경.

    • 좀 더 세분화된 전문 영역에서는 필요한 알고리듬이 없을 수도 있다.


과학 계산에 사용되는 파이썬의 라이브러리들(building blocks)


 matlab, scilab, R 등과 달리 파이썬은 과학 계산 전용으로 미리 묶여진 모듈들이 없다.(역자 주 : 현재는  python(x,y) 나 winPython 등 과학계산 전용 패키지가 배포되고 있음. )  아래에 과학 계산에 사용되는 기본적인 모듈(라이브러리)을 나열하였다.




Posted by 살레시오

댓글을 달아 주세요

  넘파이의 ndarray 객체는 리스트와 달리 브로드캐스트(broadcast)라 불리는 특성이 있는데 이는 리스트(list)객체와 확연히 구별이 되는 기능이다. 예를 들어서 다음과 같이 숫자로만 이루어진 중첩 리스트 la 를 고려해 보자.


-----------------------------------------------------------------------

>>> la = [ [1,2], [3,4] ]

-----------------------------------------------------------------------


이 리스트는 요소가 모두 숫자이지만 la+2, la/4 와 같은 연산은 지원하지 않는다. 모든 요소에 2를 더하려면 for 반복문을 사용해야만 한다. 그리고 la*2는 파이썬 문법상 전혀 다른 동작을 수행한다. 즉, 리스트 사이의 사칙연산은 수학 연산과는 전혀 관계가 없는 것이다.


  이에 반해서 ndarray는 요소간 연산이 기본적으로 가능하며 같은 크기의 객체간 산술연산은 요소끼리 행해져서 그 결과가 산출된다. 예를 들어서 다음과 같은 두 개의 ndarray 를 고려하자.


-----------------------------------------------------------------------

>>> a = np.array( la )

>>> b = np.array([[1j,2j],[3j,4j]])

-----------------------------------------------------------------------


이렇게 생성된 a와 b에 대해서는 a+1, a*3, a-b 와 같은 사칙 연산이 정의되며 각각의 요소에 대해서 연산이 수행된다. 배열간 곱셈의 경우는 행렬곱셈과는 상관이 없는 연산이므로 이것과 헷갈리면 안된다. 여기서의 곱셈은 요소간 곱셈이다. 이러한 ndarray 의 기능을 broadcast라고 한다.


  그럼 크기가 다른 객체간 연산은 어떨까? 먼저 한 쪽이 배열이고 다른 쪽이 스칼라라면 그 스칼라는 다른 배열의 크기로 확장된 후 각각의 요소에 더해진다.


-----------------------------------------------------------------------

>>> np.arange(5) + 2 # [0, 1, 2, 3, 4] + [2, 2, 2, 2, 2] 와 같다

array([2, 3, 4, 5, 6])

-----------------------------------------------------------------------


이 예를 보면 1x5 크기의 배열과 2라는 스칼라의 합이다. 2라는 스칼라는 모든 요소가 2인 1x5의 배열로 확장된 후 각 요소간 더해진다. 행렬의 경우도 마찬가지이다.


-----------------------------------------------------------------------

>>> a=np.arange(1,10).reshape(3,3) # [0,1,2, ... ,9] 배열은 3x3로 재배열한다.

>>> a**2

array([[ 1, 4, 9],

[16, 25, 36],

[49, 64, 81]])

-----------------------------------------------------------------------


크기가 다른 행렬 간에는 산술연산이 일반적으로 불가하다. 즉, 다음과 같은 행렬 a, b 간의 산술연산은 불가능하다.


-----------------------------------------------------------------------

>>> a=np.arange(1,10).reshape(3,3)

>>> b=np.array([[1,2],[3,4]])

>>> a+b # 에러 발생

-----------------------------------------------------------------------


하지만 가능한 경우가 있으니 다음의 세 가지 경우이다.


  • mxn 행렬과 mx1 벡터 간의 연산
  • m x n 행렬과 1xn 벡터 간의 연산
  • 또한 mx1 벡터와 1xn 벡터간의 연산.


이 세가지 경우는 한 쪽의 크기가 다른 쪽의 크기로 확장된 후 요소간 연산을 수행하게 된다. (다음 그림 참조)


[#00083]

Posted by 살레시오

댓글을 달아 주세요

  1. 자나깨나 2017.09.20 00:49  댓글주소  수정/삭제  댓글쓰기

    감사합니다 ㅠㅠ C++로 행렬클래스 구현하는데 도움이 많이 됐어요!

  numpy를 이용해 생성된 ndarray객체는 다양한 field와 method를 갖고 있다. ndarray객체의 field/method는 dot(.)연산자를 이용하여 접근할 수 있다. (마치 C언어의 구조체나 공용체의 필드에 접근하는 방식과 같다.)

배열의 모양에 관련된 attribute 들

먼저, ​전치행렬을 구해주는 T attribute가 있다.

​-------------------------------------------------------------------------
>>> x = np.array([[1.,2.],[3.,4.]])

>>> x
array([[ 1., 2.],
​[ 3., 4.]])

>>> x.T
array([[ 1., 3.],
[ 2., 4.]])

>>> x = np.array([1.,2.,3.,4.])
>>> x
array([ 1., 2., 3., 4.])

>>> x.T # 1차원 배열에서 T attribute 는 동작하지 않는다.
array([ 1., 2., 3., 4.])
​-------------------------------------------------------------------------​​

위에서도 언급되었지만 1차 배열에서는 이것이 동작되지 않는다는 것을 주의해야 한다. 만약 배열 A의 복소전치행렬을 구하고 싶다면 A.conj().T (혹은 A.T.conj() ) 라고 하면 된다. 그리고 M이 행렬객체라면 단순히 M.H 라고 하면된다.

배열의 크기에 관련된 attribute 들

  배열의 크기에 관련된 attribute로 ndim, shape, 그리고 size가 있다. 이 중 shape는 읽기뿐만 아니라 쓰기도 가능하다.

  ndim 은 배열이 몇 차 배열인지를 알려준다.


​-------------------------------------------------------------------------
>>> x = np.array([1, 2, 3])
>>> x.ndim
1
>>> y = np.zeros((2, 3, 4))
>>> y.ndim
3
​-------------------------------------------------------------------------

​shape는 배열의 각 차수별 크기를 튜플로 반환하거나 지정해 줄 수 있다. 단, shape를 변경할 때는 변경 전과 전체 요소의 숫자가 같아야 한다.

​-------------------------------------------------------------------------
>>> x = np.array([1, 2, 3, 4])
>>> x.shape
(4,)
>>> y = np.zeros((2, 3, 4))
>>> y.shape
(2, 3, 4)
>>> y.shape = (3, 8)
>>> y
array([[ 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0.]])
>>> y.shape = (3, 6)
Traceback (most recent call last): File "<stdin>", line 1, in <module>
ValueError: total size of new array must be unchanged
​-------------------------------------------------------------------------

size는 ​​전체 요소들의 개수를 반환하며 np.prod(obj.shape)와 같은 결과를 생성한다.

​-------------------------------------------------------------------------
>>> x = np.zeros((3, 5, 2), dtype=np.complex128)
>>> x.size
30
>>> np.prod(x.shape)
30
​​-------------------------------------------------------------------------

기타

​나머지 attribute 들은 다음 표에 정리하였다.

TSame as self.transpose(), except that self is returned if self.ndim < 2.
data

배열의 시작을 가리키는 파이썬 버퍼 객체 

dtype

배열 요소의 데이터형 

flags

배열의 메모리 구조에 대한 정보 

flat

배열의 1차원 반복자 

imag배열의 허수부
real배열의 실수부
size배열의 요소 전체의 개수
itemsize배열 요소의 바이트 크기
nbytes배열의 전체 요소가 차지하는 바이트 크기
ndim배열의 차수
shape배열의 차수별 크기.
stridesTuple of bytes to step in each dimension when traversing an array.
ctypesAn object to simplify the interaction of the array with the ctypes module.
baseBase object if memory is from some other object.

[#00082]

Posted by 살레시오

댓글을 달아 주세요