Numpy는 Numerical Python의 약자, 과학 계산용 고성능 컴퓨팅과 데이터 분석에 필요한 파이썬 패키지
기본 패키지 관리자인 pip
에는 없어서 따로 설치해야 한다. conda를 사용한다면 기본적으로 포함 되어 있기도 하므로 conda list | grep numpy
로 설치 여부를 먼저 확인한다.
설치 명령어 pip install numpy
Numpy 공식 사이트에 소개된 Numpy의 장점
ndarray
데이터 타입을 지원한다.Numpy를 사용하기 위해 ndarray
객체를 생성해야 한다. ndarray
객체를 이용하면 파이썬에서 사용하는 대규모 데이터 집합을 n차원 배열로 담을 수 있다. ndarray
를 그냥 array라고 부르기도 한다.
ndarray
만들기ndarray
객체는 arange()
와 array([])
로 만들 수 있다.
import numpy as np
# 아래 A와 B는 결과적으로 같은 ndarray 객체를 생성한다.
A = np.arange(5)
B = np.array([0,1,2,3,4]) # 파이썬 리스트를 numpy ndarray로 변환
C = np.array([0,1,2,3,'4']) # A,B와 다른 형태
D = np.ndarray((5,), np.int64, np.array([0,1,2,3,4]))
# A,B와 같은 결과를 내지만, B의 방법을 권장한다.
print(A)
print(type(A))
print("---------------------")
print(B)
print(type(B))
print("---------------------")
print(C)
print(type(C))
print("---------------------")
print(D)
print(type(D))
print("---------------------")
출력
[0 1 2 3 4]
<class 'numpy.ndarray'>
--------------------------
[0 1 2 3 4]
<class 'numpy.ndarray'>
--------------------------
['0' '1' '2' '3' '4']
<class 'numpy.ndarray'>
--------------------------
[0 1 2 3 4]
<class 'numpy.ndarray'>
C는 '4'가 하나 다를 뿐인데 0,1,2,3이 모두 문자열로 바뀌었다.
numpy.ndarray
도 array이므로 모든 요소의 자료형이 동일해야 한다.
(파이썬의 유연함 : 문자열을 모두 숫자로 바꿀 수 는 없지만 숫자를 모두 문자열로 바꿀 수는 있다.)
그래서 숫자만 있던 ndarray
에 문자열이 들어가면 모든 숫자를 문자열로 해석해서 array의 요건을 맞춰준다.
ndarray.size
ndarray.shape
ndarray.ndim
reshape()
size, shape, ndim은 각각 행렬 내 원소의 개수, 행렬의 모양, 행렬의 축(axis)의 개수를 의미한다.
reshape()
메서드는 행렬의 모양을 바꿔준다. 주의할 점은 모양을 바꾸기 전,후의 행렬의 총 원소 개수(size)가 맞아야 한다.
A = np.arange(10).reshape(2,5) # 길이 10의 1차원 행렬을 2X5 2차원 행렬로 바꿔본다
print("행렬의 모양:", A.shape)
print("행렬의 축 개수:", A.ndim)
print("행렬 내 원소의 개수:", A.size)
2X5 행렬은 원소 10개와 행렬의 모양이 맞지만, 3X3으로 reshape하면 모양과 원소의 개수가 맞지 않아 에러가 난다
A = np.arange(10)
print('A: ', A)
B = np.arange(10).reshape(2,5)
print('B: ', B)
C = np.arange(10).reshape(3,3)
print('C: ', C)
C에서 에러가 발생했다.
ValueError: cannot reshape array of size 10 into shape (3,3)
Numpy 라이브러리 내부의 자료형들은 파이썬 내장함수와 동일하지만 헷갈리는 기능이 있다. 바로 type()
과 dtype()
메서드이다.
numpy.array.dtype
type()
예시
A= np.arange(6).reshape(2, 3)
print(A)
print(A.dtype)
print(type(A))
print("-------------------------")
B = np.array([0, 1, 2, 3, 4, 5])
print(B)
print(B.dtype)
print(type(B))
print("-------------------------")
C = np.array([0, 1, 2, 3, '4', 5])
print(C)
print(C.dtype)
print(type(C))
print("-------------------------")
D = np.array([0, 1, 2, 3, [4, 5], 6]) # 이런 ndarray도 만들어질까요?
print(D)
print(D.dtype)
print(type(D))
Numpy의 원소는 꼭 동일한 데이터 type이어야 하며, Numpy의 메서드 dtype
은 Numpy ndarray의 원소 데이터 타입을 반환한다. 반면 파이썬 내장함수 type(A)
을 사용하면 행렬 A의 자료형이 반환된다.
주의 깊게 볼 점은 D의 경우이다
원소 하나를 list로 바꿨는데, Numpy의 원소는 돌일한 데이터 type이어야 하므로 에러가 발생할 줄 알았지만 정상적으로 ndarray가 만들어졌다.
대신 D.dtype
은 object
를 리턴했다. object
는 파이썬 최상위 클래스로 Numpy는 dtype
을 object
로 지정해서라도 행렬 내 dtype
을 일치시켰다. 이후에 이런 상황을 방지하기 위해 이 기능을 deprecate 시켰다.
deprecate는 더 이상 관리하지 않는 기능을 뜻한다. 메서드를 deprecate 할 예정일 때는 기능은 성공적으로 실행시키고 error가 아닌 warning 문구로 경고만 해주는데, 이는 그 기능을 더 이상 못 쓰는 것은 아니지만 이제부터 관리하지 않으니 오류가 날 확률이 높다는 뜻이다.
일반적으로는 코드 수정 방향을 알려주기 때문에 메시지를 잘 읽어보는 것이 좋다.
출력문에 적힌대로 아래와 같이 수정한다. 직접적으로 dtype
을 입력해 오류를 방지한다.
D = np.array([0,1,2,3,[4,5],6], dtype=object)
print(D)
print(D.dtype)
print(type(D))
행렬 내부 원소 확인, Numpy가 행렬 내부의 원소의 data type을 실제로 변경하는지 확인
C = np.array([0,1,2,3,'4',5])
print(C[0])
print(type(C[0]))
print(C[4])
print(type(C[4]))
print("------------------------------")
D = np.array([0,1,2,3,[4,5],6], dtype=object)
print(D[0])
print(type(D[0]))
print(D[4])
print(type(D[4]))
C[0]는 행렬 안에 문자열 '0'으로 들어가 있지만, C[0]의 숫자 0은 행렬안에 정수 0으로 들어가 있다.
Numpy는 아주 똑똑하다. 어떤 경우라도 Array 답게 연산속도를 최적화하도록 원소들을 관리하지만, 필요에 따라 효율적인 방법으로 data type을 변화시켜 관리하고 있다.
Numpy는 수학적으로 의미가 있는 행렬들을 함수로 제공하고 있다.
eye()
zeros()
ones()
코드 예시
# 단위행렬
np.eye(3)
# 0 행렬
np.zeros([2,3])
# 1 행렬
np.ones([3,3])
Numpy의 강력한 연산 기능 중 하나인 브로드캐스트(broadcast)연산
ndarray 객체에 상수 연산을 하면 각각의 원소에 어떻게 연산이 적용되는지 확인한다.
A = np.arange(9).reshape(3,3)
A
A * 2
A + 2
일반적인 연산 결과가 나오는 것을 알 수 있다.
A = np.arange(9).reshape(3,3)
# 3X3 행렬에 1X3 행렬을 더한다
B = np.array([1,2,3])
print("A : ", A)
print("B : ", B)
print("\nA+B : ", A+B)
# 3X3 행렬에 3X1 행렬을 더한다
C = np.array([[1], [2], [3]])
print("A : ", A)
print("C : ", C)
print("\nA+C : ", A+C)
# 3X3 행렬에 1X2행렬을 더할 수는 없다.
D = np.array([1,2])
print("A : ", A)
print("D : ", D)
print("\nA+D : ", A+D) # 에러 발생
결과값을 보면 직관적으로 알 수 있다. 이해가 어렵다면 Numpy.org에서 제공하는 설명자료 참고
ndarray와 상수, 또는 서로 크기가 다른 ndarray끼리 산술연산이 가능한 기능을 브로드캐스팅이라고 한다. Numpy가 파이썬 내장 리스트와 구별되는 큰 특징 중 하나.
둘의 차이를 아래 예시를 통해 비교해본다.
print([1,2]+[3,4])
print([1,2]+3)
print(np.array([1,2])+np.array([3,4]))
print(np.array([1,2])+3)
Numpy도 파이썬 내장 리스트와 비슷한 스라이스와 인덱싱 연산을 제공한다
# 3X3 행렬의 첫 번째 행을 구한다
A = np.arange(9).reshpae(3,3)
print("A:", A)
B = A[0]
print("B:", B)
# 0, 1을 인덱싱하면 A의 첫 번째 행에서 두 번째 값을 참조한다
# 두 결과는 같다
print(A[0,1])
print(B[1])
# 슬라이싱도 비슷한 결과를 보여준다
A[:-1]
위 그림과 같이 배열의 맨 오른쪽 열을 슬라이싱하고 싶다면
# 1번
print(A[:,2:])
print("--------------")
print(A[:,1:])
print("--------------")
print(A[:,:])
print("--------------")
# 2번
print(A[:,-1:])
print("--------------")
print(A[:,-2:])
print("--------------")
print(A[:,-3:])
# 두 방법 모두 같은 값을 출력
슬라이싱이 어렵다면 계속해서 연습해본다
A[1,:2]
A[:2, 1:]
A[:,-1]
Numpy에서도 다양한 난수를 의사 난수를 지원하고, 자주 쓰인다.
주로 많이 쓰이는 함수
np.random.randint()
np.random.choice()
np.random.permutation()
np.random.normal()
np.random.uniform()
의사 난수 생성 예시
print(np.random.random()) # 0~1 사이의 실수형 난수 하나를 생성
print(np.random.randint(0,10)) # 0~9 사이 1개 정수형 난수 하나를 생성
print(np.random.choice([0,1,2,3,4,5,6,7,8,9])) # 리스트에 주어진 값 중 하나를 랜덤하게 고른다
# 무작위로 섞인 배열을 만들어준다, 밑에 두 값은 동일
print(np.random.permutation(10))
print(np.random.permutation([0,1,2,3,4,5,6,7,8,9]))
# 어떤 분포를 따르는 변수를 임의로 표본추출해준다
# 정규분포
print(np.random.normal(loc=0, scale=1, size=5)) # 평균(loc), 표준편차(scale), 추출 개수(size)
# 균등분포
print(np.random.uniform(low=1, high=1, size=5)) # 최소(low), 최대(high), 추출개수(size)
행렬의 행과 열을 맞바꾸기, 행렬의 축을 서로 바꾸기 등에 사용되는 기능
arr.T
: 행렬의 행과 열 맞바꾸기np.transpose
: 축을 기준으로 행렬의 행과 열 바꾸기A = np.arange(24).reshape(2,3,4)
print("A : ", A) # A는 (2,3,4) 모양의 행렬
print("A의 전치행렬 : ", A.T)
print("A의 전치행렬의 shape : ", A.T.shape) # A의 전치행렬은 (4,3,2) 모양의 행렬
# np.transpose는 행렬의 축을 어떻게 변환해 줄지 임의로 지정해 줄 수 있다.
B = np.transpose(A, (2,0,1))
print("A : ", A) # A는 (2,3,4) 모양의 행렬
print("B : ", B) # B는 A의 3,1,2 번째 축을 자신의 1,2,3번째 축으로 가진 행렬
print("B.shape : ", B.shape) # B는 (4,2,3) 모양의 행렬
# np.transpose(A, (2,1,0))은 A.T와 같다
print("B.shape : ", B.shape)
Numpy의 주요 기능 중 하나인 통계 데이터 계산 기능을 이용해 본다.
Numpy에는 많은 통계 관련 수식을 함수로 제공해주는데, 함수들을 이용해 평균, 표준편차, 중앙값을 계산하면 예시와 같이 나타낼 수 있다.
import numpy as np
def numbers():
X = []
number = input("Enter a number (<Enter key> to quit)")
while number != "":
try:
x = float(number)
X.append(x)
except ValueError:
print('>>> Not a Number! Ignored...')
number = input("Enter a number (<Enter key> to quit)")
return X
def main():
nums = numbers() # 파이썬 리스트
num = np.array(nums) # 리스트를 Numpy ndarray로 변환
print("합", num.sum())
print("평균값", num.mean())
print("표준편차", num.std())
print("중앙값", np.median(num)) # num.median()이 아님
main()
Numpy를 이용하여 다양한 데이터를 표현하는 방법을 정리한 블로그
위 블로그에는 다양한 데이터들을 행렬 변환 하는 방법에 대해서 자세하게 설명되어 있다
그 중 이미지를 행렬변환하는 연습을 해본다.
디지털로 표현되는 이미지는 수많은 점으로 구성되어 있다. 이 점을 픽셀이라고 부른다
아래 이미지는 동일한 사이즈의 이미지에 대해 픽셀이 1개(1x1)일 때부터 10,000개(100x100)일 때 이미지의 해상도를 보여준다
이미지와 픽셀의 관계
이미지 데이터를 처리하기 위해 추가로 matplotlib와 PIL 라이브러리를 이용한다.
이 두 라이브러리는 이미지 파일을 열고, 자르고, 복사하고, RGB 색상 값을 가져오는 등 이미지 파일과 관련된 몇 가지 작업을 빠르게 수행한다.
이렇게 처리한 파일을 Numpy를 이용해 행렬로 빠르게 연산해서 이미지를 더 빠르게 작업한다.
import matplotlib as mpl
import PIL
print(f'# matplotlib : {mpl.__version__}')
print(f'#PIL: {PIL.__version__}')
# 버전 확인
예제를 연습하면서 익힌다.
이미지 조작에 쓰이는 메서드
Image.open()
Image.size()
Image.filename
Image.save()
open
자유의 여신상 파일을 예제로 연습한다.
from PIL import Image, ImageColor
import os
img_path = "newyork.jpg"
img = Image.open(img_path)
print(img_path)
print(type(img))
img
Image.open()
이라는 메서드를 이용해 이미지 파일을 open했다. 이렇게 얻은 오브젝트 img
는 PIL.JpegImagePlugin.JpegImageFile
타입을 가지고 있다.
size
계속 해서 관련 메서드를 사용해본다
img.size # 이미지 사이즈 가로X세로가 튜플 값으로 반환
W, H = img.size
print((W, H))
print(img.format) # 이미지 파일의 타입(jpeg, png 등)
print(img.mode) # 색상 정보 결과 : RGB
**이미지 자르기**
이미지를 자를 때에는 `.crop()` 메서드를 이용한다. 인자는 튜플값으로 입력, 가로 세로의 시작점과 가로, 세로의 종료점 총 4개를 입력한다.
```python
img.crop((30, 30, 100, 100))
사진이 잘려서 출력됨
저장
자른 이미지를 저장해본다.
# 새로운 이미지 파일명
cropped_img_path = "cropped_img.jpg"
img.crop((30,30,100,100)).save(cropped_img_path)
print("저장 완료")
저장이 잘 됐는지 확인해 본다
리눅스 명령어
$ ls ~/저장한 위치/cropped_img.jpg
행렬로 변환
이미지 파일을 행렬로 변환하기
import numpy as np
img_arr = np.array(img)
print(type(img))
print(type(img_arr))
print(img_arr.shape)
print(img_arr.ndim)
참고 : 위에서 img는 리스트 타입이 아니라 PIL.JpegImagePlugin.JpegImageFile
이라는 타입을 가지고 있는데 어떻게 np.array(img)
가 정상적으로 작동했는가?
PIL.JpegImagePlugin.JpegImageFile
는 PIL.Image.Image
라는 클래스를 상속받은 타입이다.(참고)
PIL.Image.Image
클래스는 리스트를 상속받지는 않았지만 __array_interface__
라는 속성이 정의되어 있어 Pillow라이브러리는 이미지를 Numpy ndarray로 변환 가능하다.
컬러이미지 파일이기 때문에 변환된 행렬은 < Height X Width X RGB Channel >의 모양이며, 3차원의 형태이다.
행렬의 값 확인
img_arr
흑백 모드
컬러 이미지 파일을 열 때, 흑백 모드로 사진을 열 수도 있다. Image.open().convert('L')
로 모드를 조정할 수 있다. (Pillow의 이미지 처리 옵션에 대한 상세한 정보
img_g = Image.open(img_path).convert('L')
img_g
행렬로 변환하는 방법은 위와 동일하다
img_g_arr = np.array(img_g)
print(type(img_g_arr))
print(img_g_arr.shape)
print(img_g_arr.ndim)
img_g_arr
단, 흑백이므로 반환된 행렬은 < Height X Width > 2차원이다.
Get Color
색상값은 RGB로 표현되는데 getcolor()
는 각 색상이 어떤 RGB값을 가지는지 보여준다.
red = ImageColor.getcolor('RED', 'RGB')
reda = ImageColor.getcolor('red', 'RGBA')
yellow = ImageColor.getcolor('yellow', 'RGB')
print(red)
print(reda)
print(yellow)
딥러닝에서 이미지 조작은 Data augmentation을 할 때 많이 사용된다.
Data augmentation은 말 그대로 데이터를 증강하는 것으로 딥러닝에서 데이터의 개수를 늘릴 때 사용하는 기법이다. 참고