[Python] 데이터 사이언스 스쿨 - 3.2 배열의 생성과 변형
Updated:
데이터 사이언스 스쿨 자료를 토대로 공부한 내용입니다.
실습과정에서 필요에 따라 내용의 누락 및 추가, 수정사항이 있습니다.
3.2 배열의 생성과 변형
넘파이의 자료형
import numpy as np
# dtype을 이용해 자료형태 확인
x = np.array([0,1,2])
print(x.dtype)
# 배열 생성시 자료 형태를 지정하지 않으면 자동으로 유추
x = np.array([0,1,2.0])
print(x.dtype)
# 문자와 숫자 섞은 후 자료 형태 지정 X
x = np.array(["A",1,2.0])
print(x.dtype)
int32
float64
<U3
-
넘파이 배열의 자료 형태를 알고 싶으면
dtype
속성을 사용한다. -
배열을 생성할 때 자료 형태를 지정하지 않으면 자동으로 유추한다.
dtype 종류
dtype 접두사 | 설명 | 사용 예 |
---|---|---|
b |
불리언 | b (참 혹은 거짓) |
i |
정수 | i8 (64비트) |
u |
부호 없는 정수 | u8 (64비트) |
f |
부동소수점 | f8 (64비트) |
c |
복소 부동소수점 | c16 (128비트) |
O |
객체 | 0 (객체에 대한 포인터) |
S |
바이트 문자열 | S24 (24 글자) |
U |
유니코드 문자열 | U24 (24 유니코드 글자) |
x = np.array([1,2,3], dtype = 'f')
print(x[0] + x[1])
x = np.array([1,2,3], dtype = 'U')
print(x[0] + x[1])
3.0
12
-
위 예시는 똑같은 배열을 만드는데 각각 float형, 문자형으로 생성하였다.
-
float형의 경우 배열의 원소를 더하면 실제 산술 계산이 이루어 진다.
-
반면에 문자형으로 지정한 경우는 문자로 인식하기에 두 문자를 붙여서 출력하였다.
Inf와 NaN
np.array([0, 1, -1, 0]) / np.array([1, 0, 0, 0])
<ipython-input-4-6ab12ec6e7a4>:1: RuntimeWarning: divide by zero encountered in true_divide
np.array([0, 1, -1, 0]) / np.array([1, 0, 0, 0])
<ipython-input-4-6ab12ec6e7a4>:1: RuntimeWarning: invalid value encountered in true_divide
np.array([0, 1, -1, 0]) / np.array([1, 0, 0, 0])
array([ 0., inf, -inf, nan])
print(np.log(0))
print(np.exp(-np.inf))
-inf
0.0
<ipython-input-5-0d5d201f1928>:1: RuntimeWarning: divide by zero encountered in log
print(np.log(0))
-
넘파이는 무한대를 의미하는
np.inf
와 정의할 수 없는 숫자를np.nan
을 사용할 수 있다. -
위 예시들을 통해서 inf나 nan이 출력되는 것을 확인 할 수 있다.
배열 생성
# 모든 값이 0인 행렬
z1 = np.zeros(5)
z2 = np.zeros((2,2))
print(z1)
print(z2)
[0. 0. 0. 0. 0.]
[[0. 0.]
[0. 0.]]
-
np.zeros()
는 모든 원소가 0인 배열을 생성한다. -
생성시 배열의 크기를 지정하면 지정한 형태로 배열을 생성한다.
# 모든 값이 1인 행렬
o1 = np.ones(5)
o2 = np.ones((2,2))
print(o1)
print(o2)
[1. 1. 1. 1. 1.]
[[1. 1.]
[1. 1.]]
np.ones()
는np.zeros()
와 유사하지만 모든 원소를 1로 반환한다.
# 자료형 지정 가능
z = np.zeros((5, 2), dtype="i")
z
array([[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0]], dtype=int32)
- 배열 생성시 자료 형태를 지정 가능하며
np.zeros()
가 아닌 다른 배열들도 지정 가능하다.
# 문자로 지정시 크기에 유의
z = np.zeros(5, dtype="U4")
z[0] = "a"
z[1] = "abcd"
z[2] = "abcde" # 짤려서 나옴
print(z)
['a' 'abcd' 'abcd' '' '']
-
위 예시는
np.zeros()
로 문자 배열 생성 후 원소 값을 바꾸어 준다. -
한 가지 유의할 점은 처음 dtype에 지정한 크기보다 길이가 큰 문자를 넣으면 값이 짤려서 저장된다.
# 다른 배열과 같은 크기로 생성하기
z = np.zeros(5)
o = np.ones_like(z, dtype= "f")
o
array([1., 1., 1., 1., 1.], dtype=float32)
np.zeors_like
,np.ones_like
는 다른 배열과 같은 크기로 원소만 각각 0,1로 생성한다.
# 배열을 생성만 하고 값은 초기화 하지 않음 (가장 빠른 속도)
e = np.empty((2, 2))
e
array([[1., 1.],
[1., 1.]])
-
배열의 크기가 커지면 배열을 초기화하는데도 시간이 걸린다.
-
np.empty()
는 배열을 생성만 하고 특정한 값으로 초기화를 하지 않는다. -
생성된 배열의 원소는 기존에 메모리에 저장되어 있던 값이 부여되어 값을 미리 알 수 없다.
# np.arange: 리스트의 range 역할
print(np.arange(5))
# np.arange(시작, 끝, 단계)
print(np.arange(1,11,2))
[0 1 2 3 4]
[1 3 5 7 9]
-
np.aragne()
는 리스트의range()
함수와 비슷한 역할을 한다. -
위 예시와 같이 시작, 끝, 추출단계를 지정하여 배열을 생성할 수 있다.
# linsapce(시작, 끝(포함), 추출 갯수)
print(np.linspace(1,10,4))
# logspace(시작, 끝(포함), 추출 갯수)
print(np.logspace(0.1,1,4)) # 10**0.1 ~ 10
[ 1. 4. 7. 10.]
[ 1.25892541 2.51188643 5.01187234 10. ]
-
np.linspace()
는 시작 숫자부터 끝 숫자까지 지정한 추출 갯수만큼 출력될 수 있게 동일한 간격으로 출력한다. -
np.logspace()
역시 유사하지만 10^시작 숫자부터 10^끝 숫자까지로 범위를 지정한다. -
두 함수는
range()
,np.arange()
와는 다르게 끝 숫자를 포함하여 출력한다.
전치 연산
M = np.array([[0,1,2],
[3,4,5]])
print(M) # 2 x 3
print("-"*10)
print(M.T) # 3 x 2
print("-"*10)
print(np.transpose(M)) # 3 x 2
[[0 1 2]
[3 4 5]]
----------
[[0 3]
[1 4]
[2 5]]
----------
[[0 3]
[1 4]
[2 5]]
- 넘파이 배열은
np.transpose()
혹은T
속성으로 전치 행렬을 출력할 수 있다.
배열의 크기 변형
# 1차원 배열 생성
x = np.arange(12)
print(x)
print("-"*20)
# 2차원으로 변환
x2 = x.reshape(3,4)
print(x2)
print("-"*20)
# 3차원으로 변환
x3 = x.reshape(2,2,3)
print(x3)
print("-"*20)
# -1을 이용하여 자동으로 지정
x4 = x.reshape(2,-1)
print(x4)
[ 0 1 2 3 4 5 6 7 8 9 10 11]
--------------------
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
--------------------
[[[ 0 1 2]
[ 3 4 5]]
[[ 6 7 8]
[ 9 10 11]]]
--------------------
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]]
-
reshape()
를 사용하면 배열의 크기를 변형 가능하다. -
차원, 행, 열에 -1을 입력하면 나머지 숫자를 고려해서 원래 원소 갯수와 동일하게 출력될 수 있게끔 변형된다.
# 무조건 1차원으로 배열 만들기 - flatten, ravel
x = np.arange(12).reshape(3,4)
x2 = x.reshape(12)
x3 = x.flatten()
x4 = x.ravel()
print(x)
print(x2)
print(x3)
print(x4)
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[ 0 1 2 3 4 5 6 7 8 9 10 11]
[ 0 1 2 3 4 5 6 7 8 9 10 11]
[ 0 1 2 3 4 5 6 7 8 9 10 11]
-
flatten()
ravel()
은 모두 1차원 배열로 크기를 변형하는 함수이다. -
flatten()
으로 생성한 배열은 원본 배열이 변경되어도 영향이 없다. -
reshape()
,ravel()
으로 생성한 배열은 원본 배열이 변경되면 같이 값이 변경된다.
x[0] = 1
print("reshape:", x2)
print("faltten:", x3)
print("ravel:", x4)
reshape: [ 1 1 1 1 4 5 6 7 8 9 10 11]
faltten: [ 0 1 2 3 4 5 6 7 8 9 10 11]
ravel: [ 1 1 1 1 4 5 6 7 8 9 10 11]
-
원본 배열을 수정하였더니
reshape()
,ravel()
으로 생성한 배열의 원소도 수정되었다. -
반대로
reshape()
,ravel()
으로 생성한 배열을 수정하면 원본 배열도 수정된다.
# 차원 확인하기
x = np.arange(3)
x2 = x.reshape(1,3)
print(x, "차원:", x.ndim)
print(x2, "차원:", x2.ndim)
[0 1 2] 차원: 1
[[0 1 2]] 차원: 2
- 같은 데이터여도 1차원과 2차원 배열은 확실히 다른 객체임을 명심하여야 한다.
# 차원 증가시키기
x = np.arange(3)
print(x[:, np.newaxis])
print(x[np.newaxis, :])
[[0]
[1]
[2]]
[[0 1 2]]
np.newaxis
를 이용하면 원래 배열에서 1차원 증가시킬 수 있다.
배열 연결
# hstack: 행의 수가 같아야함
x = np.zeros((2,3))
y = np.ones((2,5))
np.hstack([x,y])
array([[0., 0., 0., 1., 1., 1., 1., 1.],
[0., 0., 0., 1., 1., 1., 1., 1.]])
np.hstack()
은 배열을 행 연결하는 함수로 연결하는 배열의 행의 수가 같아야 한다.
# vstack: 열의 수가 같아야함
x = np.zeros((2,4))
y = np.ones((1,4))
np.vstack([x,y])
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[1., 1., 1., 1.]])
np.vstack()
은 배열을 열 연결하는 함수로 연결하는 배열의 열의 수가 같아야 한다.
# dstack: 행과 열의 수가 같아야함
x = np.zeros((4,3))
y = np.ones((4,3))
# 기존 4 x 3 -> 4 x 3 x 2
np.dstack([x,y])
array([[[0., 1.],
[0., 1.],
[0., 1.]],
[[0., 1.],
[0., 1.],
[0., 1.]],
[[0., 1.],
[0., 1.],
[0., 1.]],
[[0., 1.],
[0., 1.],
[0., 1.]]])
-
np.dstack()
은 연결하는 배열의 행과 열의 수가 같아야 한다. -
만약 4 x 3 행렬 2개를 연결하면 4 x 3 x 2로 변경되어 기존의 행과 열의 수가 달라진다.
# stack: 행과 열의 수가 같아야함 - dstack의 확장버전
x = np.arange(12) ; x = x.reshape((4,3))
y = np.arange(12,24) ; y = y.reshape((4,3))
# 기존 4 x 3 -> 2 x 4 x 3 - 깊이 연결 (디폴트 axis=0)
print(np.stack([x,y]))
print("-" * 20)
# 기존 4 x 3 -> 4 x 2 x 3 - 행 연결
print(np.stack([x,y], axis = 1))
print("-" * 20)
# 기존 4 x 3 -> 4 x 3 x 2 - 행 연결 후 전치
print(np.stack([x,y], axis = 2))
[[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
[[12 13 14]
[15 16 17]
[18 19 20]
[21 22 23]]]
--------------------
[[[ 0 1 2]
[12 13 14]]
[[ 3 4 5]
[15 16 17]]
[[ 6 7 8]
[18 19 20]]
[[ 9 10 11]
[21 22 23]]]
--------------------
[[[ 0 12]
[ 1 13]
[ 2 14]]
[[ 3 15]
[ 4 16]
[ 5 17]]
[[ 6 18]
[ 7 19]
[ 8 20]]
[[ 9 21]
[10 22]
[11 23]]]
-
np.stack()
은np.dstack()
의 확장 버전이다. -
지정한 axis에 따라서 연결 방식이 정해진다.
# r_: hstack과 비슷
np.r_[np.array([1, 2, 3]), np.array([4, 5, 6])]
array([1, 2, 3, 4, 5, 6])
-
np.r_[]
은np.hstack()
과 비슷한 역할을 수행한다. -
한 가지 특이사항은 메소드임에도 소괄호가 아닌 중괄호를 사용한다.
# c_: 차원을 증가시킨 후 연결
np.c_[np.array([1, 2, 3]), np.array([4, 5, 6])]
array([[1, 4],
[2, 5],
[3, 6]])
-
np.c_[]
은 차원을 1차원 증가시킨 후 행 연결한다. -
np.r_[]
처럼 메소드임에도 소괄호가 아닌 중괄호를 사용한다.
# tile: 동일한 배열을 반복하여 연결
a = np.array([[0, 1, 2],
[3, 4, 5]])
print(np.tile(a, 3)) # 1개 지정시 hstack
print(np.tile(a, (3,2)))
[[0 1 2 0 1 2 0 1 2]
[3 4 5 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]]
np.tile()
은 지정한 숫자 혹은 배열크기 만큼 원래 배열을 복사해서 붙인다.
연습문제 3.2.1
지금까지 공부한 명령어를 사용하여 다음과 같은 배열을 만들어라.
array([[ 0., 0., 0., 1., 1.],
[ 0., 0., 0., 1., 1.],
[ 0., 0., 0., 1., 1.],
[ 10., 20., 30., 40., 50.],
[ 60., 70., 80., 90., 100.],
[ 110., 120., 130., 140., 150.],
[ 0., 0., 0., 1., 1.],
[ 0., 0., 0., 1., 1.],
[ 0., 0., 0., 1., 1.],
[ 10., 20., 30., 40., 50.],
[ 60., 70., 80., 90., 100.],
[ 110., 120., 130., 140., 150.]])
z1 = np.zeros((3,3))
o1 = np.ones((3,2))
m1 = np.arange(10, 151, 10)
m2 = m1.reshape((3,5))
k1 = np.hstack([z1,o1])
k2 = np.vstack([k1,m2])
k3 = np.tile(k2,(2,1))
k3
array([[ 0., 0., 0., 1., 1.],
[ 0., 0., 0., 1., 1.],
[ 0., 0., 0., 1., 1.],
[ 10., 20., 30., 40., 50.],
[ 60., 70., 80., 90., 100.],
[110., 120., 130., 140., 150.],
[ 0., 0., 0., 1., 1.],
[ 0., 0., 0., 1., 1.],
[ 0., 0., 0., 1., 1.],
[ 10., 20., 30., 40., 50.],
[ 60., 70., 80., 90., 100.],
[110., 120., 130., 140., 150.]])
2차원 그리드 포인트 생성
# 좌표 만들기
x = np.arange(3)
y = np.arange(5)
# np.meshgrid
# X: 5 x 3 - vstack / Y: 5 x 3 - hstack
X, Y = np.meshgrid(x,y)
print(X)
print("-" * 20)
print(Y)
print("-" * 20)
# zip: pair를 만들어줌
[list(zip(x, y)) for x, y in zip(X, Y)]
[[0 1 2]
[0 1 2]
[0 1 2]
[0 1 2]
[0 1 2]]
--------------------
[[0 0 0]
[1 1 1]
[2 2 2]
[3 3 3]
[4 4 4]]
--------------------
[[(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)]]
-
np.meshgrid()
는 x축 벡터, y축 벡터를 인수로 받아서 두 벡터의 조합을 x축 값만 가진 행렬과 y축 값만 가진 행렬로 반환한다. -
위 예시에선 반환된 반환된 행렬을
zip()
으로 연결해서 좌표 형태로 출력하였다.
Leave a comment