import

import numpy as np

numpy공부 1단계(복습)

- 넘파이의 위력

l=[1,2,3]
a=np.array(l) ## list()

- 사칙연산 브로드캐스팅

a+1
array([2, 3, 4])
l+1
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-9-558f50c77660> in <module>
----> 1 l+1

TypeError: can only concatenate list (not "int") to list
a*2
array([2, 4, 6])
l*2
[1, 2, 3, 1, 2, 3]
a-2
array([-1,  0,  1])
l-2
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-a7ce880f546b> in <module>
----> 1 l-2

TypeError: unsupported operand type(s) for -: 'list' and 'int'
a/2
array([0.5, 1. , 1.5])
l/2
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-15-156c9f11ad90> in <module>
----> 1 l/2

TypeError: unsupported operand type(s) for /: 'list' and 'int'

- 기타수학연산가능

np.sqrt(a), np.sqrt(l)
(array([1.        , 1.41421356, 1.73205081]),
 array([1.        , 1.41421356, 1.73205081]))
np.log(a), np.log(l)
(array([0.        , 0.69314718, 1.09861229]),
 array([0.        , 0.69314718, 1.09861229]))

numpy공부 2단계

연립1차방정식의 풀이

- 아래의 연립방정식 고려

$\begin{cases} x + y = 2 \\ y + z = 2 \\ x + z = 2 \end{cases}$

- 행렬표현은?

$\begin{bmatrix} 1 &1& 0 \\ 0 & 1 & 1 \\ 1 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \end{bmatrix} =\begin{bmatrix} 2 \\ 2 \\ 2 \end{bmatrix} $

A = [[1,1,0],[0,1,1],[1,0,1]] 
A
[[1, 1, 0], [0, 1, 1], [1, 0, 1]]
A2=np.array(A)
A2
array([[1, 1, 0],
       [0, 1, 1],
       [1, 0, 1]])

- A의 원소인덱싱

A[0][0], A[0][1], A[0][2]
(1, 1, 0)
A[1][0], A[1][1], A[1][2]
(0, 1, 1)
A[2][0], A[2][1], A[2][2]
(1, 0, 1)

- A2에서의 원소인덱싱

A2[0][0], A2[0][1], A2[0][2]
(1, 1, 0)
A2[1][0], A2[1][1], A2[1][2]
(0, 1, 1)
A2[2][0], A2[2][1], A2[2][2]
(1, 0, 1)

- A2에서의 원소에 접근하는 또다른 방법 (numpy 신기술)

A2[0,0], A2[0,1], A2[0,2]
(1, 1, 0)
A2[1,0], A2[1,1], A2[1,2]
(0, 1, 1)
A2[2,0], A2[2,1], A2[2,2]
(1, 0, 1)

- 슬라이싱!

A2[0,:] ## R의 경우라면 A2[0,]
array([1, 1, 0])
A2[1,:] 
array([0, 1, 1])
A2[2,:] 
array([1, 0, 1])

- 연립방정식 문제를 다시 풀어보자.

$\begin{bmatrix} 1 & 1 & 0 \\ 0 & 1 & 1 \\ 1 & 0 & 1 \end{bmatrix} \begin{bmatrix} x_1 \\ x_2 \\ x_3 \end{bmatrix} =\begin{bmatrix} 2 \\ 2 \\ 2 \end{bmatrix} \quad \Longleftrightarrow \quad {\bf A} {\bf x} = {\bf b} $

b=np.array([2,2,2])
np.linalg.inv(A2) @ b ## R로 치면 solve(A2) %*% b 
array([1., 1., 1.])

인덱싱

- 선언

l=[11,22,33,44,55,66] 
a=np.array(l)
a
array([11, 22, 33, 44, 55, 66])

- 인덱스로 접근

l[0],l[1],l[2],l[3],l[4],l[5]
(11, 22, 33, 44, 55, 66)
a[0],a[1],a[2],a[3],a[4],a[5]
(11, 22, 33, 44, 55, 66)

- : 이용 (슬라이싱)

l[2:4] # index=2, index=3 (4는 포함되지 않음) 
[33, 44]
a[2:4]
array([33, 44])

너무 헷갈림.. 특히 마지막 원소를 포함해야할때!

l[2:6]
[33, 44, 55, 66]
a[2:6]
array([33, 44, 55, 66])

다행스럽게도 마지막의 6은 생략가능하다. 따라서 보통 아래와 같이 쓴다.

l[2:]
[33, 44, 55, 66]
a[2:]
array([33, 44, 55, 66])

동일한 논리로 처음 인덱스도 생략가능하다.

a[0:3], a[:3]
(array([11, 22, 33]), array([11, 22, 33]))

처음과 마지막을 둘다 생략한다면?

a[:]
array([11, 22, 33, 44, 55, 66])

