본문 바로가기

😀 기초/넘파이(NumPy)

3장 넘파이 배열의 생성과 변형(3.2)

3.2 배열의 생성과 변형

 

1. 넘파이의 자료형
- 넘파이 배열 즉, ndarrray 클래스는 운소가 모두 같은 조료형이어야 한다.
- array 명령으로 배열을 만들 때 자료형을 명시적으로 적용하려면 dtype 인수 사용.
- 만약 dtype 인수가 없으면 주어진 데이털르 저장할 수 있는 자료형을 스스로 유추한다.
- 만들어진 배열의 자료형을 알아내려면 dtype 속성을 보면 된다.

x = np.array([1, 2, 3])
x.dtype
#결과
dtype('int32')

x = np.array([1,0, 2.0, 3.0])
x.dtype
#결과
dtype('float64')

x = np.array([1, 2, 3.0])
x.dtype
#결과
dtype('float64')

- dtype 인수로 지정할 자료형은 다음 표에 보인것과 같은 "dtype 접두사"로 시작하는 문자열이고
- 이 글자 뒤에 오는 숫자는 바이트 수 혹은 글자 수를 의미한다.
- 예를 들어 f8은 8바이트(64비트) 부동소수점 실수를 뜻하고
- U4는 4r글자 유니코드 문자열을 뜻한다.
- 숫자를 생략하면 운영체제에 따라 알맞은 크기를 지정한다.
---
- dtype 접두사는 사이트 참고

x = np.array([1, 2, 3], dtype='f')
x.dtype
#결과
dtype('float32')

x[0] + x[1]
#결과
3.0

x = np.array([1, 2, 3], dtype="U")
x.dtype
#결과
dtype('<U1')

# 😂
x[0] + x[1]
#결과
'12'

2. Inf 와 NaN
- 넘파이에서는 무한대를 표현하는 np.inf(infinity)와 정의할 수 없는 숫자를 나타내는 

   np.nan(not a number)을 사용
- 다음 예와 같이 1을 0으로 나누려고 하거나
- 0에 대한 로그 값을 계산하면 무한대인 np.inf이 나온다.
- 0을 0으로 나누려고 시도하면 np.nan이 나온다.

np.array([0, 1, -1, 0]) / np.array([1, 0, 0, 0])
#결과
array([  0.,  inf, -inf,  nan])

# 😂 무슨 의미?
np.log(0)
#결과
-inf

# 😂 무슨 의미?
np.exp(-np.inf)
#결과
0.0

3. 배열 생성
- NumPy는 몇가지 단순한 배열을 생성하는 명령을 제공
- zeros, ones
- zeros_life, ones_like
- empyty
- arange
- linspace, logspace
---
- 크긱가 정해져 있고 모든 값이 0인 배열을 생성하려면 zeros 명령을 사용한다.
- 인수로는 배열을 크기를 뜻하는 정수를 넣는다.

a = np.zeros(5)
a
#결과
array([0., 0., 0., 0., 0.])

# 크게를 뜻하는 튜플로 입력하면
# 다차원 배열도 만들 수 있음
b = np.zeros((2, 3))
b
#결과
array([[0., 0., 0.],
       [0., 0., 0.]])
       
# array 명령과 마찬가지로 dtype 인수를 명시하면 
# 해당 자료형 원소를 가진 배열을 만든다.
c = np.zeros((5, 2), dtype="i")
c
#결과
array([[0, 0],
       [0, 0],
       [0, 0],
       [0, 0],
       [0, 0]], dtype=int32)
       
# 문자열 배열도 가능하지만
# 모든 원소의 문자열 크기가 같아야 한다.
# 만약 더 큰 크기의 문자열을 할당하면 잘릴 수 있다.
d = np.zeros(5, dtype="U4")
d
#결과
array(['', '', '', '', ''], dtype='<U4')

