Function

NumPy with ndarray

강의 홍보

Numpy ndarray 개요

  • 넘파이 array()는 ndarray로 변환 가능
  • 생성된 ndarray배열의 shape변수는 ndarray의 크기, 행과 열의 수를 튜플 형태로 가지고 있으며, 이를 통해 ndarray 배열의 차원까지 알 수 있음

(1) 배열이란?

  • NumPy에서 배열은 동일한 타입의 값을 가짐
  • shape는 각 차원의 크기를 튜플로 표시한다.
  • 차원이란 무엇일까?
    • 1차원은 보통 하나의 을 의미
    • 2차원은 평면을 의미하고, 데이터 분석에서는 보통 데이터프레임을 의미한다.
    • 3차원은 공간을 의미하고, 딥러닝에서는 보통 이미지를 의미한다. (RGB)

shape와 ndim

  • 코드를 통해서 shapendim 함수를 확인해본다.

(1) 함수 활용 예제

  • 우선 소스코드를 통해 1차원, 2차원, 3차원 함수를 만들어 봅니다.
import numpy as np
array1 = np.array([1,2,3,4,5])
print('array1 type:',type(array1))
print('array1 array 형태:',array1.shape)

array2 = np.array([[1,2,3,4,5],
                  [2,3,4,5,6]])
print('array2 type:',type(array2))
print('array2 array 형태:',array2.shape)

array3 = np.array([[[1,2,3,4,5,6]], [[3,4,5,6,7,8]]])
print('array3 type:',type(array3))
print('array3 array 형태:',array3.shape)
array1 type: <class 'numpy.ndarray'>
array1 array 형태: (5,)
array2 type: <class 'numpy.ndarray'>
array2 array 형태: (2, 5)
array3 type: <class 'numpy.ndarray'>
array3 array 형태: (2, 1, 6)

(2) shape

  • 1차원의 shape는 (3, )인데, 이는 array로 5개의 데이터를 가지고 있다는 뜻임
  • 2차원의 shape는 (2, 5)이며, 이는 array로 2차원 데이터로 2 x 5 = 10, 즉 총 10개의 데이터가 있음
  • 3차원의 shape는 (2, 1, 6)이며, 이는 array로 3차원 데이터로 2 x 1 x 6 = 12, 즉 총 12개의 데이터가 있음
  • 차원을 직관적으로 확인하려면 ndim 메서드를 사용하면 된다.
print('array1: {:0}차원, array2: {:1}차원, array3: {:2}차원'.format(array1.ndim, array2.ndim, array3.ndim))
array1: 1차원, array2: 2차원, array3:  3차원

데이터 타입

  • ndarray내 데이터값은 숫자 값, 문자열 값, 불 값 모두 가능
  • 숫자형의 경우 int형, float형 등이 제공됨
    • int: 8, 16, 32
    • float: 16, 32, 64, 128
  • 간단한 예제로 확인한다.
num_list = [7, 8, 9, 10]
print(type(num_list))
num_array = np.array(num_list)
print(type(num_array))
print(num_array, num_array.dtype)
<class 'list'>
<class 'numpy.ndarray'>
[ 7  8  9 10] int64

reshape의 중요성

  • shape를 통해 데이터를 이해하는 것은 매우 중요하다.
  • 머신러닝 알고리즘 또는 선형대수를 잘 모른다 할지라도, 머신러닝 및 데이터 세트 간의 입출력과 변환 수행 시, 1차원 데이터 또는 다차원 데이터를 요구하는 경우가 있다.
    • 이 때, 차원이 달라서 오류가 발생할 가능성이 크니, 주의를 해야 한다.
  • 이 때, 차원을 맞추는 방법으로 reshape()를 활용한다.

(1) 소스 예제

  • 다음 예제는 0~14까지의 1차원 ndarray2x5형태로, 그리고 5x2 2차원 ndarray로 변환한다.
array1 = np.arange(15)
print('array1:\n', array1)

array2 = array1.reshape(3,5)
print('array2:\n',array2)

array3 = array1.reshape(5,3)
print('array3:\n',array3)
array1:
 [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14]
array2:
 [[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]]
array3:
 [[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]
 [12 13 14]]
array1.reshape(4,3)
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-8-a40469ec5825> in <module>()
----> 1 array1.reshape(4,3)


ValueError: cannot reshape array of size 15 into shape (4,3)
  • 위 에러는 변경이 불가능한데, 당연한 얘기이지만, size가 맞지 않다.

(2) -1의 활용

  • 실전에서는 주로 -1을 활용한다.
  • -1을 인자로 사용하면 원래 ndarray와 호환되는 새로운 shape로 변환해준다.
  • 예제를 통해서 살펴보도록 한다.
array_1 = np.arange(15)
print(array_1)

