import numpy as np
넘파이를 import하는데 이후에 쓸 때 np
라는 약어를 사용
arr1 = np.array([1,2,3])
arr2 = np.array([[1,2,3],
[2,3,4]])
arr3 = np.array([[1,2,3]])
arr4 = np.array([[[1,2,3],[3,4,5],[4,5,6]], [[2,3,4],[4,5,6],[7,5,4]]])
arr1 ~ arr4까지 총 4가지 경우의 ndarray를 생성했다. 배열은 []를 기준으로 차원을 나눈다. 이 네 배열의 type를 살펴보면
print('arr1 type : ', type(arr1))
print('arr2 type : ', type(arr2))
print('arr3 type : ', type(arr3))
print('arr4 type : ', type(arr4))
arr1 type : <class 'numpy.ndarray'>
arr2 type : <class 'numpy.ndarray'>
arr3 type : <class 'numpy.ndarray'>
arr4 type : <class 'numpy.ndarray'>
네 배열 모두다 numpy.ndarray
라는 클래스이다. 그리고 이 네개의 배열의 차원과 형태를 살펴보면,
print('arr1:{:0}차원, arr2:{:1}차원, arr3:{:2}차원, arr4:{:4}차원'.format(arr1.ndim,arr2.ndim,arr3.ndim,arr4.ndim))
arr1:1차원, arr2:2차원, arr3: 2차원, arr4: 3차원
print('arr1 array 형태 : ', arr1.shape)
print('arr2 array 형태 : ', arr2.shape)
print('arr3 array 형태 : ', arr3.shape)
print('arr4 array 형태 : ', arr4.shape)
arr1 array 형태 : (3,)
arr2 array 형태 : (2, 3)
arr3 array 형태 : (1, 3)
arr4 array 형태 : (2, 3, 3)
이번엔 리스트를 생성하고 그 리스트를 가지고 ndarray를 생성해본다.
list1 = [1,2,3]
print(type(list1))
<class 'list'>
list1이라는 리스트 자료구조를 생성했다. 이 리스트를 어떻게 ndarray로 바꾸는지 한번 봐보자.
arr1 = np.array(list1)
print(type(arr1))
print(arr1, arr1.dtype)
<class 'numpy.ndarray'>
[1 2 3] int32
원래는 np.array([1,2,3])처럼 괄호 안에 리스트 구조가 들어간다. 그거를 변수로 바궜을 뿐이다.
리스트의 데이터 타입은 데이터 크기가 큰 쪽을 따라간다.
list2 = [1,2,'test']
arr2 = np.array(list2)
print(arr2, arr2.dtype)
['1' '2' 'test'] <U11
int형 데이터와 string형 데이터를 보면 string의 데이터 크기가 더 크다. 그래서 1, 2도 '1', '2'처럼 문자열로 바뀐것을 알 수 있다.
list3 = [1,2,3.0]
arr3 = np.array(list3)
print(arr3, type(arr3), arr3.dtype)
[1. 2. 3.] <class 'numpy.ndarray'> float64
이번엔 float형과 int형인 경우이다. 이 경우에도 float이 int보다 크기 때문에 1, 2가 실수형으로 변경된 것을 알 수 있다.
이번엔 배열을 생성하고 데이터 타입을 변경해보려고 한다.
arr_int = np.array([1,2,3])
arr_float = arr_int.astype('float64')
print(arr_float, arr_float.dtype)
[1. 2. 3.] float64
int형 데이터구조를 가진 배열을 astype()를 통해 float 데이터구조로 변경을 했다.
arr_int1 = arr_float.astype('int32')
print(arr_int1, arr_int1.dtype)
[1 2 3] int32
arr_float1 = np.array([1.1,2.2,3.3])
arr_int2 = arr_float1.astype('int32')
print(arr_int2, arr_int2.dtype)
[1 2 3] int32
실수형을 정수형으로 변경할 때에는 소수점 아래는 다 버린다. 이처럼 ndarray의 데이터 구조를 변경할 때에는 astype()를 사용한다.
배열을 생성할 때 변경하는 법도 있다.
seq_arr = np.arange(10, dtype='float64')
print(seq_arr)
print(seq_arr.dtype, seq_arr.shape)
[0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
float64 (10,)
배열 생성할 때 arange()라는 함수가 있는데, 이는 default값인 stop값을 넣으면 0부터 stop-1 까지 자동적으로 배열이 생성된다. 이때 dtype 파라미터를 사용해서 어떤 데이터구조로 넣을 것인지 설정할 수 있다. arange는 파라미터로 start값도 추가할 수 있다.
seq_arr = np.arange(1, 10) #default 값 : stop, 추가 인자 : start
print(seq_arr)
print(seq_arr.dtype, seq_arr.shape)
[1 2 3 4 5 6 7 8 9]
int32 (9,)
arange말고도 여러 방법의 ndarray생성 함수가 있다.
zero_arr = np.zeros((3, 2))
print(zero_arr)
print(zero_arr.dtype, zero_arr.shape)
[[0. 0.][0. 0.]
[0. 0.]]
float64 (3, 2)
zeros는 모든 인자가 0인 배열을 생성해준다. 파라미터로는 row, col의 크기를 받는다. default 데이터구조는 float이고 dtype 파라미터로 수정이 가능하다.
이와 비슷한 함수로 ones라는 함수가 있다.
one_arr = np.ones((3, 2), dtype='int32')
print(one_arr)
print(one_arr.dtype, one_arr.shape)
[[1 1][1 1]
[1 1]]
int32 (3, 2)
ones는 모든 인자가 1인 배열이다. 이 또한 default 데이터 구조가 1이다.
ndarray를 생성하고 배열의 shape를 바꿀 수 있다. 이 때 사용하는것은 reshape라는 것이다.
arr1 = np.arange(10)
print("arr1 : \n", arr1)
arr2 = arr1.reshape(2,5)
print("arr2 : \n", arr2)
arr3 = arr1.reshape(5, 2)
print("arr3 : \n", arr3)
arr1 :
[0 1 2 3 4 5 6 7 8 9]
arr2 :
[[0 1 2 3 4][5 6 7 8 9]]
arr3 :
[[0 1][2 3]
[4 5][6 7]
[8 9]]
arange로 1차원 배열을 생성한 후 reshape로 2차원 형태로 변경했다. 이 때 좀 더 편한 방법이 있는데, -1을 사용하는 것이다.
원래는 reshape를 쓸 때는 배열의 형태를 계산해야 한다. 만약 총 인자 개수가 10개인데, row=3으로 하면 오류가 난다. col = 3.3333이 될 수는 없기 때문이다.
row * col = 인자 개수이기 때문에 이를 고려해야 하는데,
arr4 = arr1.reshape(-1,5)
print("arr4 : \n", arr4)
arr4 :
[[0 1 2 3 4][5 6 7 8 9]]
이처럼 reshape(-1, 5)를 하면 col 개수를 5로 고정해놓고 자동으로 행의 크기를 계산하여 reshape해준다. 물론 여기서도 전체 인자 수는 고려해줘야 한다.
참고사항 : ndarray를 list로 자료구조 변환이 가능하다. 이는 tolist()사용
arr1 = np.arange(8) arr3d = arr1.reshape((2,2,2)) print("arr3d : \n", arr3d.tolist()) print("type : " , type(arr3d.tolist())) print("arr3d shape ", arr3d.shape)
arr3d :
[[[0, 1], [2, 3]], [[4, 5], [6, 7]]]
type : <class 'list'>
arr3d shape (2, 2, 2)
reshape(-1, 1)같은 경우 자주 사용이 된다. 머신러닝 알고리즘으로 2차원 배열을 받는 경우도 있고, stack이나 concat으로 결합할 때 유용하다.
arr5 = arr3d.reshape(-1, 1)
print("arr5 : \n", arr5.tolist())
print("arr5 shape : ", arr5.shape)
arr5 :
[[0], [1], [2], [3], [4], [5], [6], [7]]
arr5 shape : (8, 1)
배열의 데이터를 정렬할 수 있다. 기본적인 정렬 방식은 np.sort()와 ndarray.sort()가 있는데, 이 둘은 차이가 있다.
org_arr = np.array([3,1,9,5])
print("원본 : ", org_arr)
sort_arr1 = np.sort(org_arr)
print('sort1 : ', sort_arr1)
print("원본 : ", org_arr)
원본 : [3 1 9 5]
sort1 : [1 3 5 9]
원본 : [3 1 9 5]
sort_arr2 = org_arr.sort()
print('arr2 : ', sort_arr2)
print('오리지날 : ', org_arr)
arr2 : None
오리지날 : [1 3 5 9]
np.sort() 방식은 원본 배열은 수정이 되지 않는다. 대신에 ndarray.sort()는 원본 배열이 수정되는 방식이다. 내림차순으로 정렬하려면 np.sort()[::-1]을 사용하면 된다.
desc_arr1 = np.sort(org_arr)[::-1]
desc_arr1
array([9, 5, 3, 1])
그리고 np.sort() 파라미터에서 axis가 있다. 이는 이차원배열에서 열 또는 행 기준 정렬하는 방식이다.
arr2d = np.array([[8, 12],
[7, 1]])
sort_arr2_axis0 = np.sort(arr2d, axis = 0)
print(sort_arr2_axis0)
[[ 7 1][ 8 12]]
arr2d = np.array([[12, 8],
[7, 1]])
sort_arr2_axis2 = np.sort(arr2d, axis= 1)
print(sort_arr2_axis2)
[[ 8 12][ 1 7]]
axis = 0 은 행 방향으로 정렬해주고, 1은 열 방향으로 정렬해준다.
원본 행렬이 정렬되었을 때 원본 행렬의 원소에 대한 인덱스를 반환하는 함수가 있다. np.argsort()를 사용한다.
org_arr = np.array([3, 1, 9, 5])
sort_ind = np.argsort(org_arr)
print(type(sort_ind))
print(sort_ind)
<class 'numpy.ndarray'>
[1 0 3 2]
[3, 1, 9 ,5]를 오름차순으로 정렬하면 [1, 3, 9, 5]인데 이에 대한 인덱스값은
[1, 0, 3, 2]이다. 이러한 방식은 어디서 사용하느냐??
넘파이는 판다스와 다르게 메타 데이터가 없다. 따라서 실제 값과 그 값이 뜻하는 메타 데이터를 별도의 배열로 가져야 한다. 아래와 같은 경우가 한 예이다.
name_arr = np.array(['John', 'Mike', 'Sarah', 'Kate', 'Samuel'])
score_arr = np.array([78,95,84,98,88])
score_ind = np.argsort(score_arr)
print(name_arr[score_ind])
['John' 'Sarah' 'Samuel' 'Mike' 'Kate']
학생과 각각의 점수가 있고 이를 오름차순으로 정렬할 때 argsort()로 스코어 배열을 오름차순 정렬하고 각 인덱스를 반환한 배열을 만든다. 그리고 학생 배열에 해당 인덱스값을 넣어서 출력하면 학생도 따라서 오름차순되어 출력된다.