d[0] = 'abc'
d[1] = 'ABC'
d[2] = 'ABCDE'
d
#결과
array(['abc', 'ABC', 'ABCD', '', ''], dtype='<U4')

# 0이 아닌 1로 초기화된 배열을 생성하려면
# ones 명령을 사용한다.
e = np.ones((2, 3, 4), dtype="i8")
e
#결과
array([[[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]],

       [[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]]], dtype=int64)
        
# 😂 무슨 차이?? 
# 만약 크기를 튜플로 명시하지 않고
# 다른 배열과 같은 크기의 배열을 생성하고 싶다면
# ones_like, zeros_like 명령을 사용한다.
f = np.ones_like(b, dtype="f")
f
#결과
array([[1., 1., 1.],
       [1., 1., 1.]], dtype=float32)

😂
- 배열을 크기가 커지면 배열을 초기화하는데도 시간이 걸림
- 이 시간을 단축하려면 배열을 생성만 하고 특정한 값을으로 초기화를 하지 않는 empty 명령을 사용
empty 명령으로 생성된 배열에는 기존에 메모리에 저장되어있던 값이 있으므로 배열의 원소의 값을 미리 알 수 없다.

g = np.empty((4, 3))
g

#결과
array([[0.0078125, 0.0078125, 0.0078125],
       [0.0078125, 0.0078125, 0.0078125],
       [0.0078125, 0.0078125, 0.0078125],
       [0.0078125, 0.0078125, 0.0078125]])
       
# arange 명령은 NumPy 버전의 range 명령이라고 볼 수 있다.
# 특정한 규칙에 따라 증가하는 수열을 만든다.

np.arange(10)
#결과
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

np.arange(3, 21, 2) # 시작, 끝(포함x), 단계
#결과
array([ 3,  5,  7,  9, 11, 13, 15, 17, 19])

# linspace 명령이나 logspace 명령은
# 선형 구간 혹은 로그 구간을 
# 지정한 구간의 수만큼 분할한다.

np.linspace(0, 100, 5) # 시작, 끝(포함0), 갯수

#결과
array([  0.,  25.,  50.,  75., 100.])

np.logspace(0.1, 1, 10)

#결과
array([ 1.25892541,  1.58489319,  1.99526231,  2.51188643,  3.16227766,
        3.98107171,  5.01187234,  6.30957344,  7.94328235, 10.        ])

 

4. 전치 연산
- 2차원 배열의 전치(transpose)연산은 행과 열을 바꾸는 작업
- 메서드가 아니라 속성이라는 점에 유의

A = np.array([[1, 2, 3], [4, 5, 6]])
A
#결과
array([[1, 2, 3],
       [4, 5, 6]])
       
A.T
#결과
array([[1, 4],
       [2, 5],
       [3, 6]])

5. 배열의 크기 변형
- 일단 만들어진 배열의 내부 데이터를 보전한 채로 형태만 바꾸려면 reshape 명령이나 메서드를 사용한다.
- 예를 들어 12개의 원소를 가진 1차원 행렬은 3x4 형태의 2차원 행렬로 만들 수 있다.

a = np.arange(12)
a
#결과
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

b = a.reshape(3, 4)
b
#결과
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

- 😂
- 사용자가 원소의 갯수가 정해져 있기 때문에 reshape 명령의 형태 튜플의 원소 중 하나는 -1이라는 숫자로 대체할 수 있다.
- -1을 넣으면 해당 숫자는 다를 값에서 계산되어 사용된다.

a.reshape(3, -1)
#결과
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
       
a.reshape(2, 2, -1)
#결과
array([[[ 0,  1,  2],
        [ 3,  4,  5]],

       [[ 6,  7,  8],
        [ 9, 10, 11]]])
        
a.reshape(2, -1, 2)
#결과
array([[[ 0,  1],
        [ 2,  3],
        [ 4,  5]],

       [[ 6,  7],
        [ 8,  9],
        [10, 11]]])
        
