Numpy ndarray 개요

  • 머신러닝 주요 알고리즘은 선형대수와 통계 등에 기반함
  • Numpy는 선행대수 기반 프로그램을 쉽게 만들 수 있는 패키지
  • 루프 없이 대량의 데이터 배열 연산을 빠르게 처리
  • 2차원 행과 열의 데이터 처리는 판다스(Pandas)가 더 편리
  • 기반 데이터 타입 : ndarray
In [1]:
# pip install numpy
Collecting numpyNote: you may need to restart the kernel to use updated packages.
  Downloading numpy-1.19.0-cp38-cp38-win_amd64.whl (13.0 MB)
Installing collected packages: numpy
Successfully installed numpy-1.19.0

In [2]:
import numpy as np
# 별명의 관례적으로 쓰이므로 웬만하면 맞춰주는 게 좋다

ndarray

In [7]:
## 파이썬 list 값을 ndarray로 변환
# 1차원 
array1 = np.array([1,2,3])
print('array1 type: ', type(array1))

# shape : 몇 차원인지 보여줌
print('array1 array 형태: ',array1.shape)
# (3,) : 3행 (1차원)
array1 type:  <class 'numpy.ndarray'>
array1 array 형태:  (3,)
In [5]:
# 2차원 -리스트 안에 리스트
array2 = np.array([[1,2,3],
                 [2,3,4]])

print('array2 type: ', type(array2))
print('array2 array 형태: ', array2.shape)
# (2,3) : 2행 3열 (2차원)
array2 type:  <class 'numpy.ndarray'>
array2 array 형태:  (2, 3)
In [6]:
array3 = np.array([[1,2,3]])

print('array3 type: ', type(array3))
print('array3 array 형태: ', array3.shape)
# (1,3) : 1행 3열 (2차원)
array3 type:  <class 'numpy.ndarray'>
array3 array 형태:  (1, 3)

ndim() : 차원 확인

In [8]:
# ndim 차원 확인
print('array1: {:0}차원, array2: {:1}차원, array3: {:2}차원'.format(array1.ndim, array2.ndim, array3.ndim))
array1: 1차원, array2: 2차원, array3:  2차원
In [10]:
list1 = [1,2,3]
print(type(list1))

array1 = np.array(list1)
print(type(array1))

print(array1)
print(array1.dtype)
<class 'list'>
<class 'numpy.ndarray'>
[1 2 3]
int32
In [12]:
list2 = [1,2,'test']
array2 = np.array(list2)
print(array2, array2.dtype)

list3 = [1, 2, 3.0]
array3 = np.array(list3)
print(array3, array3.dtype)
['1' '2' 'test'] <U11
[1. 2. 3.] float64
In [16]:
## 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)
[1. 2. 3.] float64
[1 2 3] int32
[1 2 3] int32

ndarray를 편리하게 생성하기 - arange, zeros, ones

  • ndarray를 연속값이나 0 또는 1로 초기화해서 생성
  • 테스트용 데이터나 대규모 데이터 일괄 초기화해야 할 경우 사용
In [17]:
# 파이썬의 range()
sequence_array = np.arange(10)
print(sequence_array)
print(sequence_array.dtype, sequence_array.shape)
[0 1 2 3 4 5 6 7 8 9]
int32 (10,)
In [18]:
# 0으로 초기화 int32 
# zeros
zero_array = np.zeros((3,2),dtype='int32')
print(zero_array)
print(zero_array.dtype, zero_array.shape)

# 1. 로 초기화 float64
# ones
one_array = np.ones((3,2))
print(one_array)
print(one_array.dtype, one_array.shape)
[[0 0]
 [0 0]
 [0 0]]
int32 (3, 2)
[[1. 1.]
 [1. 1.]
 [1. 1.]]
float64 (3, 2)

reshape()

  • ndarray를 특정 차원 및 크기로 변환
  • 1차원을 2차원 2 x 5 로
In [20]:
array1 = np.arange(10) # index는 0부터
print(array1)
[0 1 2 3 4 5 6 7 8 9]
In [22]:
array2 = array1.reshape(2,5)
print(array2)
[[0 1 2 3 4]
 [5 6 7 8 9]]
In [23]:
array3 = array1.reshape(5,2)
print('array3:\n',array3)
array3:
 [[0 1]
 [2 3]
 [4 5]
 [6 7]
 [8 9]]
In [24]:
# 10개로 만들 수 없는 사이즈
array1.reshape(4,3)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-24-45509960d83f> in <module>
      1 # 10개로 만들 수 없는 사이즈
