컴퓨터/프로그래밍

[Python] 파이썬 기초 11: Numpy(5, 6)

옆동네애옹이 2024. 9. 6. 11:33
728x90

Numpy 다차원 배열의 Indexing

  • 기본 indexing
  • 고급 indexing
  • field access: 구조화 배열에 사용 (설명 생략)

기본 indexing & slicing

  • 0 ≤ ni ≤ di
    • i: axis 번호(in 다차원 배열)
    • ni: axis i의 index
    • diL axis i의 원소의 개수
  • ni < 0인 경우는 ni + di
  • 기본 slicing 문법: start : end : stop < 기존 파이썬 slicing과 동일
a = np.arnage(48).reshape(8, 6)
a[(6, 3)] # 39(tuple로 원소 접근)
a[6, 3] # 39(소괄호 생략 가능)
a[6][3] # 39(인덱싱 사용 가능)
a[(-2, -3)] # 39(음수 index: 뒤에서부터 n번째)
a[:5,2] # array([2, 8, 14, 20, 26])
a[:5:2,2] # array([2, 14, 26])

a[([0, 2], [1, 3])] # array([1, 15]): 2개의 list를 index로 지정해서 tuple로 반환
a[[0, 1], [1, 3]] # array([1, 9]): tuple 표현에서 괄호 생략 가능
a[(0, 1), (1, 3)] # array([1, 9])
# 배열 a에서 0, 5, 42, 47을 원소로 갖는 배열 만들기(네 귀퉁이)
a[[0, 0, 7, 7], [0, 5, 0, 5]] # array([0, 5, 42, 47])
a[[[0, 0,], [7, 7]], [[0, 5], [0, 5]]] # 2d array
a[[[0, 1, 30], [5, 7, 6]], [[0, 2, 3], [2, 5, 0]]]
# axis의 index (2, 3): index의 차원에 맞춰 반환되는 차원 결정됨

a[[0, 3, 1], ::2] # axis별로 index slicing 혼합해서 사용 가능.

Numpy 다차원 배열의 Bool 배열 indexing