# 다차원 배열을 무조건 1차원으로 만들기 위해서는
# flatten 이나 ravel 메서드 사용

a.flatten()
#결과
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

a.ravel()
#결과
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

- 배열 사용에서 주의할 점은 
- 길이가 5인 1차원 배열과 행, 
- 열의 갯수가 (5,1)인 2차원 배열 또는 행,
- 열의 갯수가 (1, 5)인 2차원 배열은 데이터가 같아도 엄연히 다른 객체라는 점이 있다.

x = np.arange(5)
x
#결과
array([0, 1, 2, 3, 4])

x.reshape(1, 5)
#결과
array([[0, 1, 2, 3, 4]])

x.reshape(5, 1)
#결과
array([[0],
       [1],
       [2],
       [3],
       [4]])
       
# 이렇게 같은 배열에 대한 차원만 1차원 증가시키는 경우
# newaxis 명령을 사용하기도 한다.

x[:, np.newaxis]
#결과
array([[0],
       [1],
       [2],
       [3],
       [4]])

6. 배열의 연결
- 행의 수나 열의 수가 같은 두 개 이상의 배열 연결
- hstack
- vstack
- stack
- r_
- c_
- tile

# hstack 행의 수가 같은 걸 옆으로 연결

a1 = np.ones((2, 3))
print(a1)

a2 = np.ones((2, 2))
print(a2)

np.hstack([a1, a2])

#결과
[[1. 1. 1.]
 [1. 1. 1.]]
[[1. 1.]
 [1. 1.]]
 
array([[1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.]])
       
# vstack 열의 수가 같은 걸 위아래로 연결

b1 = np.ones((2, 3))
print(a1)

b2 = np.zeros((3, 3))
print(b2)

np.vstack([b1, b2])

#결과
[[1. 1. 1.]
 [1. 1. 1.]]
[[0. 0. 0.]
 [0. 0. 0.]
 [0. 0. 0.]]
array([[1., 1., 1.],
       [1., 1., 1.],
       [0., 0., 0.],
       [0., 0., 0.],
       [0., 0., 0.]])

😂
- dstack 명령은 제 3의축, 행이나 열이 아닌 깊이(depth)방향으로 배열을 합친다.
- 가장 안쪽의 차원이 증가한다.
- 즉 가장 내부의 숫자 원소가 배열이된다.
- shape 정보로 보자면 가장 끝에 값이 2인 차원이 추가되는 것.
- 아래 예제 경우 shape 변화가 2개의(3x4) -> 1개의 (3x4x2)가 된다.

c1 = np.ones((3, 4))
c1
#결과
array([[1., 1., 1., 1.],
       [1., 1., 1., 1.],
       [1., 1., 1., 1.]])
       
c2 = np.zeros((3, 4))
c2
#결과
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])
       
np.dstack([c1, c2])
#결과
array([[[1., 0.],
        [1., 0.],
        [1., 0.],
        [1., 0.]],

       [[1., 0.],
        [1., 0.],
        [1., 0.],
        [1., 0.]],

       [[1., 0.],
        [1., 0.],
        [1., 0.],
        [1., 0.]]])
        
(np.dstack([c1, c2])).shape
#결과
(3, 4, 2)

- r_메서드는 hstack 명령과 비슷하게 배열을 좌우로 연결한다
- 다만 메서드임에도 불구하고 소괄호(())를 사용하지 않고 인덱싱과 같이 대괄호([])를 사용한다.
- 이런 특수 메서드를 인덱서(indexer)라고 한다.

np.r_[np.array([1, 2, 3]),np.array([4, 5, 6])]
#결과
array([1, 2, 3, 4, 5, 6])

# c_ 메서드는 배열의 차원을 증가시킨 후 좌우로 연결
# 만약 1차원 배열을 연결하면 2차원 배열이 된다.

np.c_[np.array([1, 2, 3]), np.array([4, 5, 6])]
#결과
array([[1, 4],
       [2, 5],
       [3, 6]])
       
