用500行纯前端代码在浏览器中营造二个Tableau

2018/05/16 · 基本功技术 ·
BI,
Tableau,
数码可视化

原著出处: naughty   

在Gartner最新的对商务智能软件的标准深入分析报告中,Tableau持续领跑。Microsoft因为PowerBI展现卓越也处在领导者象限。而现在的公司主像SAP,SAS,IBM,MicroStrategy等日益被延长了差别。

银河国际平台官方网站 1

Tableau因为其灵活,优异的数量表现已经变成BI领域里的确的领头羊。而其数据驱动的可视化和核心情想是来源于于Leland
Wilkinson的The
Grammar Of
Graphics ,
大同小异面前遇到该寻思潜濡默化的还会有PRADO的图形库ggplot。

银河国际平台官方网站 2

在数量可视化开源领域里,我们对百度支付的echarts可谓耳闻则诵,echarts经过多年的上进,其功效确实特别强劲,可用优异来描写。不过蚂蚁金服开源的依照The
Grammar Of
Graphics
的语法驱动的可视化库G2,令人气象一新。那大家就看看怎么样行使G2和500行左右的纯前端代码来落到实处多个的好像Tableau的数目剖析作用。

  • 亲自去做参见 
  • 代码参见 https://gist.github.com/gangtao/e053cf9722b64ef8544afa371c2daaee 

 

现行整理下发出来,希望我们一齐学习啊

数据加载

率先步是加载数据:

银河国际平台官方网站 3

数据加载首要选拔了四个库:

  • axios  基于Promise的HTTP客户端
  • alasql 基于JS的开源SQL数据库
  • jquery datatable JQuery的数码表格插件

数据经过作者存放在GitHub中的csv格式的文本,以REST央求的办法来加载。下边包车型地铁代码把Axios的Promise产生async/wait格局。

// Ajax async request const request = { get: url => { return new
Promise((resolve, reject) => { axios .get(url) .then(response => {
resolve({ data: response.data }); }) .catch(error => { resolve({
data: error }); }); }); } };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Ajax async request
const request = {
  get: url => {
    return new Promise((resolve, reject) => {
      axios
        .get(url)
        .then(response => {
          resolve({ data: response.data });
        })
        .catch(error => {
          resolve({ data: error });
        });
    });
  }
};

卷入好后,大家就能够用request.get()方法发送REST乞求,获取csv文件。

let csv = await request.get(url);

1
let csv = await request.get(url);

这一步恐怕会遇见跨域乞求的主题材料,github上的公文扶助跨域。

把多少存款和储蓄在三个SQL数据库中,那样做的益处是为着下一步做多少筹算的时候,能够方便的运用SQL来拓宽询问和分析。

JavaScript

class SqlTable { constructor(data) { this.data = data; } async
query(sql) { // following line of code does not run in full page view
due to security concern. // const query_str =
sql.replace(/(?<=FROM\s+)\w+/, “CSV(?)”); const query_str =
sql.replace(“table”, “CSV(?)”); return await alasql.promise(query_str,
[this.data]); } }

1
2
3
4
5
6
7
8
9
10
11
12
class SqlTable {
  constructor(data) {
    this.data = data;
  }
 
  async query(sql) {
    // following line of code does not run in full page view due to security concern.
    // const query_str = sql.replace(/(?<=FROM\s+)\w+/, "CSV(?)");
    const query_str = sql.replace("table", "CSV(?)");
    return await alasql.promise(query_str, [this.data]);
  }
}

SqlTable是一个对数据表的卷入,把csv数据存在SQL数据库表中,提供三个query()方法。这里要做的是把SQL查询个从 “SELECT
* FROM table” 变成 “SELECT * FROM CSV(?)”
表示查询参数是CSV数据。因为codepen的安全性限制,运营前向搜索的replace语句(这里的regex表示把前面是“FROM
”词的轮换为CSV(?)的)在full page
view下是不可能实行的,所以本身用了贰个更简短的举例,客商的表名就是table,那样做有为数十分多主题素材,大家假使在codepen之外的遭逢,能够用注释掉的代码。

然后把”SELECT * FROM table”的询问结果(JSON Array)用datatable来展现。