----> 2 array1.reshape(4,3)

ValueError: cannot reshape array of size 10 into shape (4,3)
In [25]:
# -1을 사용하면 가능한 사이즈 자동 생성
array1 = np.arange(10)
print(array1)

array2 = array1.reshape(-1,5)
print('array2 shape:',array2.shape)

array3 = array1.reshape(5,-1)
print('array3 shape:',array3.shape)
[0 1 2 3 4 5 6 7 8 9]
array2 shape: (2, 5)
array3 shape: (5, 2)
In [26]:
# -1을 사용해도 불가능한 사이즈는 오류
array1 = np.arange(10)
array4 = array1.reshape(-1,4)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-26-5d64809779b7> in <module>
      1 # -1을 사용해도 불가능한 사이즈는 오류
      2 array1 = np.arange(10)
----> 3 array4 = array1.reshape(-1,4)

ValueError: cannot reshape array of size 10 into shape (4)
In [56]:
# 3차원 ndarray 생성
array1 = np.arange(8)
array3d = array1.reshape((2,2,2)) # 3개부터는 튜플 형태로!
print('array3d:\n',array3d.tolist())

# 대괄호가 3개!
array3d:
 [[[0, 1], [2, 3]], [[4, 5], [6, 7]]]
In [57]:
# 3차원 ndarray를 2차원 ndarray로 변환
array5 = array3d.reshape(-1,1)
print('array5:\n',array5.tolist())
print('array5 shape:',array5.shape)
array5:
 [[0], [1], [2], [3], [4], [5], [6], [7]]
array5 shape: (8, 1)
In [58]:
# 1차원 ndarray를 2차원 ndarray로 변환
array6 = array1.reshape(-1,1)
print('array6:\n',array6.tolist())
print('array6 shape:',array6.shape)
array6:
 [[0], [1], [2], [3], [4], [5], [6], [7]]
array6 shape: (8, 1)

Indexing

단일값 추출