# tile 명령은 동일한 배열을 반복하여 연결
a = np.array([[0, 1, 2], [3, 4, 5]])
np.tile(a, 2)
#결과
array([[0, 1, 2, 0, 1, 2],
       [3, 4, 5, 3, 4, 5],
       [0, 1, 2, 0, 1, 2],
       [3, 4, 5, 3, 4, 5],
       [0, 1, 2, 0, 1, 2],
       [3, 4, 5, 3, 4, 5]])
       
np.tile(a, (3, 3))
#결과
array([[0, 1, 2, 0, 1, 2, 0, 1, 2],
       [3, 4, 5, 3, 4, 5, 3, 4, 5],
       [0, 1, 2, 0, 1, 2, 0, 1, 2],
       [3, 4, 5, 3, 4, 5, 3, 4, 5],
       [0, 1, 2, 0, 1, 2, 0, 1, 2],
       [3, 4, 5, 3, 4, 5, 3, 4, 5]])
       
np.tile(a, (4, 2))
#결과
array([[0, 1, 2, 0, 1, 2],
       [3, 4, 5, 3, 4, 5],
       [0, 1, 2, 0, 1, 2],
       [3, 4, 5, 3, 4, 5],
       [0, 1, 2, 0, 1, 2],
       [3, 4, 5, 3, 4, 5],
       [0, 1, 2, 0, 1, 2],
       [3, 4, 5, 3, 4, 5]])

7. 😂 2차원 그리드 포인트 생성
- 변수 2개인 2차운 함수의 그래프를 그리거나 표를 작성하려면 2차원 영역에 대한 (x, y)좌표값 쌍 즉, 그리드 포인트(grid point)를 생성하여 각 좌표에 대한 함수 값을 계산해야 한다.
- 예를 들어 x, y 라는 두 변수를 가진 함수에서 x가 0부터 2까지, y가 0부터 4까지의 사격형 영역에서 변화하는 과정을 보고 싶다면
- 이 사격형 영역 안의 다음과 같은 그리드 포인트들에 대해 함수르 계산해야 한다.

(x,y)=(0,0),(0,1),(0,2),(0,3),(0,4),(1,0),⋯(2,4)

- 이러한 그리드 포인트를 만드는 과정을 도와주는 것이 meshgrid 명령이다.
- meshgrid 명령은 사격 영역을 구성하는 가로축의 점들과 세로축의 점을 나타내는 두 벡터를 인수로 받아서 이 사격형 영역을 이루는 조합을 출력
- 결과는 그리드 포인트의 x 값만을 표시하는 행렬과 y 값만을 표시하는 행렬 두 개로 분리하여 출력한다.

x = np.arange(3)
x
#결과
array([0, 1, 2])

y = np.arange(5)
y
#결과
array([0, 1, 2, 3, 4])

X, Y = np.meshgrid(x, y)
X
#결과
array([[0, 1, 2],
       [0, 1, 2],
       [0, 1, 2],
       [0, 1, 2],
       [0, 1, 2]])
       
Y
#결과
array([[0, 0, 0],
       [1, 1, 1],
       [2, 2, 2],
       [3, 3, 3],
       [4, 4, 4]])
       
[list(zip(x, y)) for x, y in zip(X, Y)]
#결과
[[(0, 0), (1, 0), (2, 0)],
 [(0, 1), (1, 1), (2, 1)],
 [(0, 2), (1, 2), (2, 2)],
 [(0, 3), (1, 3), (2, 3)],
 [(0, 4), (1, 4), (2, 4)]]

'😀 기초 > 넘파이(NumPy)' 카테고리의 다른 글

3장 난수 발생과 카운팅(3.5)  (0) 2022.01.25
3장 배열의 연산(3.3)  (0) 2022.01.25
3장 넘파이 배열 프로그래밍(3.1)  (0) 2022.01.24
NumPy 한번에 끝내기  (0) 2022.01.18