06_Numpy

02_선형대수 기본

chuu_travel 2025. 3. 10. 18:01
728x90
import numpy as np

스칼라와 벡터

  • 벡터와 행렬은 특정 텐서(tensor)를 부르는 이름
  • 텐서의 이름을 부여하는 원칙은 차원인 축(axis)을 기준으로 지정
    • 텐서가 0차원인 경우(축이 없는 경우) : 스칼라(scalar)
    • 1차원인 하나의 축을 가진 경우: 벡터(vector)
    • 2차원인 2개의 축을 가진 경우: 행렬(matrix)
    • 3차원인 3개의 축을 가진 경우: 큐브(cube) 또는 3차원 텐서
    • 더 차원이 높은 경우는 차원을 기준으로 n차원 텐서 라고 부름

 

스칼라

  • 0차원 배열
  • 크기를 표현
  • 가장 기본적인 값
  • 수학에서 배운 정수나 실수는 하나의 물리량인 크기를 표현하는데, 이 값을 스칼라 라고 함
scalar_arr = np.array(43)
# array에서 스칼라는 대괄호 없이 숫자만 표시
# 대괄호는 벡터(1차원 배열) 부터 사용
scalar_arr
array(43)

 

type(scalar_arr)
numpy.ndarray

 

 

# 스칼라는 0차원
scalar_arr.ndim
0

 

# 축이 없기 때문에 빈 튜플
scalar_arr.shape
()

 

# array로 스칼라를 만들어도 dtype을 가짐
scalar_arr.dtype
dtype('int32')

 

 

벡터

  • 1차원 배열
vector = np.array([43])
vector
array([43])
type(vector)
numpy.ndarray

 

# 차원수
vector.ndim
1

 

# 데이터 개수
vector.shape
(1,)

 

 

배열 생성 함수

zeros

  • 크기가 정해져 있고 모든 값이 0인 배열 생성
  • 인수로는 배열의 크기를 전달

 

# 1차원 배열
zero_arr = np.zeros(5)
zero_arr
array([0., 0., 0., 0., 0.])

 

 

# 2차원 배열
zero_arr2 = np.zeros((2, 3))
zero_arr2
array([[0., 0., 0.],
       [0., 0., 0.]])

 

  • dtype 인수를 명시하면 해당 자료형 원소 배열을 생성
zero_arr3 = np.zeros((5, 2), dtype = int)
zero_arr3

 

array([[0, 0],
       [0, 0],
       [0, 0],
       [0, 0],
       [0, 0]])

 

 

ones

  • 모든 값이 1인 배열 생성
one_arr = np.ones((2, 3, 4), dtype = int)
one_arr
array([[[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]],

       [[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]]])

 

zeros_like, ones_like

  • 다른 배열과 같은 크기의 배열을 생성
zero_arr4 = np.zeros_like(one_arr)
zero_arr4

 

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]]])

 

one_arr2 = np.ones_like(zero_arr3)
one_arr2
array([[1, 1],
       [1, 1],
       [1, 1],
       [1, 1],
       [1, 1]])

 

 

np.ones_like(zero_arr2)
array([[1., 1., 1.],
       [1., 1., 1.]])

 

 

arange

  • 파이썬의 range와 유사한 함수
  • range와 같이 시작점, 종료점, 증감값을 인자로 전달
    • 종료점은 필수
vector = np.arange(10)
vector
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

 

vector.dtype
dtype('int32')

 

인자로 실수를 지정할 수 있음
range(10.5, 20.5, 0.5)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[71], line 1
----> 1 range(10.5, 20.5, 0.5)

TypeError: 'float' object cannot be interpreted as an integer

 

 

vector1 = np.arange(10.5, 20.5, 0.5)
vector1
array([10.5, 11. , 11.5, 12. , 12.5, 13. , 13.5, 14. , 14.5, 15. , 15.5,
       16. , 16.5, 17. , 17.5, 18. , 18.5, 19. , 19.5, 20. ])

 

vector1.dtype
dtype('float64')

 

# 1차원 배열을 생성하면서 바로 2차원 배열로 형상 변경
vector2 = np.arange(60).reshape(6, 10)
vector2
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, 24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
       [50, 51, 52, 53, 54, 55, 56, 57, 58, 59]])

 

 