In [33]:
# 1에서 9까지의 1차원 narray 생성
array1 = np.arange(start=1, stop=10) # 10까지 넣어야 9까지 나옴
print('array1:', array1)
array1: [1 2 3 4 5 6 7 8 9]
In [34]:
value = array1[2]
print(value)
print(type(value))
# index는 0 부터 시작하므로 array1[2]는 3번째 index 위치의 데이터 값을 의미
3
<class 'numpy.int32'>
In [35]:
print('맨 뒤의 값:', array1[-1], '/ 맨 뒤에서 두 번째 값:',array1[-2])
맨 뒤의 값: 9 / 맨 뒤에서 두 번째 값: 8
In [36]:
# 값 바꾸기
array1[0] = 9
array1[8] = 0
print('array1:',array1)
array1: [9 2 3 4 5 6 7 8 0]
In [37]:
# 2차원 인덱싱
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] )
[[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
In [39]:
# 1, 5, 8 가져와보기
print(array2d[0,0], array2d[1,1], array2d[2,1])
1 5 8

Slicing

  • 파이썬과 동일
In [40]:
array1 = np.arange(start=1, stop=10)
array3 = array1[0:3] # 0인덱스부터 3-1인덱스까지
print(array3)
print(type(array3))
[1 2 3]
<class 'numpy.ndarray'>
In [41]:
# 1차원 슬라이싱
array1 = np.arange(start=1, stop=10)
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 [47]:
# 2차원 슬라이싱
# 행 우선 / 콤마 뒤는 열
array1d = np.arange(start=1, stop=10)
array2d = array1d.reshape(3,3)
print('array2d:\n',array2d)
print('--------------------')

print('array2d[0:2, 0:2] \n', array2d[0:2, 0:2])
print('--------------------')
print('array2d[1:3, 0:3] \n', array2d[1:3, 0:3])
print('--------------------')
print('array2d[1:3, :] \n', array2d[1:3, :])
print('--------------------')
print('array2d[:, :] \n', array2d[:, :])
print('--------------------')
print('array2d[:2, 1:] \n', array2d[:2, 1:])
print('--------------------')
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 [49]:
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,)

fancy indexing

  • 일정한 인덱싱 집합을 리스트 또는 ndarray 형태로 지정해 해당 위치에 있는 데이터의 ndarray 반환
In [50]:
array1d = np.arange(start=1, stop=10)
array2d = array1d.reshape(3,3)

# tolist : list 로 변환
print('array2d => ',array2d.tolist())

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 =>  [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
array2d[[0,1], 2] =>  [3, 6]
array2d[[0,1], 0:2] =>  [[1, 2], [4, 5]]
array2d[[0,1]] =>  [[1, 2, 3], [4, 5, 6]]

Boolean indexing

  • 조건 필터링과 검색을 동시에 하므로 자주 사용
In [69]:
array1d = np.arange(start=4, stop=10)
print(array1d)

# [ ] 안에 array1d > 5 Boolean indexing을 적용 
array3 = array1d[array1d > 5]
print('array1d > 5 불린 인덱싱 결과 값 :', array3)
[4 5 6 7 8 9]
array1d > 5 불린 인덱싱 결과 값 : [6 7 8 9]
In [70]:
array1d > 5
Out[70]:
array([False, False,  True,  True,  True,  True])
In [71]:
boolean_indexes = np.array([False, False,  True,  True,  True,  True])
array3 = array1d[boolean_indexes]
print('불린 인덱스로 필터링 결과 :', array3)
불린 인덱스로 필터링 결과 : [6 7 8 9]
In [81]:
indexes = np.array([2,3,4,5])
array4 = array1d[indexes]
print('일반 인덱스로 필터링 결과 :',array4)
일반 인덱스로 필터링 결과 : [6 7 8 9]

행렬의 정렬 – sort( )와 argsort( )

  • 행렬 정렬 : np.sort(), ndarray.sort()
  • argsort() : 정렬된 행렬의 인덱스 반환
In [82]:
# 정렬 전 원본 행렬
org_array = np.array([ 3, 1, 9, 5]) 
print('원본 행렬:', org_array)
원본 행렬: [3 1 9 5]
In [83]:
# np.sort( )로 정렬 : 정렬된 행렬을 반환
sort_array1 = np.sort(org_array)         
print ('np.sort( ) 호출 후 반환된 정렬 행렬:', sort_array1) 
print('np.sort( ) 호출 후 원본 행렬:', org_array)
np.sort( ) 호출 후 반환된 정렬 행렬: [1 3 5 9]
np.sort( ) 호출 후 원본 행렬: [3 1 9 5]
In [84]:
# ndarray.sort( )로 정렬 : 행렬을 정렬 in place
sort_array2 = org_array.sort()
print('org_array.sort( ) 호출 후 반환된 행렬:', sort_array2)
print('org_array.sort( ) 호출 후 원본 행렬:', org_array)
org_array.sort( ) 호출 후 반환된 행렬: None
org_array.sort( ) 호출 후 원본 행렬: [1 3 5 9]
In [85]:
# :: - 역순정렬
sort_array1_desc = np.sort(org_array)[::-1] 
print ('내림차순으로 정렬:', sort_array1_desc) 
내림차순으로 정렬: [9 5 3 1]
In [86]:
array2d = np.array([[8, 12], 
                   [7, 1 ]])

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

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

argsort() : 정렬된 행렬의 인덱스 반환

In [87]:
# 오름차순
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]
In [88]:
# 내림차순
org_array = np.array([ 3, 1, 9, 5]) 
sort_indices_desc = np.argsort(org_array)[::-1]
print('행렬 내림차순 정렬 시 원본 행렬의 인덱스:', sort_indices_desc)
행렬 내림차순 정렬 시 원본 행렬의 인덱스: [2 3 0 1]
In [89]:
# 성적, 이름 데이터 정렬하기
name_array = np.array(['John', 'Mike', 'Sarah', 'Kate', 'Samuel'])
score_array= np.array([78, 95, 84, 98, 88])
In [90]:
# 성적 인덱스값으로 정렬 후 이름과 매칭해서 출력
sort_indices_asc = np.argsort(score_array)

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

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

행렬 내적 - np.dot()

  • 행렬 곱
In [91]:
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)

# 1*7 + 2*9 + 3*11 = 58
# 4*7 + 5*9 + 6*11 = 139 
행렬 내적 결과:
 [[ 58  64]
 [139 154]]

전치행렬 - np.transpose()

  • 원행렬에서 행과 열의 위치를 바꾼 행렬
In [92]:
A = np.array([[1, 2],
              [3, 4],
              [5, 6]])

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

역행렬 - np.linalg.inv()

  • 내적의 곱으로 단위행렬이 나오는 행렬
In [93]:
a = np.array(range(4)).reshape(2, 2)
a
Out[93]:
array([[0, 1],
       [2, 3]])
In [94]:
a_inv = np.linalg.inv(a)
a_inv
Out[94]:
array([[-1.5,  0.5],
       [ 1. ,  0. ]])
In [95]:
# 내적
a.dot(a_inv)
Out[95]:
array([[1., 0.],
       [0., 1.]])