파이썬에서 여러 개의 숫자를 동시에 다룰 수 있는 자료형은 크게 두 종류로 나뉘어진다.
tuple
), 리스트(list
), 사전(dictrionary
) 등 숫자들을 담을 수 있는 컬렉션 자료형그런데 튜플, 리스트, 사전은 다음과 같은 단점을 갖고 있다.
넘파이(numpy) 모듈은 언급한 단점들을 극복하기 위해 만들어진 라이브러리이며, 다음의 특징을 갖고 있다.
아래 두 개의 그래프는 각각 균등분포(Uniform Distrubution)와 표준정규분포(Standard Normal Distrubution)의 예제들이다.
|
|
위 그래프들을 생성할 때 사용된 데이터를 간단하게 생성하는 방법을 알아 보도록 하자.
In [1]:
import numpy as np
넘파이 모듈에서 가장 주요한 요소는 어레이(array)이다. 어레이의 사용법은 리스트의 경우와 기본적으로 비슷하다.
In [2]:
a = np.array([0, 1, 2, 3])
어레이의 모양과 활용은 리스트와 비슷하다.
In [3]:
print(a)
하지만 자료형은 넘파이 모듈에서 정의된 어레이인 ndarray
이다.
In [4]:
print(type(a))
array
함수는 리스트와 더불어 튜플도 입력받는다.
리스트와 튜플 어느 것을 써도 리턴값은 언제나 어레이이다.
예를 들어, 위 정의를 튜플을 이용하여 정의할 수 있다.
In [5]:
b = np.array((0, 1, 2, 3))
a
와 b
가 동일한 항목으로 구성된 리스트임을 아래와 같이 확인할 수 있다.
In [6]:
c = (a == b)
c
Out[6]:
주의사항 1: 어레이 객체들 사이의 비교는 각 항목별로 실행된다.
a == b
의 실행결과는 새로운 어레이이며 각 항목별로 비교한 결과인 불(bool) 값을
갖게 된다.
c
의 실행결과를 보면 dtype
이라는 키워드 인자가 추가되어 있다.
dtype
키워드는 생성된 어레이 객체의 각 항목의 자료형을 갖고 있다.
주의사항 2: 어레이 객체의 각 항목들은 모두 동일한 자료형이어야 한다.
리스트, 튜플과는 달리 어레이의 각 항목은 동일한 자료형들로 구성되어 있다.
In [7]:
a.dtype
Out[7]:
주의: int64
처럼 int
다음에 숫자가 붙는 경우가 있다. 몇 비트 숫자들인지를 보여주는 정보이다.
즉, 여기서는 64비트 정수라는 의미이다.
변수 b
에도 int형 숫자들로 구성된 어레이가 할당되어 있다.
In [8]:
b.dtype
Out[8]:
반면에 변수 c
는 불값(bool)으로 구성되어 있다.
In [9]:
c.dtype
Out[9]:
a
또는 b
와는 달리 c
의 경우 dtype
이 명시되는 이유는
True, False
가 참, 거짓의 의미 외에 1, 0
의 의미 또한 가질 수 있기 때문이다.
다음의 경우 dtype
키워드 인자값으로 int
를 입력하면 자료형이 달리지는 것을
확인할 수 있다.
In [10]:
d = np.array([ True, True, True, True], dtype=int)
d
Out[10]:
array
자료형은 dtype
이외에 ndim
, shapre
등 다양한 속성을 포함한다.
각 속성들의 의미와 활용은 필요할 때마다 추가로 설명될 것이다.
In [11]:
import time
start_time = time.clock()
a_list = range(0, 100000000, 2)
a_list_square = [i**2 for i in a_list]
end_time = time.clock()
list_time = end_time - start_time
print(list_time)
넘파이의 arange
함수는 range
와 동일한 기능을 수행한다.
다만 리스트가 아닌 어레이를 리턴한다.
In [12]:
import time
start_time = time.clock()
an_array = np.arange(0, 100000000, 2)
an_array**2
end_time = time.clock()
array_time = end_time - start_time
print(array_time)
주의: 앞서 두 개의 리스트를 비교할 때 처럼 어레이 관련 연산은 기본적으로 각 항목별로 실행된다.
따라사 a ** 2
는 각 항목을 제곱하라는 의미이다.
In [13]:
a_1dim = np.array([0, 1, 2, 3])
a_1dim
Out[13]:
In [14]:
a_1dim.ndim
Out[14]:
In [15]:
a_1dim.shape
Out[15]:
즉, 변수 a_1dim
에 할당된 어레이인 array([0, 1, 2, 3])
는
위 어레이를 1차원 리스트와 동일한 모양이다.
[0, 1, 2, 3]
즉, 길이가 d
인 1차원 어레이는 길이가 d
인 리스트에 대응한다.
In [16]:
a_2dim = np.array([[0, 1], [2, 3], [4, 5]])
a_2dim
Out[16]:
즉, 앞서 언급된 행렬의 첫째 행이 위 어레이의 첫 번째 리스트로, 그리고 둘째 행이 어레이의 두 번째 리스트에 해당한다.
어레이의 차원은 ndim
인스턴스변수를 이용하여 확인한다.
In [17]:
a_2dim.ndim
Out[17]:
변수 a_2dim
에 할당된 어레이의 모양(shape)은 3 x 2
행령에 해당하는 (3, 2)
이다.
In [18]:
a_2dim.shape
Out[18]:
주의: len
함수는 마치 리스트의 길이를 리턴해 주는 것처럼 어레이 인자를 받으면, 첫 번째 차원의 길이를 리턴한다.
len(a_2dim) = len([[0, 1], [2, 3], [4, 5]]) = 3
In [19]:
len(a_2dim)
Out[19]:
주의: 넘파이 모듈에는 어레이의 모양 정보를 리턴하는 shape()
라는 함수가 따로 존재한다.
즉, 이 함수는 어레이의 shape
속성 정보를 리턴한다.
In [20]:
np.shape(a_2dim)
Out[20]:
견본답안:
np.shape()
함수의 리턴값의 첫 번째 인자가 len()
함수의 리턴값과 동일하다.
In [21]:
np.shape(a_2dim)[0] == len(a_2dim)
Out[21]:
반면에 ndim
속성 값은 np.shape()
함수의 리턴값인 튜플의 길이와 동일하다.
In [22]:
len(np.shape(a_2dim)) == a_2dim.ndim
Out[22]:
아래와 같이 수동으로 구현할 수 있다.
In [23]:
a_2dim_exp = np.array([[5, 3, 1], [2, 4, 6]])
a_2dim_exp
Out[23]:
수동으로 어레이를 생성하는 방법은 실전에서는 거의 사용하지 못한다. 이유는 실전에서 다루는 데이터는 매우 크기 때문이다. 따라서 보다 간단하게 원하는 어레이를 생성하는 다양한 방법을 살펴 보자.
In [24]:
a = np.arange(10) # range(10)과 동일
a
Out[24]:
In [25]:
b = np.arange(1, 9, 2) # range(1, 9, 2)와 동일
b
Out[25]:
range()
함수와는 달리 np.arange()
함수는 0.1 등의 소수를 스텝으로 활용할 수 있다.
In [26]:
b = np.arange(1, 2, 0.2)
b
Out[26]:
주의: np.arange()
함수에 소수를 스텝으로 활용할 경우 종종 잘못된 어레이가 생성된다고 한다.
따라서 아래에서 설명하는 np.linspace()
함수를 대신 활용하는 것이 보다 안정적이다.
In [27]:
c = np.linspace(0, 1, 6) # 구간의 시작, 구간의 끝, 점의 개수
c
Out[27]:
반면에 구간의 오른쪽 끝을 제외하고 6개의 점으로 균등하게 쪼개어 어레이를 만들려면 아래와 같이 실행하면 된다.
In [28]:
d = np.linspace(0, 1, 6, endpoint=False)
d
Out[28]:
주의: 위 결과는 아래 결과에서 마지막 구간을 생략한 결과와 동일하다.
In [29]:
c = np.linspace(0, 1, 7)
c
Out[29]:
In [30]:
e = np.ones((3, 4))
e
Out[30]:
In [31]:
e = np.zeros((2, 3))
e
Out[31]:
In [32]:
e = np.eye(4)
e
Out[32]:
In [33]:
e1 = np.diag((1, 2, 3, 4)) # 인자: 튜플, 리스트 또는 1차원 어레이
e1
Out[33]:
In [34]:
e2 = np.diag([1, 2, 3, 4]) # 인자: 튜플, 리스트 또는 1차원 어레이
e2
Out[34]:
In [35]:
e3 = np.diag(np.arange(1,5)) # 인자: 튜플, 리스트 또는 1차원 어레이
e3
Out[35]:
생성된 어레이는 모두 동일하다.
In [36]:
e1 == e2
Out[36]:
In [37]:
e2 == e3
Out[37]:
난수(random number)로 구성된 어레이를 생성하기 위해
numpy.random
모듈에 있는 rand()
, randn()
함수를 활용할 수 있다.
numpy.random.rand()
함수: 균등분포를 사용하여 지정된 수만큼 [0, 1) 구간에서
난수를 구한다.numpy.random.randn()
함수: 표준정규분포 방식을 사용하여 지정된 수만큼
난수를 구한다. 각 함수의 인자는 원하는 모양(shape)에 해당하는 튜플이다.
주의: numpy.ones
또는 numpy.zeros
함수들과는 달리 추가로 괄호를 사용하지 않는다.
In [38]:
f = np.random.rand(4)
f
Out[38]:
예를 들어, 2 x 3
행렬 모양의 난수 어레이를 아래와 같이 생성할 수 있다.
먼저 6개의 난수를 생성하고 (2, 3) 모양의 2차원 어레이를 만든다.
In [39]:
f1 = np.random.rand(2, 3)
f1
Out[39]:
동일한 일을 np.random.randn()
함수를 활용하여 할 수 있다.
np.random.rand()
함수와 동일한 방식으로 작동하지만 생성된 난수들이 표준정규분포를 따른다.
즉, 생성된 난수 데이터의 평균은 0이고 표준편차는 1이다.
In [40]:
g = np.random.randn(4)
g
Out[40]:
In [41]:
g1 = np.random.randn(2, 3)
g1
Out[41]:
seed()
함수 활용난수 관련 함수들이 생성하는 난수는 사실 정말로 무작위로
생성되지는 않는다.
각 프로그래밍 언어마다 난수를 만드는 방식이 정해져 있고,
정해진 방식에 맞추어 이미 난수표를 갖고 있다.
즉, rand
함수가 호출되는 순서에 따라 사실 동일한 숫자를
생성하지만 사람 눈에는 무작위 숫자들로 보이는 것 뿐이다.
이런 현상을 해결하기 위해 시드(seed) 값을 사용하여 생성되는 난수의 순서를 보다 무작위적으로 보여지게 할 수 있다. 예를 들어 시드값을 0으로 하면 매번 동일한 난수들이 생성되는 것을 아래와 같이 확인할 수 있다.
In [42]:
np.random.seed(0)
np.random.rand(4)
Out[42]:
In [43]:
np.random.seed(0)
np.random.rand(4)
Out[43]:
반면에 시드값을 변경하면 생성되는 난수들을 정말로 무작위처럼 보이게 할 수 있다.
In [44]:
np.random.seed(1234)
np.random.rand(4)
Out[44]:
In [45]:
np.random.seed(2000)
np.random.rand(4)
Out[45]:
아래 예제는 정수들의 어레이를 생성한다.
In [46]:
a = np.array([1, 2, 3])
a.dtype
Out[46]:
앞서 생성된 정수들의 어레이를 부동소수점들의 어레이로 형변환 시키고자 한다면
dtype
속성을 float
로 바꾸면 된다.
In [47]:
b = np.array([1, 2, 3], dtype=float)
b.dtype
Out[47]:
새로 생성된 어레이의 모양도 다르다.
In [48]:
b
Out[48]:
애초부터 float 자료형으로 선언하려면 일부 정수를 부동소수점으로 표시하면 된다.
In [49]:
a = np.array([1., 2, 3.])
a.dtype
Out[49]:
어레이가 기본적으로 사용하는 자료형은 float
이다.
np.ones()
, np.zeros()
, np.eye()
, np.linspace()
등은 모두 부동소점의 어레이를 생성한다.
In [50]:
np.ones((3,3)).dtype
Out[50]:
In [51]:
np.zeros((3,3)).dtype
Out[51]:
In [52]:
np.eye(4).dtype
Out[52]:
주의: np.arange()
, np.array()
, np.diag()
등은 인자에 따라 생성된 어레이의 자료형을 결정한다.
In [53]:
np.arange(4).dtype
Out[53]:
In [54]:
np.arange(0, 4, 0.5).dtype
Out[54]:
In [55]:
np.array([1, 2, 3]).dtype
Out[55]:
In [56]:
np.array([1, 2, 3.]).dtype
Out[56]:
In [57]:
np.diag([1, 2, 3]).dtype
Out[57]:
In [58]:
np.diag([1, 2, 3.]).dtype
Out[58]:
int
와 float
이외에도 다음의 자료형들이 어레이에서 사용된다.
In [59]:
np.array([1+2j, 3+4j, 5+6j]).dtype
Out[59]:
In [60]:
np.array([True, False, False, True]).dtype
Out[60]:
In [61]:
np.array(['Hi', 'Hello']).dtype
Out[61]:
주의:
int32
, int64
, float64
, S5
처럼 자료형 이름 뒤에 숫자가 사용되기도 한다.
이는 어레이에서 사용되는 각각의 항목의 값들이 가질 수 있는 최대 크기를 의미한다.
즉, 사용된 정수들이 32비트로 모두 담을 수 있는지, 또는 사용된 문자열들의 길이가 최대 5바이트 또는 12바이트 인지를 나타낸다.
예를 들어 아래 문자열로 이루어진 어레이에서 가장 긴 문자열이 4바이트이기 때문에,
dtype
이 S4
이다.
In [62]:
np.array(['ab', 'abc', 'abcd']).dtype
Out[62]:
먼저 히스토그램을 그릴 준비를 한다.
In [63]:
import matplotlib.pyplot as plt
In [64]:
%matplotlib inline
먼저 np.random.rand()
함수가 생성한 난수들이 균등분포를 따른다는 사실을 히스토그램을 이용하여 확인하자.
아래 코드는 1,000개의 난수를 균등분포를 사용하여 생성한 다음에 히스토그램을 그린다.
In [65]:
gaussian_numbers = np.random.rand(1000)
plt.hist(gaussian_numbers, bins=10)
plt.title("Uniform Distribution")
plt.xlabel("Value")
plt.ylabel("Frequency")
plt.show()
위 그래프를 통해 알 수 있듯이 생성된 난수들은 0과 1사이에 골고루 퍼져 있도록 생성되었음을 대략 확인할 수 있다.
먼저 np.random.randn()
함수가 생성한 난수들이 표준정규분포를 따른다는 사실을 히스토그램을 이용하여 확인하자.
아래 코드는 1,000개의 난수를 표준정규분포를 사용하여 생성한 다음에 히스토그램을 그린다.
In [66]:
gaussian_numbers = np.random.randn(1000)
plt.hist(gaussian_numbers, bins=10)
plt.title("Standard Normal Distribution")
plt.xlabel("Value")
plt.ylabel("Frequency")
plt.show()
위 그래프를 통해 알 수 있듯이 생성된 난수들은 0과 1사이에 표준정규분포를 따르도록 생성되었음을 대략 확인할 수 있다.
견본답안:
In [67]:
a = np.ones((4,4))
a[2,3] = 2
a[3,1] = 6
a
Out[67]:
np.diag()
함수 활용np.diag()
함수는 최대 2개의 인자를 받는다.
np.diag(v, k=0)
v
: 1차원 또는 2차원 어레이 (리스트, 튜플 가능)
v
가 2차원 어레일 때: 해당 어레이의 k
번째 대각선을 1차원 어레이로 리턴한다.v
가 1차원 어레일 때: 해당 어레이를 k
번째 대각선으로 갖는 2차원 어레이를 리턴한다.k
: 정수 (옵션 키워드 인자)
k
번째 대각선을 의미함.k > 0
인 경우: 중앙 대각선에서 윗쪽으로 k
번째 위치한 대각선k < 0
인 경우: 중앙 대각선에서 아랫쪽으로 k
번째 위치한 대각선
In [68]:
x = np.arange(9).reshape((3,3))
x
Out[68]:
In [69]:
np.diag(x)
Out[69]:
In [70]:
np.diag(x, k=1)
Out[70]:
In [71]:
np.diag(x, k=-1)
Out[71]:
In [72]:
np.diag(np.diag(x))
Out[72]:
In [73]:
np.diag(np.diag(x, k=-1), k=1)
Out[73]:
In [74]:
np.diag(np.arange(2, 7), k=-1)
Out[74]:
In [75]:
a = np.arange(4, 0, -1).reshape((2,2))
b = np.tile(a, (2,3))
b
Out[75]: