(고등학교 수학I 수업) 주어진 데이터를 바탕으로 한 지수함수 산출 프로그램 (numpy, matplotlib, pyscript, etc)

1.프로그램 개요

입력된 데이터 포인트를 기반으로 지수 함수를 근사하고 이를 시각화하는 웹 애플리케이션

(클릭) 완성 프로그램 보러 가기

2. 주요 기능

  • 데이터 입력: 사용자는 웹페이지의 입력란에 최대 8개의 (x, y) 데이터 포인트를 직접 입력하고 ‘전송’ 버튼을 눌러 데이터를 프로그램에 전달
  • 산점도 출력: 입력된 데이터를 기반으로 ‘산출’ 버튼을 클릭하면 데이터가 점으로 표시된 산점도 그래프를 확인
  • 지수 함수 근사: 프로그램은 입력된 데이터를 y = A * e^x + B 형태의 지수 함수 모델에 가장 적합하게 근사하여, 계수 A와 B의 값을 계산하고 화면에 표시.
  • 그래프 시각화: 마지막으로, 원본 데이터의 산점도와 근사된 지수 함수 그래프를 함께 그려서 데이터의 경향성과 근사 모델을 시각적으로 비교할 수 있도록 제공

3. 작업 파일

작업 폴더 내에는 아래와 같이 3개의 파일을 준비한다.

  • pyscript.json: 파이썬 코드가 웹에서 작동하는 데 필요한 외부 라이브러리 목록을 지정
  • expon.py: 웹페이지에서 받은 데이터로 지수 함수를 계산하고 그래프를 그리는 등 모든 실제 연산을 처리
  • expon.html: 사용자가 데이터를 입력하고 결과를 확인하는 웹페이지의 전체적인 뼈대와 화면을 구성

4. 외부 라이브러리

이 프로그램에 필요한 라이브러리는 다음과 같다.

  • numpy: 입력된 점들로부터 지수 함수의 계수(A, B)를 계산
  • sympy: 계산된 계수를 이용해 y = A * e^x + B 라는 지수함수 산출
  • matplotlib: 입력된 점과 최종 지수 함수 그래프를 화면에 시각화.

5. json 파일 코드 작성

json파일은 파이썬 코드(expon.py)가 웹 브라우저에서 제대로 작동하기 위해 어떤 외부 파이썬 라이브러리(도구)가 필요한지 PyScript에게 알려주는 설정 파일의 역할을 한다.

웹 브라우저는 이 파일을 가장 먼저 읽고, 목록에 있는 라이브러리들을 미리 다운로드하여 파이썬 코드를 실행할 준비를 마친다.

작업 폴더 내 pyscript.json 파일 내에 아래와 같이 이 작업에 필요한 라이브러리를 담고 저장한다.

{
    "packages": ["numpy", "sympy", "matplotlib"]
}

6. expon.html 파일 코드 작성

이제 사용자가 보게 될 프로그램의 외형, 즉 웹페이지의 구조와 디자인을 만들 차례다. 서버, 클라이언트 중에서는 클라이언트를 구현하는 단계다.

6.1. 기본 뼈대 구성

먼저, expon.html파일에 html파일의 기본 뼈대를 아래와 같이 구성하자.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>지수 함수 계산기</title>
</head>
<body>

</body>
</html>

<코드 설명>

  • <!DOCTYPE html>: “이 문서는 HTML5 형식으로 작성되었습니다.” 라고 브라우저에게 알려주는 선언
  • <html>: 모든 HTML 요소들을 감싸는 최상위 태그
  • <head>: 페이지의 설정 정보를 담는 곳. 페이지 제목(<title>), 인코딩 방식(meta charset=”UTF-8″) 등 눈에 직접 보이지 않는 중요한 설정들이 포함.
  • <body>: 사용자의 눈에 실제로 보이는 모든 콘텐츠가 들어가는 곳. 제목, 문단, 이미지, 버튼 등 우리가 만들 모든 요소는 이 태그 안에 위치.