array_2 = array_1.reshape(-1,5)
print('array2 shape:',array_2.shape)

array_3 = array_1.reshape(5,-1)
print('array3 shape:',array_3.shape)
[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14]
array2 shape: (3, 5)
array3 shape: (5, 3)
  • array_1은 1차원 ndarray로 0~14까지의 데이터를 가지고 있다.
  • array_2array_1과 호환될 수 있는 2차원 ndarray로 변환되고, 고정된 5개의 칼럼에 맞는 로우를 자동으로 새롭게 생성해 변환하는 의미를 가진다.
    • array_3도 반대로 적용할 수 있다.
  • 그런데, 호환될 수 없는 형태는 에러가 날 것이다.

(3) 차원변환

  • 3차원을 2차원으로, 1차원을 2차원으로 변경하는 코드를 작성할 수 있다.
  • 이 때, reshape(a1, a2, a3)에서, b = a1 x a2 x a3의 값이 arrange(b)이 된다.
array_1 = np.arange(27)
array_3d = array_1.reshape((3,3,3))
print('array3d:\n',array_3d.tolist())

# 3차원 ndarray를 2차원 ndarray로 변환
array_5 = array_3d.reshape(-1,1)
print('array5:\n',array_5.tolist())
print('array5 shape:',array_5.shape)

# 1차원 ndarray를 2차원 ndarray로 변환
array_6 = array_1.reshape(-1,1)
print('array6:\n',array_6.tolist())
print('array6 shape:',array_6.shape)
array3d:
 [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [[9, 10, 11], [12, 13, 14], [15, 16, 17]], [[18, 19, 20], [21, 22, 23], [24, 25, 26]]]
array5:
 [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16], [17], [18], [19], [20], [21], [22], [23], [24], [25], [26]]
array5 shape: (27, 1)
array6:
 [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16], [17], [18], [19], [20], [21], [22], [23], [24], [25], [26]]
array6 shape: (27, 1)

선형대수 연산

  • 행렬 내적은 행렬 곱이며, np.dot()을 활용한다.

How to create my own function

강의 홍보

I. 개요

  • 나만의 함수를 작성해 본다.
  • 실행가능한 함수를 만들어 본다.

II. 기존 내장 함수

  • 함수는 특정 기능을 수행하는 코드를 의미한다.
  • 함수는 Sum(), Len()을 의미한다.
x = [1,2,3,4,5]
print(sum(x))
print(len(x))
5

III 사용자 정의 함수 예제

  • 이제 사용자 정의 함수를 사용하자.
  • 함수 선언 시, defdefine의 약자다.
def my_avg(x):
  sum_var = sum(x)
  len_var = len(x)
  return sum_var / len_var

print(my_avg(x))
3.0
  • 기본적인 사용정의 함수는 크게 매개 변수return으로 이루어진다.
    • 이 때의 매개변수는, string, int, DataFrame 등 다양하게 올 수 있다.
    • return의 의미는 일종의 함수를 실행한 뒤 반환하려는 output이다.

IV. 파이썬에서 실행모드 구축하기

  • 사용자 정의 함수를 작성하였다면, 이제부터는 Main() 함수를 활용하여 코드를 빠르게 실행할 수 있도록 한다. 아래와 같이 코드를 작성하자.
  • PyCharm, VSCode에서 main.py 안에 아래와 같이 코드를 작성해본다.
# -*- coding: utf-8 -*-

def main():
  print("안녕하세요, Main() 입니다. ")

if __name__ == "__main__":
  main()
안녕하세요, Main() 입니다. 
  • 위 구문은 일종의 파이썬 파일을 실행시키기 위한 일종의 규약이라 이해하면 좋을 것 같다.
  • __name__은 모듈의 이름이 저장되는 곳이다.
  • __main__은 모듈의 시작점과 같다. main.py에서 __name__=="__main__"은 바꾸지 않는다.
  • 이 때, 위 파일을 작성하면, shell에서 다음과 같이 실행한다.
~ $ python main.py
안녕하세요, Main() 입니다. 

(1) 파일 구조

  • 크게 두개의 파일을 작성할 것이다.
    • calculation.py
    • main.py
  • calculation.py에서 기본적인 코드를 작성한 뒤, main.py에서 해당 모듈을 가져와서 함수를 사용할 것이다.

(2) calculation.py 파일 작성 및 실행

  • 간단하게 사직연산 함수를 작성한다.
# -*- coding: utf-8 -*-
a = 3
b = 4

def plus(a, b): 
  c= a+b
  return c

def subtract(a, b):
  c = a-b
  return c

if __name__ == "__main__":
  print("a + b =", subtract(a, b))
  print("a - b =", plus(a, b))
  • 위와 같이 파일을 작성한 뒤 저장한다.
  • 그리고, shell에서 다음과 같이 실행한다.
