Processing Data(Numpy)

부천대 IOT 응용소프트웨어 수강하는 학생들을 위한 강의로 교재인 권철민님의 파이썬 머신러닝 완벽가이드 내용을 요약 및 정리하였습니다.

데이터 전처리에 들어가기 앞서 반드시 파이썬의 기본문법을 익히기 위해 30분에서 2시간 정도 투자하 점프투 파이썬 혹은 김왼손의 왼손코딩을 보고 오시길 바랍니다.

데이터 구조

  • Scalar: 한개의 원소로 이루어진 데이터

  • Vector : 원소들이 모인 집합으로 1차원 배열에 속하고, 각원소의 데이터 타입이 동일. Series

  • array : 벡터들을 모아놓은 데이터 집합으로 2차원의 배열이고, Matrix라 불린다.

  • Tensor: 백터에 높이를 추가한 형태로 같은 크기의 행렬들을 모아놓은 데이터 집합

Numpy 배열 ndarray

ndarray :

N차원(Dimension) 배열(Array) 객체다.

위 그림과 같이 1~3차원 뿐만 아니라, 4,5,6 이상의 다차원을 표현할 수 있다.

1차원은 원소로만 이루어져 있고, 2차원은 행과 열 형태, 3차원부터는행과 열, 높이를 가진 입체형태다.

NumPy :

Numerical Python의 줄임말로 고성능 수치 계산을 위한 파이썬의 패키지다.

넘파이는 ndarray 기반의 다양한 연산기능을 제공한다.

ndarray 생성방법

주피터노트북(Jupyternotebook)을 생성하고 넘파이 모듈을 불러온다.

넘파이만 불러와도 되지만 as np를 추가하여 약어로 모듈을 표현한다.

다음 Numpy 모듈에서array()함수를 생성하여 1차원의 단일list 혹은 복합 list를 입력한다.

ndarray의 shape는 ndarray.shape로 차원은 ndarray.ndim 속성으로 알 수 있다.

#넘파이 불러오기 
import numpy as np 

array1 = np.array([1,2,3]) 
print('array1 type:',type(array1)) #<class 'numpy.ndarray'>

#array1은 1차원 데이터로, (3, )의 shape을 가진다.
print('array1 array 형태:',array1.shape) 


# ndarray 내의 데이터 타입을 확인 가능
print('array1 array 데이터 타입:', array.dtype) 

array2 = np.array([[1,2,3], [2,3,4]]) 

print('array2 type:',type(array2)) #<class 'numpy.ndarray'>
print('array2 array 형태:',array2.shape)

# ndarray 내의 데이터 타입을 확인 가능
print('array2 array 데이터 타입:', array3.dtype)

array3 = np.array([[1,2,3]]) 
print('array3 type:',type(array3)) #<class 'numpy.ndarray'>

#array3는 2차원 데이터로 (1, 3)의 shape을 가진다.
print('array3 array 형태:',array3.shape)

# ndarray 내의 데이터 타입을 확인 가능
print('array3 array 데이터 타입:', array3.dtype)

ndarray 데이터 값은 숫자,문자,불(bool) 값 등 모두 가능하다.

그러나 ndarray 데이터 타입은 그 연산의 특성상 같은 데이터 타입만 가능하다. 즉 한개 ndarrat 객체에 int와 float가 함께 있을 수 없다. error는 생기지 않은나 만약에 list 1과 같이 [1,2,'test'] 와 같이 문자와 숫자가 같은 리스트 안에 있을때, array 함수의 입력값에 들어가게 되면 사이즈가 큰 문자열로 바뀌게 된다.

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)

대용량 데이터 다룰 때 보통 메모리 절약을 위해 형변환을 고려하게 되는데, Numpy 안에서 astype() 메서드를 이용하면 ndarray 내 데이터값이 float 타입에서 int, bit 등 타입 변경이 가능하다.

array_int = np.array([1, 2, 3])
array_float = array_int.astype('float64')
print(array_float, array_float.dtype)

array_int1 = array_float.astype('int32')
print(array_int1, array_int1.dtype)

array_float1 = np.array([1.1, 2.1, 3.1])
array_int2 = array_float.astype('int32')
print(array_int2, array_int2.dtype)

ndarray axis 축(행과 열)

ndarray의 shape는 행, 렬, 높이 단위로 부여되는 것이 아니라, axis 0, axis 1 과 같이 axis 단위로 부여된다.