vector2.shape
(6, 10)

 

vector3 = np.arange(60).reshape(3, 4, 5)
vector3
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, 24],
        [25, 26, 27, 28, 29],
        [30, 31, 32, 33, 34],
        [35, 36, 37, 38, 39]],

       [[40, 41, 42, 43, 44],
        [45, 46, 47, 48, 49],
        [50, 51, 52, 53, 54],
        [55, 56, 57, 58, 59]]])

 

 

 

linspace

  • arange와의 차이점
    • 종료점도 포함해서 원소를 생성
    • 세 번째 인자로 간격 대신 원소의 개수를 전달
      • 초기값은 50
    • 기본으로 실수를 가진 원소를 만듦

 

lin = np.linspace(1, 10, 10)
lin
array([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.])

 

 

lin.dtype
dtype('float64')

 

 

# 값의 간격을 표시하는 증분(incremental)을 확인할 수 있음
lin2 = np.linspace(1, 2, 10, retstep = True)
lin2
(array([1.        , 1.11111111, 1.22222222, 1.33333333, 1.44444444,
        1.55555556, 1.66666667, 1.77777778, 1.88888889, 2.        ]),
 0.1111111111111111)

 

 

# 종료값을 제외할 수도 있음
lin3 = np.linspace(1, 10, 10, endpoint = False, retstep = True)
lin3
(array([1. , 1.9, 2.8, 3.7, 4.6, 5.5, 6.4, 7.3, 8.2, 9.1]), 0.9)

 

 

벡터의 크기 및 단위 벡터 구하기

  • 선형대수의 벡터는 크기와 방향성을 가짐
  • 벡터의 크기는 노름(norm)이라고 부름
  • 벡터는 평면 좌표의 점을 표시하듯이 2개의 원소를 가진 것을 대표적으로 사용
    • 벡터의 크기는 피타고라스의 정리를 사용

 

 

벡터의 크기 계산

# 두 개의 원소를 가지는 1차원 배열 생성
v = np.array([3, 4])
v
array([3, 4])

 

# 벡터의 크기를 구하기 위해 각 원소를 제곱하여 합산한 후 제곱근 계산
v_p = np.power(v, 2)
v_p
array([ 9, 16], dtype=int32)

 

 

v_r = np.sqrt(np.sum(v_p))
v_r
5.0

 

 

# 피타고라스 정리를 구하는 hypot 함수를 사용할 수도 있음
np.hypot(v[0], v[1])
5.0

 

# 더 많은 원소를 가진 벡터를 가지고 크기를 구할 때는 np.linalg.norm 함수를 사용하면 간단함
np.linalg.norm(v)
5.0

 

 

단위 벡터(unit vector)

  • 벡터의 크기가 항상 1인 경우를 단위 벡터라고 함
  • 표준 단위벡터(standard unit vector)는 원소 중에 하나가 1이고 나머지 원소가 0인 경우
# 표준 단위벡터의 예시
e1 = np.array([1, 0, 0])
print(np.linalg.norm(e1))
1.0

 

e2 = np.array([0, 1, 0])
print(np.linalg.norm(e2))
1.0

 

 

  • 일반적인 단위 벡터를 구할 때는 벡터의 크기를 구하고 각 원소를 벡터의 크기로 나누어서 구함
# 1차원 배열을 생성하고 이 배열의 크기를 계산
v_3 = np.array([1, 2, 3])
v_3
array([1, 2, 3])

 

v_3_n = np.linalg.norm(v_3)
v_3_n
3.7416573867739413

 

 

# 해당 벡터를 벡터의 크기로 나눈 후에 norm을 계산하면 1이 나옴
# 벡터의 크기가 1인 경우를 단위 벡터라고 함
v_3_u = v_3 / v_3_n
v_3_u
array([0.26726124, 0.53452248, 0.80178373])

 

 

np.linalg.norm(v_3_u)
1.0

 

 

벡터 상등과 벡터간 거리

  • 벡터의 상등(equality) : 벡터의 크기와 방향이 같은 경우
    • 크기는 같지만 방향이 다른 경우는 상등이 아님
