넘파이의 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 살레시오

댓글을 달아 주세요

 행렬의 원하는 부분을 치환하기 위해서는 치환하고자 하는 행렬의 원소에 값을 입력한다. 먼저 다음과 같이 행렬 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 살레시오

댓글을 달아 주세요

 생성된 행렬의 개별 요소를 접근하려면 행렬에 괄호 안에 행과 열의 번호를 콤마(,)로 구분하여 써주면 되는데 예를 들어 A행렬의 r행 c열 요소는 다음과 같이 접근한다.


 A(r, c)

 

여기서 r과 c를 인덱스(index)라고 한다. 사용 예를 들면 다음과 같다.

>> A = [ 1 2 3; 4 5 6]
>> b = A(1, 2) // 2가 변수 b에 저장된다.
>> A(2,3) = 3*%i // 2행 3열의 요소가 6에서 3i 로 변경된다.

만약  B가 벡터라면 다음과 같이 두 가지 방법으로 인덱싱을 할 수 있다.


>> B = [2 3 5 7 11 13 17 19]
>> c = B(1, 5)  // 1행 5열 요소이므로 11이 변수 c에 저장된다.
>> d = B(5) // 위와 같다.
>> E = (1:100)’
>> f = E(5,1) // 5행 1열의 요소이므로 5가 변수 f에 저장된다.
>> g = E(5) // 위와 같다.

 

이 예제와 같이 벡터의 인덱싱은 하나만 지정할 수 있다.


 사실 행렬은 내부적으로 1차원 배열로 관리되며 따라서 인덱스를 하나만 지정할 수는 있다. 예를 들어 다음과 같이 H행렬이 있다고 가정할 때


>> H = [ 10, 20, 30; -40, -50, -60,]

 

H(1)은 H(1,1)과 같이 10이고 H(2)는 H(2,1)의 요소인 -40이다. H(3)은 H(2,1)과 같다.

H(1) == H(1,1)

H(3) == H(1,2)

H(5) == H(1,3)

H(2) == H(2,1)

H(4) == H(2,2)

H(6) == H(2,3)

[ 그림 1] H행렬의 인덱싱

하지만 행렬의 경우 이런 방식으로 인덱싱하는 것을 추천하지 않으며 사용 빈도도 낮다.

 하나의 행 또는 하나의 열 전체를 선택하고자 할 때는 콜론(:)을 인덱스로 사용한다. 예를 들어서 H행렬의 1행 전체와 2열 전체를 선택해서 J1과 J2에 대입하고자 하면 다음과 같이 한다.


>> J1=H(1,:) // 1행, 전체 (즉 그냥 1행)
J1  =
    10.    20.    30.  
>> J2=H(:,2) // 전체. 2열 (즉 그냥 2열)
J2  =
    20.  
  - 50.  

이와 같이 콜론(:)이 인덱싱에서 단독으로 쓰이면 전체(all)의 의미를 갖는다. 혼동의 여지가 있는 것은 이전 포스트에서 설명한 콜론(:)연산자의 경우이다. 예를 들어서 2x100 행렬 K를 다음과 같이 생성했다고 가정해 보자.

>> K = [1:100; 100:-1:1]

이 행렬의 1열부터 10열까지 동시에 선택해서 변수 L에대입하려면 다음과 같이 하면 된다.


>> L = K(:, 1:10)

 

두 번째 열인덱스에 1:10 이라고 콜론 연산자를 사용했다. 따라서 이 명령은 다음과 완전히 동일하다.

>> L = K(:, [1 2 3 4 5 6 7 8 0 10])


이렇게 하는 것 보다는 전자가 훨씬 간단하므로 전자를 사용할 것이다. 하지만 콜론 연산자를 사용하는 것은 연속되거나 등간격의  행이나 열을 선택하는 경우에만 사용할 수 있다. 예를 들어서 2열, 4열, 6열… 20열을 동시에 취하려면 다음과 같이 하면 된다..

>> M = K(:, 2:2:20)


하지만 등간격이 아닌 경우에는 행벡터로 각각을 직접 지정해 주어야 한다. 예를 들어서 1열, 2열, 3열 5열 7열 11열 을 동시에 취하려면 다음과 같이 하면 된다.


>> M = K(:, [1 2 3 4 5 11])

등간격이 아니므로 이렇게 직접 지정해 주어야 한다.


 만약 어떤 행렬의 가장 마지막 행(또는 열)을 선택하고 싶다면 $지정자를 이용하면 된다.(MATLAB의 경우에는 end 지정자가 있다.) 예를 들어 K행렬의 가장 마지막 열을 N변수에 대입하고 싶다면다음과  같이 하면 된다.