- 부울값을 이용한 인덱싱

l=[11,22,33,44,55,66] 
a=np.array(l)
a
array([11, 22, 33, 44, 55, 66])
a[ [False, True, False, True, False, True] ]
array([22, 44, 66])
a<33
array([ True,  True, False, False, False, False])
a[a<33]
array([11, 22])

리스트는 안된다.

l<33 ## 일단 여기서 막힘 
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-74-0617af835ebc> in <module>
----> 1 l<33 ## 일단 여기서 막힘

TypeError: '<' not supported between instances of 'list' and 'int'

reshape

- reshape

l = [11,22,33,44,55,66]
a = np.array(l)
a
array([11, 22, 33, 44, 55, 66])
a.reshape(2,3)
array([[11, 22, 33],
       [44, 55, 66]])
a ## reshape은 a자체가 변화하지 않음 
array([11, 22, 33, 44, 55, 66])
b = a.reshape(2,3)
b
array([[11, 22, 33],
       [44, 55, 66]])

- b를 다시 a처럼 바꾸고 싶다면?

b.reshape(6)
array([11, 22, 33, 44, 55, 66])
b.flatten()
array([11, 22, 33, 44, 55, 66])
b.ravel()
array([11, 22, 33, 44, 55, 66])
b.reshape(-1)
array([11, 22, 33, 44, 55, 66])

numpy공부 3단계

- 2차원 array a,b를 선언하자.

a=np.array([[11,22,33,44]]).reshape(2,2)
a
array([[11, 22],
       [33, 44]])
b=np.array([[11,22,33,44,55,66]]).reshape(2,3)
b
array([[11, 22, 33],
       [44, 55, 66]])

- a. + TAB, b. + TAB

a.shape, b.shape
((2, 2), (2, 3))
a.size, b.size
(4, 6)
a.itemsize, b.itemsize
(8, 8)
  • ?
a.strides, b.strides
((16, 8), (24, 8))
  • ?

a의 형태

a
array([[11, 22],
       [33, 44]])

b의 형태

b
array([[11, 22, 33],
       [44, 55, 66]])

- itemsize와 strides의 의미를 유추하기 위해서 c,d를 더 만들어보자.

c = np.array([11,22,33,44]).reshape(4,1)
d = np.array([11,22,33,44])

c의 형태

c
array([[11],
       [22],
       [33],
       [44]])

d의 형태

d
array([11, 22, 33, 44])

- a,b,c,d 속성비교

a.shape, b.shape, c.shape, d.shape ## 차원 
((2, 2), (2, 3), (4, 1), (4,))
a.size, b.size, c.size, d.size ## 원소의 수
(4, 6, 4, 4)
a.itemsize, b.itemsize, c.itemsize, d.itemsize ## 항상 8? 
(8, 8, 8, 8)
a.strides, b.strides, c.strides, d.strides ## 차원이랑 관련이 있어보임.. + 8의 배수 
((16, 8), (24, 8), (8, 8), (8,))
a
array([[11, 22],
       [33, 44]])

- itemsize, strides는 무엇?

  • itemsize: 숫자하나를 저장하는 필요한 메모리 공간
  • strides: (다음 행으로 가기위해서 JUMP해야하는 메모리 공간수, 다음 열로 가기위해서 JUMP해야하는 메모리 공간수)

- strides에 대한 추가해설

(오브젝트b)

b
array([[11, 22, 33],
       [44, 55, 66]])
b.reshape(-1)
array([11, 22, 33, 44, 55, 66])
b.strides
(24, 8)

- itemsize는 항상 8이다?

e=np.array([11,22,33,44,55,66],dtype='int32')
e
array([11, 22, 33, 44, 55, 66], dtype=int32)
e.itemsize
4
e=e.reshape(2,3)
e
array([[11, 22, 33],
       [44, 55, 66]], dtype=int32)
e.strides
(12, 4)

참조

- a를 선언, b는 a의 참조

a=np.array([[1,2],[3,4]])
b=a ## 참조 
a
array([[1, 2],
       [3, 4]])
b
array([[1, 2],
       [3, 4]])
a.shape
(2, 2)
b.shape
(2, 2)

- a의 shape을 바꾸어보자 $\to$ b도 같이 바뀐다

a.shape = (4,)
a
array([1, 2, 3, 4])
b
array([1, 2, 3, 4])
id(a),id(b)
(139811809650992, 139811809650992)

view

- a를 선언, b는 a의 view

a=np.array([[1,2],[3,4]]) 
b=a.view() ## shallow copy 
a
array([[1, 2],
       [3, 4]])
b
array([[1, 2],
       [3, 4]])
a.shape
(2, 2)
b.shape
(2, 2)
a.shape= (4,1)
a
array([[1],
       [2],
       [3],
       [4]])
