250805 [ Day 22 ] - NumPy (4)

TaeHyun·2025년 8월 5일

TIL

목록 보기
22/185

시작하며

오늘은 NumPy의 마지막 수업이었다. 처음에는 행렬이라는 낯선 개념에 다양한 기능들까지 배우느라 따라가는 것만으로도 버거웠는데, 이제는 행렬과 NumPy에 꽤 익숙해진 것 같다. 특히 어려웠던 axis 개념도 이제는 충분히 이해하고 활용할 수 있을 것 같다.
물론 NumPy의 기능이 워낙 다양해서 문제를 풀 때마다 필요한 함수를 찾아보고 있지만, 계속 사용하다 보면 자주 쓰는 것들은 자연스럽게 익숙해질 것 같다.

행렬의 곱셈

  • 두 행렬을 곱해서 새로운 행렬을 생성하는 연산
  • 첫번째 행렬의 열수와 두번째 행렬의 행수가 같아야 곱셈이 가능
  • np.dot( ), np.matmul( ) → 행렬의 곱셈의 경우 np.matmul( ) 사용을 권장

np.dot(a, b)

  • 배열의 내적 연산 (같은 위치의 숫자끼리 곱해서 모두 더함)
  • 내적 : 두 벡터가 서로 얼마나 같은 방향을 보고 있는지 숫자로 나타내는 것
  • 데이터 분석에서 벡터란 배열과 같음
  • 스칼라 연산 (0 차원)
a = np.array(3)
b = np.array(4)

print(np.dot(a, b)) # 12

  • 1 차원 배열간 연산 → 내적 연산
a = np.array([1,2,3])
b = np.array([4,5,6])

print(np.dot(a, b)) # 32

  • 2 차원 배열간 연산 → 행렬 곱셈
a = np.array([[1,2,3], [4,5,6]])
b = np.array([[1,2], [3,4], [5,6]])

print(np.dot(a, b))
# [[22 28]
#  [49 64]]

np.matmul(a, b)

  • Matrix Multiplication
  • 스칼라 연산 시도시 에러 발생
a = np.array(3)
b = np.array(4)

print(np.matmul(a, b))
# ValueError: matmul: Input operand 0 does not have enough dimensions (has 0, gufunc core with signature (n?,k),(k,m?)->(n?,m?) requires 1)

  • 1 차원 배열간 연산 → 내적 연산
a = np.array([1,2])
b = np.array([3,4])

print(np.matmul(a, b)) # 11

  • 2 차원 배열간 연산 → 행렬 곱셈
a = np.array([[1,2,3], [4,5,6]])
b = np.array([[1,2], [3,4], [5,6]])

print(np.matmul(a, b))
# [[22 28]
#  [49 64]]

  • 2D * 1D 간 연산 가능
c = np.array([[1,2], [3,4]])
d = np.array([5,6])
print(np.matmul(c, d))
# [17 39]
print(np.matmul(d, c))
# [23 34]

  • A x B 배열과 B x C 배열의 연산 결과는 A x C 배열로 나옴
  • np.matmul( ) 연산은 @ 연산자로 사용 가능
e = np.array([[1,2,3]])
f = np.array([[1,2], [3,4], [5,6]])
print(np.matmul(e, f))
# [[22 28]]
print(e @ f)
# [[22 28]]

배열의 형태 변형과 차원 확장/축소

array.ravel()

  • 다차원 배열을 1 차원 배열로 펼침
  • 결과를 View로 반환 (View를 변경시 원본도 변경)
a = np.array([[1,2,3], [4,5,6]])
print(a)
# [[1 2 3]
#  [4 5 6]]

flat = a.ravel()
print(flat)
# [1 2 3 4 5 6]

flat[2] = 100
print(flat)
# [  1   2 100   4   5   6]

print(a)
# [[  1   2 100]
#  [  4   5   6]]

array.flatten()

  • 다차원 배열을 1 차원 배열로 펼침
  • 결과를 복사본으로 반환 (복사본 변경시 원본은 변경되지 않음)