>>N=K(:,$)
N  =
   100.  
   1.  

 

다른 예로 P라는 100x100는 행렬이 있다고 하고 이 행렬의 마지막 행의 1열부터 50열까지를 선택하고 싶다면

>> P($, 1:50)

라고 지정하면 된다. 마지막행 전체를 선택하려면 P($ , :)라고 하면 될 것이다.

 한 가지 특이한 점은 이 $ 지정자는 특정한 값을 가지지 않는데도 불구하고  마치 특수 상수처럼 사용된다는 점이다. 이 말은 이 지정자가 자유롭게 변수에 저장될 수 있다는 말이며 다음과 같은 명령이 가능하다.


>> x = $
>> y = [ 3 5 7 $]
>> z = 5:$
>> Q = P(:, z)

이 예의 마지막 명령은 P행렬의 5열부터 마지막 열까지를 취해서 Q변수에 대입하는 것이다.




Posted by 살레시오

댓글을 달아 주세요

 파이썬3의 문자열은 내부적으로 (유니코드) 문자의 배열로 취급된다. 예를 들어보자.


>>> s=“Hello World”


문자열

H

e

l

l

o


w

o

r

l

d

인덱스 (기본 방향)

0

1

2

3

4

5

6

7

8

9

10

인덱스 (역방향)

-11




-7

-6

-5

-4

-3

-2

-1


문자열에 포함된 각각의 문자에 매겨진 이 번호를 인덱스(index)라고 한다. 이 예에서 문자열의 길이는 11이고  인덱스는 0부터 시작한다. 인덱스가 1부터 시작하지 않고 0부터 시작함에 주의해야 한다. (다른 프로그래밍 언어에서도 배열의 인덱싱은 보통 0부터 시작한다.)


>>> s[0]

H

>>> s[6]

w

>>> s[-1]

d


마지막의 s[-1]과 같이 음수는 뒤에서부터 세는 것이다. 따라서 뒤에서 첫 번째 문자인 ‘d’가 된다.


>>> s[-2]

l

>>> s[-6]


만일


>>>  a=”python is the best.”


라는 문자열에서 첫 단어를 뽑아내고 싶다면 아래와 같이 한다.


>>> b=a[0:6]


인덱스 ‘0:6’ 이 뜻하는 것은 ‘0부터 5까지’ 이다. 끝 번호 6은 포함하지 않는다는 것에 주의해야 한다. 이렇게 콜론(:)을 이용하여 연속적인 인덱스를 지정하는 것을 슬라이싱(slicing)이라고 한다. 문자열의 마지막까지 지정하려면 끝 번호를 생략하면 된다.


>>> c = a[7:] # ‘is the best.’ 가 c에 저장된다.


반대로 시작 번호가 생략되면 문자열의 처음부터 선택된다.


>>> d = a[:8] # ‘python is’ 가 d에 저장된다.


그리고 시작 번호와 끝 번호가 모두 생략된다면, 즉 e=a[:] 이라고 하면 문자열 전체가 선택이 된다. 즉, e에는 a 문자열 전체가 저장된다.


슬라이싱에서도 인덱싱과 마찬가지로 음수를 사용할 수 있다.


>>> f = a[:-5]


결과를 확인해 보기 바란다. 이 경우에도 끝 번호는 포함되지 않으므로 첫 문자부터 -6번 문자까지 뽑아져서 f에 저장된다. 슬라이싱을 정리하면 다음과 같다.


  • s[m:n] 은 s[m] 부터 s[n-1] 까지의 부분 문자열이다.


예를 들어서 만약 문자열 h를 5번째 문자를 기준으로 둘로 나눠서 hl, hr에 정하고 싶다면 다음과 같이 하면 될 것이다.


>>> hl = h[:5]
>>> hr = h[5:]


이러한 기능을 이용해서 문자열 자체를 바꿀 수는 없다는 것에 주의하자. 즉, 다음과 같이 문자열의 일부분을 바꾸는 것은 불가능한다.


>>>a[0] = ’x’ #불가능하다


이는 문자열은 한 번 내용이 정해지면 내용을 읽는 수는 있지만 변경될 수는 없는 자료형이기 때문이다. (이러한 자료형을 immutable 하다고 한다.)




Posted by 살레시오

댓글을 달아 주세요

  1. 익명 2015.10.14 01:05  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다