6.2. head 태그 작성

<head> 태그 안에 아래 코드를 추가한다.

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>지수 함수 계산기</title>

    <link rel="stylesheet" href="https://pyscript.net/releases/2024.1.1/core.css">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <script type="module" src="https://pyscript.net/releases/2024.1.1/core.js"></script>

</head>

<코드 설명>

  • <link rel=”stylesheet” …>: 외부 CSS (디자인 스타일시트) 파일을 불러온다.
    – core.css: PyScript가 자체적으로 사용하는 스타일.
    – bootstrap.min.css: ‘부트스트랩’이라는 유명한 디자인 도구. 버튼, 입력창 등을 빠르고 예쁘게 만들어줌.
  • <script type=”module” …>: **PyScript의 핵심 엔진(core.js)**을 불러옴. 이 코드가 있어야만 웹 페이지가 파이썬 코드를 알아듣고 실행할 수 있게 됨.

6.3. (body) 제목, 데이터 입력폼 구현

웹페이즈의 제목, 데이터 입력폼을 먼저 구성하자. 아래 코드를 <body>태그 안에 작성한다.

<body>
   
    <h1 class='maintitle'> 실생활 속 지수함수(beta)</h1>
    <h3 class="notice"> 시간이 조금 걸립니다. '전송' 버튼이 활성화될 때까지 기다려주세요. </h3>
    
    <div>
        <h2 class='title'> 1. 데이터 입력 </h2>
        <input id="data_1_x" type='number' class='inputs' placeholder="x1">
        <input id="data_1_y" type='number' class='inputs' placeholder="y1"><br>
        <input id="data_2_x" type='number' class='inputs' placeholder="x2">
        <input id="data_2_y" type='number' class='inputs' placeholder="y2"><br>
        
        <!-- (나머지 8번까지의 입력창들도 동일한 패턴으로 추가) -->
    </div>

</body>

<코드 설명>

  • <h1>, <h2>, <h3>: 페이지의 제목과 안내 문구. 숫자가 클수록 글자가 크다.
  • <div>: 관련된 요소들을 하나로 묶어주는 투명한 상자. 구역을 나누는 역할.
  • <input>: 사용자가 직접 값을 입력할 수 있는 입력창.
    – id=”data_1_x”: 이 입력창에 붙여준 고유한 이름표. 파이썬이 나중에 이 이름표를 보고 정확히 이 입력창의 값을 참조한다.
    – placeholder=”x1″: 사용자가 아무것도 입력하지 않았을 때 희미하게 보이는 안내 문구.

6.4. (body) 데이터 전송 버튼, 결과 표시 공간

방금 추가한 <div>블록의 가장 아래에 이어서 아래 내용을 작성한다.

<body>
   
    <h1 class='maintitle'> 실생활 속 지수함수(beta)</h1>
    <h3 class="notice"> 시간이 조금 걸립니다. '전송' 버튼이 활성화될 때까지 기다려주세요. </h3>
    
    <div>
        <h2 class='title'> 1. 데이터 입력 </h2>
        <input id="data_1_x" type='number' class='inputs' placeholder="x1">
        <input id="data_1_y" type='number' class='inputs' placeholder="y1"><br>
        <input id="data_2_x" type='number' class='inputs' placeholder="x2">
        <input id="data_2_y" type='number' class='inputs' placeholder="y2"><br>
        
        <!-- (나머지 8번까지의 입력창들도 동일한 패턴으로 추가) -->

        <!-- 5. 파이썬 함수 실행시킬 버튼 (py-click으로 연결) -->
        <button py-click="point" type="button" class="btn btn-secondary">  전송  </button>
        <!-- 6. 파이썬 계산 결과가 표시될 공간 -->
        <h4 class = 'content' id = 'result1'></h4>
    </div>

</body>

