220328 Numpy TIL

Numpy

이번 포스팅에서는 이번에 처음 배워보는 넘파이(Numpy)에 대해서 개념 및 기본 사용법에 대해서 정리해보려고 한다.

넘파이(Numpy)?

1
2
(1) Numpy는 C언어로 구성되었으며, 고성능의 수치계산을 위해 나온 패키지이며, Numerical Python의 약자이다.
(2) Python을 활용한 데이터 분석을 수행할 때, 그리고 데이터 시각화나 전처리를 수행할 때, NumPy는 매우 자주 사용되기 때문에 중요하다.

넘파이의 기본 사용

모듈 Import

1
2
3
import numpy as np

print(np.__version__)

넘파이를 활용한 배열 생성

넘파이를 사용해서 배열을 생성할 수 있는데, 그 타입이 ndarray이다. ndarray는 N-dimensional array의 줄임말이다. 넘파이의 배열은 일차원 배열부터 다차원 배열의 형태를 만들 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 1D array 정의하기 (첫 번째 depth에 배열이 들어간다.)
array_1D = np.array([1,8,27,64])
print(array_1D)

# output:
# [1 8 27 64]


# 2D array 정의하기 (두 번째 depth에 배열이 들어간다.)
array_2D = np.array([[1,2,3,4],[2,4,9,16],[4,8,18,32]])

# output:
# array([[ 1, 2, 3, 4],
# [ 2, 4, 9, 16],
# [ 4, 8, 18, 32]])


# 3D array 정의하기 (세 번째 depth에 배열이 들어간다.)
array_3D = np.array([[[1,2,3,4],[5,6,7,8]],[[1,2,3,4],[9,10,11,12]]])

# output:
# array([[[ 1, 2, 3, 4],
# [ 5, 6, 7, 8]],

# [[ 1, 2, 3, 4],
# [ 9, 10, 11, 12]]])

배열에 대한 정보 확인 메소드

- 배열의 정보를 확인하는 다양한 함수 존재
- 현재 저장된 배열에 대해 RAM의 주소를 확인

1
print(array_2D.data) #<memory at 0x7f445e6339f0>
- 배열의 구조를 확인 할 수 있다.
1
print(array_2D.shape) #(3, 4)
- 배열의 데이터 타입을 확인할 수 있다.
1
print(array_2D.dtype) #int64
- 배열간의 간격 및 각 요소간의 간격에 대해서도 확인이 가능하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np

arr = np.array([[1,2,3,4],[5,6,7,8]])

# strides : 걸음걸이, 보폭 (Numpy에서는 각 dimensions를 건너가는데 몇 bytes나 뛰어넘어야 하는지에 대한 정보)

# 8바이트인 int64형이 각 배열에 4개씩 있기 때문에, 32bytes로, dimension간의 간격은 32, 각 배열의 elements간의 간격은 8바이트이므로 (32, 8)로 표기

arr.strides # (32, 8) - (dimensions 간의 간격, elements 간의 간격)

# Numpy에서는 배열을 생성할때 두 번째 인자로 내부에 들어갈 데이터의 타입을 지정해줄 수 있다.
# int32 - 4byte
# int8 - 8byte
arr = np.array([[1,2,3,5],[4,5,6,10]], 'int32')
arr.strides # (16, 4) - (dimensions 간의 간격, elements 간의 간격)

NumPy를 활용한 다양한 객체 생성

- NumPy 패키지내의 함수를 활용하여 다양한 방식으로 패키지 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import numpy as np

# Array of 1
ones = np.ones((3,4)) # 행, 열
print(ones)

# 1로 채워진 3행 4열 배열 생성
#output:
# [[1. 1. 1. 1.]
# [1. 1. 1. 1.]
# [1. 1. 1. 1.]]

print(ones.ndim) # 차원 수 확인
#output:
# 2

# Array of 0
zeros = np.zeros((2,3,5), dtype=np.int16) # 3차원, 2차원, 1차원의 개수
print(zeros)

#output:
# [[[0 0 0 0 0]
# [0 0 0 0 0]
# [0 0 0 0 0]]

