RL Researcher

Numpy 본문

AI Basic/Numpy

Numpy

Lass_os 2020. 12. 22. 22:40

- 넘파이 공식링크(https://numpy.org/)

- Document(https://numpy.org/doc/1.18/)

1. 기초 개념(The Basics)


Numpy에서 Object는 동차(Homogeneous) 다차원 배열이라고 하는데 Homogeneous하다는 것은 단순히 다차원 배열로 표현한다라고 생각하시면 됩니다. Numpy에서는 모든 배열의 값이 기본적으로 같은 타입이여야 합니다. 그리고 Numpy에서는 각 차원(Dimension)을 축(axis)이라고 표현합니다.

2. 넘파이 모듈 불러오기


import의 사전적 의미는 '다른 컴퓨터 시스템으로부터 자기의 시스템 안에 데이터 등을 들여놓는 행위'라고 정의하고 있습니다.

import는 이미 만들어진 파이썬 프로그램 파일, 라이브러리안에 있는 파일 등을 사용할 수 있게 해주는 명령어입니다. 여기서 라이브러리는 도서관이라는 뜻이며, 파이썬 에서 사용가능한 유용한 프로그램들을 모아놓은 곳입니다. 기본적인 라이브러리는 파이썬 설치 시 자동으로 컴퓨터에 설치가 됩니다.

import numpy as np

as는 파이썬 이용자들이 편리하게 불러올 수 있도록 약어를 지정할 수 있습니다.(np는 많이 사용하는 약어이기에 사용했습니다.)

3. 배열 생성


넘파이 배열은 다음과 같이 사용할 수 있습니다. shape는 ndarray의 형태를 출력할 수 있습니다.

array1 = np.array([1,2,3])
print('array1 type:',type(array1))
print('array1 array 형태:',array1.shape)

array2 = np.array([[1,2,3],
                  [2,3,4]])
print('array2 type:',type(array2))
print('array2 array 형태:',array2.shape)

array3 = np.array([[1,2,3]])
print('array3 type:',type(array3))
print('array3 array 형태:',array3.shape)

========================================================================

<output>
array1 type: <class 'numpy.ndarray'>
array1 array 형태: (3,)
array2 type: <class 'numpy.ndarray'>
array2 array 형태: (2, 3)
array3 type: <class 'numpy.ndarray'>
array3 array 형태: (1, 3)

ndim을 통해서 ndarray의 차원을 출력할 수도 있습니다.

print('array1: {:0}차원, array2: {:1}차원, array3: {:2}차원'.format(array1.ndim,array2.ndim,array3.ndim))

========================================================================

<output>
array1: 1차원, array2: 2차원, array3:  2차원

4. ndarray Type


ndarray를 생성하게 되면, 기본적으로 요소의 타입이 정해지게 됩니다. ndarray의 타입은 dtype(data type)을 통해 ndarray의 타입을 출력할 수 있습니다. 위의 예제에서 만들어 놓았던 array1의 dtype을 출력 해보겠습니다.

print(array1, array1.dtype)

========================================================================

<output>
[1 2 3] int32

기존에 리스트로 생성했던 것들을 np.array함수를 통해 ndarray로 변환할 수도 있습니다.

list1 = [1, 2, 3.0]
array2 = np.array(list1)
print(array2, array2.dtype)

========================================================================

<output>
[1. 2. 3.] float64

※ list내의 2개의 변수가 int형이고, 1개의 변수가 float형일 경우에는 dtype은 float형으로 출력되게 됩니다.

5. ndarray의 형변환(astype)


ndarray를 통한 연산을 수행하게 되는 경우 dtype이 맞지 않으면 연산이 되지 않는 경우가 발생합니다. 이런 경우를 대비해 ndarray의 dtype을 맞춰주기 위하여 astype이라는 함수를 사용할 수 있습니다.

array_int = np.array([1, 2, 3])
array_float = array_int.astype('float64')
print(array_float, array_float.dtype)

array_int1= array_float.astype('int32')
print(array_int1, array_int1.dtype)

array_float1 = np.array([1.1, 2.1, 3.1])
array_int2= array_float1.astype('int32')
print(array_int2, array_int2.dtype)

========================================================================

<output>
[1. 2. 3.] float64
[1 2 3] int32
[1 2 3] int32

6. ndarray 편리하게 생성하기(arange, zeros, ones)


ndarray를 생성할 때, 10개의 변수가 있다면 일일이 타이핑하여도 문제없겠지만 향후 머신러닝에 들어가게 될 값을 생성할때 많은 값들이 필요한 경우가 있기 때문에, arange함수를 사용하게 된다면 편리하게 생성할 수 있습니다.

sequence_array = np.arange(10)
print(sequence_array)
print(sequence_array.dtype, sequence_array.shape)

========================================================================

<output>
[0 1 2 3 4 5 6 7 8 9]
int32 (10,)

다음과 같이 arange함수를 사용할 수도 있지만, 만약 모든 행렬이 0인 ndarray를 생성하고 싶은 경우 zeros함수를 통해 쉽게 ndarray를 생성할 수 있습니다.

zero_array = np.zeros((3,2),dtype='int32')
print(zero_array)
print(zero_array.dtype, zero_array.shape)

========================================================================

<output>
[[0 0]
 [0 0]
 [0 0]]
int32 (3, 2)

0인 ndarray를 생성했다면, 다음은 1인 ndarray를 생성해 보겠습니다. 1으로 이루어진 ndarray를 생성하는 경우 ones함수를 통해 쉽게 생성할 수 있습니다.

one_array = np.ones((3,2))
print(one_array)
print(one_array.dtype, one_array.shape)

========================================================================

<output>
[[1. 1.]
 [1. 1.]
 [1. 1.]]
float64 (3, 2)

7. reshpe함수


만약 arange함수를 통해 10까지의 ndarray를 생성했다고 하였을 때, 행렬과의 곱셈을 위해 Broadcasting해야 하기 때문에 차원을 변경해줘야 하는 경우가 발생합니다. 다음과 같은 경우 reshape함수를 통해 ndarray의 shape,dimension을 변경할 수 있습니다.

array1 = np.arange(10)
print('array1:\n', array1)

array2 = array1.reshape(2,5)
print('array2:\n',array2)

array3 = array1.reshape(5,2)
print('array3:\n',array3)

========================================================================

<output>
array1:
 [0 1 2 3 4 5 6 7 8 9]
array2:
 [[0 1 2 3 4]
 [5 6 7 8 9]]
array3:
 [[0 1]
 [2 3]
 [4 5]
 [6 7]
 [8 9]]

만약 reshape함수를 통해 변경하고자 할 때, 아래와 같이 ndarray의 shape을 맞춰주지 않는다면 에러가 발생합니다.(유의!)

array1.reshape(4,3)

========================================================================

<output>
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-10-a40469ec5825> in <module>()
----> 1 array1.reshape(4,3)

ValueError: cannot reshape array of size 10 into shape (4,3)

ndarray를 생성하다보면, 범위가 너무 넓어져 reshape에 대한 고민을 하게됩니다. 그런 경우 앞의 shape 또는, 뒤의 shape를 명시한 후 -1을 포함한다면 python내에서 알아서 행렬을 만들어줄 것입니다.

array1 = np.arange(8)
array3d = array1.reshape((2,2,2))
print('array3d:\n',array3d.tolist())

# 3차원 ndarray를 2차원 ndarray로 변환
array5 = array3d.reshape(-1,1)
print('array5:\n',array5.tolist())
print('array5 shape:',array5.shape)

# 1차원 ndarray를 2차원 ndarray로 변환
array6 = array1.reshape(-1,1)
print('array6:\n',array6.tolist())
print('array6 shape:',array6.shape)

========================================================================

<output>
array3d:
 [[[0, 1], [2, 3]], [[4, 5], [6, 7]]]
array5:
 [[0], [1], [2], [3], [4], [5], [6], [7]]
array5 shape: (8, 1)
array6:
 [[0], [1], [2], [3], [4], [5], [6], [7]]
array6 shape: (8, 1)

※ 위의 tolist함수는 list형으로 변환을 한것입니다.

8. ndarray indexing


python 기본문법에서도 진행했지만, ndarray의 행렬연산에서도 indexing이 가능합니다.

# 1에서 부터 9 까지의 1차원 ndarray 생성 
array1 = np.arange(start=1, stop=10)
print('array1:',array1)
# index는 0 부터 시작하므로 array1[2]는 3번째 index 위치의 데이터 값을 의미
value = array1[2]
print('value:',value)
print(type(value))

========================================================================

<output>
array1: [1 2 3 4 5 6 7 8 9]
value: 3
<class 'numpy.int32'>

파이썬 기본에서도 진행하였으니 설명은 따로 없이 진행하겠습니다.

print('맨 뒤의 값:',array1[-1], ', 맨 뒤에서 두번째 값:',array1[-2])

========================================================================

<output>
맨 뒤의 값: 9 , 맨 뒤에서 두번째 값: 8

array내의 변수 값 변경도 python 기본에서 설명했으니 코드로 보여드리도록하겠습니다.

array1[0] = 9
array1[8] = 0
print('array1:',array1)

========================================================================

<output>
array1: [9 2 3 4 5 6 7 8 0]

ndarray는 파이썬의 기본 문법에서 사용하는 방식과는 다르게 행과 열을 지정하여 indexing이 가능합니다.

array1d = np.arange(start=1, stop=10)
array2d = array1d.reshape(3,3)
print(array2d)

print('(row=0,col=0) index 가리키는 값:', array2d[0,0] )
print('(row=0,col=1) index 가리키는 값:', array2d[0,1] )
print('(row=1,col=0) index 가리키는 값:', array2d[1,0] )
print('(row=2,col=2) index 가리키는 값:', array2d[2,2] )

========================================================================

<output>
[[1 2 3]
 [4 5 6]
 [7 8 9]]
(row=0,col=0) index 가리키는 값: 1
(row=0,col=1) index 가리키는 값: 2
(row=1,col=0) index 가리키는 값: 4
(row=2,col=2) index 가리키는 값: 9

9. ndarray slicing


슬라이싱에 관해서도 파이썬 기본에서 다루었으니 참고 부탁드립니다.

array1 = np.arange(start=1, stop=10)
array3 = array1[0:3]
print(array3)
print(type(array3))

========================================================================

<output>
[1 2 3]
<class 'numpy.ndarray'>
array1 = np.arange(start=1, stop=10)
array4 = array1[:3]
print(array4)

array5 = array1[3:]
print(array5)

array6 = array1[:]
print(array6)

========================================================================

<output>
[1 2 3]
[4 5 6 7 8 9]
[1 2 3 4 5 6 7 8 9]

ndarray의 slicing도 indexing과 마찬가지로, shape별로 짜를 수도 있습니다.

array1d = np.arange(start=1, stop=10)
array2d = array1d.reshape(3,3)
print('array2d:\n',array2d)

print('array2d[0:2, 0:2] \n', array2d[0:2, 0:2])
print('array2d[1:3, 0:3] \n', array2d[1:3, 0:3])
print('array2d[1:3, :] \n', array2d[1:3, :])
print('array2d[:, :] \n', array2d[:, :])
print('array2d[:2, 1:] \n', array2d[:2, 1:])
print('array2d[:2, 0] \n', array2d[:2, 0])

========================================================================

<output>
array2d:
 [[1 2 3]
 [4 5 6]
 [7 8 9]]
array2d[0:2, 0:2] 
 [[1 2]
 [4 5]]
array2d[1:3, 0:3] 
 [[4 5 6]
 [7 8 9]]
array2d[1:3, :] 
 [[4 5 6]
 [7 8 9]]
array2d[:, :] 
 [[1 2 3]
 [4 5 6]
 [7 8 9]]
array2d[:2, 1:] 
 [[2 3]
 [5 6]]
array2d[:2, 0] 
 [1 4]
print(array2d[0])
print(array2d[1])
print('array2d[0] shape:', array2d[0].shape, 'array2d[1] shape:', array2d[1].shape )

========================================================================

<output>
[1 2 3]
[4 5 6]
array2d[0] shape: (3,) array2d[1] shape: (3,)

10. Fancy indexing


Fancy indexing은 정수나 불린(boolean)값을 가지는 다른 Numpy 배열로 배열을 인덱싱할 수 있는 기능을 의미합니다.

예제를 통해 살펴보겠습니다.

array1d = np.arange(start=1, stop=10)
array2d = array1d.reshape(3,3)

array3 = array2d[[0,1], 2]
print('array2d[[0,1], 2] => ',array3.tolist())

array4 = array2d[[0,1], 0:2]
print('array2d[[0,1], 0:2] => ',array4.tolist())

array5 = array2d[[0,1]]
print('array2d[[0,1]] => ',array5.tolist())

========================================================================

<output>
array2d[[0,1], 2] =>  [3, 6]
array2d[[0,1], 0:2] =>  [[1, 2], [4, 5]]
array2d[[0,1]] =>  [[1, 2, 3], [4, 5, 6]]

11. Boolean indexing


Boolean indexing이란 조건문을 가지고 True, False로 Indexing을 수행하는 것을 의미합니다. 예제를 통해 살펴보겠습니다.

array1d = np.arange(start=1, stop=10)
# [ ] 안에 array1d > 5 Boolean indexing을 적용 
array3 = array1d[array1d > 5]
print('array1d > 5 불린 인덱싱 결과 값 :', array3)

========================================================================

<output>
array1d > 5 불린 인덱싱 결과 값 : [6 7 8 9]

아래와 같이 값을 저장하지 않고 해당 조건에 대하여 출력하는 경우, ndarray가 True, False값으로 반환됩니다.

array1d > 5

========================================================================

<output>
array([False, False, False, False, False,  True,  True,  True,  True])

아래와 같이 array값에 대하여 Boolean Indexing값을 집어넣어 필터링을 수행할 수도 있습니다.

boolean_indexes = np.array([False, False, False, False, False,  True,  True,  True,  True])
array3 = array1d[boolean_indexes]
print('불린 인덱스로 필터링 결과 :', array3)

========================================================================

<output>
불린 인덱스로 필터링 결과 : [6 7 8 9]
indexes = np.array([5,6,7,8])
array4 = array1d[indexes]
print('일반 인덱스로 필터링 결과 :',array4)

========================================================================

<output>
일반 인덱스로 필터링 결과 : [6 7 8 9]

11. ndarray의 정렬(sort(), argsort())


sort는 파이썬 기본에서 사용하였으니 언급하지 않고 실습을 통해 수행하겠습니다.

argsort는 정렬하고자 하는 array를 argsort함수를 사용하게 되면, ndarray를 정렬하는 인덱스 ndarray를 반환합니다.

org_array = np.array([ 3, 1, 9, 5]) 
print('원본 행렬:', org_array)
# np.sort( )로 정렬 
sort_array1 = np.sort(org_array)         
print ('np.sort( ) 호출 후 반환된 정렬 행렬:', sort_array1) 
print('np.sort( ) 호출 후 원본 행렬:', org_array)
# ndarray.sort( )로 정렬
sort_array2 = org_array.sort()
print('org_array.sort( ) 호출 후 반환된 행렬:', sort_array2)
print('org_array.sort( ) 호출 후 원본 행렬:', org_array)

========================================================================

<output>
원본 행렬: [3 1 9 5]
np.sort( ) 호출 후 반환된 정렬 행렬: [1 3 5 9]
np.sort( ) 호출 후 원본 행렬: [3 1 9 5]
org_array.sort( ) 호출 후 반환된 행렬: None
org_array.sort( ) 호출 후 원본 행렬: [1 3 5 9]

아래 예제처럼 내림차순 정렬도 가능합니다.

sort_array1_desc = np.sort(org_array)[::-1]
print ('내림차순으로 정렬:', sort_array1_desc) 

========================================================================

<output>
내림차순으로 정렬: [9 5 3 1]

다음은 argsort함수 예제를 보겠습니다.

org_array = np.array([3, 1, 9, 5]) 
sort_indices = np.argsort(org_array)
print(type(sort_indices))
print('행렬 정렬 시 원본 행렬의 인덱스:', sort_indices)

========================================================================

<output>
<class 'numpy.ndarray'>
행렬 정렬 시 원본 행렬의 인덱스: [1 0 3 2]
org_array = np.array([3, 1, 9, 5]) 
sort_indices_desc = np.argsort(org_array)[::-1]
print('행렬 내림차순 정렬 시 원본 행렬의 인덱스:', sort_indices_desc)

========================================================================

<output>
행렬 내림차순 정렬 시 원본 행렬의 인덱스: [2 3 0 1]

다음 예제처럼 문자열 ndarray에 담아 정렬이 가능합니다.

import numpy as np

name_array = np.array(['John', 'Mike', 'Sarah', 'Kate', 'Samuel'])
score_array= np.array([78, 95, 84, 98, 88])

sort_indices_asc = np.argsort(score_array)
print('성적 오름차순 정렬 시 score_array의 인덱스:', sort_indices_asc)
print('성적 오름차순으로 name_array의 이름 출력:', name_array[sort_indices_asc])

========================================================================

<output>
성적 오름차순 정렬 시 score_array의 인덱스: [0 2 4 1 3]
성적 오름차순으로 name_array의 이름 출력: ['John' 'Sarah' 'Samuel' 'Mike' 'Kate']

12. 선형대수 연산(행렬 내적과 전치 행렬 구하기)


행렬 내적이라고 하는 것은 행렬의 곱셈을 뜻합니다.(위의 Broadcasting과 연관있습니다.)

A = np.array([[1, 2, 3],
              [4, 5, 6]])
B = np.array([[7, 8],
              [9, 10],
              [11, 12]])

dot_product = np.dot(A, B)
print('행렬 내적 결과:\n', dot_product)

========================================================================

<output>
행렬 내적 결과:
 [[ 58  64]
 [139 154]]

전치행렬이라고 하는 것은 선형대수학에서, 행과 열을 교환하여 얻는 행렬을 말합니다. 실습을 통해 알아보겠습니다.

A = np.array([[1, 2],
              [3, 4]])
transpose_mat = np.transpose(A)
print('A의 전치 행렬:\n', transpose_mat)

========================================================================

<output>
A의 전치 행렬:
 [[1 3]
 [2 4]]
Comments