1-1. NumPy 개요
In [1]:
from IPython.core.display import display, HTML
display(HTML("<style> .container{width:90% !important;}</style>"))

Numpy(넘파이)

: 파이썬에서 선형대수 기반의 프로그램을 쉽게 만들 수 있도록 지원하는 패키지입니다.
→ 많은 머신러닝 알고리즘이 넘파이 기반으로 작성되어 있고, 데이터 입출력도 넘파이 배열 타입을 사용하기 때문에 중요한 패키지입니다.

1. ndarray 개요

넘파이의 기반 데이터 타입으로 ndarray를 이용해서 넘파이에서 다차원 베열을 쉽게 생성하고 다양한 연산을 수행할 수 있습니다.

In [2]:
# 넘파이 모듈 불러오기, 관례적으로 np라는 축약어로 넘파이모듈을 표현해줍니다.
import numpy as np

array( ) 함수 : 파이썬의 리스트와 같은 다양한 인자를 입력받아 ndarray로 변환하는 기능을 수행합니다.
→ 생성된 ndarray 배열의 shape변수는 ndarray의 행과 열의 수를 튜플 형태로 가지고 있고, 이를 통해 ndarray 배열의 차원을 알 수 있습니다.

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

array2 = np.array([[1,2,3], 
                            [2,3,4]]) ## 2차원 array
print('\narray2 type:', type(array2))
print('array2의 array 형태', array2.shape)

array3 = np.array([[1,2,3]]) ## array1과 동일하지만 이 경우에는 명확히 열과 행을 가진 2차원 array
print('\narray3 type:', type(array3))
print('array3의 array 형태', array3.shape)
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)


위의 예제에서 array1과 array3은 명백히 다른 차원을 가지고 있습니다
: [ ](대괄호를 한 번만 사용하면) : 1차원 , [[ ]](대괄호를 두 번 사용하면) : 2차원
→ 알고리즘 데이터 입출력, 변환시 명확히 1차원 또는 다차원 데이터를 요구하는 경우가 빈번하기 때문에 이 차이를 인지하는 것은 매우 중요합니다.

In [4]:
# 각 array의 차원을 ndarray.ndim을 이용해 확인
print('array1: {:0}차원'.format(array1.ndim))
print('array2: {:0}차원'.format(array2.ndim))
print('array3: {:0}차원'.format(array3.ndim))
array1: 1차원
array2: 2차원
array3: 2차원

2. ndarray의 데이터 타입

ndarray 내의 데이터 값은 숫자, 문자열, 불 값 모두 가능합니다.
→ 단, 하나의 array 내의 데이터 타입은 같은 타입만 가능합니다. 심지어 숫자형인 int 와 float도 함께 있을 수 없습니다.

In [5]:
list1 = [1, 2, 3]
print(type(list1))
print('-----------------')
array1 = np.array(list1)
print(type(array1))
print(array1, array1.dtype)
<class 'list'>
-----------------
<class 'numpy.ndarray'>
[1 2 3] int64


서로 다른 데이터 유형이 섞여있는 리스트를 ndarray로 변경하려고 하면 데이터 크기가 더 큰 타입으로 형 변환을 일괄 적용합니다.
→ str > float > int

In [6]:
### int와 str 형이 섞여 있는 리스트
list2 = [1, 2, 'test']
array2 = np.array(list2)
print(array2, array2.dtype)
print('유니코드 문자열값으로 데이터가 변환 되었음')
['1' '2' 'test'] <U21
유니코드 문자열값으로 데이터가 변환 되었음
In [7]:
## int와 float형이 섞여 있는 리스트
list3 = [1, 2, 3.0]
array3 = np.array(list3)
print(array3, array3.dtype)
print('float형으로 데이터가 변환되었음')
[1. 2. 3.] float64
float형으로 데이터가 변환되었음


ndarray 내의 데이터 타입 변경도 astype() 를 이용하면 가능
→ 이를 이용하여 메모리를 절약할 수 있습니다

가령, int형만으로도 충분한 float타입의 데이터라면 int형으로 바꿔서 메모리를 절약할 수 있습니다.