이때, axis 0은 행을 세는 방향(row1, row2, row3, ...) axis 1은 열을 세는 방향 방향(column1, column2, column3, ...)이다.

Quiz. 위 그림의 3차원 배열의 모양은?

axis 0의 크기가 4 , axis1의 크기는 3, axis2 의 크기는 2이기 때문에 shape는 (4,3,2) 가 된다.

axis 기반으로 아래 예제를 수행하면 다음과 같다.

array2 = np.array([[1,2,3], [2,3,4]])
print(array2.sum()) 
print(array2.sum(axis=0)) 
print(array2.sum(axis=1))

전체를 더하게 되면 15가 나오고, axis0 방향, 행 방향으로 더하게 되면 [3,5,7] 의 결과가, axis1 컬럼 방향으로 더하게 되면 [6,9]가 나온다.

ndarray 간편하게 생성하는법 (arnage, zeros, ones)

특정 크기의 차원을 가진 ndarray 의 값이 연속적이나 0 혹은 1로 생성해야 될 경우 arrange(), zeros(),ones()를 이용해 쉽게 array를 생성한다. 주로 테스트용도나 대규모 데이터를 일괄적으로 초기화 할때 자주 사용된다.

#0부터 하나씩 값을 증가해가며 9까지 10개 원소를 가진 1차원의 list를 생성
np.arange(10) #[0,1,2,3,4,5,6,7,8,9]
#start =1 부터 종료 index -1 까지 ndarray를 생성
np.arange(1,10)  #array([1, 2, 3, 4, 5, 6, 7, 8, 9])

#np.zeros example
#3행 2열 형태의 데이터 값이 0인 ndarray 생
zero_array = np.zeros((3,2),dtype='int32')
print(zero_array)
print(zero_array.dtype, zero_array.shape)

#np.ones example
#3행 2열 형태의 데이터 값이 1인 형태인 ndarray 생
one_array = np.ones((3,2)) 
print(one_array)
print(one_array.dtype, one_array.shape)


#인덱스를 사용해 단일 값 추출
print(array1d[2])
print('맨 뒤의 값:',array1[-1], ', 맨 뒤에서 두번째 값:',array1[-2])

ndarray 차원,크기를 변경- reshape()

  • reshape()는 ndarray를 특정 차원 및 형태로 변환해준다.

위 그림과 같이 (2,3) 의 ndarray를 (1,6)형태로 변환하고 싶을때 사용하면 된다.

예를들어 종종 reshape(-1,3)와 같이 인자값이 -1이 들어가는 경우가 있는데, 이는 axis 크기를 가변적으로 변하고 인자값 3에 해당하는 axis 크기만 고정하여 shape를 변환한다.

#reshape example
array1=np.array([[8,9,10], [12,13,14]])
print(array)# [[8, 9, 10], [12, 13, 14]]
print(np.shape(array)) #(2, 3)
print(array1.reshape((1, 6)))
print(array1.reshape((-1, 3)))

output:
[[ 8  9 10 12 13 14]]
[[ 8  9 10]
 [12 13 14]]

또한 변환할 수 없는 shape 구조를 입력 시 오류가 발생한다.

예를들어 (2,3) 형태인 배열을 (4,3)으로 변환하라고 요청 시 오류가 발생한다.

그리고 -1 값은 1개의 인자만 입력해야 한다.

array1=np.array([[8,9,10], [12,13,14]])
array1.reshape(4,3)

output:
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-20-01b483b3320f> in <module>
      1 array1=np.array([[8,9,10], [12,13,14]])
----> 2 array1.reshape(4,3)

ValueError: cannot reshape array of size 6 into shape (4,3)



array1.reshape(-1,-1)

output:
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-21-316fa50b9ed1> in <module>
----> 1 array1.reshape(-1,-1)

ValueError: can only specify one unknown dimension

ndarray 데이터 세트 선택- Indexing

ndarray의 일부 데이터 세트나 특정 데이터만을 선택하여 필터링 해는 인덱싱에 대해 알아보겠다.

인덱싱 유형

인덱싱 유형

단일값 추출

원하는 인데스 위치의 데이터만 반환

슬라이싱(slicing)

연속된 인덱스의 ndarray를 추출한다. 'start : end' 를 사용하여 시작인덱스와 종료인덱스-1 사이의 ndarray를 반환