<코드 설명>

  • <button>: 사용자가 클릭할 수 있는 버튼
    – py-click=”point”: “이 버튼을 클릭하면, 파이썬 파일의 point 함수를 실행시켜라” 라는 PyScript 명령어
  • <h4 id=’result1′></h4>: 결과가 표시될 공간. 지금은 비어있지만, 파이썬이 계산을 끝내고 id가 result1인 이곳에 ‘전송완료’와 같은 결과를 쓸 예정.

이와 같은 패턴으로 2,3,4번 기능에 대한 버튼과 표시 공간도 추가해준다.(<<스스로)

6.5. (body) HTML파일과 파이썬을 연결하기

웹페이지에 보여질 모든 컨텐츠가 준비되었다. 마지막으로, 이 HTML파일이 어떤 파이썬 파일과 통신해야 하는지 알려주는 코드를 <body> 태그 내 가장 아래에 추가하자.

    <!-- ... (지금까지 작성한 모든 body 콘텐츠) ... -->
    
    
    <script type = "py" src = "./expon.py" config = "./pyscript.json"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
    
</body>

<코드 설명>

  • <script type=”py” …>: 이 스크립트가 일반적인 자바스크립트가 아니라 pyscript용 코드임을 브라우저에 알려주는 역
  • config=”./pyscript.json”: 파이썬 코드를 실행하기 전, pyscript.json 파일을 먼저 읽어오도록 설정. PyScript는 이 파일에 명시된 numpy, matplotlib 같은 라이브러리들을 미리 준비.
  • src=”./expon.py”: 라이브러리 준비가 끝나면, expon.py 파일에 있는 메인 파이썬 코드를 불러와 실행

요약하면, ‘필요한 파이썬 라이브러리를 먼저 설치하고, 그 다음에 expon.py 파일을 실행하라’ 는 순차적인 명령이다.

이 스크립트를 <body> 태그의 가장 마지막에 두는 이유는 웹페이지의 로딩 속도를 향상시키기 위함이다. 브러우저는 페이지의 모든 시각적 요소(글자, 버튼 등)를 사용자에게 먼저 보여주고, 그 후에 기능적인 스크립트를 로드한다. 이 방식은 사용자가 빈 화면을 보는 시간을 줄여준다.

7. expon.py 파일 코드 작성

지금까지 프로젝트의 ‘준비물 목록'(pyscript.json), ‘얼굴'(expon.html)을 만들었다. 이제 프로젝트의 ‘두뇌’에 해당하는 py파일을 만들 차례이다.

7.1. 라이브러리 임포트

expon.py 파일 안에 아래 코드를 입력하여 패키지를 임포트하자.

import numpy as np
import matplotlib.pyplot as plt
import sympy as sy

from pyscript import document

7.2. x,y 데이터 입력 및 전송

이제, 사용자가 입력한 8개 데이터의 x, y 값을 각각 담을 빈 리스트를 만들고, 지수 함수 계수를 저장할 변수를 만들자.

# 패키지 임포트 부분 (생략)

# xv,yv, coeff_fitting
xv = []
yv = []

coeff_fitting = 0

이제, xv, yv 리스트에 데이터 값을 차곡차곡 담고, 전송 완료 메시지를 출력하는 과정이다.

# 패키지 임포트 부분(생략) 
# xv,yv, coeff_fitting (생략)

# point() 부분

def point(event):
    
    for i in range(1, 9):
        
        data_x = document.querySelector("#data_" + str(i) + "_x")
        data_y = document.querySelector("#data_" + str(i) + "_y")
        
        data_x = float(data_x.value)
        data_y = float(data_y.value)
        
        xv.append(data_x)
        yv.append(data_y)
    
   
    result1 = document.querySelector('#result1')
    result1.innerText = '전송완료'