a = np.array([[1,2], [3,4], [5,6], [7,8]])
print(a)
# [[1 2]
#  [3 4]
#  [5 6]
#  [7 8]]

flat = a.flatten()
print(flat)
# [1 2 3 4 5 6 7 8]

flat[4] = 0
print(flat)
# [1 2 3 4 0 6 7 8]

print(a)
# [[1 2]
#  [3 4]
#  [5 6]
#  [7 8]]

np.expand_dims(a, axis)

  • 지정한 위치에 차원 추가 (차원 확장)
  • 추가되는 차원은 1 차원
  • axis=0
a0 = np.expand_dims(a, axis=0)
print(a0.shape)
# (1, 2, 3)
print(a0)
# [[[1 2 3]
#   [4 5 6]]]

  • axis=1
a1 = np.expand_dims(a, axis=1)
print(a1.shape)
# (2, 1, 3)
print(a1)
# [[[1 2 3]]
# 
#  [[4 5 6]]]

  • axis=2
a2 = np.expand_dims(a, axis=2)
print(a2.shape)
# (2, 3, 1)
print(a2)
# [[[1]
#   [2]
#   [3]]
# 
#  [[4]
#   [5]
#   [6]]]

np.squeeze(a, axis)

  • 배열에서 크기가 1인 차원을 제거
  • axis 값을 지정하지 않는 경우 크기가 1인 차원 모두 제거
a = np.array([[[[1], [2], [3]]]])
print(a.shape)
# (1, 1, 3, 1)

s = np.squeeze(a)
print(s)
# [1 2 3]
print(s.shape)
# (3,)

  • axis 값을 지정하는 경우
a2 = np.zeros((1,4,1,2))
print(a2.shape)
# (1, 4, 1, 2)

s1 = np.squeeze(a2, axis=0)
print(s1.shape)
# (4, 1, 2)

s2 = np.squeeze(a2, axis=2)
print(s2.shape)
# (1, 4, 2)

  • 차원의 크기가 1이 아닌 차원은 제거할 수 없음
s3 = np.squeeze(a2, axis=1)
# ValueError: cannot select an axis to squeeze out which has size not equal to one

np.unique()

  • 배열에서 중복된 요소를 제거 후 고유값의 오름차순으로 배열 반환
a = np.array([1,1,2,2,6,2,2,3,3,3,4,4,5])

u1 = np.unique(a)
print(u1)
# [1 2 3 4 5 6]

  • 여러 요소를 반환 가능
u2, idx, inv, cnt = np.unique(a, return_index=True, return_inverse=True, return_counts=True)

print("인덱스 :", idx) # 고유값이 원본에서 처음 나타나는 인덱스
# 인덱스 : [ 0  2  7 10 12  4]
print("원본의 고유값 인덱스 :", inv)
# 원본의 고유값 인덱스 : [0 0 1 1 5 1 1 2 2 2 3 3 4]
print("값의 등장 횟수 :", cnt)
# 값의 등장 횟수 : [2 4 3 2 1 1]

배열의 결합과 분리

np.concatenate()

  • 배열 시퀀스를 결합
  • axis 값을 지정하지 않는 경우 기본 값 = 0
a = np.array([[1,2], [3,4]]) # (2, 2)
b = np.array([[5,6]])

result = np.concatenate((a, b))
print(result)
# [[1 2]
#  [3 4]
#  [5 6]]
print(result.shape)
# (3, 2)

c = np.array([[7], [8], [9]])
result2 = np.concatenate((result, c), axis=1)
print(result2)
# [[1 2 7]
#  [3 4 8]
#  [5 6 9]]
print(result2.shape)
# (3, 3)

  • 결합하는 axis를 제외한 나머지 차원이 같아야 결합 가능
result0 = np.concatenate((a, b), axis=1)
# ValueError

np.stack()

  • 새로운 차원을 추가하면서 결합
  • shape 형태가 같아야 결합 가능
a = np.array([1,2,3]) # (3,)
b = np.array([4,5,6])