In [8]:
array_int = np.array([1, 2, 3])
array_float = array_int.astype('float64')
print(array_float, array_float.dtype)
[1. 2. 3.] float64
In [9]:
array_int1 = array_float.astype('int32')
print(array_int1, array_int1.dtype)
[1 2 3] int32
In [10]:
array_float1 = np.array([1.1, 2.1, 3.1])
array_int2 = array_float1.astype('int32')
print(array_int2, array_int2.dtype)
print('int형으로 변환되면서 소수점이 사라짐')
[1 2 3] int32
int형으로 변환되면서 소수점이 사라짐

3. ndarray 편리하게 생성하기

특정 크기, 차원을 가진 ndarray를 연속값이나 0, 1로 초기화해서 쉽게 생성해야할 경우,
또는 테스트용 데이터를 만들거나 대규모 데이터를 일괄적으로 초기화해야할 때 arange( ), zeros( ), ones( ) 함수를 사용할 수 있습니다.

arange( ): 파이썬의 range와 비슷하게 0부터 '인자값-1'의 값을 순차적으로 ndarray의 값으로 변환

In [11]:
sequence_array = np.arange(10)
print(sequence_array)
print(sequence_array.dtype)
print(sequence_array.shape)
[0 1 2 3 4 5 6 7 8 9]
int64
(10,)
In [12]:
sequence_array2 = np.arange(3, 10) # 시작값을 설정할 수 도 있음
print(sequence_array2)
[3 4 5 6 7 8 9]

zeros( ) : 튜플형태의 shape 값을 입력하면, 모든 값을 0으로 채운 ndarray를 반환
ones( ) : 튜플형태의 shape 값을 입력하면, 모든 값을 1으로 채운 ndarray를 반환
→ 함수 인자로 dtpye을 설정하지 않으면 default로 float64형의 데이터로 ndarray 생성해줍니다.

In [13]:
zero_array = np.zeros((3, 2), dtype='int32')
print(zero_array)
print(zero_array.dtype)
print(zero_array.shape)
[[0 0]
 [0 0]
 [0 0]]
int32
(3, 2)
In [14]:
one_array = np.ones((2,3), dtype='int32')
print(one_array)
print(one_array.dtype)
print(one_array.shape)
[[1 1 1]
 [1 1 1]]
int32
(2, 3)

4. ndarray의 차원과 크기를 변경하는 reshape( )

In [15]:
array1 = np.arange(10)
print('array1: \n', array1)
array1: 
 [0 1 2 3 4 5 6 7 8 9]
In [16]:
array2 = array1.reshape(2, 5)
print('array2: \n', array2)
array2: 
 [[0 1 2 3 4]
 [5 6 7 8 9]]
In [17]:
array3 = array1.reshape(5, 2)
print('array3: \n', array3)
array3: 
 [[0 1]
 [2 3]
 [4 5]
 [6 7]
 [8 9]]

reshape() 는 지정된 사이즈로 변경이 불가능하면 오류를 발생시킵니다.

In [18]:
array1.reshape(4,3)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-18-a40469ec5825> in <module>
----> 1 array1.reshape(4,3)

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

reshape( ) 의 인자를 -1로 적용하면, 원래 ndarray와 호환되는 새로운 ndarray로 변환해줍니다

In [19]:
array1 = np.arange(10)
print(array1)
print('--------------------')
array2 = array1.reshape(-1, 5)
print(array2, '  -->   shape : ', array2.shape)
print('--------------------')
array3 = array1.reshape(5, -1)
print(array3, '  -->  shape: ', array3.shape)
[0 1 2 3 4 5 6 7 8 9]
--------------------
[[0 1 2 3 4]
 [5 6 7 8 9]]   -->   shape :  (2, 5)
--------------------
[[0 1]
 [2 3]
 [4 5]
 [6 7]
 [8 9]]   -->  shape:  (5, 2)


-1을 인자로 사용하더라도, 호환될 수 없는 형태로는 변환될 수 없습니다.

In [20]:
array4 = array1.reshape(-1, 4)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-20-7e1461ea4d71> in <module>
----> 1 array4 = array1.reshape(-1, 4)

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

reshape(-1,1) : 원본 ndarray가 어떤 형태라도 2차원이고, 여러개의 로우, 1개의 컬럼을 가진 ndarray로 변환됩니다.

In [21]:
array1 = np.arange(8)
print('1차원 array: \n', array1)

# 1차원 ndarray를 3차원으로 변환
array3d = array1.reshape((2,2,2))
print('\n3차원 array: \n', array3d)
print('\nlist로 변환된 array3d: \n', array3d.tolist()) # tolist() : list 형태로 변환


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