<코드 설명>

  • document.querySelector(“#…”)는 HTML에서 특정 id를 가진 요소를 찾아오는 명령
  • float() : 실숫값으로 변환해주는 함수
  • append() : 리스트에 전달값을 담는 함수
  • …innerText : 텍스트를 입력

7.3. 지수함수로 근사

이제 입력받은 xv, yv 리스트로부터 지수함수를 근사하는 과정을 함수로 구현한다.

# point() 부분 (생략)

# 지수함수 근사 부분
def exp_func(event):
    global coeff_fitting
    
    coeff_fitting = np.polyfit(np.exp(xv), yv, 1)
    
    
    result_a = document.querySelector('#result_coeff_a')
    result_a.innerText = coeff_fitting[0]
    result_b = document.querySelector('#result_coeff_b')
    result_b.innerText = coeff_fitting[1]

<코드 설명>

  • np.polyfit(np.exp(xv), yv, 1) : numpy의 polyfit 함수. 즉, 지수함수로 근사하는 함수로서, xv의 값들을 x값, yv의 값들을 y값으로 하여 y = A * (e^x) + B 꼴의 함수로 근사, A, B의 값을 반환함.

나머지 부분들은 HTML 문서에서 id가 result_coeff_a, result_coeff_b인 공간을 찾아서, 그 안에 numpy가 계산한 지수 함수의 A, B 계수 값을 텍스트로 써넣으라는 명령이다.

7.4. 산점도 그리기

# 패키지 임포트 부분(생략) 
# xv,yv, coeff_fitting (생략)
# point() 부분 (생략)

# 산점도 그리기
def plot_1(event):
    plt.scatter(xv, yv, color='blue')
    plt.title('산점도')
    plt.show() 

7.5. 산점도와 지수함수 그래프 함께 출력

# 패키지 임포트 부분(생략) 
# xv,yv, coeff_fitting (생략)
# point() 부분 (생략)
# 산점도 그리기(생략)

# 산점도, 지수함수 그래프 함께 출력

def plot_2(event):
    x = sy.symbols('x')
    
    exp_fitting_func = coeff_fitting[0] * sy.exp(x) + coeff_fitting[1]
    
    
    exp_fitting_func_numeric = sy.lambdify(x, exp_fitting_func, 'numpy')

   
    x_vals = np.linspace(xv[0] - 1, xv[-1] + 1, 100)
    
    plt.plot(x_vals, exp_fitting_func_numeric(x_vals), label='Exponential Fit', color='crimson')

    
    plt.scatter(xv, yv, label='Data Points', color='blue')

    
    plt.xlabel('x')
    plt.ylabel('y')
    plt.legend()
    plt.grid(True)
    plt.show() 

<코드 설명>

  • x = sy.symbols(‘x’) : 심파이에서 x를 변수로 설정한다는 뜻
  • plt.scatter() : 점을 찍어 산점도를 그림.
  • plt.plot() : 꺾은선그래프 그림.
  • plot_2() 요약 :  sympy로 수학 공식을 만든 뒤, lambdify를 이용해 이 공식을 그래프로 그릴 수 있는 숫자 계산용 함수로 변환하는 과정.
  • plt.show()가 호출되면, PyScript는 완성된 그래프를 웹페이지의 지정된 공간에 이미지로 보여줌.

8. 웹 배포

이로써 3개 파일 작성이 끝났다. 세 파일을 하나의 작업 폴더에 담고, 웹에 배포해 보자. 나의 경우, 깃허브에서 제공하는 깃허브 페이지(GitHub Page)를 이용하여 정적 사이트 호스팅(Static Site Hosting)하였다. (참고 : (클릭) 깃허브 페이지 정적 사이트 호스팅 소개 (준비중))

먼저, 세 파일을 GitHub 저장소에 업로드한다. 그리고, 저장소의 설정 메뉴에서 GitHub Pages 기능을 활성화하였다. 그러면

https://나의깃허브아이디.github.io/html파일명

의 url링크를 통해 누구나 접속할 수 있다. 예를 들어, 나의 경우,

  • 깃허브 아이디 : chamingyung
  • html파일명 : expon

이었으므로, 나의 웹페이지 url 링크는 https://chamingyung.github.io/expon이 된다.


코멘트

댓글 남기기

Cha's Record에서 더 알아보기

지금 구독하여 계속 읽고 전체 아카이브에 액세스하세요.

계속 읽기