a = np.arange(0, 10, 2) # array([0, 2, 4, 6, 8])
idx = np.array[[T, T, F, T, F]
a[idx] # array([0, 2, 6]): idx를 다른 배열의 index로 지정할 수 있어.
# 값이 True인 index가 a의 index로 저장할 수 있어

# Bool 배열 만드는 법
1 < a # array([F, T, T, T, T]): 비교 연산자 적용(broadcasting)
a < 3 # array([T, T, F, F, F])
(1 < a) & (a < 3) # array([F, T, F, F, F]): 두 비교연산 논리곱
index = (1 < a) & (a < 3)
index 
import matplotlib.pyplot as plt
from scipy import misc
face = misc.face()
plt.impshow(face)
plt.show()
face.shape # (768, 1024, 3): 해당 영상은 세로 768, 가로 1024, 한 픽셀이 RGB 3개 값으로 구성됨

타원 이용, raccoon의 얼굴 부분만 남기고 검은색으로 변경하기: Bool array indexing 사용

타원의 중심 (Xc, Yc)

  • 타원의 방정식

$(x-xc)^2/a^2 + (y-yc)^2/b^2 = 1$

  • 타원의 외부영역

$(x-xc)^2/a^2 + (y-yc)^2/b^2 > 1$

  • 영상에서 왼쪽 위 모서리 좌표가 (0, 0)이고, 세로축의 아래방향을 x축의 양의 방향, 가로축의 오른쪽 방향을 y축의 양의 방향으로 정의
  • 3차원 영상 배열에서, axis0=x축, axis1=y축, axis2=RGB(color)
# 2차원 배열의 axis의 X,Y 형식 표현: grid표현
# numpy의 broadcasting 이용하면 좀 쉽게 표현할 수 있음
np.ogrid[0:5, 0:3] # ogrid: numpy 배열의 mash grid return.
# x축, y축 배열을 반환
X, Y = np.ogrid[0:face.shape[0], 0:face.shape[1]]
X,shape, Y.shape #((768, 1), (1, 1024))

# 3차원 영상 배열에서, axis0=x축, axis1=y축, axis2=RGB(color)
# 즉, axis0, 1로 공간 상 위치 나타낼 수 있음.
# 각 pixel이 2d array 사이 표현 가능: axis의 index 표현
face = misc.face()
center = np.array([350, 650]) # 라쿤의 얼굴
a = 180 # x축 방향이 단축
b = 270 # y축 방향이 장축

idx = (X-center[0])**2/a**2 + (Y-center[1])**2 / b**2 > 0.98
idx &= (X-center[0])**2/a**2 + (Y-center[1])**2 / b**2 < 1.02

face_with_eclipse = face.copy()
face_with_eclipse[idx] = (255, 255, 255)
plt.imshow(face_with_eclipse)
plt.show()
center = np.array([360, 650])
a = 180
b = 270

idx = (X-center[0])**2/a**2 + (Y-center[1])**2 / b**2 > 1
# idx는 2차원 bool array(타원 외부: True, 내부: False 지정)
cropped = face.copy()
cropped[idx] = (0, 0, 0) # True인 부분만 검은색으로 변경
plt.imshow(cropped)
plt.show()

마름모 이용, raccoon의 얼굴 부분만 남기고 검은색으로 변경하기

  • 네 꼭지점의 좌표:

$A(350, 380), B(170, 650), C(350, 920), D(530, 650)$

  • 두 점(x1, y1)과 (x2, y2)를 잇는 직선의 방정식

$y - y1 = ((y2-y1)/(x2-x1))*(x-x1)$

  • 두 점 (x1, y1)과 (x2, y2)를 잇는 직선의 윗부분

$y - y1 > ((y2-y1)/(x2-x1)) * (x-x1)$

  • 또는

$y-y2 > ((y2-y1)/(x2-x1))*(x-x2)$

직선 윗부분, 아랫부분이 수학에서의 x,y축과 다름에 주의할 것.

numpy 영상 배열에서는 우측상단이 (0,0)이고, 우측/하단으로 갈 수록 좌표 커짐.

# 4개 꼭지점 좌표
A = np.array([350, 380])
B = np.array([170, 650])
C = np.array([350, 920])
D = np.array([530, 650])

# 직선 AB 윗부분
slopeAB = (B-A)[1] / (B-A)[0]
idx1 = Y - A[1] < slopeAB * (X - A[0]) # y값이 직선 식보다 작은 부분.
img1 = face.copy()
img1[idx1] = (0, 0, 0) #아랫 부분에는 True

# 직선 CD 윗부분
idx2 = Y - C[1] > slopeAB * (X - C[0])
img1[idx2] = (0, 0, 0)

# 직선 BC 윗부분
slopeBC = (C-B)[1] / (C-B)[0]
idx3 = Y - C[1] > slopeBC * (X - C[0])
img1[idx3] = (0, 0, 0)

# 직선 DA 아랫부분
idx4 = Y - A[1] < slopeBV * (X - A[0])
img1[idx4] = (0, 0, 0)

plt.imshow(img1)
plt.show()
# 정리하면 bool 배열로 True만 (0, 0, 0)으로 지정한 거.
face = misc.face()
A = np.array([350, 380])
B = np.array([170, 650])
C = np.array([350, 920])
D = np.array([530, 650])

slopeAB = (B-A)[1] / (B-A)[0]
idx1 = Y - A[1] < slopeAB * (X - A[0])
idx2 = Y - C[1] > slopeAB * (X - C[0])
slopeBC = (C-B)[1] / (C-B)[0]
idx3 = Y - C[1] > slopeBC * (X - C[0])
idx4 = Y - A[1] < slopeBV * (X - A[0])
idx = idx1 & idx2 & idx3 & idx4
img2 = face.copy()
img2[idx] = (0, 0, 0)

plt.imshow(img1)
plt.show()

Numpy ndarray를 이용한 연산

  • 기본적으로 원소&원소 간 vector 연산 이용.
a = np.array([1, 2, 3, 3, 2, 5]).reshape(2, 3)
b = np.array([-1, 3, 5], [1, 4, 2]])
c = a + b

np.add(a, b) # 덧셈연산
a - b # 뺄셈연산
np.subtract(a, b) # 뺼셈연산, as well

Numpy 다차원 배열의 원소 단위 사칙연산

a * b # 곱셈연산
np.multiply(a, b) # 곱셈연산

a / b  # 나눗셈 연산 
np.divide(a, b) # 나눗셈 연산
np.true_divide(a, b) # 나눗셈 연산

a // b # 나눗셈 정수로 반환
np.floor_divide(a, b) # 나눗셈 정수로 반환

a % b # 나머지 연산
np.mod(a, b) #나머지 연산

np.divmod(a, b) #배열 a 원소들을 배열 b로 나눠 몫/나머지 배열 각각 반환

a ** 2 # 제곱
pow(a, 2) # python의 core library에서 제공하는 함수, np각 원소에 pow 적용 배열
np.power(a, 2) # 제곱 연산

np.sqrt(b) # root 값 반환하는데, 음수 있는 경우 'nan'으로 반환. 
# nan: not a number IEEE 754에서의 nan과 같으며, 유효하지 않은 missing value.

b ** 0.5 # 수의 0.5승(1/2승)으로 표현 가능

1 / a # 각 배열의 역수 반환
pow(a, b)  # ValueError: integers to negative integer powers are not allowed