# 두 벡터가 동일한 경우
a = np.array([3, 4])
b = np.array([3, 4])
# np.array_equal() : 두 배열이 동일한지 확인하는 함수
np.array_equal(a, b)
True

 

 

# 동일한 두 개의 배열의 크기를 구하여 크기가 동일한지 비교
a_1 = np.linalg.norm(a)
b_1 = np.linalg.norm(b)
a_1 == b_1
True

 

 

b_2 = np.linalg.norm(-b)
b_2
5.0

 

a_1
5.0

 

 

# 방향은 반대지만 크기는 같음
a_1 == b_2
True

 

# 하지만 방향이 반대이기 때문에 동일한 벡터는 아님
np.array_equal(a, -b)
False

 

  • 두 벡터의 거리는 두 벡터 간의 차를 계산한 후에 제곱을 합산한 후 제곱근 계산
# 두 개의 벡터 생성
c = np.arange(10, 13)
d = np.arange(0, 3)

 

c
array([10, 11, 12])

 

d
array([0, 1, 2])

 

np.sqrt(np.sum(np.square(c - d)))
17.320508075688775

 

# norm 함수에 두 벡터의 차를 인자로 전달하는 방법도 있음
np.linalg.norm(c-d)
17.320508075688775

 

 

행렬(Matrix)

  • 벡터는 하나의 차원이 축을 가져서 1차원 배열로 표시했지만 행렬은 두 개의 축인 차원을 가짐
    • 이 차원의 수평 방향을 행(row), 수직 방향을 열(column)이라고 함
  • 보통 선형대수는 대괄호 내에 행과 열의 인덱스 위치에 숫자를 나열해서 표시
  • 행렬을 부를 때는 보통 m개 행, n개 열로 이루어져서 m x n 행렬이라고 함
a = np.array([[43], [44]])
a
array([[43],
       [44]])

 

type(a)
numpy.ndarray

 

# a 변수에 저장된 것도 하나의 배열이므로 하나의 자료형을 가짐
a.dtype
dtype('int32')

 

# 배열의 차원 확인
a.ndim
2

 

a.shape
(2, 1)

 

# np.full(형상, 채워질 값, 자료형) : 다차원 배열의 원소의 값을 동일하게 채움
b = np.full((3, 4), 7, "int64")
b
array([[7, 7, 7, 7],
       [7, 7, 7, 7],
       [7, 7, 7, 7]], dtype=int64)

 

 

2차원을 1차원으로 축소

x = np.array([[1, 4, 5],
             [4, 5, 6],
             [7, 8, 9]])
x

 

array([[1, 4, 5],
       [4, 5, 6],
       [7, 8, 9]])
# flatten() : 1차원 배열을 반환. 배열의 복사본을 반환
x.flatten()
array([1, 4, 5, 4, 5, 6, 7, 8, 9])
x
array([[1, 4, 5],
       [4, 5, 6],
       [7, 8, 9]])
# ravel() : 1차원 배열을 반환. 원본 배열의 뷰를 제공
x.ravel()
array([1, 4, 5, 4, 5, 6, 7, 8, 9])
x
array([[1, 4, 5],
       [4, 5, 6],
       [7, 8, 9]])

 

# flatten() 은 원본 배열에는 영향을 미치지 않음
np.may_share_memory(x.flatten(), x)
False

 

# ravel()은 원본 배열에 영향을 미칠 수 있음
np.may_share_memory(x.ravel(), x)
True

 

 

행렬의 종류

 

정사각행렬(Square Matrix)

  • 동일한 행과 열을 가져서 n x n 행렬인 경우를 정사각행렬이라고 함
  • 정사각행렬의 차수(order)가 n이면 n차 정사각행렬이라고 부름
 

행렬의 궤적(Trace)

  • 정사각행렬은 가장 일반적인 행렬
  • 정사각행렬의 행과 열의 동일한 인덱스에 있는 원소들은 대각선상에 위치함
  • 이 대각선상의 원소들의 합을 행렬의 궤적(Trace)이라고 부름
  • 행렬의 궤적은 정사각행렬의 전치를 사용해도 대각선의 원소는 변하지 않아 궤적의 합이 항상 동일
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
a
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

 

a.shape
(3, 3)

 

# np.trace() : 행렬의 궤적을 계산
np.trace(a)
15

 

 