# axis=0
s1 = np.stack((a,b))
print(s1)
# [[1 2 3]
#  [4 5 6]]
print(s1.shape)
# (2, 3)

# axis=1
s2 = np.stack((a,b), axis=1)
print(s2)
# [[1 4]
#  [2 5]
#  [3 6]]
print(s2.shape)
# (3, 2)

  • np.hstack() : 배열을 열 방향으로 순서대로 쌓음
a = np.array([1,2,3])
b = np.array([4,5,6])

h = np.hstack((a, b))
print(h)
# [1 2 3 4 5 6]

  • np.vstack() : 배열을 행 방향으로 순서대로 쌓음
a2 = np.array([[1], [2], [3]])
b2 = np.array([[4], [5], [6]])

v = np.vstack((a2, b2))
print(s)
# [[1]
#  [2]
#  [3]
#  [4]
#  [5]
#  [6]]

np.split()

  • 배열을 여러개의 하위배열로 분할
a = np.arange(9)
s = np.split(a, 3)
print(a)
# [0 1 2 3 4 5 6 7 8]
print(s)
# [array([0, 1, 2]), array([3, 4, 5]), array([6, 7, 8])]
print(s[0])
# [0 1 2]
print(s[1])
# [3 4 5]
print(s[2])
# [6 7 8]

  • 배열을 같은 크기로만 나눌 수 있음
a = np.arange(9)
s = np.split(a, 4)
# ValueError: array split does not result in an equal division

  • 인덱스를 기준으로 분할하면 일정하지 않은 크기로 분할 가능
a = np.arange(16).reshape(4,4)
print(a)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]
#  [12 13 14 15]]

s1 = np.split(a, [1,3])
for part in s1:
    print(part)
# [[0 1 2 3]]
# [[ 4  5  6  7]
#  [ 8  9 10 11]]
# [[12 13 14 15]]

  • axis=1 인 경우
s2 = np.split(a, 2, axis=1)
for part in s2:
    print(part)
# [[ 0  1]
#  [ 4  5]
#  [ 8  9]
#  [12 13]]
# [[ 2  3]
#  [ 6  7]
#  [10 11]
#  [14 15]]

배열의 정렬

np.sort(array), array.sort()

  • np.sort(array)
    • 정렬된 복사본 반환
  • array.sort()
    • 원본 배열을 정렬
a = np.array([3,1,4,2])
s = np.sort(a)
print(s)
print(a)
# [1 2 3 4]
# [3 1 4 2]
a.sort()
print(a)
# [1 2 3 4]

  • 내림차순 : sort한 후 배열을 뒤집어야 함
print(a[::-1])
# [4 3 2 1]

  • 2 차원 배열 정렬
a = [[6,5,4], [3,1,2]]

s1 = np.sort(a, axis=0)
print(s1)
# [[3 1 2]
#  [6 5 4]]

s2 = np.sort(a, axis=1)
print(s2)
# [[4 5 6]
#  [1 2 3]]

np.argsort()

  • 정렬 인덱스를 반환
a = np.array([3,1,4,2])
idx = np.argsort(a)
print(idx)
# [1 3 0 2]
print(a[idx])
# [1 2 3 4]

마치며

앞으로를 생각해보면 지금이 가장 중요한 시기인 것 같다. 프로젝트가 시작되고 바빠지면 기초 공부는 뒷전이 되기 쉬운데, 그렇게 되기 전에 지금부터 탄탄하게 기초를 다져놓아야 한다고 생각한다.하지만 막상 실천하려니 체력과 시간이 부족해서 현재 공부하고 있는 것들조차 충분히 못하고 있는 것 같아 아쉽다. 내일부터는 Pandas 공부가 시작된다고 하니, 지금이라도 할 수 있는 만큼 최대한 기초를 단단히 해놓아야겠다

NOTION

MY NOTION (NumPy. 02)
MY NOTION (NumPy. 03)

profile
Hello I'm TaeHyunAn, Currently Studying Data Analysis

0개의 댓글