ex) 1: 5 -> 인덱스 1과 5 사이의 인덱스 4까지 ndarray의 데이터를 반

팬시 인덱싱(Fancy Indexing)

일정한 인덱싱 집합을 리스트(list) 혹은 ndarray 형태로 반

슬라이싱과 유사하지만 불연속적인 인덱스 값을 가져올 수 있다.

불린 인덱싱(Boolean Indexing)

특정 조건에 해당하는지 여부인 True/False 값 인덱싱 집합을 기반으로 True에 해당하는 인덱스 위치에 있는 ndarray를 반

불린 인덱싱 활용도가 굉장히 높다.

단일값 추출:

ndarray는 axis 를 기준으로 0부터 시작하는 위치 인덱스 값을 가지고 있다.

보통 해당 인덱스 값을 [](브리킷)에 명시하여 값을 추출한다.

[-1]과 같이 마이너스가 인덱스로 사용되면 맨뒤에서 위치를 지정한다.

import numpy as np

a = np.array([1, 2, 4, 8, 16, 32])
print(a.shape)
print(a[2])
print(a[-1])

output:
(6,)
4
32


array1d = np.arange(start=1, stop=10)
array2d = array1d.reshape(3,3)
print(array2d)

print('(row=0,col=0) index 가리키는 값:', array2d[0,0] )
print('(row=0,col=1) index 가리키는 값:', array2d[0,1] )
print('(row=1,col=0) index 가리키는 값:', array2d[1,0] )
print('(row=2,col=2) index 가리키는 값:', array2d[2,2] )


output:
[[1 2 3]
 [4 5 6]
 [7 8 9]]
(row=0,col=0) index 가리키는 값: 1
(row=0,col=1) index 가리키는 값: 2
(row=1,col=0) index 가리키는 값: 4
(row=2,col=2) index 가리키는 값: 9

슬라이싱

슬라이싱은 : 을 이용하여 연속된 값을 선택한다.

콜른을 기준으로 앞에 오는숫자는 시작 인데스, 뒤에오는 숫자는 종료 인덱스인데, 주의할점이 종료인덱스-1 까지의 값을 반환한다. 콜론앞에 시작인덱스가 없다면 맨처음 인덱스 위치를 말하고, 콜른 뒤에 역시 없다면 마찬가지로 마지막 인덱스 위치를 말한다. 슬라이싱 역시 다차원에서 사용이 가능하다.

슬라이싱은 매우 중요하니 아래 예제를 통해 실습을 진행해 보겠다.

import numpy as np

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:3, 0:3] \n', array2d[1:3, 0:3])
print('array2d[1:3, :] \n', array2d[1:3, :])
print('array2d[:, :] \n', array2d[:, :])
print('array2d[:2, 1:] \n', array2d[:2, 1:])
print('array2d[:2, 0] \n', array2d[:2, 0])


output:
array2d:
 [[1 2 3]
 [4 5 6]
 [7 8 9]]
array2d[0:2, 0:2] 
 [[1 2]
 [4 5]]
array2d[1:3, 0:3] 
 [[4 5 6]
 [7 8 9]]
array2d[1:3, :] 
 [[4 5 6]
 [7 8 9]]
array2d[:, :] 
 [[1 2 3]
 [4 5 6]
 [7 8 9]]
array2d[:2, 1:] 
 [[2 3]
 [5 6]]
array2d[:2, 0] 
 [1 4]

팬시인덱싱 (Fancy Indexing)

팬시 인덱싱은 리스트나 ndarray로 인덱스 집합을 지정하면 해당위치의 인덱스에 해당하는 ndarray 를 반환한다.

import numpy as np

k = np.arange(0,80,10)
print(k)
print(k.shape)
print(k[[1,2,5]])

output:
[ 0 10 20 30 40 50 60 70]
(8,)
[10 20 50]

k1 = np.arange(1,10)
k2= k1.reshape(-1,3)
print(k2.shape , k2)

output:
(3, 3) 
[[1 2 3]
 [4 5 6]
 [7 8 9]]

print(k2[[0,1],2])
print(k2[[0,1],0:2])
print(k2[[0,1]])

output:
[3 6]
[[1 2]
 [4 5]]
[[1 2 3]
 [4 5 6]]

불린인덱싱(Boolean indexing)

조건 필터링과 검색을 동시에 할 수 있기에 자주 사용되는 인덱싱 방식으로 index를 지정하는 []내에 조건문을 그대로 기재하면, for loop/if esle문 보다 훨씬 간단하게 구현 가능하다.

