ndarray 다루기

CharliePark·2020년 8월 24일
0

TIL

목록 보기
9/67

권철민 선생님의 <파이썬 머신러닝 완벽 가이드> 책과 강의를 통해 공부하며 정리한 내용입니다.
/ 강의

(파이썬 머신러닝 완벽 가이드 Chap 01.03)

 

 

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

arange() - 0부터 함수 인자값의 -1까지 연속값으로 초기화

zeros() - 0으로 초기화

ones() - 1로 초기화

테스트용으로 데이터를 만들거나 대규모의 데이터를 일괄적으로 초기화해야 할 경우에 사용된다.

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,)

 

arange의 syntax는 아래와 같다
arange([start, ]stop, [step, ]dtype=None)

default 값은 stop 값이고, start 값, step 값도 부여할 수 있으며, dtype을 지정하는 것도 가능하다.

array_start = np.arange(4, 10)
print(array_start)

array_step = np.arange(4, 10, 2)
print(array_step)

array_step_only = np.arange(10, step = 2)
print(array_step_only)

array_dtype = np.arange(10, dtype=float)
print(array_dtype)
print(array_dtype.dtype, array_dtype.shape)

output

[4 5 6 7 8 9]
[4 6 8]
[0 2 4 6 8]
[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
float64 (10,)

 


zeros() 는 함수 인자로 튜플 형태의 shape 값을 입력하면 모든 값을 0으로 채운 해당 shape의 ndarray를 반환한다

ones() 는 함수 인자로 튜플 형태의 shape 값을 입력하면 모든 값을 1으로 채운 해당 shape의 ndarray를 반환한다

두 함수 모두 dtype을 지정해주지 않으면 float64 형의 dtype을 가진 ndarray로 반환한다.

zero_array_default = np.zeros((3,2))
print(zero_array_default)
print(zero_array_default.dtype, zero_array_default.shape)

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

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

one_array_dtype = np.ones((3, 2), dtype = 'int32')
print(one_array_dtype)
print(one_array_dtype.dtype, one_array_dtype.shape)

output

[[0. 0.]
 [0. 0.]
 [0. 0.]]
float64 (3, 2)
[[0 0]
 [0 0]
 [0 0]]
int32 (3, 2)
[[1. 1.]
 [1. 1.]
 [1. 1.]]
float64 (3, 2)
[[1 1]
 [1 1]
 [1 1]]
int32 (3, 2)

 

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

reshape() 메서드는 ndarray를 특정 차원 및 크기로 변환한다.

아래의 예제는 0~19 까지의 1차원 ndarray를

4 rows x 5 columns (2차원)

5 rows x 4 columns (2차원)

2 rows x 5 columns x 2 heights (3차원)

으로 변환하는 예제이다

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

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

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

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

output

array1:
 [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]
array2:
 [[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]
 [15 16 17 18 19]]
array3:
 [[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]
 [16 17 18 19]]
array4:
 [[[ 0  1]
  [ 2  3]
  [ 4  5]
  [ 6  7]
  [ 8  9]]

 [[10 11]
  [12 13]
  [14 15]
  [16 17]
  [18 19]]]

당연한 얘기지만, shape 사이즈가 맞지 않으면 변경할 수 없고, reshape() 는 오류를 발생시킨다

 

reshape() 를 사용할 때, 인자를 -1로 적용하면 고정된 나머지 인자에 따라서 shape를 자동 적용해준다.

(물론 shape 사이즈가 호환돼야 가능하다)

아까의 예제에서 인자를 -1로 바꿔서 실행하면 아래와 같다

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

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

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

array4 = array1.reshape(2, -1, 2)
print('array4:\n', array4)

output

array1:
 [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]
array2:
 [[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]
 [15 16 17 18 19]]
array3:
 [[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]
 [16 17 18 19]]
array4:
 [[[ 0  1]
  [ 2  3]
  [ 4  5]
  [ 6  7]
  [ 8  9]]

 [[10 11]
  [12 13]
  [14 15]
  [16 17]
  [18 19]]]

이때 array4에서 유의할 점이 있는데, -1로 지정하는 unknown dimension 은 오직 하나만 지정가능하다

array4 = array1.reshape(2, -1, -1) (x)

array4 = array1.reshape(2, -1, 2) (o)

 

-1 인자는 reshape(-1, 1), reshape(-1,) 과 같은 형태로 자주 사용된다.

reshape(-1, 1) 은 원본 ndarray가 어떤 형태라도 2차원이고, 여러개의 로우를 가지되 반드시 1개의 칼럼을 가진 ndarray로 변환됨을 보장한다.

reshape(-1,) 는 원본 ndarray가 어떤 형태라도 1차원으로 변환됨을 보장한다.

여러개의 ndarray를 stack이나 concat으로 결합할 때 형태를 통일하기 위해 사용된다.

아래의 예제는 reshape(-1, 1) 를 이용해 1차원을 2차원으로, 3차원을 2차원으로 변환한다.

(ndarray는 tolist() 메서드를 이용해 리스트 자료형으로 변환할 수 있는데, print를 이용할 때 시각적으로 더 이해하기 쉽게 바꾸기 위해 쓴다.)

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)

# 3차원 ndarray를 1차원 ndarray로 변환
array7 = array3d.reshape(-1,)
print('array7:\n', array7.tolist())
print('array7 shape:', array7.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)
array7:
 [0, 1, 2, 3, 4, 5, 6, 7]