a.trace()
15

 

 

대각행렬(Diagonal Matrix)

  • 정사각행렬일 때 대각선의 원소(인덱스가 같은값)를 제외한 모든 원소가 0으로 구성되는 행렬을 대각행렬 이라고 함
b = np.array([[1, 0, 0], 
              [0, 3, 0], 
              [0, 0, 5]])
b
array([[1, 0, 0],
       [0, 3, 0],
       [0, 0, 5]])

 

# np.diagonal() : 대각선의 원소만 추출
b.diagonal()
array([1, 3, 5])

 

np.diagonal(b)
array([1, 3, 5])

 

 

대각행렬의 위치(diag)

  • 대각행렬은 행과 열의 인덱스가 같을 때 대각선의 원소에 값을 넣어서 만드는데, 이 대각선을 주 대각선이라고 함
  • 주 대각선 이외의 대각선의 위치는 주 대각선을 기준0으로 하고 상위는 양수로, 하위는 음수로 표시

 

# np.diag(1차원배열, 대각선의 위치) : 대각행렬 생성
# 1차원 배열을 대각선에 위치시킨 후, 행렬의 나머지는 0으로 채움
c = np.diag(np.arange(1, 4))
c

 

array([[1, 0, 0],
       [0, 2, 0],
       [0, 0, 3]])

 

 

 
# 주 대각선보다 한칸 위에 대각선을 구성
d = np.diag(np.arange(1, 4), k = 1)
d

 

array([[0, 1, 0, 0],
       [0, 0, 2, 0],
       [0, 0, 0, 3],
       [0, 0, 0, 0]])

 

 

# 주 대각선보다 한칸 아래에 대각선을 구성
e = np.diag(np.arange(1, 4), k = -1)
e
array([[0, 0, 0, 0],
       [1, 0, 0, 0],
       [0, 2, 0, 0],
       [0, 0, 3, 0]])

 

 

단위행렬(Unit Matrix)

  • 대각행렬 중에 주 대각선의 원소가 모두 1인 행렬을 단위행렬이라고 부름
  • 임의의 수에 1을 곱하면 임의의 수에 변화가 없는 것처럼 행렬에 다른 행렬곱을 수행해도 항상 동일한 행렬을 반환하는 행렬임
  • 보통 단위 행렬은 대문자 I로 표시
# np.eye(행의 개수, 열의 개수) : 단위 행렬 만들기
h = np.eye(3, 3)
h
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

 

# np.eye() 메서드는 대각선의 위치를 조정할 수 있도록 매개변수 k를 제공
i = np.eye(3, 3, k = 1)
i
array([[0., 1., 0.],
       [0., 0., 1.],
       [0., 0., 0.]])

 

 

항등행렬(Identity Matrix)

  • 단위행렬은 항등행렬 이라고도 부름
# 단위행렬을 만들어주는 함수
I = np.identity(3)
I
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])

 

# 항등행렬을 다른 배열과 곱하면 배열의 값은 변경되지 않음
s = np.arange(1, 10).reshape(3, 3)
s
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

 

s @ I
array([[1., 2., 3.],
       [4., 5., 6.],
       [7., 8., 9.]])

 

 

삼각행렬

  • 정사각행렬에서 주 대각선을 기준으로 위나 아래의 원소의 값이 전부 0이고 나머지 부분에는 값이 들어있는 행렬을 삼각행렬(Triangular Matrix)라고 함
  • 삼각행렬은 주 대각선을 기준으로 원소의 값이 들어간 위치에 따라 이름을 다르게 부여함
    • 대각선 위 부분만 0이 아닌 원소로 구성하면 상삼각행렬(Upper triangular matrix)
    • 주 대각선 아래 부분만 0이 아닌 원소로 구성하면 하삼각행렬(Lower triangular matrix)
a = np.arange(1, 10).reshape(3, 3)
a
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

 

# np.tril(): 만들어진 행렬을 인자로 전달하면 하삼각행렬을 반환
np.tril(a)
array([[1, 0, 0],
       [4, 5, 0],
       [7, 8, 9]])

 

# np.tril()의 매개변수 k에 대각선의 위치를 이동하는 것도 가능
np.tril(a, k = -1)
array([[0, 0, 0],
       [4, 0, 0],
       [7, 8, 0]])

 

