一、本文目的:
最近为一家企业做一个网站系统,涉及到一些指标公式的制定和显示问题。其中指标公式的制定包括:直线方程式、多项式方程式以及由这两种方程式组合的分段函数。经过讨论,方案如下:
1、使用适合的数据结构进行相关字段的存储和解析:x的区间、直线的斜率和偏置、多项式的系数等。
2、后台使用python进行相关字段的解析和处理。
3、前端使用js进行相关字段的解析,并使用highcharts进行显示。
本文的贡献如下:
1、实现了直线方程式、多项式方程式以及由这两种方程式组合的分段函数,使得用户可以根据需求,制定相关的公式。
2、通过js和bootstrap实现了较好的用户交互界面,用户可添加任意数量的直线方程式或者多项式方程式,同时还可以删除由于误操作添加的方程式。
3、实现了分段函数的显示:直线方程式 + 曲线方程式。
效果如下:
二、准备工作:
1、搭建【django】开发环境:http://blog.csdn.net/houchaoqun_xmu/article/details/53693347
2、下载【highcharts】插件:https://www.hcharts.cn/demo/highcharts
3、下载【jQuery】插件:http://jquery.com/download/
三、本文核心:
1、添加多项式方式的输入框,并提供删除功能,如下图所示:
2、数据结构:
一般情况:没有分段函数
=== x_Value:存储直线和多项式方程中x的区间,以下划线“_”分隔,形如:0_100表示从0到100。
=== k_Value:存储直线方程的斜率,多项式方程中带有未知数x的系数(以分号;分隔)。
=== b_Value:存储直线方程的偏置,多项式方程中不带未知数x的系数。
特殊情况:含有分段函数,直线+直线、直线+多项式、多项式+多项式
如下图所示为“直线+多项式+直线”的模式,以此为例,分析 x_Value、k_Value 和 b_Value 的取值:
=== x_Value = 0_7_28_inf,表示区间[0, 7]、[7, 28]和[28, 正无穷]
=== k_Value = 0_ 0.00346;-0.15469;-2.93331_0,以下划线_为分隔符分别表示3个曲线对应的值,第一个0对应区间[0, 7]中直线方程的斜率、0.00346;-0.15469;-2.93331对应区间[7, 28]中多项式方程未知数的系数,第二个0对应区间[28, 正无穷]中直线方程的斜率。值得一提的是,如何判别是普通直线或者多项式方程的办法就是通过分号【;】进行判断。
=== b_Value = 100_127.46927_0,对应3个区间的3个偏置项。
3、解析【x_Value】【k_Value】【b_Value】,并分别对直线方程和多项式方程进行处理:
通过函数 strip() 和 split() 将字符串通过下划线_分隔为list[ ]
X_list = x_Value.strip().split('_')
K_list = k_Value.strip().split('_')
B_list = b_Value.strip().split('_')
根据上述的例子,处理后如下所示:
=== X_list = [u'0', u'7', u'28', u'inf']
=== K_list = [u'0', u' 0.00346;-0.15469;-2.93331', u'0']
=== B_list = [u'100', u'127.46927', u'0']
根据 K_list[ ] 进行分段处理,K_list[i]中不存在分号【;】的就是普通直线,存在分号【;】的就是多项式方程,如下所示:
for k in K_list:
# print k
expression = ""
if k.find(";") == -1:
print "=== line ==="
if K_list[i]=='0':
expression = "y = " + B_list[i]
elif K_list[i]=='1':
if (B_list[i]).find("-") == -1:
if B_list[i]==0:
expression = "y = x"
else:
expression = "y = x + " + B_list[i]
else:
expression = "y = x " + B_list[i]
else:
if (B_list[i]).find("-") == -1:
if B_list[i]==0:
expression = "y = " + K_list[i] + "x"
else:
expression = "y = " + K_list[i] + "x + " + B_list[i]
else:
expression = "y = " + K_list[i] + "x " + B_list[i]
else:
print "=== curve ==="
curve_list = k.strip().split(';')
j = 0
curve_len = len(curve_list)
print "curve_len = ",curve_len
for j in range(curve_len):
# exponent: 指数
# curve_list[j]: 系数
exponent = str(curve_len - j)
print "exponent = ",exponent
if curve_list[j] == "0":
expression = expression
elif curve_list[j] == "1":
if exponent == "1":
expression = expression + " + x"
else:
expression = expression + " + " + "x^" + exponent
elif curve_list[j] == "-1":
if exponent == "1":
expression = expression + " - x "
else:
expression = expression + " - x^" + exponent
# coefficient > 0 except 1
elif (curve_list[j]).find("-") == -1:
if exponent == "1":
# the begin position.
if j == 0:
expression = curve_list[j] + "x"
else:
expression = expression + " + " + curve_list[j] + "x"
else:
if j == 0:
expression = curve_list[j] + "x^" + exponent
else:
expression = expression + " + " + curve_list[j] + "x^" + exponent
# coefficient < 0 except -1
else:
if exponent == "1":
expression = expression + curve_list[j] + "x"
else:
expression = expression + curve_list[j] + "x^" + exponent
if (B_list[i]).find("-") == -1:
expression = "y = " + expression + " + " + B_list[i]
else:
expression = "y = " + expression + B_list[i]
expressions_list.append(display_expression(X_list[i], X_list[i+1], expression))
i = i + 1
使用类 display_expression 存储每一个区间的信息,包括区间的起始位置和表达式,如下所示:
### class ###
class display_expression:
def __init__(self, x_start, x_end, expression):
self.x_start = x_start
self.x_end = x_end
self.expression = expression
注:js的解析和处理思想类似。
四、实现步骤:
1、基于django,创建一个工程SettingFormula和app_SettingFormula:
python .\django-admin.py startproject SettingFormula
python .\manage.py startapp app_SettingFormula
2、配置settings.py:
=== 在【INSTALLED_APPS】处加入新建的app_SettingFormula
=== 配置static和templates:
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
)
TEMPLATE_DIRS = (
os.path.join(BASE_DIR, 'templates'),
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
)
3、引入相关的.js和.css文件
<link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/Water.css' %}">
<script type="text/javascript" src = "{% static 'js/jquery.min.js' %}" ></script>
<script type="text/javascript" src = "{% static 'js/bootstrap.js' %}" ></script>
<script type="text/javascript" src = "{% static 'js/highcharts.js' %}" ></script>
4、实现简单的html界面
<div style="text-align: center;">
<label style="font-size: 18px;">公式的定制:直线方程式 + 多项式方程式</label>
</div>
<div class="row" style="padding-top: 30px;">
<div class="col-md-3"></div>
<!-- 定制指标公式 - 相关信息 -->
<div class="col-md-6">
<div class="panel" style="border: 1px solid #e0e0e0;">
<div class="panel-heading">
<label>制定直线方程式和多项式方程式:</label>
<button id="btn_save" class="btn btn-sm btn-success pull-right" type="button">保存设置</button>
</div>
<div style="text-align: center;padding: 5px 0;background-color: #f0f0f0;">
<input id="newBtn" class="btn btn-sm btn-success" type="button" value="添加直线方程">
<a class="btn btn-sm btn-success" data-toggle="modal" data-target="#Add_curve" role="button">添加多项式方程</a>
</div>
<div class="panel-body">
<table id="IndicatorFormula_modify">
<tr></tr>
</table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="panel" style="border: 1px solid #e0e0e0;">
<div class="panel-heading">
<label>当前指标公式信息:</label>
</div>
<div class="panel-body">
<table class="" style="margin: 5px;">
{% for expression in expressions_list %}
<tr style="font-size: 15px;">
<td><span class="glyphicon glyphicon-hand-right"></span> 区间:[{{ expression.x_start }} , {{ expression.x_end }}]</td>
<td width="5%"></td>
<td>表达式:{{expression.expression}}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<div id="LineContainer_test"></div>
</div>
</div>
<br>
<div class="modal fade" id="Add_curve">
<div class="modal-dialog" style="width:300px;">
<div class="modal-content">
<div class="modal-body">
请输入曲线最高次指数:<input type="text" id="curve_num" style="width:60px;">
<input id="newBtn_curve" class="btn-sm btn-success pull-right" type="button" value="确定">
</div>
</div>
</div>
</div>
5、使用js实现用户点击“添加直线方程”和“添加多项式方程”弹出对应的输入框:
// line
$("#newBtn").bind("click", function(){
// console.log(this.id)
// console.log(count);
var seq = count.toString();
// console.log(seq);
line_num.push(1);
$str = ""
$str += "<tr>";
$str += "<td>";
$str += "起始点: x_0 = <input id=\"x" + seq + "\" name=\"input_x\" type=\"text\" style=\"width: 40px;\"> ";
$str += "截止点: x_1 = <input id=\"x" + seq + "\" name=\"input_x_1\" type=\"text\" style=\"width: 40px;\"> ";
$str += "<td>";
$str += "直线斜率 = <input id=\"k" + seq + "\" name=\"input_k\" type=\"text\" style=\"width: 40px;\">";
$str += "</td>";
$str += "<td>";
$str += "直线偏置 = <input id=\"y" + seq + "\" name=\"input_b\" type=\"text\" style=\"width: 40px;\">";
$str += "</td>";
$str += "<td width=\"10%\"><input id=\"deleteLine_" + count + "\" class = \"btn btn-sm btn-success\" type=\"button\" value=\"删除表达式\" onClick='getDel(this)'></td>";
$str += "</tr>";
$("#IndicatorFormula_modify").append($str);
count++;
});
// curve
$("#newBtn_curve").bind("click", function(){
var curve_num = $("#curve_num").val();
// console.log(curve_num);
if(curve_num > 0){
$('#Add_curve').modal('hide');
line_num.push(parseInt(curve_num));
var count_curve = 0;
var seq = count.toString();
var curve_length = curve_num;
var seq_curve = count_curve.toString();
$str = ""
$str += "<tr style='color:#960000;' id='curveID_0'>";
$str += "<td>";
$str += "起始点: x_0 = <input id=\"x" + seq + "\" name=\"input_x\" type=\"text\" style=\"width: 40px;\"> ";
$str += "截止点: x_1 = <input id=\"x" + seq + "\" name=\"input_x_1\" type=\"text\" style=\"width: 40px;\"> ";
$str += "<td>";
$str += "曲线系数 = <input id= coefficient_" + curve_length.toString() + " name=\"input_k\"" + " type=\"text\" style=\"width: 40px;\"> " + "x^" + curve_length;
$str += "</td><td></td>";
$str += "<td width=\"10%\"><input id=\"deleteLine_" + count_curve + "\" class = \"btn btn-sm btn-success\" type=\"button\" value=\"删除表达式\" onClick='getDelCurve(" + curve_num.toString() + ")'></td>";
$str += "</tr>";
$("#IndicatorFormula_modify").append($str);
count_curve++;
for(var i = count_curve; i <= curve_length; i++){
var seq_curve = i.toString();
var curveID = 'curveID_' + i.toString();
var curve_tr_content = "style = 'color:#960000;' id = " + curveID;
$str = ""
$str += "<tr " + curve_tr_content + ">";
$str += "<td></td>";
$str += "<td>";
if ((curve_length - i) != 0)
{
$str += "曲线系数 = <input id= coefficient_" + (curve_length - i).toString() + " name=\"input_k\"" + " type=\"text\" style=\"width: 40px;\"> " + "x^" + (curve_length - i);
}
else
{
$str += "曲线偏置 = <input id=\"y" + seq + "\" name=\"input_b\" type=\"text\" style=\"width: 40px;\">";
}
$str += "</td><td></td>";
$str += "<td></td>";
$str += "</tr>";
$("#IndicatorFormula_modify").append($str);
}
}else{
alert("请输入大于0的数值!");
}
count++;
});
6、删除直线方程和多项式方程的输入框:
// delete line input
function getDel(select_remove){
$(select_remove).parent().parent().remove();
}
// delete curve input
function getDelCurve(delete_num){
delete_num = parseInt(delete_num);
for(var i = 0; i <= delete_num; i++){
delete_id = "curveID_" + i.toString();
console.log(delete_id);
$("tr[id=" + delete_id + "]").remove();
}
}
7、将用户填写的数据通过post方法提交至后台:
function post(URL, PARAMS) {
var temp = document.createElement("form");
temp.action = URL.toString();
temp.method = "post";
temp.style.display = "none";
for (var x in PARAMS) {
var opt = document.createElement("textarea");
opt.name = x;
opt.value = PARAMS[x];
console.log(opt.value);
temp.appendChild(opt);
}
// alert("xx");
document.body.appendChild(temp);
temp.submit();
return temp;
}
function IsNum(s)
{
if(s.toLowerCase() == "inf"){
return true;
}
if (s!=null && s!="")
{
return !isNaN(s);
}
return false;
}
$("#btn_save").bind("click", function(){
var x = "";
var b = "";
var k = "";
var x_list = document.getElementsByName("input_x");
var b_list = document.getElementsByName("input_b"); // y 表示偏置b
var k_list = document.getElementsByName("input_k");
var x_1_list = document.getElementsByName("input_x_1");
// console.log(x_list);
// console.log(b_list);
// console.log(k_list);
// console.log(x_1_list);
for (var i = 0; i < x_list.length; ++i)
{
// alert("ss");
if(!IsNum(x_list[i].value) || !IsNum(b_list[i].value) || !IsNum(x_1_list[i].value))
{
alert("请确认输入都为数字或者[Inf]表示无限大.");
return;
}
if(parseFloat(x_list[i]) > 1 || parseFloat(x_list[i]) < 0)
{
alert(x_list[i].id + "输入不合法。");
return;
}
if(parseFloat(b_list[i]) > 100 || parseFloat(x_list[i]) < 0)
{
alert(b_list[i].id + "输入不合法。");
return;
}
}
for (var i = 0; i < k_list.length; ++i)
{
if(!IsNum(k_list[i].value))
{
alert("请确认输入都为数字");
return;
}
}
var k_seq = 0;
for (var i = 0; i < x_list.length; ++i)
{
x = x + x_list[i].value + "_";
b = b + b_list[i].value + "_";
var sub_k = "";
for (var j = 0; j < line_num[i]; ++j)
{
sub_k = sub_k + k_list[k_seq + j].value + ";";
// console.log("k_seq: " + k_seq + j);
}
k_seq = k_seq + line_num[i];
sub_k = sub_k.substring(0, sub_k.length - 1);
k = k + sub_k + "_";
}
x = x + x_1_list[x_1_list.length - 1].value
// console.log(b_list[b_list.length - 1].value)
// console.log(x_list[x_list.length - 1].value)
// console.log()
// console.log(b);
b = b.substring(0, b.length - 1);
k = k.substring(0, k.length - 1);
// console.log(x);
// console.log(b);
// console.log(k);
// console.log(typeof(k));
post("", { x_Value: x, b_Value: b, k_Value: k });
});
8、根据post传来的数据,后台使用python进行数据处理:
def get_IndicatorFormula_expression(x_Value, k_Value, b_Value):
X_list = x_Value.strip().split('_')
K_list = k_Value.strip().split('_')
B_list = b_Value.strip().split('_')
# print "---"
# print "X_list",X_list
# print "B_list",B_list
i = 0
expressions_list = []
# line and curve
# find()函数找不到时返回为-1。
for k in K_list:
# print k
expression = ""
if k.find(";") == -1:
print "=== line ==="
if K_list[i]=='0':
expression = "y = " + B_list[i]
elif K_list[i]=='1':
if (B_list[i]).find("-") == -1:
if B_list[i]==0:
expression = "y = x"
else:
expression = "y = x + " + B_list[i]
else:
expression = "y = x " + B_list[i]
else:
if (B_list[i]).find("-") == -1:
if B_list[i]==0:
expression = "y = " + K_list[i] + "x"
else:
expression = "y = " + K_list[i] + "x + " + B_list[i]
else:
expression = "y = " + K_list[i] + "x " + B_list[i]
else:
print "=== curve ==="
curve_list = k.strip().split(';')
j = 0
curve_len = len(curve_list)
print "curve_len = ",curve_len
for j in range(curve_len):
# exponent: 指数
# curve_list[j]: 系数
exponent = str(curve_len - j)
print "exponent = ",exponent
if curve_list[j] == "0":
expression = expression
elif curve_list[j] == "1":
if exponent == "1":
expression = expression + " + x"
else:
expression = expression + " + " + "x^" + exponent
elif curve_list[j] == "-1":
if exponent == "1":
expression = expression + " - x "
else:
expression = expression + " - x^" + exponent
# coefficient > 0 except 1
elif (curve_list[j]).find("-") == -1:
if exponent == "1":
# the begin position.
if j == 0:
expression = curve_list[j] + "x"
else:
expression = expression + " + " + curve_list[j] + "x"
else:
if j == 0:
expression = curve_list[j] + "x^" + exponent
else:
expression = expression + " + " + curve_list[j] + "x^" + exponent
# coefficient < 0 except -1
else:
if exponent == "1":
expression = expression + curve_list[j] + "x"
else:
expression = expression + curve_list[j] + "x^" + exponent
if (B_list[i]).find("-") == -1:
expression = "y = " + expression + " + " + B_list[i]
else:
expression = "y = " + expression + B_list[i]
expressions_list.append(display_expression(X_list[i], X_list[i+1], expression))
i = i + 1
return expressions_list
9、基于highcharts绘制坐标图:
$(function () {
var x = "{{ x_Value }}", b = "{{ b_Value }}", k = "{{ k_Value }}";
var i;
var xValue = x.split("_");
var bValue = b.split("_");
var kValue = k.split("_"); // 未知量的系数
// console.log(xValue);
// console.log(bValue);
// console.log(kValue);
// console.log(parseFloat(xValue[2] * kValue[1]) + parseFloat(bValue[1]));
// console.log();
var data = new Array();
// 判断线段的类型:
// 1、只有直线
// 2、有直线,又有曲线
var has_curve = false;
// 如果包含子串,则返回大于等于0的索引,否则返回-1
for(var k = 0; k < kValue.length; k++){
if(kValue[k].indexOf(";")>=0){
has_curve = true;
break;
}
}
// has_curve == true 表示包含曲线
if(has_curve == true){
console.log("there is curve.");
for(i = 0; i < bValue.length; i++){
if((xValue[i+1]) && (xValue[i+1].toLowerCase() == "inf")){
xValue[i+1] = 2 * xValue[i] + 40;
}
if(kValue[i].indexOf(";")>=0){
// 曲线处理
coefficient_list = kValue[i].split(";");
var y_0 = 0, y_1 = 0, y_2 = 0, y_3 = 0, y_4 = 0, y_5 = 0;
for(var k = 0; k < coefficient_list.length; k++){
y_0 = y_0 + parseFloat(coefficient_list[k]) * Math.pow(xValue[i], parseInt( coefficient_list.length - k));
y_1 = y_1 + parseFloat(coefficient_list[k]) * Math.pow(parseInt(xValue[i+1]/4), parseInt( coefficient_list.length - k));
y_2 = y_2 + parseFloat(coefficient_list[k]) * Math.pow(parseInt(xValue[i+1]/2), parseInt( coefficient_list.length - k));
y_3 = y_3 + parseFloat(coefficient_list[k]) * Math.pow(parseInt(xValue[i+1]/1.5), parseInt( coefficient_list.length - k));
y_4 = y_4 + parseFloat(coefficient_list[k]) * Math.pow(parseInt(xValue[i+1]/1.2), parseInt( coefficient_list.length - k));
y_5 = y_5 + parseFloat(coefficient_list[k]) * Math.pow(xValue[i+1], parseInt( coefficient_list.length - k));
}
y_0 = y_0 + parseFloat(bValue[i]);
y_0 = y_0.toFixed(2);
data.push([parseFloat(xValue[i]), parseFloat(y_0)]);
y_1 = y_1 + parseFloat(bValue[i]);
y_1 = y_1.toFixed(2);
data.push([parseFloat(parseInt(xValue[i+1]/4)), parseFloat(y_1)]);
y_2 = y_2 + parseFloat(bValue[i]);
y_2 = y_2.toFixed(2);
data.push([parseFloat(parseInt(xValue[i+1]/2)), parseFloat(y_2)]);
y_3 = y_3 + parseFloat(bValue[i]);
y_3 = y_3.toFixed(2);
data.push([parseFloat(parseInt(xValue[i+1]/1.5)), parseFloat(y_3)]);
y_4 = y_4 + parseFloat(bValue[i]);
y_4 = y_4.toFixed(2);
data.push([parseFloat(parseInt(xValue[i+1]/1.2)), parseFloat(y_4)]);
y_5 = y_5 + parseFloat(bValue[i]);
y_5 = y_5.toFixed(2);
data.push([parseFloat(xValue[i+1]), parseFloat(y_5)]);
}
else{
// 直线处理
var y_0 = parseFloat((xValue[i]) * kValue[i]) + parseFloat(bValue[i]);
y_0 = y_0.toFixed(2);
data.push([parseFloat(xValue[i]), parseFloat(y_0)]);
var y_1 = parseFloat((xValue[i+1]) * kValue[i]) + parseFloat(bValue[i]);
y_1 = y_1.toFixed(2);
data.push([parseFloat(xValue[i+1]), parseFloat(y_1)]);
}
}
// LineContainer_test
$('#LineContainer_test').highcharts({
title: {
text: ''
},
credits: {
enabled: false
},
xAxis: {
minorTickInterval: 1,
tickInterval: 5,
},
yAxis: {
title: {
text: null
},
tickInterval: 1
},
tooltip: {
headerFormat: '<b>{series.name}</b><br />',
pointFormat: 'x = {point.x}, y = {point.y}'
},
series: [{
name: '标准化得分',
data: data,
type: 'spline',
}]
});
}else{
console.log("there is only line.");
for(i = 0; i < bValue.length; i++){
if((xValue[i+1]) && (xValue[i+1].toLowerCase() == "inf")){
xValue[i+1] = 2 * xValue[i] + 80;
}
// two points to form a line.
// console.log("==================");
var y_0 = parseFloat((xValue[i]) * kValue[i]) + parseFloat(bValue[i]);
y_0 = y_0.toFixed(2);
data.push([parseFloat(xValue[i]), parseFloat(y_0)]);
// console.log(parseFloat(xValue[i]));
// console.log(parseFloat(y_0));
var y_1 = parseFloat((xValue[i+1]) * kValue[i]) + parseFloat(bValue[i]);
y_1 = y_1.toFixed(2);
data.push([parseFloat(xValue[i+1]), parseFloat(y_1)]);
// console.log(parseFloat(xValue[i+1]));
// console.log(parseFloat(y_1));
}
$('#LineContainer_test').highcharts({
chart: {
height:263,
backgroundColor: '#f0f0f0'
},
credits: {
enabled: false
},
title: {
text: ' '
},
xAxis: {
minorTickInterval: 1,
tickInterval: 5,
},
yAxis: {
type: 'linear',
minorTickInterval: 0.1,
tickInterval: 5,
title:{
enabled: false,
text: 'y值'
}
},
legend: {
layout: 'vertical',
backgroundColor: '#f0f0f0',
floating: true,
align: 'left',
x: 30,
verticalAlign: 'top',
y: 30
},
tooltip: {
headerFormat: '<b>{series.name}</b><br />',
pointFormat: 'x = {point.x}, y = {point.y}'
},
series: [{
data,
pointStart: 0,
name: '指标公式图',
}]
});
}
});
五、本文效果展示:
1、制定直线方程式:
2、制定多项式方程式:
3、制定直线方程式 + 多项式方程式:
六、源码分享:
1、index.html
{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=IE9,Chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>
定制指标公式
</title>
<link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/Water.css' %}">
</head>
<body>
<div style="text-align: center;">
<label style="font-size: 18px;">公式的定制:直线方程式 + 多项式方程式</label>
</div>
<div class="row" style="padding-top: 30px;">
<div class="col-md-3"></div>
<!-- 定制指标公式 - 相关信息 -->
<div class="col-md-6">
<div class="panel" style="border: 1px solid #e0e0e0;">
<div class="panel-heading">
<label>制定直线方程式和多项式方程式:</label>
<button id="btn_save" class="btn btn-sm btn-success pull-right" type="button">保存设置</button>
</div>
<div style="text-align: center;padding: 5px 0;background-color: #f0f0f0;">
<input id="newBtn" class="btn btn-sm btn-success" type="button" value="添加直线方程">
<a class="btn btn-sm btn-success" data-toggle="modal" data-target="#Add_curve" role="button">添加多项式方程</a>
</div>
<div class="panel-body">
<table id="IndicatorFormula_modify">
<tr></tr>
</table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="panel" style="border: 1px solid #e0e0e0;">
<div class="panel-heading">
<label>当前指标公式信息:</label>
</div>
<div class="panel-body">
<table class="" style="margin: 5px;">
{% for expression in expressions_list %}
<tr style="font-size: 15px;">
<td><span class="glyphicon glyphicon-hand-right"></span> 区间:[{{ expression.x_start }} , {{ expression.x_end }}]</td>
<td width="5%"></td>
<td>表达式:{{expression.expression}}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<div id="LineContainer_test"></div>
</div>
</div>
<br>
<div class="modal fade" id="Add_curve">
<div class="modal-dialog" style="width:300px;">
<div class="modal-content">
<div class="modal-body">
请输入曲线最高次指数:<input type="text" id="curve_num" style="width:60px;">
<input id="newBtn_curve" class="btn-sm btn-success pull-right" type="button" value="确定">
</div>
</div>
</div>
</div>
<script type="text/javascript" src = "{% static 'js/jquery.min.js' %}" ></script>
<script type="text/javascript" src = "{% static 'js/bootstrap.js' %}" ></script>
<script type="text/javascript" src = "{% static 'js/highcharts.js' %}" ></script>
<script>
function post(URL, PARAMS) {
var temp = document.createElement("form");
temp.action = URL.toString();
temp.method = "post";
temp.style.display = "none";
for (var x in PARAMS) {
var opt = document.createElement("textarea");
opt.name = x;
opt.value = PARAMS[x];
console.log(opt.value);
temp.appendChild(opt);
}
// alert("xx");
document.body.appendChild(temp);
temp.submit();
return temp;
}
function IsNum(s)
{
if(s.toLowerCase() == "inf"){
return true;
}
if (s!=null && s!="")
{
return !isNaN(s);
}
return false;
}
// delete line input
function getDel(select_remove){
$(select_remove).parent().parent().remove();
}
// delete curve input
function getDelCurve(delete_num){
delete_num = parseInt(delete_num);
for(var i = 0; i <= delete_num; i++){
delete_id = "curveID_" + i.toString();
console.log(delete_id);
$("tr[id=" + delete_id + "]").remove();
}
}
$(document).ready(function() {
var count = 0;
var line_num = [];
// line
$("#newBtn").bind("click", function(){
// console.log(this.id)
// console.log(count);
var seq = count.toString();
// console.log(seq);
line_num.push(1);
$str = ""
$str += "<tr>";
$str += "<td>";
$str += "起始点: x_0 = <input id=\"x" + seq + "\" name=\"input_x\" type=\"text\" style=\"width: 40px;\"> ";
$str += "截止点: x_1 = <input id=\"x" + seq + "\" name=\"input_x_1\" type=\"text\" style=\"width: 40px;\"> ";
$str += "<td>";
$str += "直线斜率 = <input id=\"k" + seq + "\" name=\"input_k\" type=\"text\" style=\"width: 40px;\">";
$str += "</td>";
$str += "<td>";
$str += "直线偏置 = <input id=\"y" + seq + "\" name=\"input_b\" type=\"text\" style=\"width: 40px;\">";
$str += "</td>";
$str += "<td width=\"10%\"><input id=\"deleteLine_" + count + "\" class = \"btn btn-sm btn-success\" type=\"button\" value=\"删除表达式\" onClick='getDel(this)'></td>";
$str += "</tr>";
$("#IndicatorFormula_modify").append($str);
count++;
});
// curve
$("#newBtn_curve").bind("click", function(){
var curve_num = $("#curve_num").val();
// console.log(curve_num);
if(curve_num > 0){
$('#Add_curve').modal('hide');
line_num.push(parseInt(curve_num));
var count_curve = 0;
var seq = count.toString();
var curve_length = curve_num;
var seq_curve = count_curve.toString();
$str = ""
$str += "<tr style='color:#960000;' id='curveID_0'>";
$str += "<td>";
$str += "起始点: x_0 = <input id=\"x" + seq + "\" name=\"input_x\" type=\"text\" style=\"width: 40px;\"> ";
$str += "截止点: x_1 = <input id=\"x" + seq + "\" name=\"input_x_1\" type=\"text\" style=\"width: 40px;\"> ";
$str += "<td>";
$str += "曲线系数 = <input id= coefficient_" + curve_length.toString() + " name=\"input_k\"" + " type=\"text\" style=\"width: 40px;\"> " + "x^" + curve_length;
$str += "</td><td></td>";
$str += "<td width=\"10%\"><input id=\"deleteLine_" + count_curve + "\" class = \"btn btn-sm btn-success\" type=\"button\" value=\"删除表达式\" onClick='getDelCurve(" + curve_num.toString() + ")'></td>";
$str += "</tr>";
$("#IndicatorFormula_modify").append($str);
count_curve++;
for(var i = count_curve; i <= curve_length; i++){
var seq_curve = i.toString();
var curveID = 'curveID_' + i.toString();
var curve_tr_content = "style = 'color:#960000;' id = " + curveID;
$str = ""
$str += "<tr " + curve_tr_content + ">";
$str += "<td></td>";
$str += "<td>";
if ((curve_length - i) != 0)
{
$str += "曲线系数 = <input id= coefficient_" + (curve_length - i).toString() + " name=\"input_k\"" + " type=\"text\" style=\"width: 40px;\"> " + "x^" + (curve_length - i);
}
else
{
$str += "曲线偏置 = <input id=\"y" + seq + "\" name=\"input_b\" type=\"text\" style=\"width: 40px;\">";
}
$str += "</td><td></td>";
$str += "<td></td>";
$str += "</tr>";
$("#IndicatorFormula_modify").append($str);
}
}else{
alert("请输入大于0的数值!");
}
count++;
});
$("#btn_save").bind("click", function(){
var x = "";
var b = "";
var k = "";
var x_list = document.getElementsByName("input_x");
var b_list = document.getElementsByName("input_b"); // y 表示偏置b
var k_list = document.getElementsByName("input_k");
var x_1_list = document.getElementsByName("input_x_1");
// console.log(x_list);
// console.log(b_list);
// console.log(k_list);
// console.log(x_1_list);
for (var i = 0; i < x_list.length; ++i)
{
// alert("ss");
if(!IsNum(x_list[i].value) || !IsNum(b_list[i].value) || !IsNum(x_1_list[i].value))
{
alert("请确认输入都为数字或者[Inf]表示无限大.");
return;
}
if(parseFloat(x_list[i]) > 1 || parseFloat(x_list[i]) < 0)
{
alert(x_list[i].id + "输入不合法。");
return;
}
if(parseFloat(b_list[i]) > 100 || parseFloat(x_list[i]) < 0)
{
alert(b_list[i].id + "输入不合法。");
return;
}
}
for (var i = 0; i < k_list.length; ++i)
{
if(!IsNum(k_list[i].value))
{
alert("请确认输入都为数字");
return;
}
}
var k_seq = 0;
for (var i = 0; i < x_list.length; ++i)
{
x = x + x_list[i].value + "_";
b = b + b_list[i].value + "_";
var sub_k = "";
for (var j = 0; j < line_num[i]; ++j)
{
sub_k = sub_k + k_list[k_seq + j].value + ";";
// console.log("k_seq: " + k_seq + j);
}
k_seq = k_seq + line_num[i];
sub_k = sub_k.substring(0, sub_k.length - 1);
k = k + sub_k + "_";
}
x = x + x_1_list[x_1_list.length - 1].value
// console.log(b_list[b_list.length - 1].value)
// console.log(x_list[x_list.length - 1].value)
// console.log()
// console.log(b);
b = b.substring(0, b.length - 1);
k = k.substring(0, k.length - 1);
// console.log(x);
// console.log(b);
// console.log(k);
// console.log(typeof(k));
post("", { x_Value: x, b_Value: b, k_Value: k });
});
});
</script>
<script>
{% if x_Value and k_Value and b_Value %}
$(function () {
var x = "{{ x_Value }}", b = "{{ b_Value }}", k = "{{ k_Value }}";
var i;
var xValue = x.split("_");
var bValue = b.split("_");
var kValue = k.split("_"); // 未知量的系数
// console.log(xValue);
// console.log(bValue);
// console.log(kValue);
// console.log(parseFloat(xValue[2] * kValue[1]) + parseFloat(bValue[1]));
// console.log();
var data = new Array();
// 判断线段的类型:
// 1、只有直线
// 2、有直线,又有曲线
var has_curve = false;
// 如果包含子串,则返回大于等于0的索引,否则返回-1
for(var k = 0; k < kValue.length; k++){
if(kValue[k].indexOf(";")>=0){
has_curve = true;
break;
}
}
// has_curve == true 表示包含曲线
if(has_curve == true){
console.log("there is curve.");
for(i = 0; i < bValue.length; i++){
if((xValue[i+1]) && (xValue[i+1].toLowerCase() == "inf")){
xValue[i+1] = 2 * xValue[i] + 40;
}
if(kValue[i].indexOf(";")>=0){
// 曲线处理
coefficient_list = kValue[i].split(";");
var y_0 = 0, y_1 = 0, y_2 = 0, y_3 = 0, y_4 = 0, y_5 = 0;
for(var k = 0; k < coefficient_list.length; k++){
y_0 = y_0 + parseFloat(coefficient_list[k]) * Math.pow(xValue[i], parseInt( coefficient_list.length - k));
y_1 = y_1 + parseFloat(coefficient_list[k]) * Math.pow(parseInt(xValue[i+1]/4), parseInt( coefficient_list.length - k));
y_2 = y_2 + parseFloat(coefficient_list[k]) * Math.pow(parseInt(xValue[i+1]/2), parseInt( coefficient_list.length - k));
y_3 = y_3 + parseFloat(coefficient_list[k]) * Math.pow(parseInt(xValue[i+1]/1.5), parseInt( coefficient_list.length - k));
y_4 = y_4 + parseFloat(coefficient_list[k]) * Math.pow(parseInt(xValue[i+1]/1.2), parseInt( coefficient_list.length - k));
y_5 = y_5 + parseFloat(coefficient_list[k]) * Math.pow(xValue[i+1], parseInt( coefficient_list.length - k));
}
y_0 = y_0 + parseFloat(bValue[i]);
y_0 = y_0.toFixed(2);
data.push([parseFloat(xValue[i]), parseFloat(y_0)]);
y_1 = y_1 + parseFloat(bValue[i]);
y_1 = y_1.toFixed(2);
data.push([parseFloat(parseInt(xValue[i+1]/4)), parseFloat(y_1)]);
y_2 = y_2 + parseFloat(bValue[i]);
y_2 = y_2.toFixed(2);
data.push([parseFloat(parseInt(xValue[i+1]/2)), parseFloat(y_2)]);
y_3 = y_3 + parseFloat(bValue[i]);
y_3 = y_3.toFixed(2);
data.push([parseFloat(parseInt(xValue[i+1]/1.5)), parseFloat(y_3)]);
y_4 = y_4 + parseFloat(bValue[i]);
y_4 = y_4.toFixed(2);
data.push([parseFloat(parseInt(xValue[i+1]/1.2)), parseFloat(y_4)]);
y_5 = y_5 + parseFloat(bValue[i]);
y_5 = y_5.toFixed(2);
data.push([parseFloat(xValue[i+1]), parseFloat(y_5)]);
}
else{
// 直线处理
var y_0 = parseFloat((xValue[i]) * kValue[i]) + parseFloat(bValue[i]);
y_0 = y_0.toFixed(2);
data.push([parseFloat(xValue[i]), parseFloat(y_0)]);
var y_1 = parseFloat((xValue[i+1]) * kValue[i]) + parseFloat(bValue[i]);
y_1 = y_1.toFixed(2);
data.push([parseFloat(xValue[i+1]), parseFloat(y_1)]);
}
}
// LineContainer_test
$('#LineContainer_test').highcharts({
title: {
text: ''
},
credits: {
enabled: false
},
xAxis: {
minorTickInterval: 1,
tickInterval: 5,
},
yAxis: {
title: {
text: null
},
tickInterval: 1
},
tooltip: {
headerFormat: '<b>{series.name}</b><br />',
pointFormat: 'x = {point.x}, y = {point.y}'
},
series: [{
name: '标准化得分',
data: data,
type: 'spline',
}]
});
}else{
console.log("there is only line.");
for(i = 0; i < bValue.length; i++){
if((xValue[i+1]) && (xValue[i+1].toLowerCase() == "inf")){
xValue[i+1] = 2 * xValue[i] + 80;
}
// two points to form a line.
// console.log("==================");
var y_0 = parseFloat((xValue[i]) * kValue[i]) + parseFloat(bValue[i]);
y_0 = y_0.toFixed(2);
data.push([parseFloat(xValue[i]), parseFloat(y_0)]);
// console.log(parseFloat(xValue[i]));
// console.log(parseFloat(y_0));
var y_1 = parseFloat((xValue[i+1]) * kValue[i]) + parseFloat(bValue[i]);
y_1 = y_1.toFixed(2);
data.push([parseFloat(xValue[i+1]), parseFloat(y_1)]);
// console.log(parseFloat(xValue[i+1]));
// console.log(parseFloat(y_1));
}
$('#LineContainer_test').highcharts({
chart: {
height:263,
backgroundColor: '#f0f0f0'
},
credits: {
enabled: false
},
title: {
text: ' '
},
xAxis: {
minorTickInterval: 1,
tickInterval: 5,
},
yAxis: {
type: 'linear',
minorTickInterval: 0.1,
tickInterval: 5,
title:{
enabled: false,
text: 'y值'
}
},
legend: {
layout: 'vertical',
backgroundColor: '#f0f0f0',
floating: true,
align: 'left',
x: 30,
verticalAlign: 'top',
y: 30
},
tooltip: {
headerFormat: '<b>{series.name}</b><br />',
pointFormat: 'x = {point.x}, y = {point.y}'
},
series: [{
data,
pointStart: 0,
name: '指标公式图',
}]
});
}
});
{% endif %}
</script>
</body>
</html>
2、view.py
# -*- coding: utf-8 -*-
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
from app_SettingFormula.models import *
from django.views.decorators.csrf import csrf_exempt
# Create your views here.
@csrf_exempt
def index(request):
context = {}
if request.method == 'POST':
x_Value = request.POST.get('x_Value')
k_Value = request.POST.get('k_Value')
b_Value = request.POST.get('b_Value')
# Formula(XValue = x_Value, KValue = k_Value, BValue = b_Value).save()
print x_Value
print b_Value
print k_Value
context['x_Value'] = x_Value
context['k_Value'] = k_Value
context['b_Value'] = b_Value
if x_Value and k_Value and b_Value:
context['expressions_list'] = get_IndicatorFormula_expression(x_Value, k_Value, b_Value)
else:
print("x_Value or k_Value or b_Value exist null")
return render(request, 'index.html', context)
def get_IndicatorFormula_expression(x_Value, k_Value, b_Value):
X_list = x_Value.strip().split('_')
K_list = k_Value.strip().split('_')
B_list = b_Value.strip().split('_')
# print "---"
# print "X_list",X_list
# print "B_list",B_list
i = 0
expressions_list = []
# line and curve
# find()函数找不到时返回为-1。
for k in K_list:
# print k
expression = ""
if k.find(";") == -1:
print "=== line ==="
if K_list[i]=='0':
expression = "y = " + B_list[i]
elif K_list[i]=='1':
if (B_list[i]).find("-") == -1:
if B_list[i]==0:
expression = "y = x"
else:
expression = "y = x + " + B_list[i]
else:
expression = "y = x " + B_list[i]
else:
if (B_list[i]).find("-") == -1:
if B_list[i]==0:
expression = "y = " + K_list[i] + "x"
else:
expression = "y = " + K_list[i] + "x + " + B_list[i]
else:
expression = "y = " + K_list[i] + "x " + B_list[i]
else:
print "=== curve ==="
curve_list = k.strip().split(';')
j = 0
curve_len = len(curve_list)
print "curve_len = ",curve_len
for j in range(curve_len):
# exponent: 指数
# curve_list[j]: 系数
exponent = str(curve_len - j)
print "exponent = ",exponent
if curve_list[j] == "0":
expression = expression
elif curve_list[j] == "1":
if exponent == "1":
expression = expression + " + x"
else:
expression = expression + " + " + "x^" + exponent
elif curve_list[j] == "-1":
if exponent == "1":
expression = expression + " - x "
else:
expression = expression + " - x^" + exponent
# coefficient > 0 except 1
elif (curve_list[j]).find("-") == -1:
if exponent == "1":
# the begin position.
if j == 0:
expression = curve_list[j] + "x"
else:
expression = expression + " + " + curve_list[j] + "x"
else:
if j == 0:
expression = curve_list[j] + "x^" + exponent
else:
expression = expression + " + " + curve_list[j] + "x^" + exponent
# coefficient < 0 except -1
else:
if exponent == "1":
expression = expression + curve_list[j] + "x"
else:
expression = expression + curve_list[j] + "x^" + exponent
if (B_list[i]).find("-") == -1:
expression = "y = " + expression + " + " + B_list[i]
else:
expression = "y = " + expression + B_list[i]
expressions_list.append(display_expression(X_list[i], X_list[i+1], expression))
i = i + 1
return expressions_list
### class ###
class display_expression:
def __init__(self, x_start, x_end, expression):
self.x_start = x_start
self.x_end = x_end
self.expression = expression
完整工程源码,请到这里下载:
1、【CSDN博客资源】http://download.csdn.net/detail/houchaoqun_xmu/9834798
注:Github的代码最新,而且有进展会持续更新。
七、总结:
本文基于【django + js + highcharts】实现了普通直线方程式和曲线方程式的制定,包括:
1、用户根据自身需求添加普通直线方程式或曲线方程式,弹出对应的输入框。
2、用户根据自身需求可删除上一步添加的输入框。
3、设计相应的数据结构,根据用户所输入的方程式信息进行解析,后台使用python语言进行解析,返回对应的区间和方程式。
4、基于highcharts绘制方程式对应的坐标系,前端使用js进行解析,根据方程式的类型(普通直线或者多项式方程式)做相应的处理。
本文目前只实现了普通直线方程式和曲线方程式的处理,尚未扩展到一般的曲线,如今后有这方面的需求,实现后会再补充整理分享给大家。
值得一提的是,本文的方法目前仍存在如下缺陷:
1、对highcharts插件不够熟悉,绘制多项式曲线的时候,效果不是很好,本文采用的方法是使用【type: 'spline'】,在相应的区间内绘制多个点,构成对应的曲线,如下图所示,多项式方程式【y = 2x^3 + 0】在区间[0, 100]内,绘制了20个点构成了对应的曲线。
上图的效果还不错,但是,细心的同学可以注意到区间 [0, 25] 中,由于没有绘制多个点,导致不能更好的逼近曲线,代码中有详细解释这个问题。此外,这个方法在“仅有多项式方程式”的情况表现良好,但是,在混合方程式中的表现就不尽人意,比如“直线+多项式+直线”,如下图所示,多项式曲线部分的效果不是很好,可能是因为图不够大,但这不是能够接受的理由,后续有机会再继续完善。
2、按照如下过程会有bug:
先添加直线方程的输入框 --> 再添加曲线方程的输入框(如:最高指数幂为3) --> 删除直线方程的输入框 --> 输入曲线的相关参数(如:y = -2x^3 + x^2 -1),此时会出现问题:出来的是一条直线,因为一开始删除的直线方程输入框的原因,导致系统把当前的曲线识别为直线的输入框。
=== 解决bug: