이전에 수집한 데이터를 flask를 통해서 표현해보는 것을 진행했다.
단순히 표현하는 수준이고, 내가 했던 것을 까먹지 않기 위해 작성한다.
참고: https://wikidocs.net/78513
이전에 수집한 실거래 데이터를 활용해볼 예정이다.
수집 관련 게시글 : https://dovah.tistory.com/32?category=830150
다만, 이번 과정에선 db 연결은 하지 않고 로컬 파일로 진행했다.
파이참에서 flask 프로젝트에는 프로젝트 내에 static과 templates폴더가 필요하다.
static의 경우 css, templates의 경우는 html 파일이 포함된다.
1. 데이터 호출
from flask import Flask, render_templa
from database import get_total_data
from database import load_data
from database import now_index
from get_info import get_sales_info
data = get_total_data()
data = data.reset_index(drop=True)
app = Flask(__name__)
get_total_data함수는 기존에 API를 통해서 수집한 파일을 로컬에 저장했는데, 이 로컬 파일을 다시 호출하는 함수이다.
이 함수의 경우 폴더 내에 있는 모든 파일을 합친 이후, 약간의 전처리를 하도록 진행했다.
import pandas as pd
from glob import glob
def get_total_data(path = '/home/dovah/apart_sales_info/*.pkl'):
file_list = glob(path)
data_list = []
for file in file_list:
data = pd.read_pickle(file)
data_list.append(data)
data = pd.concat(data_list)
data['거래금액'] = data['거래금액'].apply(lambda x: int(x.replace(',', '')))
data['거래일자'] = pd.to_datetime(
data['년'] + '-' + data['월'].apply(lambda x: x.zfill(2)) + '-' + data['일'].apply(lambda x: x.zfill(2)))
data = data[['거래금액', '건축년도', '도로명', '법정동', '아파트', '전용면적', '지역코드', '층',
'거래일자']]
data = data.rename(columns = {'거래금액':'price', '건축년도':'build_date', '도로명':'road_name', '법정동':'region',
'아파트':'name', '전용면적':'size', '지역코드':'region_code', '층':'floor',
'거래일자':'sales_date' })
return data
우선 문자열 형태 되어있던 거래금액을 정수형으로 변환하고, 년/월/일 다 분할되어있는 값을 거래일자로 통합했다.
이후 데이터 내에 필요한 변수만 남기고, 해당 변수명을 수정했다.
(거래금액과 거래일자의 경우 새로 만들때 애초에 price, sales_date로 변환해주는게 더 나을것 같다)
2. get/post 분기점
사실 이 부분은 위의 위키독스를 거의 그대로 따라했다.
@app.route('/', methods=('GET', 'POST'))
def index():
if request.method == 'POST':
index = request.form.get('index')
index = int(index)
temp_data = data.iloc[index]
return render_template('index.html', data=temp_data)
elif request.method == 'GET':
index = 1
temp_data = data.iloc[index]
return render_template('index.html', data=temp_data)
우선 post일 경우 정수값을 받고 해당 정수값에 맞는 데이터를 표현 하도록했다.
그렇지 않고 get일 경우 첫번째 항목의 데이터를 표현하도록했는데, 생각해보니 python에서 1은 실질적으로 2이기 때문에...
두번째 항목의 데이터가 맞는 표현이다.
여기서 보면 index.html을 사용하는데 index.html의 경우 templates 폴더 내에 생성해둔 html 파일이다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="'viewport" content="width=device-width, initial-scale=1.0">
<title>Title</title>
<link rel="stylesheet" href="{{url_for('static', filename='css/style.css')}}">
</head>
<body>
<h1> page</h1>
price : {{data.price}}
build year : {{data.build_date}}
sales year : {{data.sales_date}}
address : {{data.region}}
<form action="/" method="POST">
다음 내역 : <input type = "text", name="index">
<input type = "submit" value="전송하기">
</form>
</body>
</html>
head부분은 html 파일을 생성하면 기본적으로 작성되는 부분이다.
body부분을 보면 두 개로 구분을 했는데, price~address 부분은 특정 데이터값을 표현하는 부분이며,
밑에 form은 새로운 인덱스 정보를 받는 부분이다.
그럼 중요한 것은 이 특정 데이터는 어떻게 설정되느냐인데, 이는 위에 index 함수를 통해서 결정된다.
form부분에 인덱스값을 전달하면(post), 그에 맞춰 상단의 데이터가 업데이트 되도록 설정했다.
만약 전달받은 값이 없다면, 앞서 언급한 두번째 데이터가 표현된다.
아무것도 전송하지 않았을 경우에는 index 값이 1인 데이터가 나타난다.
15라는 값을 전송했을 때 나타나는 데이터의 값이다.
3. 요약 테이블
def get_sales_info(data, value='sales_date'):
daily_info = data.groupby(value).agg({'price':'sum', 'region':'count'}).reset_index()
daily_info.columns = [value, 'total_price', 'count']
daily_info = daily_info.sort_values(value).reset_index(drop=True)
return daily_info
@app.route('/daily')
def daily_info():
daily_info = get_sales_info(data)
return render_template('daily_info.html',tables=[daily_info.to_html(classes='data')],
titles=data.columns.values)
get_sales_info 함수는 다른 코드에 작성되어 있으며, 해당 함수를 호출해서 사용했다.
get_sales_info는 별도로 변수명을 지정하지 않으면, 거래일자별 데이터가 나오도록 설정했다.
daily_info 함수를 보면 요약 데이터를 받은 후, 기존에 작성된 html에 맞춰서 표현되도록 했다.
render_templates은 잘은 모르지만 기존에 작성되있는 html 파일에 내가 원하는 데이터들을 반영시키는 함수 같다.
daily_info.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="'viewport" content="width=device-width, initial-scale=1.0">
<title>Title</title>
<link rel="stylesheet" href="{{url_for('static', filename='css/style.css')}}">
</head>
<body>
<h1> page</h1>
{% for table in tables %}
{{titles[loop.index]}}
{{ table|safe }}
{% endfor %}
</form>
</body>
</html>
사실 html에 대해서는 크롤링하는 정도밖에 몰라서 인터넷에 있는 코드를 그냥 복붙했다.
받은 데이터프레임을 표로 만들어주는 것이 다이다.
4. pandas df.to_html()과 얼마나 차이가 날까
html로 표를 만드는 것을 찾다가, 전 직장에서 분석 결과ㄹ html 파일로 내보낸적이 있엇는데,
그때는 별도의 과정없이 바로 내보냈던것이 생각났다.
바로 pandas 에서 제공하는 to_html()을 활용하는 것이다.
def load_data(data, idx):
return data.iloc[idx].reset_index()
@app.route('/sales_info/<int:index_id>')
def sales_info(index_id):
temp_sales_info = load_data(data, index_id)
return temp_sales_info.to_html()
load_data 함수의 경우 주소?에서 받은 인덱스 값에 해당하는 데이터를 호출하는 함수이다.
여기서는 별도의 html없이 데이터프레임을 바로 노출시켰다.
*참고로 load_data함수에서 reset_index()를 하지않으면 dataframe이 아닌 series기 때문에, to_html()함수가 적용되지 않는다.
사실상 큰 차이는 없는 것 같다.
다만 html 파일을 생성해서 하는 경우 표 이외에 다른 것들도 표현할 수 있겠지만,
to_html 함수를 통해서 바로 넘기는 경우에는 해당 표만 노출 되는 것 같다.
추가로 render_templates에 html 파일 대신 바로 df.to_html()을 사용해봤는데 안된다...
나름 혼자 끙끙대면서 해본건데 글로 적으니 몇 안되는 것 같다.
이후에는 단순 표 뿐만 아니라 plotly나 bokeh와 같은 interactive한 그래프를 표현해보고자한다.
그리고 마지막으론 실 db와 연결해서 실시간성을 반영해보면 어떨까...
'Analysis Tips' 카테고리의 다른 글
[한글 분석] kr-wordrank와 soynlp를 활용한 한글 분석 (0) | 2022.01.15 |
---|---|
[API] 주택 구입 부담 지수 API 연결 (2) | 2021.08.28 |
[공공데이터] DB에 저장하고 Flask와 연결해보기 (0) | 2021.01.10 |
[공공데이터] 아파트 실거래 매매 API 연결 (0) | 2021.01.03 |
[Python] Selenium으로 KBO 기록실을 털어보자 -2탄 (2) | 2020.07.13 |
댓글