function sanitizeData(jsonArray) { let newKey;
jsonArray.forEach(function(item) { for (key in item) { newKey =
key.replace(/s/g, “”).replace(/./g, “”); if (key != newKey) {
item[newKey] = item[key]; delete item[key]; } } }); return
jsonArray; } function displayData(tableId, data) { // tricky to clone
array let display_data = JSON.parse(JSON.stringify(data));
display_data = sanitizeData(display_data); let columns = []; for
(let item in display_data[0]) { columns.push({ data: item, title:
item }); } $(“#” + tableId).DataTable({ data: display_data, columns:
columns, destroy: true }); }

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
27
28
function sanitizeData(jsonArray) {
  let newKey;
  jsonArray.forEach(function(item) {
    for (key in item) {
      newKey = key.replace(/s/g, "").replace(/./g, "");
      if (key != newKey) {
        item[newKey] = item[key];
        delete item[key];
      }
    }
  });
  return jsonArray;
}
 
function displayData(tableId, data) {
  // tricky to clone array
  let display_data = JSON.parse(JSON.stringify(data));
  display_data = sanitizeData(display_data);
  let columns = [];
  for (let item in display_data[0]) {
    columns.push({ data: item, title: item });
  }
  $("#" + tableId).DataTable({
    data: display_data,
    columns: columns,
    destroy: true
  });
}

这一步有两点要留意:

  1. 数量中,借使列的名字中有隐含点,空格等字符,比方Iris数据汇总的Sepal.Length,datatable是力无法及平常显示的,这里要调用sanitizeData()方法把列名,也正是JsonArray中Json对象的品质名中的点和空格去掉。
  2. sanitizeData()方法会改换输入对象,所以在流传以前做了二个纵深拷贝,这里运用JSON的stringfy和parse方法能够对JSON包容的对象有效的正片。

此处要专心,Iris数据汇总在datatable中的列名都不展现点,但实际数据并从未变动。

 

先看效果图:
银河国际平台官方网站 4
用了jquery.image-maps.js这么些插件 下载地址
//www.jb51.net/jiaoben/57930.html
原理是:
因而拖动计算出方今热区可活动模块的left top right bottom
对应area的 coords 属性集成上边的岗位,就足以兑现热区了。
对应的模块代码是:

数据希图

多少加载达成,大家来到第二步的多少企图阶段。数据希图是数码准确项目最花时间的一步,常常须要对数据开展大气的涤荡,变形,收取等专门的学问,使得数据变得可用。

在这一步大家做了两件事:

一是显得数据的一个摘要,让我们起初了然多少的大约,为进一步的数码变形和管理做好盘算。

其一是Iris数据集的摘要:

银河国际平台官方网站 5

function isString(o) { return typeof o == “string” || (typeof o ==
“object” && o.constructor === String); } function summaryData(data) {
let summary = {}; summary.count = data.length; summary.fields = [];
for (let p in data[0]) { let field = {}; field.name = p; if (
isString(data[0][p]) ) { field.type = “string”; } else { field.type
= “number”; } summary.fields.push(field); } for (let f of
summary.fields) { if ( f.type == “number” ) { f.max = d3.max(data, x
=> x[f.name]); f.min = d3.min(data, x => x[f.name]); f.mean =
d3.mean(data, x => x[f.name]); f.median = d3.median(data, x =>
x[f.name]); f.deviation = d3.deviation(data, x => x[f.name]); }
else { f.values = Array.from(new Set(data.map(x => x[f.name]))); }
} return summary; }

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
27
28
29
30
31
32
function isString(o) {
    return typeof o == "string" || (typeof o == "object" && o.constructor === String);
}
 
function summaryData(data) {
  let summary = {};
  summary.count = data.length;
  summary.fields = [];
  for (let p in data[0]) {
    let field = {};
    field.name = p;
    if ( isString(data[0][p]) ) {
      field.type = "string";
    } else {
      field.type = "number";
    }
    summary.fields.push(field);
  }
  
  for (let f of summary.fields) {
      if ( f.type == "number" ) {
        f.max = d3.max(data, x => x[f.name]);
        f.min = d3.min(data, x => x[f.name]);
        f.mean = d3.mean(data, x => x[f.name]);
        f.median = d3.median(data, x => x[f.name]);
        f.deviation = d3.deviation(data, x => x[f.name]);
      } else {
        f.values = Array.from(new Set(data.map(x => x[f.name])));
      }
  }
  return summary;
}