# 정수의 음수승은 구할 수 없다
a ** b # 가능

Numpy bit shift

a = np.array([1, 2, 3])
b = np.array([2, 3, 4])
a ** b # array([1, 8, 81])
np.power(a, b) # array([1, 8, 81])

# bit shift의 피연산자로 배열 사용 가능
2 << a # array([4, 8, 16]): 2를 왼쪽으로 1, 2, 3bit 이동
# 2: bit shift 대상, array([1, 2, 3])이 bit 이동 연산자.
a << 2
# a에 있는 각 원소를 왼쪽으로 2bit shift

Numpy의 수학 함수: 삼각함수

  • math module: argument가 스칼라값
  • numpy: universal function. 함수를 받아서 배열로 반환
x = np.array([0, 30, 45, 60, 90])
x = np.radians(x) # x를 radian 값으로 변경
# sin, cos함수는 argumnet를 radian으로 받으며 sin, cos 값을 array로 만들어 반환
y = np.sin(x) 
z = np.cos(x)

# 어떤 경우는 실수로 표현되나, 어떤 경우는 scientific notation으로 표현

# printoptions: 화면 출력 유효자리 숫자 제한 가능
# but, scientific notation 사용될지는 모름
with np.printoptions(precision=3):
	print(y)
	print(z)
# argument suppress=True: scientific notation 사용X
with.np.printiptions(precision=3, suppress=True):
	print(y)
	print(z)

Numpy의 Universal Function, ufunc

  • universal function: numpy내에서 'vectorization'을 구현(벡터화)
    • vectorization: 반복되는 문장을 벡터 기반 연산으로 변경하는 연산
    • 산술, 비교 연산에 np배열을 피연산자로 사용하는 경우, 같은 idx끼리 연산 적용
    • 반복 구조(loop)대신 ndarray에 모든 함수를 적용하는 것
import math
import numpy as np
x = np.array([1, 4, 9])
y = [math.sqrt(a) for a in x]
print(y) # [1.0, 2.0, 3.0]

x = np.array([1, 4, 9])
y = np.sqrt(x) 
print(y) # [1, 2, 3]
  • Numpy 제공 vectorization functions
    • sin, cos, tan, arcsin, arccos, arctan, hypot, arctan2, degrees, radians, rad2deg, deg2rad, log, log10.
    • e.g. np.hypot(x, y) : 전달되는 두 argument의 제곱의 합의 sqrt 써서 ndarray로 return
    • shape이 달라도, broadcasting 가능한 구조면 return 가능

Numpy의 통계 함수: min, max, median, mean, std, var

x = np.random.normal(3, 0.5, 5) #평균 3, 표편 0.5인 정규분포를 따르는 난수 5개 출력

np.min(x) # 최소값
np.max(x) # 최대값
np.median(x) # 중간값 
np.mean(x) # 평균
np.std(x) # 표준편차
np.var(x) # 분산

# 다차원 배열에 적용 가능: axis 지정 여부에 따라 함수 적용 원소 대상 달라짐
# axis 지정X: 다차원 배열을 1차원 배열로 취급: 다차원 배열의 모든 원소에서 최대/중간 찾음
x2 = np.random.normal(3, 0.5, (4,5))
 #평균 3, 표편 0.5인 정규분포를 따르는 난수 20개를 (4, 5)shape으로 출력
np.min(x2)
np.min(x2)
# axis 지정O: 지정된 axis 방향으로 값 출력
np.min(x2, axis=0) # 최소값 5개 array로 return
np.min(x2, axis=1) # 최소값 4개 array로 return
# n차원 배열도 마찬가지, axis 지정해야 다른 결과 출력됨

RGB color > 흑백으로 변경

from scipy import misc
face = misc.face()
plt.imshow(face)
plt.show()

face_gray = np.mean(face, axis=-1) # 각 pixel의 RGB값 평균 구하기, axis2=-1
face_gray.shape, face_gray.dtype #(768, 1024), dtype('float64')
# 원래 color 영상 표현은 uint8이었는데 평균 구하는 과정에서 float으로 변경
face_gray.min(), face_gray.max() # (0.333333333, 254.0)
# 최대, 최소값이 8bit 표현: axis 지정 없으므로 전체배열에서 최대/최소 나옴
# 0, 255 범위 벗어나지 않음

plt.imshow(face_gray, cmap='gray')
plt.show()
import imageio
file1 = '....jpg'
file2 = '...jpg'
# jpag = 국제표준영상 압축하는 한 방법.
# 상당히 복잡한 알고리즘에 의해, 데이터 함축 저장된 것.

img1 = imageio.imread(file1)
img2 = imageio.imread(file2)
# imread: decoding 해서 영상 구성하는 배열 확인 가능.
imag1.shape, img2.shape # ((1276, 1920, 3), (1276, 1920, 3)): 두 이미지 가로세로 same