동작방식을 조금 더 상세히 설명하면 다음과 같다.

  • step 1: 아래 예제의 array1d 변수의 array 값 중 값이 5보다 큰 값들만 추출하고 싶을때 [](브리킷)안에 입력을 한다.

  • step2: if 조건과 같이 조건에 충족하는 값들만 True로 저장

  • step3: True 값으로 저장된 index 값만 데이터 조회를 하여 보여준다.

import numpy as np

array1d = np.arange(start=1, stop=10)
target = []
for i in range(0, 9):
    if array1d[i] > 5:
        target.append(array1d[i])
print(target) #[6, 7, 8, 9]  

##boolean indexing##
array1d = np.arange(start=1, stop=10)
print(array1d) #[1 2 3 4 5 6 7 8 9]

var1 = array1d > 5
print("var1:",var1) #[False False False False False  True  True  True  True]

array3 = array1d[array1d > 5]
print('array1d > 5 불린 인덱싱 결과 값 :', array3) #[6, 7, 8, 9]  

행렬의 정- sort() , argsort()

넘파이의 행열을 정렬하는 방식은 두가지로 나뉜다.

  • np.sort() :원 행렬은 유지하고 정렬된 행렬을 반환

  • ndarray.sort() : 원 행렬 자체를 정렬한 형태로 변환하고, 반환값은 None

sort의 기본 출력형태는 오름차순이지만 내림차순으로 정렬시 [::-1]을 적용하면 된다.

argsort() 를 이용해 반환된 인덱스를 팬시 인덱스로 적용하면 추출이 가능하다. 주로 key value mapping을 할떄 자주쓴다.

import numpy as np

org_array = np.array([ 3, 1, 9, 5]) 
print(np.sort(org_array)) #[1 3 5 9]

sort_indices = np.argsort(org_array)
print(type(sort_indices)) #<class 'numpy.ndarray'>
print('행렬 정렬 시 원본 행렬의 인덱스:', sort_indices) 
#행렬 정렬 시 원본 행렬의 인덱스: [1 0 3 2]

org_array = np.array([ 3, 1, 9, 5]) 
print(np.sort(org_array)[::-1]) #[9 5 3 1]
sort_indices_desc = np.argsort(org_array)[::-1]
print('행렬 내림차순 정렬 시 원본 행렬의 인덱스:', sort_indices_desc)
#행렬 내림차순 정렬 시 원본 행렬의 인덱스: [2 3 0 1]


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)


org_array = np.array([ 3, 1, 9, 5]) 
print(np.sort(org_array))

sort_indices = np.argsort(org_array)
print(type(sort_indices))
print('행렬 정렬 시 원본 행렬의 인덱스:', sort_indices)
# 행렬 정렬 시 원본 행렬의 인덱스: [1 0 3 2]
sort_indices_desc[[0,3]] #array([2, 1], dtype=int64)

행렬 내적과 전치 행렬

행렬내적(행렬 곱)

행렬내적은 두 행렬의 곱으로 np.dot() 을 이용해 계산이 가능하다.

행렬의 내적을 구할때 왼쪽행렬 A의 열개수와 오른쪽 행렬 B의 행개수가 동일할 때 가능하다.

import numpy as np

A = np.array([[1, 2],
              [3, 4])
B = np.array([[5, 6],
              [7, 8]])

print(np.dot(A, B))

A = np.array([[1, 2, 3],
              [4, 5, 6]])
B = np.array([[7, 8],
              [9, 10],
              [11, 12]])

dot_product = np.dot(A, B)
print('행렬 내적 결과:\n', dot_product)

전치 행렬

원 행렬에서 행과 열 위치를 교환한 원소로 구성한 행렬을 전치행렬이라고 한다.

전치 행렬은 transpose() 를 이용해 구한다. 혹은 간편하게 .T를 이용한다.

import numpy as np
A = np.array([[1, 2, 3],
              [4, 5, 6]])

transpose_A = np.transpose(A)

print('A의 원본 행렬:\n', A)
print('A의 전치 행렬:\n', transpose_A)

#.T를 이용해 구한 결과 
print(A.T)


#output:
array([[1, 4],
       [2, 5],
       [3, 6]])
       .

Reference

[1] 권철민 - 파이썬머신러닝 완벽 가이드

Last updated