# 1차원의 ndarray를 2차원으로 변환
array6 = array1.reshape(-1,1)
print('\narray6: \n', array6)
1차원 array: 
 [0 1 2 3 4 5 6 7]

3차원 array: 
 [[[0 1]
  [2 3]]

 [[4 5]
  [6 7]]]

list로 변환된 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]]

4. 넘파이의 ndarray의 데이터세트 선택하기 - 인덱싱(indexing)

(1) 특정 데이터만 추출 : 원하는 위치의 인덱스 값을 지정하면 해당 위치의 데이터가 반환
(2) 슬라이싱(slicing) : 연속된 인덱스 상의 ndarray를 추출하는 방식
(3) 팬시 인덱싱(fancy indexing) : 일정한 인덱싱 집합을 리스트 또는 ndarray 형태로 지정해 해당 위치에 있는 데이터의 ndarray를 반환
(4) 불린 인덱싱(boolean indexing) : True/False 값 인덱싱 집합을 기반으로 True에 해당하는 인덱스 위치에 있는 데이터의 ndarray를 반환

(1) 특정 데이터만 추출

: ndarray 객체에 해당하는 위치의 인덱스 값을 [] 안에 입력

In [22]:
# 1부터 9까지의 1차원 ndarray 생성
array1 = np.arange(start=1, stop=10)
array1
Out[22]:
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
In [23]:
# index는 0부터 시작하므로 array1[2]는 3번째 index 위치의 데이터 값을 의미
value = array1[2]
print('value: ', value)
print(type(value))
value:  3
<class 'numpy.int64'>
In [24]:
# 인덱스 -1은 맨 뒤의 데이터 값, -2는 맨뒤에서 두번째에 있는 데이터 값
print('맨 뒤의 값: ', array1[-1])
print('맨 뒤에서 두번째 값: ', array1[-2])
맨 뒤의 값:  9
맨 뒤에서 두번째 값:  8

단일 인덱스를 이용해 ndarray 내의 데이터 값을 수정할 수 있습니다.

In [25]:
array1[0] = 9
array1[8] = 0
array1
Out[25]:
array([9, 2, 3, 4, 5, 6, 7, 8, 0])

2차원 ndarray의 경우에는 (row 위치, column 위치)로 로우와 컬럼의 위치 인덱스를 통해 접근

In [26]:
array1d = np.arange(start=1, stop = 10)
array2d = array1d.reshape(3, 3)
print(array2d)
[[1 2 3]
 [4 5 6]
 [7 8 9]]
In [27]:
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])
(row=0, col =0) index 가리키는 값:  1
(row=0, col=1) index 가리키는 값:  2
(row=1, col=0) index 가리키는 값:  4
(row=2, col=2) index 가리키는 값 9

(2) 슬라이싱

' : ' 기호를 이용

  • ' : ' 기호 앞에 시작 인덱스를 생략하면 자동으로 맨 처음인 0인 인덱스로 간주
  • ' : ' 기호 뒤에 종료 인덱스를 생략하면 자동으로 맨 마지막 인덱스로 간주
  • ' : ' 기호 앞뒤에 시작/종료 인덱스를 생략하면 자동으로 맨 처음/맨 마지막 인덱스로 간주 (결국, 전체 선택)
In [28]:
array1 = np.arange(1, 10)
array3 = array1[0:3]
print(array3)
print(type(array3))
[1 2 3]
<class 'numpy.ndarray'>
In [29]:
array4 = array1[:3]
print(array4)

array5 = array1[3:]
print(array5)

array6 = array1[:]
print(array6)
[1 2 3]
[4 5 6 7 8 9]
[1 2 3 4 5 6 7 8 9]
In [30]:
# 2차원 ndarray에서 슬라이싱으로 데이터 접근하기
array2d = array1.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])
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]
In [31]:
# 2차원 ndarray에서 뒤에 오는 인덱스를 없애면 1차원 인덱스를 반환
print(array2d[0])
print(array2d[1])
print('array2d[0] shape: ', array2d[0].shape, 'array2d[1] shape: ', array2d[1].shape)
[1 2 3]
[4 5 6]
array2d[0] shape:  (3,) array2d[1] shape:  (3,)

(3) 팬시 인덱싱