此处大家运用多少的项目判定出每种字段是数值型依然字符型。对于字符型的字段,大家使用JS6的Set来获取全数的Unique数据。对于数值型,大家应用d3的max,min,mean,median,deviation方法计算出相应的最大值,最小值,平平均数量,中位数和谬误。

另两个就是运用SQL查询来对数据开展越来越加工。

银河国际平台官方网站 6

上海教室的例子中大家选择限制条件获得一个Iris数据的子集。

除此以外G2还提供了Dataset的功能:

  • 源数据的解析,将csv, dsv,geojson
    转成标准的JSON,查看Connector
  • 加工数据,包涵 filter,map,fold(补多少)
    等操作,查看 Transform
  • 总结函数,汇总总结、百分比、封箱
    等计算函数,查看 Transform
  • 独特数据管理,满含 地理数据、矩形树图、桑基图、文字云
    的多少处理,查看 Transform

数据管理是一个非常的大的话题,大家的对象是利用尽大概少的代码完毕八个数额剖析的工具,所以这一步仅仅是采纳alasql提供的SQL查询来管理数据。

 

复制代码 代码如下:

数据显示

数码管理好后正是大家的主题内容,数据呈现了。

银河国际平台官方网站 7

这一步关键是应用select2提供的选项控件营造图形语法来驱动数据展现。如上海体育场合所示,对应的G2代码图形语法为:

g2chart.facet(‘rect’, { fields: [ ‘Admit’, ‘Dept’ ], eachView(view) {
view.interval().position(‘Gender*Freq’).color(‘Gender’).label(‘Freq’);
} });

1
2
3
4
5
6
g2chart.facet(‘rect’, {
  fields: [ ‘Admit’, ‘Dept’ ],
  eachView(view) {
    view.interval().position(‘Gender*Freq’).color(‘Gender’).label(‘Freq’);
  }
});

图表语法主要满含以下多少个非常重要的因素:

 

<!–模块体现 begin–>
<div class=”modeShow”>
<div id=”debug”></div>
<div class=”imgMap mapBox”>
<img src=”../images/hot_images_map.png” name=”test” border=”0″
usemap=”#Map1″ width=”980″ height=”450″ ref=’imageMaps’ />
<map name=”Map1″>
<area shape=”rect” coords=”300,80,500,150″ href=”mall.10010.com”
/>
</map>
</div>
</div>
<!–模块呈现 end—>

几何标识 吉优metry

几何标志概念了利用什么的几何图形来表征数据。G2今后补助如下那几个几何标识:

geom 类型 描述
point 点,用于绘制各种点图。
path 路径,无序的点连接而成的一条线,常用于路径图的绘制。
line 线,点按照 x 轴连接成一条线,构成线图。
area 填充线图跟坐标系之间构成区域图,也可以指定上下范围。
interval 使用矩形或者弧形,用面积来表示大小关系的图形,一般构成柱状图、饼图等图表。
polygon 多边形,可以用于构建色块图、地图等图表类型。
edge 两个点之间的链接,用于构建树图和关系图中的边、流程图中的连接线。
schema 自定义图形,用于构建箱型图(或者称箱须图)、蜡烛图(或者称 K 线图、股票图)等图表。
heatmap 用于热力图的绘制。

此间要留神,intervalstack是法定帮忙的,但是文书档案未有提到,在翻阅G2的API文书档案的时候,作者也发觉文书档案讲的不是很清楚,有多数地点尚未讲驾驭哪些运用API。这也是开源软件值得革新的地方。

 

js代码:

图表属性 Attributes

图片属性对应视觉编码中的区别因素,大家能够参照作者的另一博客 数码可视化中的视觉属性 。

图表属性首要有以下三种。

  1. position:地点,二维坐标系内映射至 x 轴、y 轴;
  2. color:颜色,饱含了色彩、饱和度和亮度;
  3. size:大小,分歧的几何标志对大小的定义有反差;
  4. shape:形状,几何标识的形象决定了有些具体图表类型的表现情势,譬喻点图,能够动用圆点、三角形、图片表示;线图能够有折线、曲线、点线等表现方式;
  5. opacity:发光度,图形的折射率,那几个天性从某种意义上的话可以运用颜色代表,需求运用
    ‘rgba’ 的花样,所以在 G2 中大家独立出来。