array7 shape: (8,)

 

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

  1. 특정한 데이터만 추출 : 원하는 위치의 인덱스 값을 지정하면 해당 위치의 데이터가 반환.
  2. 슬라이싱 (Slicing) : 슬라이싱은 연속된 인덱스상의 ndarray를 추출하는 방식. ':' 기호를 사이에 두고 시작 인덱스와 종료 인덱스를 표시하면 시작 인덱스에서 종료 인덱스 -1위치에 있는 데이터의 ndarray를 반환.
  3. 팬시 인덱싱 (Fancy Indexing) : 일정한 인덱싱 집합을 리스트 또는 ndarray 형태로 지정해 해당 위치에 있는 데이터의 ndarray를 반환.
  4. 불린 인덱싱 (Boolean Indexing) : 특정 조건에 해당하는지 여부인 True/False 값 인덱싱 집합을 기반으로 True 에 해당하는 인덱스 위치에 있는 데이터의 ndarray 를 반환.

 

단일 값 추출

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

# 1부터 9까지의 1차원 ndarray 생성
array1 = np.arange(start = 1, stop = 10)
print('array1:', array1)

value = array1[2]
print('value:', value)
print(type(value))

# 뒤에서 부터 인덱싱할 수 있다
value_backward1 = array1[-1]
value_backward2 = array1[-3]
print('value_backward1:', value_backward1)
print('value_backward2:', value_backward2)

# 수정도 가능하다
array1[5] = 22
print('array1:', array1)

output

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

 

다차원 ndarray에서 단일 값을 추출하는 예제.

1차원 ndarray를 생성하고, 2차원의 3x3 shape 으로 변환 후, [axis0, axis1]을 통해 2차원 ndarray에서 데이터 추출

(axis0 = row, axis1 = column)

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

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

output

[[1 2 3]
 [4 5 6]
 [7 8 9]]
[0,0] : 1
[0,1] : 2
[1,0] : 4
[2,2] : 9

 

3차원 이상의 ndarray는 직관적으로 row와 column을 생각하며 indexing 하기 어렵다.

따라서, indexing의 순서는 차원이 높은 쪽에서 낮은 쪽으로 (바깥쪽에서 안쪽으로) 진행된다고 알고 있어야 한다.

array1d = np.arange(start=1, stop=25)
array3d = array1d.reshape(2, 3, 4)
print(array3d)
print('array3d[0,0,0]', array3d[0,0,0])
print('array3d[0,1,1]', array3d[0,1,1])
print('array3d[1,2,3]', array3d[1,2,3])

output

[[[ 1  2  3  4]
  [ 5  6  7  8]
  [ 9 10 11 12]]

 [[13 14 15 16]
  [17 18 19 20]
  [21 22 23 24]]]
array3d[0,0,0] 1
array3d[0,1,1] 6
array3d[1,2,3] 24

 


Slicing

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

output

[1 2 3 4 5 6 7 8 9]
[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]

 


2차원도 동일하게 slicing 할 수 있으나, 각 axis를 ','로 구분하는 것만 다르다.

또한, axis에 따라 slicing과 indexing을 혼용할 수 있다.

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]) # Slicing과 Indexing을 혼용

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]

 

2차원 ndarray에서 뒤에 오는 index를 없애면 1차원 ndarray를 반환.

3차원 ndarray에서 뒤에 오는 index를 없애면 2차원 ndarray를 반환.

첫번째 axis만 indexing 되고 그 뒤의 axis는 그 차원 그대로 호출되는 것이다.

array1d = np.arange(start=1, stop=25)
array2d = array1d.reshape(4,6)
array3d = array1d.reshape(2,3,4)
print(array2d[0])
print(array2d[1])
print(array3d[0])
print(array3d[1])
print('array2d[0] shape:', array2d[0].shape, 'array2d[1] shape:', array2d[1].shape)
print('array3d[0] shape:', array3d[0].shape, 'array3d[1] shape:', array3d[1].shape)

output

[1 2 3 4 5 6]
[ 7  8  9 10 11 12]
[[ 1  2  3  4]
 [ 5  6  7  8]
 [ 9 10 11 12]]
[[13 14 15 16]
 [17 18 19 20]
 [21 22 23 24]]
array2d[0] shape: (6,) array2d[1] shape: (6,)
array3d[0] shape: (3, 4) array3d[1] shape: (3, 4)

 


Fancy Indexing

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

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

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


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

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

output

[[1 2 3]
 [4 5 6]
 [7 8 9]]
array2d[[0,1], 2] =>  [3, 6]
<class 'numpy.ndarray'>
array2d[[0,2], 0:2] =>  [[1, 2], [7, 8]]
array2d[[0,1]] =>  [[1, 2, 3], [4, 5, 6]]

array3 = array2d[[0,1], 2] 이라고 지정하면, array2d[0,2], array2d[1,2] 값을 모두 반환한다.

 


Boolean Indexing

조건 필터링과 검색을 동시에 할 수 있다.

index를 지정하는 []내에 조건문을 그대로 기재하면, for loop/if esle문 보다 훨씬 간단하게 구현 가능.

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

# [ ] 안에 array1d > 5 라는 Boolean Indexing 을 적용
array3 = array1d[array1d > 5]
print('array1d > 5 인 값 :', array3)

output

[1 2 3 4 5 6 7 8 9]
array1d > 5 인 값 : [6 7 8 9]

 

ndarray 객체에 조건식을 할당하면 True, False로 이뤄진 ndarray 객체가 반환된다.

이를 다시 ndarray 안에 넣으면 True 값이 있는 인덱스로 변환되고, 해당되는 인덱스 위치의 데이터만 반환하게 된다.

boolean_indexing = array1d > 5
indexes = np.array([5, 6, 7, 8])
print(boolean_indexing)
print(indexes)
print(array1d[boolean_indexing], array1d[indexes])

output

[False False False False False  True  True  True  True]
[5 6 7 8]
[6 7 8 9] [6 7 8 9]

0개의 댓글