b
array([[1, 2],
       [3, 4]])
id(a), id(b)
(139811818705616, 139811818705424)

- 그런데..

a[0]=100
a
array([[100],
       [  2],
       [  3],
       [  4]])
b
array([[100,   2],
       [  3,   4]])

- 출생의 비밀

b
array([[100,   2],
       [  3,   4]])
b.base
array([[100],
       [  2],
       [  3],
       [  4]])
  • ? 이거 바뀐 a아니야?
id(b.base)
139811818705616
id(a)
139811818705616

- View

  • b가 a의 뷰라는 의미는, b가 a를 소스로하여 만들어진 오브젝트란 의미이다.
  • 따라서 이때 b.base는 a가 된다.
  • b는 자체적으로 데이터를 가지고 있지 않으며 a와 공유한다.
  • 이러한 의미에서 view를 shallow copy 라고 부른다. (가장 껍데기 구조만 복사하므로)

note1 원본 ndarray의 일 경우는 .base가 None으로 나온다.

a.base

note2 b.base의 shpae과 b의 shape은 아무 관련없다.

b.shape
(2, 2)
b.base.shape
(4, 1)

copy

- a를 선언, b는 a의 copy

a=np.array([[1,2],[3,4]])
b=a.copy()
id(a),id(b)
(139811818708208, 139811818707344)

- a의 shape을 바꿔도 b에는 적용되지 않음

a.shape = (4,1)
a
array([[1],
       [2],
       [3],
       [4]])
b
array([[1, 2],
       [3, 4]])

- 그리고 a[0]의 값을 바꿔도 b에는 적용되지 않음.

a[0]=100
a
array([[100],
       [  2],
       [  3],
       [  4]])
b
array([[1, 2],
       [3, 4]])

- b의 출생을 조사해보니..

a.base,b.base
(None, None)

출생의 비밀은 없었다. 둘다 원본.

.copy의 한계(?)

a=np.array([1,[1,2]],dtype='O')
a
array([1, list([1, 2])], dtype=object)
b=a.copy()
b
array([1, list([1, 2])], dtype=object)
a[0]=222
a
array([222, list([1, 2])], dtype=object)
b
array([1, list([1, 2])], dtype=object)
a[1][0]=333
a
array([222, list([333, 2])], dtype=object)
b
array([1, list([333, 2])], dtype=object)

해결책: 더 깊은 복사

import copy 
a=np.array([1,[1,2]],dtype='O')
b=copy.deepcopy(a)
a
array([1, list([1, 2])], dtype=object)
b
array([1, list([1, 2])], dtype=object)
a[0]=100
a,b
(array([100, list([1, 2])], dtype=object),
 array([1, list([1, 2])], dtype=object))
a[1][0]=200
a,b
(array([100, list([200, 2])], dtype=object),
 array([1, list([1, 2])], dtype=object))

일반적으로 넘파이를 이용할때 자주 사용하는 데이터 구조인 행렬, 텐서등을 이용하면 위와 같은 copy모듈은 불필요함

별명, 뷰, 카피

- test 함수 작성

def test(a,b): 
    if id(a) == id(b): 
        print("별명")
    elif id(a) == id(b.base) or id(a.base)==id(b): 
        print("뷰")
    elif (id(a.base)!=id(None) and id(b.base)!=id(None)) and id(a.base) == id(b.base):
        print("공통의 base를 가짐")
    else: 
        print("카피, 혹은 아무 관련없는 오브젝트") 

- 잘 동작하나?

(테스트1)

a=np.array([1,2,3,4])
b=a
test(a,b)
별명

(테스트2)

a=np.array([1,2,3,4])
b=a.view()
test(a,b)

(테스트3)

a=np.array([1,2,3,4])
b=a.view()
c=a.view()
test(b,c)
공통의 base를 가짐
test(a,b)
test(a,c)

(테스트4)

a=np.array([1,2,3,4])
b=a.copy()
test(a,b)
카피, 혹은 아무 관련없는 오브젝트

- 우리가 사용했던 어떠한 것들이 뷰가 나올지 카피가 나올지 사실 잘 모른다.

a=np.array([1,2,3,4])
b=a[:3]
a
array([1, 2, 3, 4])
b
array([1, 2, 3])
test(a,b)
c=a[[0,1,2]]
c
array([1, 2, 3])
test(a,c)
카피, 혹은 아무 관련없는 오브젝트

-

a=np.array([[1,2],[3,4]])
a
array([[1, 2],
       [3, 4]])
b=a.flatten()
c=a.ravel()
d=a.reshape(-1)
test(a,b)
카피, 혹은 아무 관련없는 오브젝트
test(a,c)
test(a,d)
test(c,d)
공통의 base를 가짐
test(b,c)
카피, 혹은 아무 관련없는 오브젝트