img = np.hstack((img1, img2)) # axis1방향으로 합성
plt.figure(figsize = (12, 6))
plt.imshow(img)
plt.show()

두 영상의 합성

$$ af(x, y) + (1-a)g(x, y), 0<=a<=1 $$

  • 영상의 2차원 식 표현: x, y 함수로 표현 가능.
  • x, y: 공간 상에서 위치 표현
  • f(x, y): x,y축에 있는 화소의 밝기. color라면 f는 3차원 vector(RGB)
  • 두 영상을 f(x, y), g(x, y)로 표현해서 더하는 연산 해보기.
# 영상의 정보를 3중 list로 표현했다면: 위 영상 합성을 위해 3중 for 필요
# 하지만 이 영상의 정보를 3d array로 표현했기 때문에, broadcasting 사용 가능
mixed_images = [a*img1 + (1-a)*img2 for a in np.arange(0, 1.1, 0.1)]
len(mixed_images) # 11개의 ndarray

# 11장의 image 표시. g가 2번째 영상이면 점점 커져.
plt.imshow(mixed_images[0].astype(np.uint8))
plt.show()

plt.imshow(mixed_images[2].astype(np.uint8))
plt.show()

plt.imshow(mixed_images[3].astype(np.uint8))
plt.show()

plt.imshow(mixed_images[5].astpype(np.uint8)) # 두 영상의 평균 영상
plt.imshow(mixed_images[6].astpype(np.uint8))
plt.imshow(mixed_images[10].astpype(np.uint8))

파일 입출력

  • ndarray 변수 arr을 file_name의 text로 저장

np.savetxt('file_name', arr, delimiter='')

  • file_name 텍스트 파일을 읽어서 ndarray 변수 arr에 할당

arr = np.loadtxt('file_name', delimiter='')

a = np.arange(1, 7).reshape((2, 3))
a.dtype # int32
np.savetxt('.csv', a, delimiter='.') #delimiter: 숫자 사이 구분자
# 실행하면 현재 directory에 저장됨

# scientific notation 말고 정수형으로 저장하고 싶을 때
np.savetxt('test_txt.csv', a, fmt="%6d', delimiter='.')

# 읽어들일 파일, delimiter는 ,(구분자)
b = np.loadtxt('.csv', delimiter='.')
b2 = np.loadtxt('.csv', delimiter='.') # 정수형 data여도 실수로 읽어들임
b3 = np.loadtxt('.csv', dtype=np.int, delimiter='.') # 정수형 데이터로 읽어들이기

Numpy 함수 이용해서 그래프 그리기

import matplotlib.pyplot as plt
import numpy as np

plt.plot([1, 2, 3, 4], [2, 3, 2, 3])
# 2개의 sequence type data로, 같은 위치의 두 값을 좌표로 삼아서 직선으로 연결
# (1, 2) (2, 3) (3, 2) (4, 3) 직선으로 연결: 가로세로축 연결
plt.show()

# 직선으로 연결할 점의 x좌표들을 모은 배열 / 각 x에 대응하는 sin 배열. 배열 2개 필요해
t = np.linspace(0, 2, 201) # 가로축 원하는 구간: 일정한 배열 값으로 만들어

x1 = np.sin(2*np.pi*t) # 1d array t에 np.pi*2 곱해지고, 201개에 sin 적용해 배열로 제작
print(t.shape, x1.shape) # (201,) (201,)
plt.plot(t, x1)
plt.show()
# 눈으로 보기에 연속함수인 것처럼 보이지만, 사실 201개의 점들을 연결한 그림.
# 곡선 그릴 때 float함수에 충분히 많은 값 가져가야 해
a = np.array([[1, 2, 3]])
np.repeat(a, 3, axis=0)
# axis0 방향대로 a라는 배열을 3번 반복하는 함수

t = np.linspace(0, 2, 51)
x = 0.5*(np.sin(2*np.pi*t) + 1)
# -1 <= sin <= 1이므로, 1 더하면 0<=sin<=2, 0.5 곱하면 0<=sin<=1
# 즉 0, 1사이의 값을 가지며 원소 갯수 51개인 배열이 x에 저장
x = x.reshape(1, -1) # 1-by-51
x1 = np.repeat(x, 10, axis=0) # axis0방향으로 x를 10번 반복해 만든 array
x1 = imshow(x1, cmap='gray')
# imshow에 전달하는 이차원 배열은 실수일때 0,1사이를, 정수일때는 0,255 사이 값을 전달
# 0일 때는 검은색 1일 때는 흰색. 0.5/1/0.5/1 밝기 오르락 내리락 하는 걸 보여줌
plt.show()
728x90