# [[0 0 0 0 0]
# [0 0 0 0 0]
# [0 0 0 0 0]]]

# Array with random value
np.random.random((2,2)) # 2차원, 1차원의 개수

#output:
# array([[0.59266798, 0.64868564],
# [0.93648129, 0.3351101 ]])


# Empty 배열
emptyArray = np.empty((3, 2))
print(emptyArray)

#output:
# [[4.63755739e-310 0.00000000e+000]
# [0.00000000e+000 0.00000000e+000]
# [0.00000000e+000 0.00000000e+000]]


# Full Array
fullArray = np.full((2, 2), 7) # 2행 2열 배열에 숫자 7로 채워라라는 의미
print(fullArray)

# output
#[[7 7]
# [7 7]]

# Array of Evenly-Spaced Values (1차원 배열)
# arange 특정한 규칙에 따라 증가하는 수열을 생성한다.
evenSpacedArray = np.arange(10,50,5)
print(evenSpacedArray) # [10 15 20 25 30 35 40 45]


# linspace & logspace 명령은 선형 구간 혹은 로그 구간을 지정한 구간의 수 만큼 분할을 한다.
evenSpacedArray2 = np.linspace(0,2,9)
print(evenSpacedArray2)
# [0. 0.25 0.5 0.75 1. 1.25 1.5 1.75 2. ]

evenSpacedArray3 = np.logspace(0,2,9)
print(evenSpacedArray3)
# [ 1. 1.77827941 3.16227766 5.62341325 10.
# 17.7827941 31.6227766 56.23413252 100. ]

NumPy 배열 정렬

1
# 내용 추가 정리하기

2022/03/29 추가 내용

3차원 배열 연습문제

3rd dimentional array
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import numpy as np

# 위와 같은 요소로 이루어진 3차원 ndarray를 만들고 a1이라는 이름으로 저장하여 출력
a1 = np.array([[[10, 11, 12],
[13, 14, 15],
[16, 17, 18]],

[[20, 21, 22],
[23, 24, 25],
[26, 27, 28]],

[[30, 31, 32],
[33, 34, 35],
[36, 37, 38]]])

a1

# ndaray의 차원 확인
a1.ndim # 3

# ndarray의 구조 확인
a1.shape #(3, 3, 3)

# Q1: [[30, 31, 32],
# [33, 34, 35],
# [36, 37, 38]] 만 출력

a1[2, :, :] # 행에서 마지막 행만 출력되고 열은 모두 출력되도록 슬라이싱

# Q2: [[[10, 11, 12],
# [13, 14, 15]],

# [[20, 21, 22],
# [23, 24, 25]],

# [[30, 31, 32],
# [33, 34, 35]]] 만 출력되도록

a1[:, :2] # 모든 행은 출력되고, 열은 마지막 열을 제외한 0,1번째만 출력이되야하므로, :2로 슬라이싱 해준다.

# Q3: [20, 23] 만 출력되도록 슬라이싱하기
a1[1, :2, 0] # 두 번째 행 요소에서 열의 0번째에서 2개 추출

# Q4: [[14, 17],
# [24, 27],
# [34, 37]]
a1[:, 1:, 1]

형태(Shape) 변경

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import numpy as np

np.arange(6).reshape((3,2)) # 0~5 숫자를 요소로가지는 3행 2열 배열을 생성
np.arange(6).reshape((3,-1)) # -1은 3행을 기준으로 했을 때 자동으로 열을 알아서 맞춰준다.

arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])
newarr = arr.reshape(2, 2, -1)
print(newarr)

# output:
# [[[1 2]
# [3 4]]

# [[5 6]
# [7 8]]]

# NumPy에서 -1은 차원에 대한 정확한 숫자를 명시하지 않아도 된다는 의미이다. -1을 입력하게 되면, NumPy가 알아서 계산을 해줄 것이다.

arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
newarr = arr.reshape(4, 3) # 1-D to 2-D

arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
newarr = arr.reshape(2, 3, 2) # 1-D to 3-D