# np.triu(): 만들어진 행렬을 인자로 전달하면 상삼각행렬을 반환
np.triu(a)
array([[1, 2, 3],
       [0, 5, 6],
       [0, 0, 9]])

 

 

np.triu(a, k = 1)
array([[0, 2, 3],
       [0, 0, 6],
       [0, 0, 0]])

 

 

삼각행렬의 전치관계

  • 삼각행렬의 전치관계는 하삼각행렬은 상삼각행렬로, 상삼각행렬은 하삼각행렬로 변함
b = np.triu(a)
b
array([[1, 2, 3],
       [0, 5, 6],
       [0, 0, 9]])

 

b.T
array([[1, 0, 0],
       [2, 5, 0],
       [3, 6, 9]])

 

c = np.tril(a)
c
array([[1, 0, 0],
       [4, 5, 0],
       [7, 8, 9]])

 

c.T
array([[1, 4, 7],
       [0, 5, 8],
       [0, 0, 9]])

 

  • 상삼각행렬과 하삼각행렬의 동일 인덱스 요소의 곱은 주 대각선만 곱한 결과임
b * c
array([[ 1,  0,  0],
       [ 0, 25,  0],
       [ 0,  0, 81]])

 

대칭행렬(symmetric matrix)

  • 주 대각선을 기준으로 아래의 원소와 위의 원소가 동일한 값을 가진 행렬
    • 대칭행렬을 가지고 전치행렬을 만들어도 동일한 형태를 유지함
  • 따라서 대칭행렬의 원소 간의 관계를 수식으로 표현하면 (aij)=(aji) 

$(a_{ij}) = (a_{ji})$ (lateX(레이텍)으로 표시)

 

# 대칭행렬
x = np.array([[1, 2, 3],
             [2, 3, 5],
             [3, 5, 6]])
x
array([[1, 2, 3],
       [2, 3, 5],
       [3, 5, 6]])

 

 

# 대칭행렬의 전치행렬을 확인해도 동일함
x.T
array([[1, 2, 3],
       [2, 3, 5],
       [3, 5, 6]])
  • 대칭 행렬과 해당 행렬의 전치행렬을 더하면 원소들의 값이 두 배로 변하지만 항상 대칭행렬임
  • @ 연산을 해도 대칭행렬
x + x.T
array([[ 2,  4,  6],
       [ 4,  6, 10],
       [ 6, 10, 12]])

 

x @ x.T
array([[14, 23, 31],
       [23, 38, 51],
       [31, 51, 70]])

 

 

반 대칭행렬(Skew Symmetric Matrix)

  • 주대각 성분을 기준으로 값은 같지만 부호가 반대의 원소를 가진 행렬
  • 이 행렬과 전치행렬 간의 덧셈을 하면 전체 값이 0이 나옴
    • 원소들이 대칭이지만 부호가 반대이므로 동일한 값을 빼는 것과 같음
y = np.array([[0, 2, -3],
              [-2, 0, 5],
              [3, -5, 0]])
y
array([[ 0,  2, -3],
       [-2,  0,  5],
       [ 3, -5,  0]])

 

y + y.T
array([[0, 0, 0],
       [0, 0, 0],
       [0, 0, 0]])

 

# 전치행렬과 원 행렬에 부호를 변경하면 모든 원소는 동일한 값임
y.T == -y
array([[ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True]])

 

 

# 반 대칭행렬과 이 행렬의 전치행렬 간의 뺄셈을 계산하면 반 대칭행렬을 그대로 유지함
y - y.T
array([[  0,   4,  -6],
       [ -4,   0,  10],
       [  6, -10,   0]])

 

# 반 대칭행렬과 이 행렬의 전치행렬을 행렬곱 연산으로 계산하면 대칭행렬로 변경됨
y @ y.T
array([[ 13, -15, -10],
       [-15,  29,  -6],
       [-10,  -6,  34]])
728x90

'06_Numpy' 카테고리의 다른 글

Numpy(넘파이)_연습문제  (0) 2025.03.11
01_넘파이(Numpy)_기초  (0) 2025.03.10
넘파이(NumPy) documentation 확인 방법  (0) 2025.03.10