~ $ python calculation.py
a + b = -1
a - b = 7

(3) main.py 작성 및 실행

  • 기존 calculation.py에서 if~이하의 구문을 제거한 후, 다시 저장한다.
  • 이번에는 main.py에서 아래와 같이 파일을 작성한다.
# -*- coding: utf-8 -*-
import calculation as cal

a = 3
b = 4

def main():
  print("안녕하세요, Main() 입니다. ")
  print("a + b =", cal.subtract(a, b))
  print("a - b =", cal.plus(a, b))

if __name__ == "__main__":
  main()
  • 그리고 이번에는 shell에서 main.py를 실행한다.
~ $ python main.py
안녕하세요, Main() 입니다. 
a + b = -1
a - b = 7

(4) 소결론

  • 같은 파일 경로에 있다면, 다른 file에서 함수(=module)을 불러올 때는 패키지에서 파일을 불러오는 것처럼, import ~형태로 사용할 수 있다.
  • 그리고, 각 파일명 안에는 다양한 작성할 수 있고, 또한 불러올 수 있다.

V. 두개의 폴더를 활용한 실행모드 구축

  • 이제 한 폴더 안에서 다른 파일의 함수를 불러올 수 있음을 확인하였다.
  • 이제는 두개의 폴더를 만들어 각각의 기능을 구현해본다.
  • 폴더는 크게 두가지다.
    • 사칙연산을 의미하는 arithmetic
    • 데이터 전처리를 의미하는 dataPreprocessing
  • 각각의 폴더안에 각 2가지의 파일을 작성할 예정이다.
  • 마지막으로 main.py는 독립적으로 위치해 놓는다.

(1) Arithmetic 폴더

  • plus.pysubtract.py안에 함수를 각각 저장한 뒤 작성한다.
    • plus.py
# -*- coding: utf-8 -*-
def add(a, b): 
  c= a+b
  return c
  • subtract.py
# -*- coding: utf-8 -*-
def minus(a, b):
  c = a-b
  return c

(2) dataPreprocessing 폴더

  • 파일 불러오기를 실행하는 importData.py와 데이터 전처리를 담당하는 processing.py에 해당하는 소스코드 작성 후 각각 저장한다.
    • importData.py
# -*- coding: utf-8 -*-

def readData():
    print("~~ 데이터를 불러옵니다 ~~ ")
    data = "빅쿼리에서 불러오는 데이터"
    return data
  • processing.py
# -*- coding: utf-8 -*-
from time import sleep

def process_data(data):
    print("~~ 데이터 전처리 함수를 실행합니다! ~~")
    modified_data = data + "가 수정 완료 되었습니다."
    sleep(3)
    print("~~ 데이터 전처리가 끝났습니다! ~~")
    return modified_data

(3) main.py 수정

  • 다음은 main.py를 아래와 같이 수정하도록 한다.
# -*- coding: utf-8 -*-
from arithmetic import plus as pl
from arithmetic import subtract as sub
from dataPreprocessing import processing
from dataPreprocessing import importData

a = 3
b = 4

def main():
  print("~~ 사칙 연산을 시작합니다 ~~ ")
  print("a + b =", sub.minus(a, b))
  print("a - b =", pl.add(a, b))
  print("~~ 사칙 연산을 종료합니다 ~~ ")

  ## 데이터 전처리 시작
  data = importData.readData()
  processing.process_data(data)

if __name__ == "__main__":
  main()
  • 그 다음 shell에서 다음과 같이 실행하면 아래와 같은 결과물을 얻게 될 것이다.
~ $ python main.py
~~ 사칙 연산을 시작합니다 ~~ 
a + b = -1
a - b = 7
~~ 사칙 연산을 종료합니다 ~~ 
~~ 데이터를 불러옵니다 ~~ 
~~ 데이터 전처리 함수를 실행합니다! ~~
~~ 데이터 전처리가 끝났습니다! ~~

(4) 파일구조 리뷰

  • 파일 구조는 아래와 같다.
.
├── arithmetic # 폴더
│   ├── plus.py
│   └── subtract.py
├── dataPreprocessing # 폴더
│   ├── importData.py
│   └── processing.py
├── main.py

VI. 결론

  • from의 각각의 폴더명을 의미한다.
  • import는 동일 폴더내의 다양한 py 파일명을 의미한다.
  • 각각의 파일명안에 있는 다양한 함수들을 불러와서 사용할 수 있다.
  • 프로젝트 파일을 제출할 시에는 위와 같이 main.py를 실행만 하더라도 결과가 나올 수 있도록 프로젝트 파일을 Refactoring해서 업로드하는 것을 추천한다.