# 3차원 배열에서는 2차원 변환과는 다르게 (행, 열) 순이 아닌 (열, 행) 순이다.

2022/03/30 추가 작성

다양한 NumPy 메소드

(1) mean : 평균

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
a = np.arange(2, 41, 2).reshape(2, 5, -1)

# output :
# array([[[ 2, 4],
# [ 6, 8],
# [10, 12],
# [14, 16],
# [18, 20]],

# [[22, 24],
# [26, 28],
# [30, 32],
# [34, 36],
# [38, 40]]])

a.mean()
np.mean(a)

(2) flatten : depth가 있는 배열을 평탄하게 만들때 사용

1
2
a.flatten()
np.flatten(a) # AttributeError flatten은 예외적으로 NumPy의 모듈 함수가 아니다. ndarray 객체의 자체 메소드로만 사용이 가능하다.

연결(Concatenate)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 2 x 2 배열과 1 X 2 배열을 합칠때에는 행 기준으로 붙여야되기 때문에 axis=0이 되어야 한다.
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])

a.ndim # 2
a.shape # (2, 2)
b.ndim # 2
b.shape # (1, 2)

np.concatenate((a, b), axis=0)
# array([[1, 2],
# [3, 4],
# [5, 6]])

np.concatenate((b, a), axis=0)
# array([[5, 6],
# [1, 2],
# [3, 4]])

# 열이 같으면 행을 기준으로 붙일 수 있고, 행이 같으면 열을 기준으로 붙일 수 있다.
# 위에서 b를 reshape를 통해 a와 같은 2행으로 reshape하고, 열을 기준으로 concatenate해주도록 하자.

b = b.reshape(2, -1)
np.concatenate((b, a), axis=1) # axis=1은 열을 기준으로 2차원 배열 a와 b를 붙여준다는 의미이다.
# array([[5, 1, 2],
# [6, 3, 4]])

.T = 전치행렬 (행과 열을 교환)

reshape를 사용하여 직접 행, 열의 수를 지정하여 행과 열이 교환된 배열의 모양으로 수정해줄 수도 있지만, 간단하게 .T를 사용해서 간단하게 행과 열이 교환된 배열의 모양으로 만들어줄 수 있다.
1
np.concatenate((b.T, a), axis=1)

브로드 캐스팅(Broadcasting)

배열을 가지고 연산을 할 때, 배열의 모양(shape)이 서로 다른 경우에도 서로 다른 배열간의 산술연산이 가능하도록 하는 메커니즘이 바로 브로드 캐스팅이다.
통신에서도 이 브로드 캐스팅이라는 용어가 있는데, 뭔가 신호나 패킷을 특정 도메인 영역에 뿌려준다는 것을 생각하면, NumPy에서의 브로드 캐스팅은 배열내의 전체적인 산술연산을 해주는 것이라고 생각하면 된다.

1
2
3
[브로드 캐스팅의 조건]
(1) 두 ndarray 중 최소한 하나의 배열의 차원이 1 이하이면 가능
(2) 두 ndarray의 차원의 축의 길이가 동일하면 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 브로드 캐스팅 예시

# 스칼라와 벡터 연산
vector = np.random.randint(1,10, size=(4,1))
2 * vector
# output
# array([[2],
# [4],
# [4],
# [4]])

vector = [[9],
[8],
[4]]
mat = [[7, 1, 6, 3],
[1, 2, 4, 5],
[5, 4, 3, 5]]

vector * mat

# result :
# array([[63, 9, 54, 27],
# [ 8, 16, 32, 40],
# [20, 16, 12, 20]])

배열 조작(Array Manipulation)

Adding/Removing Elements

np.delete([target array], [row num], [0: row, 1: column])

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

np.delete(arr,1,0) #0번째 축은 행, 1행을 제거

np.delete(arr,1,1) #1번째 축은 열, 1열을 제거

np.mean(arr,axis=0) #0번재 축은 행, 행에서의 평균

np.mean(arr,axis=1) #1번째 축은 열, 열끼리의 평균