파이썬 머신러닝 완벽 가이드: 다양한 캐글 예제와 함께 기초 알고리즘부터 최신 기법까지 배우는/권철민 지음을 참고하여 공부하고 정리하는 스터디 포스팅입니다.
1. NumPy(넘파이)란?¶
- NumPy는 선형대수 기반의 프로그램을 쉽게 만들 수 있도록 지원하는 대표적인 패키지입니다.
- NumPy는 배열 기반의 연산에 더해 다양한 데이터 핸들링 기능을 제공해 줍니다.
2. ndarray 클래스¶
- 넘파이의 기반 데이터 타입은 ndarray입니다. ndarray를 활용하면 다차원(Multi-dimen-sion)배열을 쉽게 생성할 수 있고 다양한 연산들을 수행할 수 있습니다.
- ndarray는 같은 종류의 데이터만 배열의 담을 수 있습니다.
2-1 배열 생성하기¶
- array() 함수는 다양한 인자를 입력받아서 ndarray로 변환하는 기능을 하는 함수입니다.
행과 열의 수를 튜플 형태로 가지고 있으며 이를 통해 ndarray 배열의 차원까지 알 수 있습니다.
ndarray.shape: ndarray의 차원과 크기를 튜플 형태로 나태내어 줍니다.
ndarray.dim: 각 array의 차원을 알려주는 함수입니다.
array1=np.array([1,2,3])
print('array1 type:',type(array1))
print('array1 array 형태:',array1.shape)
array2=np.array([[1,2,3],
[2,3,4]])
print('array2 type:',type(array2))
print('array2 array 형태:',array2.shape)
array3=np.array([[1,2,3]])
print('array3 type:',type(array3))
print('array3 array 형태:',array3.shape)
print('array1 : {:0}차원, array2 : {:1}차원, array3 : {:2}차원'.format(array1.ndim, array2.ndim,array3.ndim))
list1=[1,2,3]
print(type(list1))
array1=np.array(list1)
print(type(array1))
print(array1,array1.dtype)
ndarry는 같은 데이터 타입만 가능하다고 위에서 정리하였습니다. 만약에 다른 데이터 유형이 담긴 리스트를 ndarry로 변경하면 어떻게 되는지 확인해봅시다.
list2=[1,2,'test']
array2=np.array(list2)
print(array2,array2.dtype)
list3=[1,2,3.0]
array3=np.array(list3)
print(array3,array3.dtype)
위에 결과를 봤을 때 데이터 크기가 더 큰 데이터 타입으로 형 변환이 적용이 된다는 것을 확인할 수 있습니다. list2의 경우 숫자형 값 1,2가 문자열 값인'1'과 '2'로 변환된 것을 알 수 있습니다.
2-2 ndarry를 편하게 해주는 함수¶
- arange(): range()와 비슷한 함수로 쉽게 말해 array를 range로 표현하는 함수입니다.
0부터 함수 인자 값 -1까지의 값을 순차적으로 ndarray의 데이터 값으로 변환하여 줍니다.
sequence_array=np.arange(10)
print(sequence_array)
print(sequence_array.dtype,sequence_array.shape)
위 예시에서는 0부터 10에서 -1을 더한 9까지의 연속적인 값으로 구성된 1차원 ndarry를 만들어 줍니다.
- zeros(): 튜플 형태의 shape값을 입력했을 때 모든 값을 0으로 채운 ndarray를 반환해 주는 함수입니다.
zero=np.zeros((3,2),dtype='int32')
print(zero)
print(zero.dtype,zero.shape)
- ones(): zeros와 비슷한 함수로 모든 값을 1로 채운 ndarry를 반환해 주는 함수입니다.
one=np.ones((3,2))
print(one)
print(one.dtype,one.shape)
2-2 ndarry의 차원과 크기를 변경해 주는 reshape()¶
- reshape()는 ndarray를 원하는 차원과 크기로 변환해 주는 함수입니다.
array1=np.arange(10)
print('array1\n',array1)
array2=array1.reshape(2,5)
print('array2\n',array2)
array3=array1.reshape(5,2)
print('array3\n',array3)
입력한 함수 인자에 맞게 변환이 일어난 것을 확인할 수 있습니다.
- reshape()는 데이터의 형태를 모를 때 유용하게 활용을 할 수 있습니다. 행과 열중 하나의 값을 정해주고 나머지 부분에 -1을 넣어주면 내가 정한 값에 맞춰서 변환이 일어나게 됩니다.
다시 정리해 보면 -1의 의미는 변경된 배열의 -1 위치의 차원은 "원래 배열의 길이와 남은 차원으로 부터 추정”이 된다라고 말할 수 있습니다. 자세한 부분은 예시를 통해 알아보도록 하겠습니다.
array1=np.arange(10)
print(array1)
array2=array1.reshape(-1,5)
array3=array1.reshape(5,-1)
print('array2:',array2.shape)
print('array3:',array3.shape)
위에 예시중 reshape(-1,5)의 의미는 고정된 5개의 컬럼에 맞는 로우를 자동으로 새롭게 변환하라는 의미입니다. 결과값을 보면 그ㅔ 맞게 2x5의 형태로 변환이 된 것을 확인할 수 있습니다.
array1=np.arange(10)
array4=array1.reshape(-1,4)
하지만 위의 예시처럼 10개의 1차원 데이터를 4개의 컬럼을 가진 로우로는 변경할 수 없기 때문에 에러가 발생하게 됩니다.
# 1부터 9까지의 1차원 ndarray 생성하기 #
array1=np.arange(start=1,stop=10)
print(array1)
value=array1[2]
print(value)
value2=array1[-1]
print(value2)
인덱스는 0부터 시작하기 때문에 array1[2]는 3번째 인덱스 위치의 값인 3이 출력된 것을 확인할 수 있습니다. 인덱스 -1은 맨 뒤의 데이터값으로 9가 출력된 것을 확인할 수 있습니다.
print(array1)
array1[0]=8
array1[7]=1
print(array1)
위와 같이 데이터값도 간단히 변경을 할 수 있습니다.
다음은 다차원의 ndarry에서 단일 값을 추출해 보도록 하겠습니다.
여기서 핵심은 콤마(,)를 사용하여 인덱스를 통해 접근한다는 점입니다.
ex) [row,col]
위에서 배운 reshape함수를 활용해서 1차원 데이터를 2차원 데이터로 변환시킨 후 실습을 해보도록 하겠습니다.
array1d=np.arange(start=1,stop=10)
array2d=array1d.reshape(3,3)
print(array2d)
print(array2d[0,0])
print(array2d[0,1])
print(array2d[1,0])
print(array2d[2,2])
위 예시중 array2[1,0]을 살펴보자면 [row,col]이므로 1행중 0번째 열값인 4를 의미하게 됩니다.
2. 슬라이싱(Slicing)¶
- 슬라이싱은 연속된 인덱스상의 ndarray를 추출하는 방법입니다.
- ':'기호를 사용해서 연속된 데이터를 추출할 수 있습니다.(:사이에 시작 인덱스와 종료 인덱스를 표시하는 방식입니다.)
print(array1)
array3=array1[0:3]
print(array3)
슬라이싱 기호인 ':' 사이의 시작, 종료 인덱스는 생략하여 표시할수도 있습니다.
1) ':'기호 앞에 시작 인덱스를 생략하면 맨 처음 인덱스인 0으로 간주하게 됩니다.
2) ':'기호 뒤에 종료 인덱스를 생략하면 맨 마지막 인덱스로 간주하게 됩니다.
3) ':'기호 앞과 뒤에 시작과 종료 인덱스를 생략하면 맨처음과 맨 마지막 인덱스를 간주하게 됩니다.
print(array1)
array4=array1[:3]
print(array4)
array5=array1[3:]
print(array5)
array6=array1[:]
print(array6)
다음은 2차원 ndarray에서 슬라이싱으로 데이터를 출력해 보도록 하겠습니다. 슬라이싱도 앞의 예제와 비슷하게 콤마(,)를 사용하여 인덱스에 접근하게 됩니다.
array1d=np.arange(start=1,stop=10)
array2d=array1d.reshape(3,3)
print("array2d\n",array2d)
print("array2d[0:2,0:2]\n",array2d[0:2,0:2])
print("array2d[1:4,0:4]\n",array2d[1:4,0:4])
print("array2d[:5,:]\n",array2d[:5,:])
3. 팬시 인덱싱(Fancy Indexing)¶
- 일정한 인덱싱 집합을 리스트 또는 ndarray 형태로 지정하여 해당 위치에 있는 데이터의 ndarray를 반환해 줍니다.
2차원 데이터를 통해 실습을 해보겠습니다.
array1d=np.arange(start=1,stop=10)
array2d=array1d.reshape(3,3)
print(array2d)
array3=array2d[[0,1],2]
print('array2d[[0,1],2] => ',array3.tolist())
array4=array2d[[0,1],0:2]
print('array2d[[0,1],0:2] =>', array4.tolist())
위의 예시중 array2d[[0,1],2]를 살펴보자면 로우 축에 팬시 인덱싱인[0,1]을 컬럼 축에는 인덱싱 2를 적용한 것입니다. 그 결과(row,col)인덱스가 (0,2),(1,2)로 적용이 되어서 [3,6]이 반환된 것을 확인할 수 있습니다.
불린 인덱싱(Boolean Indexing)¶
- 특정 조건에 해당이 되는지 True/False값 인덱싱 집합을 기반으로 하여 True에 해당하는 인덱스 위치에 있는 ndarray의 값을 반환합니다.
- 불린 인덱싱은 조건과 검색을 동식에 할 수 있기 때문에 매우 많이 사용되는 인덱싱 방식입니다.(for loop/if else문 보다 훨씬 간단하게 구현할 수 있습니다.)
array1d > 5
위와 같이 ndarray 객체에 조건식만 붙이게 되면 False,True 형태로 출력이 됩니다. 해석을 해보자면 5보다 큰 데이터는 True, 그렇지 않은 경우는 False가 반환되는 것을 확인할 수 있습니다.
# []안에 array1d > 5 Boolean indexing을 적용
array3=array1d[array1d>5]
print(array3)
조건으로 반환된 ndarry 객체를 인덱싱을 지정하는 [] 안에 입력하게 되면 False값은 무시되고 True값이 있는 위치의 인덱스 값만 반환이 되는것을 확인할 수 있습니다. 다시 말하자면 False 인덱스가 있는 0~4는 무시되고 인덱스 [5,6,7,8]이 만들어지며 이 위치 인덱스에 해당되는 값인 [6,7,8,9]가 출력된 것입니다.
3. 행렬의 정렬(sort와 argsort)¶
넘파이에서 행렬을 정렬 해주는 대표적인 방법은 np.sort()와 ndarray.sort()가 있습니다. 이에 더해 정렬된 행렬의 인덱스를 반환하는 argsort()에 대해서도 알아보도록 하겠습니다.
- np.sort(): 넘파이에서 sort()를 호출(원 행렬은 그대로 유지한 채 원 행렬의 정렬된 행렬을 반환)
- ndarray.sort(): 행렬 자체에서 호출(원 행렬 자체를 정렬한 형태로 반환하며 반환 값은 None)
- argsort(): 정렬 행렬의 원본 행렬 인덱스를 ndarray형으로 반환시켜 줍니다.
설명만으로는 이해가 잘 안 갈 수 있으니 예제를 통해 알아봅시다.
org_array=np.array([1,5,7,3])
print('원본 행렬:',org_array)
#np.sort()로 정렬
sort_array1=np.sort(org_array)
print('np.sort() 호출 후 반환된 정렬 행렬',sort_array1)
print('np.sort() 호출 후 원본 행렬',org_array)
#ndarray.sort()로 정렬
sort_array2=org_array.sort()
print('org_array.sort() 호출 후 반환된 행렬',sort_array2)
print('org_array.sort() 호출 후 원본 행렬',org_array)
원본 행렬[1,5,7,3]에 대해서 np.sort()는 원본 행렬을 변경하지 않고 정렬된 형태로 반환이 되며, ndarray.sort()는 원본 행렬 자체를 정렬한 값으로 변환함을 알 수 있습니다. 두 함수는 모두 기본적으로 오름차순으로 정렬을 하게 됩니다. 만약 내림차순으로 정렬을 하고 싶다면 [::-1]을 적용하면 됩니다.
sort_array1_desc=np.sort(org_array)[::-1]
print(sort_array1_desc)
행렬이 2차원 이상이 경우에는 axis축 값 설정을 통해서 row또는 col방향으로 정렬을 할 수 있습니다.
array2d=np.array([[8,12],[7,1]])
# 로우 방향으로 정렬
sort_array2d_axis0=np.sort(array2d,axis=0)
print('로우 방향으로 정렬:\n',sort_array2d_axis0)
# 로우 방향으로 정렬
sort_array2d_axis1=np.sort(array2d,axis=1)
print('컬럼 방향으로 정렬:\n',sort_array2d_axis1)
# np.argsort()
org_array=np.array([3,1,9,5])
sort_indices=np.argsort(org_array)
print(sort_indices)
정렬 행렬의 원본 행렬 인덱스가 잘 출력된 것을 확인할 수 있습니다.
여기서도 내림차순으로 정렬을 하고 싶으면 위에서와 같이 [::-1]을 적용하면 됩니다.
org_array=np.array([3,1,9,5])
sort_indices_desc=np.argsort(org_array)[::-1]
print(sort_indices_desc)
argsort()는 넘파이에서 매우 활용도가 높습니다. 넘파이의 ndarray는 실제 값과 그 값이 뜻하는 메타 데이터를 별도의 ndarray로 각각 가져야 합니다.
예들 들어 학생별로 시험 성적을 데이터로 표현을 하고자 한다면 학생의 이름과 시험 성적을 각각 ndarray로 가져야 합니다.
import numpy as np
name=np.array(['minji',"choonseok","yoonju","jongwon","siyeon"])
score=np.array([98,85,80,95,97])
sort_indicies_asc=np.argsort(score)
print(sort_indicies_asc)
print(name[sort_indicies_asc])
예시로 시험 성적순으로 학생 이름을 출력해 보았습니다.
위와 비슷한 예제로 넘파이의 데이터 추출에서 많이 사용이 됩니다
'Machine Learning' 카테고리의 다른 글
사이킷런으로 시작하는 머신러닝 - Model Selection 모듈(2)- (0) | 2020.04.09 |
---|---|
사이킷런으로 시작하는 머신러닝 - Model Selection 모듈(1)- (0) | 2020.04.09 |
사이킷런으로 시작하는 머신러닝 - 사이킷런의 기반 프레임워크 익히기- (1) | 2020.04.09 |
데이터프레임(DataFrame) 다루기 ◑_◐ (0) | 2020.04.07 |
판다스(Pandas)의 자료구조 (0) | 2020.04.06 |