국비 데이터 분석 2차 프로젝트 결산 - 3
분석이 끝난 후 시간이 너무 남아서 웹사이트로 만들었다.
필자는 자바스크립트를 이론만 공부하고 작업은 처음이라 시행착오가 많았는데 생각보다 적용이 잘돼서 나름 괜찮았다.
배운게 장고, 파이썬이라서 일단 장고로 시작했다.
하지만 D3 geograph에서 꽉 막혀서 canvas부터 새로 공부한건 비밀...
5페이지에 숨겨진 한 페이지 총 6페이지를 만들었다.
css가 약해서 예전에 모 사이트 클론코딩하면서 만들어본 것을 가져와 약간 바꿔 사용했다.
우선 메인 화면의 html 이다.
딱히 특별한 것은 없고 d3를 사용해서 world map을 가져와 꾸며봤다.
d3가 구글에 올라온 정보들은 죄다 v4이전이라서, v6로 해보느라 힘들었다.
<!DOCTYPE html>
{% load static %}
<html lang="ko">
<head>
<meta charset="utf-8">
<title>조별과제 2호</title>
{% block css %}
<link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.6/dist/web/static/pretendard.css">
<link rel="stylesheet" href={% static 'main/css/style.css' %}>
{% endblock css %}
{% block script2 %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.min.js"></script>
<script src="https://d3js.org/d3.v6.min.js"></script>
{% endblock script2 %}
</head>
<body>
<div>
{% include 'main/header.html' %}
</div>
<section class="hero">
<div class="hero-heading">
<h1>
<span class="small">코로나 2년간을 기록하다.</span><br>
{% block title_ %}
<span class="big">코로나를 통계해보는 사이트</span>...
{% endblock title_ %}
</h1>
</div>
</section>
{% block content %}
<section class="headline wrap" >
<body>
<div id="world_map" onclick="location.href='{% url 'index' %}';">
<canvas width="800" height="400"></canvas>
</div>
</body>
{% endblock content %}
{% block script %}
<script src ={% static 'main/js/home.js' %} type = 'text/javascript'></script>
{% endblock script %}
</html>
Doctor 페이지
의사수 데이터를 가져와 코로나 데이터랑 결합하기 위해 사용했던 데이터를 페이지로 받았다.
저 위의 한국부분의 나라들을 선택하면 데이터 분석과 그래프가 바뀐다.
그래프는 chart.js를 사용했다.
일단 기본적인 장고 세팅을 끝내고, 가볍게 만들 생각으로 main app을 추가했다. 돔은 일단 패스
Url 을 추가하고, 바로 메인으로 받아버리게끔 path 값을 안줬다.
from django.urls import path, include
urlpatterns = [
path('', include('main.urls')),
]
urlpatterns = [
path('doctor/', views.doctor, name='doctor'),
]
스크립트로만 짤 것이기에 views.py도 짧게
def doctor(request):
return render(request, 'main/doctor.html')
각 나라를 select option으로 선택하게끔 html을 짰다. 이건 내가 아직 초보라서 노가다를 뛰었다. 템플렛 내부의 반복문으로 돌려봤는데 즉각처리가 안돼고, 이상해져서 그냥 일일이 적었다. 할 당시에는 생각이 안났는데, 지금 생각해보니 '스크립트로 반복문 돌려서 html에 추가해주는 형식으로 코드를 짰으면 어땠을까?' 싶다.
아래는 doctor.html의 코딩이다.
<!-- doctor.html -->
{% extends "./home.html" %}
{% load static %}
{% block header %}
{% endblock header%}
{% block title_ %}
<span class="big">각 나라 의사 수의 변화</span>...
{% endblock title_ %}
{% block content %}
<section class="headline wrap" >
<form id='new_contry_form' name ='new_contry_form' onsubmit='return false;'>
<select name="new_contry">
<option value="NEW_KOR" selected>한국</option>
<option value="NEW_AUS">호주</option>
<option value="NEW_AUT">오스트리아</option>
<option value="NEW_BEL">벨기에</option>
<option value="NEW_BRA">브라질</option>
<option value="NEW_CAN">캐나다</option>
<option value="NEW_CHE">스위스</option>
<option value="NEW_CHN">중국</option>
<option value="NEW_CZE">체코</option>
<option value="NEW_DEU">독일</option>
<option value="NEW_DNK">덴마크</option>
<option value="NEW_ESP">스페인</option>
<option value="NEW_EST">에스토니아</option>
<option value="NEW_FIN">핀란드</option>
<option value="NEW_FRA">프랑스</option>
<option value="NEW_GBR">영국</option>
<option value="NEW_HUN">헝가리</option>
<option value="NEW_IDN">인도네시아</option>
<option value="NEW_IRL">아일랜드</option>
<option value="NEW_ISL">아이슬란드</option>
<option value="NEW_ISR">이스라엘</option>
<option value="NEW_ITA">이탈리아</option>
<option value="NEW_JPN">일본</option>
<option value="NEW_LTU">리투아니아</option>
<option value="NEW_LUX">룩셈부르크</option>
<option value="NEW_LVA">라트비아</option>
<option value="NEW_MEX">멕시코</option>
<option value="NEW_NLD">네덜란드</option>
<option value="NEW_NOR">노르웨이</option>
<option value="NEW_NZL">뉴질랜드</option>
<option value="NEW_POL">폴란드</option>
<option value="NEW_RUS">러시아</option>
<option value="NEW_SVK">슬로바키아</option>
<option value="NEW_SVN">슬로베니아</option>
<option value="NEW_SWE">스웨덴</option>
<option value="NEW_TUR">튀르키예</option>
<option value="NEW_USA">미국</option>
<option value="NEW_ZAF">남아공</option>
</select>
<input type="submit" id='new_contry_send' value='보기'>
</form>
<canvas id="myChart"></canvas>
<div>
<h2 class="headline-heading">
데이터 분석
</h2>
<p class="headline-description" id='country_name_now'>
<span class="headline-description" id='Korean_contry_name'>
한국의
</span>
1,000명당 의사 수의 변화입니다.
</p>
<p class="headline-description">
해당 국가의 피어슨 상관 계수는
<span class="headline-description" id='PearsonP'>
0.997568689 이며 <br> 이 값은 0.5 보다 크므로 유의미한 상관관계를 갖습니다.
</span>
</p>
<p class="headline-description">
해당 값의 유의 확률은
<span class="headline-description" id='PearsonP_Pvalue'>
1.414810028e-45이며 <br> 이 값은 0.05보다 작으므로 우연히 일어날 가능성은 작다고 할 수 있습니다.
</span>
</p>
{% endblock content %}
{% block script %}
<script src ={% static 'main/js/main.js' %} type = 'text/javascript'></script>
{% endblock script %}
설명을 해보면, select를 만들고 option을 여러개 줬다. 한국을 selected로 기본 값으로 주고
form 안에 넣어 submit으로 보내면 js로 받도록 해줬다.
그 다음에 canvas로 chart js를 받아오도록 했다.
다음 구간을 나누고 아래 데이터 분석이 '한국'일 때, 즉 기본값일 때 보이도록 하드코딩 했다. 대신에 스크립트에서 form으로 새 option이 선택되면 안의 내용물이 바뀌게끔 해줬다.
스크립트를 보면 아래와 같다. 처음에 계획적으로 접근을 못해서 이름을 main.js로 지었다.
초기값 설정하고 바로 보여주기 위해서 아래에 한번 더 적었다. (원래 이러면 안되는데... ㅠ)
(전체를 함수로 만들어서 작업했어야 할까?, 아니면 다른 방법이 있었을까?..)
//main.js
//스타일 초기화
// Chart.platform.disableCSSInjection = true;
// const send = document.querySelector('#new_contry_send');
//주소 선택하기
const new_contry_form = document.querySelector('#new_contry_form');
//submit 눌렀을 때
new_contry_form.addEventListener('submit', () =>{
// select에서 가져오기
const select_contry_name = document.querySelector('select[name="new_contry"]');
// console.log(select_contry_name.value)
//fetch 주소 가져오기
async function AusnewData() {
const AusUrl = '../static/main/json/' + select_contry_name.value + '.json';
// console.log(AusUrl)
const response = await fetch(AusUrl);
const data = await response.json();
// console.log(data)
// console.log(data[0])
return data;
};
//배열 형식으로 데이터 저장
AusnewData().then(data => {
const new_years=[]
const new_datas=[]
for (let key in data){
const AusYear = data[key].TIME;
const AusValue = data[key].Value;
// console.log(AusYear);
// console.log(AusValue);
new_years.push(AusYear);
new_datas.push(AusValue);
}
// console.log(new_years);
// console.log(new_datas);
const ctx = document.getElementById('myChart').getContext('2d');
const chart = new Chart(ctx, {
// 만들기 원하는 차트의 유형
type: 'line',
// 데이터 집합을 위한 데이터
data: {
labels: new_years,
datasets: [{
label: '1,000 명당 의사 수의 변화',
backgroundColor: 'rgb(173,255,47)',
borderColor: 'rgb(255, 255, 255)',
data: new_datas,
pointHoverBorderColor : 'rgb(0,255,255)',
pointHoverBorderWidth : 30,
}]
},
// 설정은 여기서 하세요
options: {
legend: {
labels: {
fontColor : 'white',
fontSize : 18
}
},
aspectRatio: 1,
scales: {
xAxes: [{
ticks:{
fontColor : "rgba(251, 203, 9, 1)",
fontSize : 15,
},
gridLines:{
color: "rgba(87, 152, 23, 1)",
lineWidth: 1
}
}],
x: {
type: 'linear',
position: 'bottom'
},
yAxes: [{
ticks: {
beginAtZero: true,
// stepSize : 1,
fontColor : "rgba(251, 203, 9, 1)",
fontSize : 20,
},
gridLines:{
color: 'rgba(166, 201, 226, 1)',
lineWidth:3
}
}]
}
}
});
//한글 이름 가져오기
async function newContryKorean() {
const new_korean_url = '../static/main/json/newKorean.json';
const response = await fetch(new_korean_url);
const data = await response.json();
return data;
};
newContryKorean().then(data => {
for (let key in data){
if (data[key].NEW_COUNTRY === select_contry_name.value){
const korean_name = data[key].KOREAN_NAME
document.querySelector('#Korean_contry_name').innerHTML = korean_name
}
}
})
//피어슨 json 가져오기
async function newContryData() {
const new_country_url = '../static/main/json/newList.json';
console.log(new_country_url)
const response = await fetch(new_country_url);
const data = await response.json();
return data;
};
//피어슨 계수 구하기
newContryData().then(data => {
for (let key in data){
if (data[key].LOCATION === select_contry_name.value){
const PRResult = data[key].PearsonRResult;
// console.log(data[key].LOCATION);
// console.log(PRResult);
// console.log(PRResult[0]);
// console.log(PRResult[1]);
if (PRResult[0]>=0.6 || PRResult[0]<=-0.6){
document.querySelector('#PearsonP').innerHTML = PRResult[0] + '이며 <br> 이 값은 |0.6| 보다 크므로 유의미한 상관관계를 갖습니다.'
}else if (PRResult[0]>=0.4 || PRResult[0]<=-0.4){
document.querySelector('#PearsonP').innerHTML = PRResult[0] + '이며 <br> 이 값은 |0.4| 과 |0.6| 사이이므로 약한 상관관계를 갖습니다.'
}else {
document.querySelector('#PearsonP').innerHTML = PRResult[0] + '이며 <br> 이 값은 |0.4| 보다 작으므로 관계가 적다고 할 수 있습니다.'
}
// document.querySelector('#country_name_now').innerHTML = select_contry_name.value
if (PRResult[1]<=0.05){
document.querySelector('#PearsonP_Pvalue').innerHTML = PRResult[1] + '이며 <br> 이 값은 0.05보다 작으므로 우연히 일어날 가능성은 작다고 할 수 있습니다.'
}else {
document.querySelector('#PearsonP_Pvalue').innerHTML = PRResult[1] + '이며 <br> 이 값은 0.05보다 크므로 데이터로 사용하기 부적절합니다.'
}
}
}
})
});
})
// 시작시 한국으로 설정
async function AusnewData() {
const AusUrl = '../static/main/json/NEW_KOR.json';
console.log(AusUrl)
const response = await fetch(AusUrl);
const data = await response.json();
// console.log(data)
// console.log(data[0])
return data;
};
AusnewData().then(data => {
const new_years=[]
const new_datas=[]
for (let key in data){
const AusYear = data[key].TIME;
const AusValue = data[key].Value;
// console.log(AusYear);
// console.log(AusValue);
new_years.push(AusYear);
new_datas.push(AusValue);
}
// console.log(new_years);
// console.log(new_datas);
const ctx = document.getElementById('myChart').getContext('2d');
const chart = new Chart(ctx, {
// 만들기 원하는 차트의 유형
type: 'line',
// 데이터 집합을 위한 데이터
data: {
labels: new_years,
datasets: [{
label: '1,000 명당 의사 수의 변화',
backgroundColor: 'rgb(173,255,47)',
borderColor: 'rgb(255, 255, 255)',
data: new_datas,
pointHoverBorderColor : 'rgb(0,255,255)',
pointHoverBorderWidth : 30,
}]
},
// 설정은 여기서 하세요
options: {
legend: {
labels: {
fontColor : 'white',
fontSize : 18
}
},
aspectRatio: 1,
scales: {
xAxes: [{
ticks:{
fontColor : "rgba(251, 203, 9, 1)",
fontSize : 20,
},
gridLines:{
color: "rgba(87, 152, 23, 1)",
lineWidth: 1
}
}],
x: {
type: 'linear',
position: 'bottom'
},
yAxes: [{
ticks: {
beginAtZero: true,
// stepSize : 1,
fontColor : "rgba(251, 203, 9, 1)",
fontSize : 20,
},
gridLines:{
color: 'rgba(166, 201, 226, 1)',
lineWidth:3
}
}]
}
}
});
});
id가 new_country_form 인 것을 찾아 new_country_form으로 지정하고 여기서 addEventListener로 전송됐을 때 이벤트가 발송하게 했다.(지금 생각해보니 차라리 버튼을 눌렀을 때 event 발생하고, 보내졌을 때는 이벤트 막는 방식으로 했어야 했다.. 중간중간에 자기 멋대로 그래프가 이전으로 돌아가려 하던데, 이랬으면 막히지 않았을까..?)
name=new_country인 애, 즉 select에 어떤 value가 설정되면, 그 값을 AusnewData() 함수의 fetch 해주는 url로 지정하게끔 해줬다. async await로 비동기 통신해주고, json을 data로 받았다.
이 데이터를 chart.js의 넣기 위해서 배열 형태도 데이터를 새로 저장했다.
그 다음은 ctx를 만들고 (캔버스용), chart를 그렸다.
아래 설정들은 공식 문서를 참고해가면서 바꿨다.
데이터 분석파트를 어떻게 넘길지 고민하다가 js에서 직접 넘기기로 했다. 비동기 통신을 이용해 한글과 국가명칭이 있는 json을 넘기도록 했다.(지금생각해보니 이렇게 안 짜고 option의 text로 찍어서 넘겨도 됐을 듯 싶다.)
피어슨도 jupyter notebook 에서 json으로 바꾼 파일을 가져와 비동기 통신으로 넘겼다.
받아온 데이터의 키를 이용해서 피어슨 계수가 절대값 0.6보다 크면 유의미한 상관관계 0.4와 0.6 사이이면 약한 상관관계
그보다 작으면 관계가 적다고 보고, 유의확률도 0.05보다 작을때만 우연성이 적다고 봤다.
이것을 innerHTML을 이용해 doctor.html의 span의 내용을 바꾸게끔 해줬다.
문제있는 코드도 많지만, 아직 내 실력 부족을 한탄하며... 계속 공부해야겠지 싶다.