-
Python Programming (6) - 선형대수Python Programming 2020. 3. 28. 06:53728x90
6.Linear_algebra 6. Linear algebra¶
Python에서의 선형 대수¶
- 이 단원에서는 선형 대수의 여러 개념을 Python 기본 함수와 리스트 등을 이용하여 구현하는 연습을 한다.
- 벡터를 생성하고, 벡터와 행렬 연산을 수행할 수 있는 함수를 작성한다.
- 이 후, Python 모듈인 numpy를 이용하여 직접 만들었던 선형 대수의 기능과 비교해 본다.
벡터 - Vectors¶
- 벡터는 벡터 공간의 원소를 벡터라 하며,
- 백터들은 서로 더하거나 스칼라에 의해 곱해질 수 있다.
- 벡터를 숫자들의 리스트라고 생각해 보자.
In [1]:height_weigth_age = [70, # inches 170, # pounds 40 ] # years
In [2]:grades = [95, # exam1 80, # exam2 75, # exam3 62 ] # exam4
- Python의 list는 벡터 연산을 제공하지 않기 때문에, 벡터 연산을 추가해 보자.
벡터 합과 차¶
- 원소별로 계산 : 다음을 구현하고 싶음
- [1, 2] 더하기 [3, 4] = [4, 6]
- [5, 3] 빼기 [1, 7] = [4, -4]
In [3]:def vector_add(v, w): """adds two vectors componentwise""" return [v_i + w_i for v_i, w_i in zip(v,w)]
In [4]:vector_add([1,2], [3,4])
Out[4]:In [5]:vector_add([1,2,3], [3,4,5])
Out[5]:In [6]:v = [1, 2, 3] w = [2, 3, 4] z = vector_add(v, w) y = vector_add(z, v) print(y)
In [7]:def vector_subtract(v, w): """subtracts two vectors componentwise""" return [v_i - w_i for v_i, w_i in zip(v,w)]
In [8]:vector_subtract([0, 0, 1], [1, 2, 3])
Out[8]:여러 개의 벡터들의 합¶
- 벡터들의 리스트가 있을 때, 리스트 내의 벡터들을 원소 별로 합하기
In [9]:def vector_sum(vectors): result = vectors[0] for vector in vectors[1:]: result = vector_add(result, vector) return result
In [10]:vectors = [[1, 2, 3], [2, 3, 0], [0, 1, -2]] vector_sum(vectors)
Out[10]:In [11]:from functools import reduce # 앞의 vector_sum과 같은 기능을 가진다. def vector_sum(vectors): return reduce(vector_add, vectors)
In [12]:vectors = [[1, 2, 3], [2, 3, 0], [0, 1, -2]] vector_sum(vectors)
Out[12]:In [13]:# 내부적으로는 다음과 동치 vector_add(vector_add([1, 2, 3], [2, 3, 0]), [0, 1, -2])
Out[13]:스칼라 곱¶
- 목표 : 3*[1, 2, 3] = [3, 6, 9]
In [14]:def scalar_multiply(c, v): return [c * vi for vi in v]
In [15]:scalar_multiply(3, [1, 2, 3])
Out[15]:- 컴포넌트별 평균 : vector_mean([1, 2], [2, 4], [3, 6]) == [3, 6]
In [16]:def vector_mean(vectors): n = len(vectors) return scalar_multiply(1/n, vector_sum(vectors))
In [17]:vector_mean([[1, 2], [2, 4], [3, 6]])
Out[17]:- 단, Python 2.x의 경우 위의 나누기에 실수 나누기를 적용하기 위해서는 파일 위쪽에 다음을 표기
- Python 3.x는 실수 나누기가 적용되기 때문에 상관 없음
In [18]:from __future__ import division
dot product¶
- 목표 :
- dot( [1, 2, 3], [0, 1, 2]) = sum([1 * 0, 2 * 1, 3 * 2]) = sum([0, 2, 6]) = 8
In [19]:def dot(v, w): """v_1 * w_1 + ... + v_n * w_n""" return sum(v_i * w_i for v_i, w_i in zip(v, w))
In [20]:dot([1, 2, 3], [0, 1, 2])
Out[20]:제곱합¶
- 벡터 원소들의 제곱의 합
In [21]:def sum_of_squares(v): """v_1 * v_1 + ... + v_n * v_n""" return dot(v, v)
In [22]:sum_of_squares([0, 1, 2])
Out[22]:- 벡터의 크기(magnitude) : 제곱합의 제곱근
In [23]:import math def magnitude(v): return math.sqrt(sum_of_squares(v))
In [24]:magnitude([3, 4])
Out[24]:In [25]:magnitude([5, 12])
Out[25]:벡터 사이의 거리¶
- 한 벡터에서 다른 벡터를 뺀 후, 크기를 구하는 것과 동일
In [26]:def distance(v, w): return magnitude(vector_subtract(v, w))
In [27]:distance([1, 2], [2, 3])
Out[27]:NumPy¶
- NumPy는 Python에서의 수학/과학 컴퓨팅의 기본 패키지로서
- numpy 모듈에는 지금까지 행한 벡터 연산들이 구현되어 있음.
- numpy 패키지 (모듈)는 Python을 사용하는 거의 모든 수치 계산에 사용된다.
- Python을 위한 벡터, 행렬 및 고차원 데이터 구조를 제공한다.
- http://www.numpy.org/
numpy
모듈을 이용하기 위해서는 다음과 같이import
를 먼저 진행한다. 시작 시 한 번만 불러오면 된다.In [28]:import numpy as np
다음은 dot product 예제이다.
In [29]:# dot product np.dot([1,2], [3,4])
Out[29]:Numpy array¶
numpy의 다양한 기능은 array라는 데이터구조를 바탕으로 이루어진다.
- 효율적이고 계산이 편리함
numpy array는 다양한 방법을 통해 만들 수 있다.
- 파이썬 리스트 또는 튜플을 이용하는 방법
arange
,linspace
등과 같이 numpy 배열을 생성하는 데 사용되는 함수를 사용하는 방법- 파일에서 데이터를 읽어들이는 방법
- 다차원 배열을 구현
- 수학적 계산에 특화
Python list로부터 numpy array 만들기¶
In [30]:import numpy as np # 이미 한 번 import하였으면 다시 하지 않아도 된다. a = np.array([0, 1, 2, 3]) a
Out[30]:In [31]:print(a)
In [32]:a.ndim # 1 차원
Out[32]:In [33]:a.shape # 형태 : (4,)
Out[33]:In [34]:len(a) # 4
Out[34]:In [35]:# matrix: Python list로 이루어진 list를 이용하여 matrix 만들기 b = np.array([[0,1,2], [3,4,5]]) b
Out[35]:In [36]:b.ndim # 2차원
Out[36]:In [37]:b.shape # 형태 (2,3)
Out[37]:In [38]:len(b) # 2 : 첫번째 차원의 길이
Out[38]:array는 list와 비슷해 보이지만, numpy에서 array라는 별도의 데이터구조를 이용하는 몇 가지 이유가 있다.
- Python list는 동적으로 할당되며, list내의 원소들이 서로 다른 데이터형을 가질 수 있다.
- 이러한 점은 벡터나 행렬 계산을 느리게 혹은 불가능하게 한다.
- 반면 numpy array내의 원소들의 데이터 형은 일정하고(homogeneous), 변하지 않기 때문에, 메모리 효율적이고, 행렬이나 벡터 계산을 빠르게 할 수 있다.
- 이미 데이터 타입이 결정된 array의 원소를 다른 데이터 타입으로 변경하면 에러가 발생한다.
In [39]:b.dtype
Out[39]:In [40]:b[0,0] = "a"
array 생성¶
numpy에서는 다양한 방법을 통해 array를 생성할 수 있도록 도와준다.
In [43]:a = np.arange(10) # range 함수와 비슷 a
Out[43]:In [44]:b = np.arange(1, 9, 2) b
Out[44]:In [45]:c = np.linspace(0, 1, 6) #시작, 끝, 숫자 개수 c
Out[45]:In [46]:d = np.ones((3, 3)) # 1로 이루어진 다차원 배열 d
Out[46]:In [47]:e = np.zeros((2, 2)) # 0으로 이루어진 다차원 배열 e
Out[47]:In [48]:f = np.eye(3) # identity 행렬 f
Out[48]:In [49]:g = np.diag(np.array([1, 2, 3, 4])) # 대각 행렬 g
Out[49]:array 생성(2)¶
numpy.random 모듈을 이용한 랜덤 array 생성
In [50]:np.random.rand(4) # uniform in [0, 1]
Out[50]:In [51]:np.random.randn(4) # standard normal
Out[51]:In [52]:2.5 * np.random.randn(4) + 3 # 평균 3, 표준편차 2.5인 정규분포
Out[52]:위 예제들에서 경우에 따라 마침표 (예 : 2. vs 2)가 표시된다는 것을 알 수 있다. 이는 데이터 유형의 차이 때문이다. 마침표가 없는 것은 정수형 데이터, 마침표가 있는 것은 실수형 데이터를 의미한다. numpy array는 한 종류의 데이터 유형만 가질 수 있다.
Basic Slicing and Indexing¶
- Python list와 비슷하게 [ ]를 이용하여 원소에 접근한다.
- slicing 또한 Python list와 마찬가지로
start:stop:step
를 이용한다.
In [53]:v = np.array([1, 2, 3, 4, 5, 6]) v
Out[53]:In [54]:v[0]
Out[54]:In [55]:v[2], v[-1] # -1은 마지막 원소
Out[55]:In [56]:v[1:5:2]
Out[56]:In [57]:v[::-1] # 순서 뒤집기
Out[57]:2차원 array에서는 행(row)과 열(column)의 인덱스에 기반한다.
In [58]:M = np.array([[1, 2], [3, 4]]) M
Out[58]:In [59]:M[1, 1]
Out[59]:In [60]:# 인덱스 하나만 사용하면 하나의 행을 슬라이싱 M[1] # row 1
Out[60]:In [61]:M[1, :] # row 1 (여기서 :는 모든 열을 의미)
Out[61]:In [62]:M[:, 1] # column 1 (여기서 :는 모든 행을 의미)
Out[62]:In [63]:M[0, 0] = 10 M
Out[63]:대입 연산도 마찬가지로 이루어질 수 있다.
In [64]:M[1, :] = -1 M
Out[64]:In [65]:M[:, 1] = 777 M
Out[65]:In [66]:a = np.array([[n + m * 10 for n in range(6)] for m in range(6)]) a
Out[66]:<img src = "figure/numpy_indexing.png", width = 500, height = 500> https://scipy-lectures.org/intro/numpy/array_object.html#what-are-numpy-and-numpy-arrays
In [67]:a[0, 3:5]
Out[67]:In [68]:a[4:, 4:]
Out[68]:In [69]:a[:, 2]
Out[69]:In [70]:a[2::2, ::2]
Out[70]:In [71]:a[1:3, 1:3]
Out[71]:In [72]:a[::2, ::2]
Out[72]:<img src = "figure/numpy_fancy_indexing.png", width = 600, height = 600>
Advanced Indexing¶
:
를 이용하는 slicing이 아닌 정수 혹은 Boolean의 list, array, tuple 등으로 이루어진 indexing을 말한다.In [73]:# 행과 열의 index들이 모두 slicing이 아닐 때 - 정수로 이루어진 리스트 a[[0,1,2,3,4], [1,2,3,4,5]]
Out[73]:In [74]:a[(0,1,2,3,4), (1,2,3,4,5)]
Out[74]:In [75]:# 아래와 같이 해석할 수 있다. np.array([a[0,1], a[1,2], a[2,3], a[3,4], a[4,5]])
Out[75]:In [76]:# slicing과는 다르다. a[0:5, 1:6]
Out[76]:In [77]:# 행과 열의 index중 하나라도 slicing일 때는 slicing의 규칙을 따른다. a[3:, [0,2,5]]
Out[77]:In [78]:# Boolean array를 이용할 수도 있다. mask = np.array([1, 0, 1, 0, 0, 1], dtype=bool) mask
Out[78]:In [79]:a[mask, 2]
Out[79]:In [80]:row_indices = [1, 2, 3] a[row_indices]
Out[80]:In [81]:col_indices = [1, 2, -1] # -1은 마지막 원소를 나타냄 a[row_indices, col_indices]
Out[81]:In [82]:b = np.array([n for n in range(5)]) b
Out[82]:In [83]:row_mask = np.array([True, False, True, False, False]) b[row_mask]
Out[83]:In [84]:# same thing row_mask = np.array([1,0,1,0,0], dtype=bool) b[row_mask]
Out[84]:In [85]:x = np.arange(0, 10, 0.5) x
Out[85]:In [86]:x[(5 < x) * (x < 7.5)]
Out[86]:- 행렬의 원소별 곱셈
In [87]:M * N
Numpy array에서의 복사와 참조¶
- 성능 향상을 위해 파이썬에서의 많은 경우 복사를 하지 않고 참조를 하는 경우가 많다.
In [88]:A = np.array([[1, 2], [3, 4]]) A
Out[88]:In [89]:# B는 A를 참조만 한다. B = A
In [90]:# B의 변화는 A에 영향을 미침 B[0,0] = 10 B
Out[90]:- 만약 이런 현상을 원치 않는다면,
.copy()
를 이용하여 복사한다.
In [91]:C = np.copy(A) C[0, 0] = -1 C
Out[91]:array slicing 또한 기본적으로 참조를 바탕으로 하므로 주의해야 한다.
- view를 제공한다고도 표현한다.
In [92]:# A는 바뀌지 않음 A
Out[92]:In [93]:a = np.arange(10) b = a[::2] b
Out[93]:In [94]:b[0] = 12 b
Out[94]:In [95]:a # 주의
Out[95]:In [96]:a = np.arange(10) c = a[::2].copy() #복사하여 사용하면 c[0] = 12 c
Out[96]:In [97]:a
Out[97]:In [98]:# Numpy array의 경우 a = np.arange(10) b = a[:] # 참조가 발생함 b[0] = 10 a
Out[98]:In [99]:# Python list의 경우는 다르다. x = list(range(10)) y = x[:] # list는 이 경우 복사함 y[0] = 10 x
Out[99]:반면 정수 리스트나 Boolean 리스트를 바탕으로 하는 advanced indexing은 복사를 기본으로 한다.
In [100]:a = np.arange(10) d = a[[0,1,2]]
In [101]:d[0] = 10 d
Out[101]:In [102]:# a는 바뀌지 않음 a
Out[102]:numpy와 numpy.linalg를 이용한 선형대수¶
- numpy는 벡터와 행렬 연산에 있어 강력한 기능을 가지고 있다.
- 벡터 합과 차
In [103]:a = np.array([3,1,-1]) b = np.arange(3) c = a + b d = b - a
- 스칼라 곱
In [104]:4 * a
Out[104]:- 제곱합
In [105]:np.sum(a**2)
Out[105]:- dot product
In [106]:np.dot(a, b)
Out[106]:- 벡터 사이의 거리
In [107]:np.linalg.norm(a-b)
Out[107]:- 벡터 원소별 곱셈
In [108]:a * b
Out[108]:In [109]:M = np.array([[1,2], [3,4]]) M
Out[109]:- transpose
In [110]:M.T
Out[110]:- 행렬식
In [111]:np.linalg.det(M)
Out[111]:- 역행렬
In [112]:np.linalg.inv(M)
Out[112]:- 해 찾기
In [113]:c = np.array([2,1]) np.linalg.solve(M, c)
Out[113]:- 행렬곱
In [114]:N = np.array([[-1,1],[2,1]]) N
Out[114]:In [115]:np.matmul(M,N)
Out[115]:- 행렬의 원소별 곱셈
In [116]:M * N
Out[116]:728x90'Python Programming' 카테고리의 다른 글
Python Programming (8) - 파일 입출력 (0) 2020.03.28 Python Programming (7) - 데이터 시각화 (0) 2020.03.28 Python Programming (5) - 리스트 (0) 2020.03.28 Python Programming (4) - 함수 (0) 2020.03.28 Python Programming (3) - 반복문 (0) 2020.03.28