리스트나 ndarray로 인덱스 집합을 지정하면 해당 위치의 인덱스에 해당하는 ndarray를 반환하는 인덱싱 방식

In [32]:
array1d = np.arange(1, 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())
array2d[[0,1], 2] ==>  [3, 6]
array2d[[0,1], 0:2] ==> [[1, 2], [4, 5]]
array2d[[0,1]] ==> [[1, 2, 3], [4, 5, 6]]

(4) 불린 인덱싱

: 조건 필터링과 검색을 동시에 할 수 있기 때문에, 매우 자주 사용되는 인덱싱 방식

ndarray의 인덱스를 지정하는 [ ] 내에 조건문을 그대로 기재

In [33]:
array1d = np.arange(1, 10)
# array1d > 5 조화결과
print(array1d > 5)
# [ ] 안에 array1d > 5 Boolean indexing을 적용
array3 = array1d[array1d > 5]
print('array1d > 5 불린 인덱싱 결과 값: ', array3)
[False False False False False  True  True  True  True]
array1d > 5 불린 인덱싱 결과 값:  [6 7 8 9]
In [34]:
# array1d > 5와 동일한 ndarray를 array1d[ ]안에 입력하면 동일한 데이터 세트가 반환
boolean_indices = np.array([False, False, False, False, False, True, True, True, True])
array3 = array1d[boolean_indices]
print(array3)
[6 7 8 9]

5. 행렬의 정렬 -sort( )와 argsort( )

행렬 정렬

np.sort( ) : 넘파이에서 sort()를 호출 → 원행렬은 그대로 유지한 채, 정렬된 행렬을 반환합니다.
ndarray.sort( ) : 원 행렬 자체를 정렬한 형태로 변환하며 별도로 반환하는 값은 없습니다.

In [35]:
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)
원본 행렬:  [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]


기본적으로 오름차순으로 행렬 내 원소를 정렬합니다.
→ 내림차순으로 정렬하기 위해서는 [::-1] 을 적용하면 됩니다.

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

행렬이 2차원 이상일 경우 axis 축 값 설정을 통해 로우 방향, 또는 컬럼 방향으로 정렬을 수행할 수 있습니다.

In [37]:
array2d = np.array([[8, 12], 
                              [7, 1]])

sort_array2d_axis0 = np.sort(array2d, axis = 0)
print('로우 방향으로 정렬 : \n', sort_array2d_axis0)

sort_array2d_axis1 = np.sort(array2d, axis = 1)
print('컬럼 방향으로 정렬 : \n', sort_array2d_axis1)
로우 방향으로 정렬 : 
 [[ 7  1]
 [ 8 12]]
컬럼 방향으로 정렬 : 
 [[ 8 12]
 [ 1  7]]

정렬된 행렬의 인덱스 반환하기

np.argsort( ) : 원본 행렬이 정렬되었을 때, 기존 원본 행렬의 원소에 대한 인덱스가 필요할 때 사용하며 원본 행렬의 인덱스를 ndarray 형태로 반환합니다.

In [38]:
org_array = np.array( [ 3, 1, 9, 5])
sort_indices = np.argsort(org_array)
print(type(sort_indices))
print('행렬 정렬 시 원본 행렬의 인덱스: ', sort_indices)
<class 'numpy.ndarray'>
행렬 정렬 시 원본 행렬의 인덱스:  [1 0 3 2]


내림차순으로 정렬 시 [::-1] 을 적용합니다.

In [39]:
sort_indices_desc = np.argsort(org_array)[::-1]
print('행렬 내림차순 정렬 시 원본 행렬의 인덱스: ', sort_indices_desc)
행렬 내림차순 정렬 시 원본 행렬의 인덱스:  [2 3 0 1]

6. 선형대수 연산 - 행렬 내적과 전치행렬 구하기

행렬 내적(행렬곱)

행렬 A와 행렬 B의 내적은 np.dot( ) 을 이용해 계산합니다.

In [40]:
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('행렬 A와 B의 내적: \n', dot_product)
행렬 A와 B의 내적: 
 [[ 58  64]
 [139 154]]

전치 행렬

원행렬의 행과 열 위치를 바꾼 행렬로 np.transpose( )를 이용합니다.

In [41]:
A = np.array([[1, 2],
                     [3, 4]])

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

+ Recent posts