在营造语法的时候,大家把图片属性绑定二个要么多个数据字段。

 

复制代码 代码如下:

坐标系 Coordinates

坐标系是将三种职位标度结合在一道构成的
2 维定位系统,描述了多少是怎么样映射到图片所在的平面。

G2提供了以下两种坐标系:

coordType 说明
rect 直角坐标系,目前仅支持二维,由 x, y 两个互相垂直的坐标轴构成。
polar 极坐标系,由角度和半径 2 个维度构成。
theta 一种特殊的极坐标系,半径长度固定,仅仅将数据映射到角度,常用于饼图的绘制。
helix 螺旋坐标系,基于阿基米德螺旋线。

 

(function($) {
jQuery.fn.imageMaps = function(setting) {
var $container = this;
if ($container.length == 0) return false;
$container.each(function(){
var container = $(this);
var $images = container.find(‘img[ref=imageMaps]’);
$images.wrap(‘<div class=”image-maps-conrainer
image-maps-conrainerEdit”
style=”position:relative;”></div>’).css(‘border’,’1px solid
#ccc’);
$images.each(function(){
var _img_conrainer = $(this).parent();
_img_conrainer.append(‘<div class=”button-conrainer”><a
href=”javascript:void(0)” class=”addHot”>增添火爆</a><a
href=”javascript:void(0)” class=”addImg”>上传图片</a><a
class=”delSub delMode”
href=”javascript:void(0)”>删除×</a></div>’).append(‘<div
class=”link-conrainer”><ul></ul><div
class=”clr”></div></div><div
class=”clr”></div><span
class=”numFloor”>模块-1</span>’).append($.browser.msie ?
$(‘<div class=”position-conrainer”
style=”position:absolute”></div>’).css({
background:’#fff’,
opacity:0
}) : ‘<div class=”position-conrainer”
style=”position:absolute”></div>’);
var _img_offset = $(this).offset();
var _img_conrainer_offset = _img_conrainer.offset();
_img_conrainer.find(‘.position-conrainer’).css({
top: _img_offset.top – _img_conrainer_offset.top,
left: _img_offset.left – _img_conrainer_offset.left,
width:$(this).width(),
height:$(this).height(),
border:’1px solid transparent’
});
var map_name = $(this).attr(‘usemap’).replace(‘#’,”);
if(map_name !=”){
var index = 1;
var _link_conrainer = _img_conrainer.find(‘.link-conrainer ul’);
var _position_conrainer =
_img_conrainer.find(‘.position-conrainer’);
var image_param = $(this).attr(‘name’) == ” ? ” : ‘[‘+
$(this).attr(‘name’) + ‘]’;
container.find(‘map[name=’+map_name+’]’).find(‘area[shape=rect]’).each(function(){
var coords = $(this).attr(‘coords’);
$(this).attr(‘ref’,”1″);
_link_conrainer.append(‘<li ref=”‘+index+'”
class=”map-link”><span
class=”link-number-text”>热点’+index+'</span>: <input
type=”text” size=”60″ name=”link’+index+'” class=”linkHref”
value=”‘+$(this).attr(‘href’)+'” /><input type=”hidden”
class=”rect-value” name=”rect’+index+'” value=”‘+coords+'”
/></li>’);
coords = coords.split(‘,’);
_position_conrainer.append(‘<div ref=”‘+index+'”
class=”map-position”
style=”left:’+coords[0]+’px;top:’+coords[1]+’px;width:’+(coords[2]-coords[0])+’px;height:’+(coords[3]-coords[1])+’px;”><div
class=”map-position-bg”></div><span
class=”link-number-text”>热点’+index+'</span><span
class=”delete”>X</span><span
class=”resize”></span></div>’);
index++;
});
}
});
});
$container.find(‘.button-conrainer .addHot’).live(“click”,function(){
var _link_conrainer = $(this).parent().parent().find(‘.link-conrainer
ul’);
var _position_conrainer =
$(this).parent().parent().find(‘.position-conrainer’);
var index = _link_conrainer.find(‘.map-link’).length +1;
银河国际平台官方网站,var _coordsMap = $(this).parent().parent().next(‘map’);
var image =
$(this).parent().parent().find(‘img[ref=imageMaps]’).attr(‘name’);
image = (image == ” ? ” : ‘[‘+ image + ‘]’);
_link_conrainer.append(‘<li ref=”‘+index+'”
class=”map-link”><span
class=”link-number-text”>热点’+index+'</span>: <input
type=”text” size=”60″ name=”link’+index+'” class=”linkHref” value=””
/><input type=”hidden” class=”rect-value” name=”rect’+index+'”
value=”300,80,500,150″ /></li>’);
_position_conrainer.append(‘<div ref=”‘+index+'”
class=”map-position”
style=”left:300px;top:80px;width:200px;height:70px;”><div
class=”map-position-bg”></div><span
class=”link-number-text”>热点’+index+'</span><span
class=”delete”>X</span><span
class=”resize”></span></div>’);
var coords = _link_conrainer.find(‘input[name=rect’+ index
+’]’).val();
_coordsMap.append(‘<area ref=”‘+index+'” href=”” coords=”‘+ coords
+'” shape=”rect”>’);
$(“input[name=’link”+index+”‘]”).val(“请输入本火热对应的链接地址”);
bind_map_event();
define_css();
//添加map热区
});
//修改链接地址
$(“.linkHref”).live(“blur”,function(){
var valueHref = $(this).val();
var thisRef = $(this).parent().attr(‘ref’);
var appArea = $(this).parents(“.link-conrainer”).parent().next(‘map’);
$(this).val(valueHref);
appArea.find(‘area[ref=’+thisRef+’]’).attr(“href”,valueHref);
});
//绑定map事件
function bind_map_event(){
$(‘.position-conrainer .map-position
.map-position-bg’).each(function(){
var map_position_bg = $(this);
var conrainer = $(this).parent().parent();
map_position_bg.unbind(‘mousedown’).mousedown(function(event){
map_position_bg.data(‘mousedown’, true);
map_position_bg.data(‘pageX’, event.pageX);
map_position_bg.data(‘pageY’, event.pageY);
map_position_bg.css(‘cursor’,’move’);
return false;
}).unbind(‘mouseup’).mouseup(function(event){
map_position_bg.data(‘mousedown’, false);
map_position_bg.css(‘cursor’,’default’);
return false;
});
conrainer.mousemove(function(event){
if (!map_position_bg.data(‘mousedown’)) return false;
var dx = event.pageX – map_position_bg.data(‘pageX’);
var dy = event.pageY – map_position_bg.data(‘pageY’);
if ((dx == 0) && (dy == 0)){
return false;
}
var map_position = map_position_bg.parent();
var p = map_position.position();
var left = p.left+dx;
if(left <0) left = 0;
var top = p.top+dy;
if (top < 0) top = 0;
var bottom = top + map_position.height();
if(bottom > conrainer.height()){
top = top-(bottom-conrainer.height());
}
var right = left + map_position.width();
if(right > conrainer.width()){
left = left-(right-conrainer.width());
}
map_position.css({
left:left,
top:top
});
map_position_bg.data(‘pageX’, event.pageX);
map_position_bg.data(‘pageY’, event.pageY);
bottom = top + map_position.height();
right = left + map_position.width();
var newArea = new Array(left,top,right,bottom).join(‘,’);
var mapApp = conrainer.parent().next(‘map’);
mapApp.find(‘area[ref=’+map_position.attr(‘ref’)+’]’).attr(“coords”,newArea);
$(‘.link-conrainer li[ref=’+map_position.attr(‘ref’)+’]
.rect-value’).val(newArea);
return false;
}).mouseup(function(event){
map_position_bg.data(‘mousedown’, false);
map_position_bg.css(‘cursor’,’default’);
return false;
});
});
$(‘.position-conrainer .map-position .resize’).each(function(){
var map_position_resize = $(this);
var conrainer = $(this).parent().parent();
map_position_resize.unbind(‘mousedown’).mousedown(function(event){
map_position_resize.data(‘mousedown’, true);
map_position_resize.data(‘pageX’, event.pageX);
map_position_resize.data(‘pageY’, event.pageY);
return false;
}).unbind(‘mouseup’).mouseup(function(event){
map_position_resize.data(‘mousedown’, false);
return false;
});
//点击撤除拖动
conrainer.unbind(‘click’).click(function(event){
map_position_resize.data(‘mousedown’, false);
return false;
});
conrainer.mousemove(function(event){
if (!map_position_resize.data(‘mousedown’)) return false;
var dx = event.pageX – map_position_resize.data(‘pageX’);
var dy = event.pageY – map_position_resize.data(‘pageY’);
if ((dx == 0) && (dy == 0)){
return false;
}
var map_position = map_position_resize.parent();
var p = map_position.position();
var left = p.left;
var top = p.top;
var height = map_position.height()+dy;
if((top+height) > conrainer.height()){
height = height-((top+height)-conrainer.height());
}
if (height <20) height = 20;
var width = map_position.width()+dx;
if((left+width) > conrainer.width()){
width = width-((left+width)-conrainer.width());
}
if(width <50) width = 50;
map_position.css({
width:width,
height:height
});
map_position_resize.data(‘pageX’, event.pageX);
map_position_resize.data(‘pageY’, event.pageY);
bottom = top + map_position.height();
right = left + map_position.width();
var newArea = new Array(left,top,right,bottom).join(‘,’);
var mapApp = conrainer.parent().next(‘map’);
mapApp.find(‘area[ref=’+map_position.attr(‘ref’)+’]’).attr(“coords”,newArea);
$(‘.link-conrainer li[ref=’+map_position.attr(‘ref’)+’]
.rect-value’).val(newArea);
return false;
}).mouseup(function(event){
map_position_resize.data(‘mousedown’, false);
return false;
});
});
$(‘.position-conrainer .map-position
.delete’).unbind(‘click’).click(function(){
var ref = $(this).parent().attr(‘ref’);
var _link_conrainer =
$(this).parent().parent().parent().find(‘.link-conrainer ul’);
var _coordsMap = $(this).parent().parent().parent().next(‘map’);
var _position_conrainer =
$(this).parent().parent().parent().find(‘.position-conrainer’);
_link_conrainer.find(‘.map-link[ref=’+ref+’]’).remove();
_position_conrainer.find(‘.map-position[ref=’+ref+’]’).remove();
_coordsMap.find(‘area[ref=’+ref+’]’).remove();
var index = 1;
_link_conrainer.find(‘.map-link’).each(function(){
$(this).attr(‘ref’,index).find(‘.link-number-text’).html(‘热点’+index);
index ++;
});
index = 1;
_position_conrainer.find(‘.map-position’).each(function(){
$(this).attr(‘ref’,index).find(‘.link-number-text’).html(‘热点’+index);
index ++;
});
index = 1;
_coordsMap.find(‘area’).each(function(){
$(this).attr(‘ref’,index);
index ++;
});
});
}
bind_map_event();
function define_css(){
//样式定义
$container.find(‘.map-position .resize’).css({
display:’block’,
position:’absolute’,
right:0,
bottom:0,
width:5,
height:5,
cursor:’nw-resize’,
background:’#000′
});
}
define_css();
};
})(jQuery);

分面 Facet

分面,将一份数据根据有个别维度分隔成多少子集,然后创制一个图片的矩阵,将每三个数据子集绘制到图片矩阵的窗格中。分面其实提供了多少个效能:

  1. 依据钦点的维度划分数据集;
  2. 对图纸进行排版。

G2帮忙以下的分面类型:

分面类型 说明
rect 默认类型,指定 2 个维度作为行列,形成图表的矩阵。
list 指定一个维度,可以指定一行有几列,超出自动换行。
circle 指定一个维度,沿着圆分布。
tree 指定多个维度,每个维度作为树的一级,展开多层图表。
mirror 指定一个维度,形成镜像图表。
matrix 指定一个维度,形成矩阵分面。

在意,在自身的代码中,为了简化使用,只协理list和rect,当绑定二个字段的时候用list,绑定多个字段的时候用rect。

除去上边提到的要素,当然还会有为数相当多另外的元素大家并没有包蕴和帮助,举例:坐标轴,图例,提醒等等。

关于图形的语法的更加多内容,请参见这里。

变化图形语法的基本代码如下:

function getFacet(faced, grammarScript) { let facedType = “list”; let
facedScript = “” grammarScript =
grammarScript.replace(chartScriptName,”view”); if ( faced.length == 2 )
{ facedType = “rect”; } let facedFields = faced.join(“‘, ‘”) facedScript
= facedScript + `${ chartScriptName }.facet(‘${ facedType }’, {n`;
facedScript = facedScript + ` fields: [ ‘${ facedFields }’ ],n`;
facedScript = facedScript + ` eachView(view) {n`; facedScript =
facedScript + ` ${ grammarScript };n`; facedScript = facedScript + `
}n`; facedScript = facedScript + `});n`; return facedScript }
function getGrammar() { let grammar = {}, grammarScript =
chartScriptName + “.”; grammar.geom = $(‘#geomSelect’).val();
grammar.coord = $(‘#coordSelect’).val(); grammar.faced =
$(‘#facetSelect’).val(); geom_attributes.map(function(attr){
grammar[attr] = $(‘#’ + attr + “attr”).val(); }); grammarScript =
grammarScript + grammar.geom + “()”;
geom_attributes.map(function(attr){ if (grammar[attr].length > 0)
{ grammarScript = grammarScript + “.” + attr + “(‘” +
grammar[attr].join(“*”) + “‘)”; } }); if (grammar.coord) {
grammarScript = grammarScript + “;n ” + chartScriptName + “.” +
“coord(‘” + grammar.coord + “‘);”; } else { rammarScript = grammarScript

  • “;”; } if ( grammar.faced ) { if ( grammar.faced.length == 1 ||
    grammar.faced.length == 2 ) { grammarScript = getFacet(grammar.faced,
    grammarScript); } } console.log(grammarScript) return grammarScript; }
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function getFacet(faced, grammarScript) {
  let facedType = "list";
  let facedScript = ""
  grammarScript = grammarScript.replace(chartScriptName,"view");
  if ( faced.length == 2 ) {
      facedType = "rect";
  }
  let facedFields = faced.join("’, ‘")
  facedScript = facedScript + `${ chartScriptName }.facet(‘${ facedType }’, {n`;
  facedScript = facedScript + `  fields: [ ‘${ facedFields }’ ],n`;
  facedScript = facedScript + `  eachView(view) {n`;
  facedScript = facedScript + `    ${ grammarScript };n`;
  facedScript = facedScript + `  }n`;
  facedScript = facedScript + `});n`;
  return facedScript
}
 
function getGrammar() {
  let grammar = {}, grammarScript = chartScriptName + ".";
  grammar.geom = $(‘#geomSelect’).val();
  grammar.coord = $(‘#coordSelect’).val();
  grammar.faced = $(‘#facetSelect’).val();
  geom_attributes.map(function(attr){
    grammar[attr] = $(‘#’ + attr + "attr").val();
  });
  
  grammarScript = grammarScript + grammar.geom + "()";
  geom_attributes.map(function(attr){
    if (grammar[attr].length > 0) {
      grammarScript = grammarScript + "." + attr + "(‘" + grammar[attr].join("*") + "’)";
    }
  });
  
  if (grammar.coord) {
    grammarScript = grammarScript + ";n " + chartScriptName + "." + "coord(‘" + grammar.coord + "’);";
  } else {
    rammarScript = grammarScript + ";";
  }
  
  if ( grammar.faced ) {
    if ( grammar.faced.length == 1 ||
        grammar.faced.length == 2 ) {
      grammarScript = getFacet(grammar.faced, grammarScript);
    }
  }
  
  console.log(grammarScript)
  return grammarScript;
}

此间有几点要潜心:

  • 行使JS的模版字符串能够有效的协会代码片段
  • 利用eval试行组织好的语法驱动的代码来响应select的change事件,以博取可以的交互性。在生育情况,要留意该办法的安全性隐患,因为纯前端,eval能带来的威迫比相当的小,生产中,可以把那些实施放在安全的沙箱中运作
  • 您须要精通图形语法,并非随机的三结合都能使得出有效的图样。

那边对于select2的多选,有多少个小的唤起,在缺省气象下,多选的顺序是一定的相继,并不借助于采用的相继,不过无数图片语法和字段的逐个有关,所以我们选拔如下的诀要来对号入座select的挑选事件。

function updateSelect2Order(evt) { let element =
evt.params.data.element; let $element = $(element); $element.detach();
$(this).append($element); $(this).trigger(“change”); }

1
2
3
4
5
6
7
function updateSelect2Order(evt) {
  let element = evt.params.data.element;
  let $element = $(element);
  $element.detach();
  $(this).append($element);
  $(this).trigger("change");
}

如此那般做便是历次选中后,把当前选中的门类移到多少最后的职责。

 

页面引用:$(‘.imgMap’).imageMaps();

有的事例

好了,下边我们就来看有些事例,驾驭一下哪些利用图形语法来解析和斟酌数据。

 

Iris数据集散点图

银河国际平台官方网站 8

图表语法:

g2chart.point().position(‘Sepal.Length*Petal.Length’).color(‘Species’).size(‘Sepal.Width’)

1
g2chart.point().position(‘Sepal.Length*Petal.Length’).color(‘Species’).size(‘Sepal.Width’)

 

Car数据集折线图

银河国际平台官方网站 9

图表语法:

g2chart.line().position(‘id*speed’);

1
g2chart.line().position(‘id*speed’);

切换来极坐标:

银河国际平台官方网站 10

图表语法:

g2chart.line().position(‘id*speed’); g2chart.coord(‘polar’);

1
2
g2chart.line().position(‘id*speed’);
g2chart.coord(‘polar’);

 

Berkeley数据柱状图

银河国际平台官方网站 11

数据管理:

SELECT SUM(Freq) as f , Gender FROM table GROUP BY Gender

1
SELECT SUM(Freq) as f , Gender FROM table GROUP BY Gender

图片语法:

g2chart.interval().position(‘Gender*f’).color(‘Gender’).label(‘f’);

1
g2chart.interval().position(‘Gender*f’).color(‘Gender’).label(‘f’);

 

伯克利数据聚成堆柱状图

银河国际平台官方网站 12

数量处理:

SELECT SUM(Freq) as f , Gender , Admit FROM table GROUP BY Gender, Admit

1
SELECT SUM(Freq) as f , Gender , Admit FROM table GROUP BY Gender, Admit

图表语法:

g2chart.intervalStack().position(‘Gender*f’).color(‘Admit’)

1
g2chart.intervalStack().position(‘Gender*f’).color(‘Admit’)

 

Berkeley数据饼图

银河国际平台官方网站 13

数量处理:

SELECT SUM(Freq) as f , Gender FROM table GROUP BY Gender

1
SELECT SUM(Freq) as f , Gender FROM table GROUP BY Gender

图片语法:

g2chart.intervalStack().position(‘f’).color(‘Gender’).label(‘f’);
g2chart.coord(‘theta’)

1
2
g2chart.intervalStack().position(‘f’).color(‘Gender’).label(‘f’);
g2chart.coord(‘theta’)

 

Beck雷数据分面包车型大巴使用

银河国际平台官方网站 14

图片语法:

g2chart.facet(‘rect’, { fields: [ ‘Dept’, ‘Admit’ ], eachView(view) {
view.coord(‘theta’);
view.intervalStack().position(‘Freq’).color(‘Gender’); } });

1
2
3
4
5
6
7
g2chart.facet(‘rect’, {
  fields: [ ‘Dept’, ‘Admit’ ],
  eachView(view) {
    view.coord(‘theta’);
    view.intervalStack().position(‘Freq’).color(‘Gender’);
  }
});

越多的剖判图形留给大家去品尝

 

总结

本文分享了四个施用纯前端技巧构建叁个好像Tableau的BI应用的例证,整个代码计算:

  • JS 370 行 JS6
  • HTML 69 + 9 + 5 = 83
  • CSS 21

总计474
行,用那样少的代码就会一气浑成三个看上去勉强能够的BI工具,还算不错啊。当然这里根本是出于开源社区提供了这么多好的前端库以供应用,作者要做的无非是让它们有效的做事在同步。这几个只可以算是个原型,从效率和质感上来讲都不成熟,可是能在浏览器中不借助其余的服务器来完成BI的数据深入分析效果与利益,应该会有那壹个人想要在融洽的使用中嵌一个吗?

结缘本身事先分享的TensorflowJS的文章,下边一步大概是加盟预测效果,为多少剖判参预智能,前端选拔的前景,不可捉摸!

 

参考

  • axios  基于Promise的HTTP客户端
  • alasql 基于JS的开源SQL数据库
  • jquery datatable JQuery的多少表格插件
  • select2 JQuery的挑三拣四控件插件
  • 连带博客 使用开源软件火速搭建数据深入分析平台 
  • 连带博客 数量可视化中的视觉属性

    2 赞 1 收藏
    评论

银河国际平台官方网站 15