整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          JavaFX中TableView的CSS相關(guān)設(shè)置

          、特殊的table設(shè)置

          • TableView的單元之間去掉行橫線
          .table-view .table-row-cell {  
              -fx-background-insets: 0;  
          }  
          • TableView的單元之間去掉沒有數(shù)據(jù)的豎線
          table-row-cell:empty .table-cell {  
              -fx-border-width: 0px;  
          }  
          • TableView的單元之間去掉豎線
          table-row-cell .table-cell {  
              -fx-border-width: 0px;  
          }  
          • TableView的TableColumn的列頭設(shè)置
          .table-view .column-header{
              -fx-border-color:white lightgray white white;  
          }
          • table的空閑的列頭設(shè)置
          .table-view .filler{
               -fx-background-color: white;    
          }
          • table的列首背景設(shè)置,其中包括column-header,filler,MenuButton
          .table-view .column-header-background{   
             -fx-background-color: white;    
          }
          • table的垂直滾動條設(shè)置
          .table-view > .virtual-flow > .scroll-bar:vertical{
              -fx-background-insets: 0, 0 0 0 1;
              -fx-padding: -1 -1 -1 0;
          }
          • table的水平滾動條設(shè)置
          .table-view > .virtual-flow > .scroll-bar:horizontal{
              -fx-background-insets: 0, 1 0 0 0;
              -fx-padding: 0 -1 -1 -1;
          }
          • table的邊角設(shè)置
          .table-view > .virtual-flow > .corner {
              -fx-background-color: derive(-fx-base,-1%); /*-fx-base 是modena 預(yù)先定義的顏色*/
          }
          • 選擇一行
          .table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:selected{
              -fx-background: -fx-selection-bar;
              -fx-table-cell-border-color: derive(-fx-selection-bar, 20%);
          }
          • 當(dāng)控件沒有聚焦時選擇
          .table-row-cell:filled > .table-cell:selected{
              -fx-background: -fx-selection-bar-non-focused;
              -fx-table-cell-border-color: derive(-fx-selection-bar-non-focused, 20%);
          }
          • 聚焦的單元 (鍵盤導(dǎo)航)
          .table-view:focused:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell > .table-cell:focused{
              -fx-background-color: -fx-background, -fx-cell-focus-inner-border, -fx-background;
              -fx-background-insets: 0, 1, 2;
          }
          .table-view{
              /* Constants used throughout the tableview. */
              -fx-table-header-border-color: -fx-box-border;
              -fx-table-cell-border-color: derive(-fx-color,5%);
          }

          二、TableView tableRow編輯

          • tableRow
          /* Each row in the table is a table-row-cell. Inside a table-row-cell is any number of table-cell. */
          .table-row-cell {
              -fx-background: -fx-control-inner-background;
              -fx-background-color: -fx-table-cell-border-color, -fx-background;
              -fx-background-insets: 0, 0 0 1 0;
              -fx-padding: 0;
              -fx-text-fill: -fx-text-background-color;
          }
          • tableRow單元單數(shù)tableCell
          .table-row-cell:odd {
              -fx-background: -fx-control-inner-background-alt;
          }

          三、TableView 單元tableCell編輯

          • tableCell
          .table-cell {
              -fx-padding: 0.166667em; /* 2px, plus border adds 1px */
              -fx-background-color: null;
              -fx-border-color: transparent -fx-table-cell-border-color transparent transparent;
              -fx-cell-size: 2.0em; /* 24 */
              -fx-text-fill: -fx-text-background-color;
          }
          • tableCell選擇單元
          .table-view > .virtual-flow > .clipped-container > .sheet > .table-row-cell .table-cell:selected {
              -fx-background-color: -fx-table-cell-border-color, -fx-background;
              -fx-background-insets: 0, 0 0 1 0;
          }
          • tableCell最右可視單元
          /* When in constrained resize mode, the right-most visible cell should not have
             a right-border, as it is not possible to get this cleanly out of view without
             introducing horizontal scrollbars (see RT-14886). */
          .table-view:constrained-resize > .virtual-flow > .clipped-container > .sheet > .table-row-cell > .table-cell:last-visible {
              -fx-border-color: transparent;
          }

          四、TableView列頭編輯

          • TableView列大小重新調(diào)整線
          /* The column-resize-line is shown when the user is attempting to resize a column. */
          .table-view .column-resize-line {
              -fx-background: -fx-accent;
              -fx-background-color: -fx-background;
              -fx-padding: 0.0em 0.0416667em 0.0em 0.0416667em; /* 0 0.571429 0 0.571429 */
          }
          • TableView列頭背景
          /* This is the area behind the column headers. An ideal place to specify background
             and border colors for the whole area (not individual column-header's). */
          .table-view .column-header-background{
              -fx-background-color: -fx-inner-border, -fx-body-color;
              -fx-background-insets: 0, 1;
          }
          • TableView列頭行設(shè)置背景
          /* The column header row is made up of a number of column-header, one for each
             TableColumn, and a 'filler' area that extends from the right-most column
             to the edge of the tableview, or up to the 'column control' button. */
          .table-view .column-header,
          .table-view .filler,
          .table-view > .column-header-background > .show-hide-columns-button,
          .table-view:constrained-resize .filler{
              -fx-background-color: -fx-box-border, -fx-inner-border, -fx-body-color;
              -fx-background-insets: 0, 0 1 1 0, 1 2 2 1;
              -fx-font-weight: bold;
              -fx-size: 2em;
              -fx-text-fill: -fx-selection-bar-text;
              -fx-padding: 0.166667em;
          }
          • TableView空閑列
          .table-view .filler,
          .table-view:constrained-resize .filler{
              -fx-background-insets: 0, 0 0 1 0, 1 1 2 1;
          }
          • TableView列頭展示隱藏列按鈕
          .table-view > .column-header-background > .show-hide-columns-button {
              -fx-background-insets: 0, 0 0 1 1, 1 1 2 2;
          }
          • TableView列頭排序順序點容器
          .table-view .column-header .sort-order-dots-container{
              -fx-padding: 2 0 2 0;
          }
          • TableView列頭排序順序
          .table-view .column-header .sort-order{
              -fx-font-size: 0.916667em; /* 11pt - 1 less than the default font */
          }
          • TableView列頭排序順序點
          .table-view .column-header .sort-order-dot {
              -fx-background-color: -fx-mark-color;
              -fx-padding: 0.115em;
              -fx-background-radius: 0.115em;
          }
          • TableView列頭文本標(biāo)簽
          .table-view .column-header .label{
              -fx-alignment: center;
          }
          /* Plus Symbol */
          .table-view .show-hide-column-image,
           {
              -fx-background-color: -fx-mark-color;
              -fx-padding: 0.25em; /* 3px */
              -fx-shape: "M398.902,298.045c0.667,0,1.333,0,2,0c0,0.667,0,1.333,0,2c0.667,0,1.333,0,2,0c0,0.667,0,1.333,0,2c-0.667,0-1.333,0-2,0c0,0.666,0,1.332,0,1.999c-0.667,0-1.333,0-2,0c0-0.667,0-1.333,0-1.999c-0.666,0-1.333,0-1.999,0c0-0.667,0-1.334,0-2c0.666,0,1.333,0,1.999,0C398.902,299.378,398.902,298.711,398.902,298.045z"; 
          }
          • TableView列拖拽頭留下的空閑區(qū)
          /* When a column is being 'dragged' to be placed in a different position, there
             is a region that follows along the column header area to indicate where the
             column will be dropped. This region can be styled using the .column-drag-header
             name. */
          .table-view .column-drag-header {
              -fx-background: -fx-accent;
              -fx-background-color: -fx-selection-bar;
              -fx-border-color: transparent;
              -fx-opacity: 0.6;
          }
          • TableView當(dāng)前正在移動且半透明覆蓋的列
          /* Semi-transparent overlay to indicate the column that is currently being moved */
          .table-view .column-overlay{
              -fx-background-color: darkgray;
              -fx-opacity: 0.3;
          }
          • TableView列頭排序箭頭
          /* Header Sort Arrows */
          .table-view /*> column-header-background > nested-column-header >*/ .arrow{
              -fx-background-color: -fx-mark-color;
              -fx-padding: 0.25em 0.3125em 0.25em 0.3125em; /* 3 3.75 3 3.75 */
              -fx-shape: "M 0 0 h 7 l -3.5 4 z";
          }
          • TableView沒有行列

          TableView簡介

          QTableView是模型-視圖(Model-View)框架類之一,是Qt模型-視圖框架的組成部分,它實現(xiàn)了一個表格視圖。在一個應(yīng)用需要和一批數(shù)據(jù)進(jìn)行交互,需要以表格形式輸出這些信息的時候,QTableView是最合適的選擇。

          QTableView實現(xiàn)了QAbstractItemView類定義的接口,因此它能夠顯示從QAbstractItemModel類派生的模型提供的數(shù)據(jù)。

          我們可以通過使用鼠標(biāo)單擊某個單元格或者使用箭頭來導(dǎo)航表格視圖中的單元格。QTableView擁有一個水平表頭和垂直標(biāo)表頭。表格視圖中顯示的條目與其他視圖中的條目一樣,使用標(biāo)準(zhǔn)委托類來渲染和編輯。

          QTableView常用方法:

          • setModel(): 設(shè)置視圖的Model類;
          • horizontalHeader(): 獲得水平表頭;
          • verticalHeader(): 獲得垂直表頭;
          • rowHeight(): 獲得每一行的高度;
          • columnWidth(): 獲得列的寬度;
          • hideRow(): 隱藏指定行;
          • showRow(): 顯示指定行;
          • hideColumn(): 隱藏指定列;
          • showColumn(): 顯示指定列;
          • selectRow(): 選擇指定行;
          • selectColumn(): 選擇指定列。

          QTableView類繼承關(guān)系:

          測試QTableView

          在測試代碼中,我們使用QStandardItemModel作為QTableView的模型類, 對視圖的屬性做了一些限制(只能選中一行,只允許行選中模式,不可編輯等等)。當(dāng)我們選中某一行是,在狀態(tài)條上添加信息顯示,在菜單欄實現(xiàn)了添加行和刪除行的功能。完整代碼如下:

          import sys
          from PyQt5 import QtCore, QtGui, QtWidgets
          from PyQt5.QtCore import Qt,QItemSelection, QItemSelectionModel, QModelIndex
          from PyQt5.QtGui import QStandardItemModel, QStandardItem
          from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableView,
                                       QAbstractItemView, QHeaderView, QMenu, QMenuBar, QAction)
           
          class DemoTableView(QMainWindow):
              def __init__(self, parent=None):
                  super(DemoTableView, self).__init__(parent)   
                  
                   # 設(shè)置窗口標(biāo)題
                  self.setWindowTitle('實戰(zhàn)PyQt5: QTableView 演示')      
                  # 設(shè)置窗口大小
                  self.resize(520, 360)
                
                  self.initUi()
                  
              def initUi(self):
                  
                  #行數(shù)和列數(shù)
                  self.rows = 8
                  self.cols = 4
                  
                  #設(shè)置水平表頭信息
                  model = QStandardItemModel(self.rows, self.cols, self)
                  hTitle=[]
                  for col in range(self.cols):
                      hTitle.append('第{}列'.format(col+1))
                  model.setHorizontalHeaderLabels(hTitle)
                  #設(shè)置垂直表頭信息
                  vTitle=[]
                  for row in range(self.rows):
                      vTitle.append('第{}行'.format(row+1))
                  model.setVerticalHeaderLabels(vTitle)
                  
                  #設(shè)置Item里的內(nèi)容
                  for row in range (self.rows):
                      for column in range (self.cols):
                          item = QStandardItem('(row %s, column %s)'%(row, column))
                          model.setItem(row, column, item)
                  
                  tableView = QTableView(self)
                  tableView.setModel(model)
                  #設(shè)置只能選中一行
                  tableView.setSelectionMode(QAbstractItemView.SingleSelection)
                  #不可編輯
                  tableView.setEditTriggers(QTableView.NoEditTriggers)
                  #設(shè)置只有行選中
                  tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
                  #所有列自動拉伸,充滿界面
                  tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
                  
                  #顯示選中行的信息
                  tableView.selectionModel().currentChanged.connect(self.onCurrentChanged)
                          
                  self.model = model
                  self.tableView = tableView
                  self.setCentralWidget(tableView)
              
                  #添加菜單項
                  menuBar = self.menuBar()
                  fileMenu = menuBar.addMenu('文件')
                  editMenu = menuBar.addMenu('編輯')
                  
                  #退出應(yīng)用
                  appExit = QAction('退出', self)
                  appExit.triggered.connect(self.close)
                  fileMenu.addAction(appExit)
                  
                  #添加一行
                  editAppend = QAction('添加', self)
                  editAppend.triggered.connect(self.appendRow)
                  #刪除一行
                  editRemove = QAction('刪除', self)
                  editRemove.triggered.connect(self.removeRow)
                  editMenu.addAction(editAppend)
                  editMenu.addAction(editRemove)
                  
              def appendRow(self):
                  row = self.model.rowCount()
                  items = list()
                  for col in range (self.cols):
                      items.append(QStandardItem('(row %s, column %s)'%(row, col)))
                  #添加一行
                  self.model.appendRow(items)
                  #更新頭信息
                  self.model.setVerticalHeaderItem(row, QStandardItem('第{}行'.format(row+1)))
              
              def removeRow(self):
                  #獲取選中的行
                  sel = self.tableView.selectionModel().selectedRows()
                  print(sel)
                  if sel:
                      #下面刪除時,選中多行中的最后一行,會被刪掉;不選中,則默認(rèn)第一行刪掉
                      index=self.tableView.currentIndex()
                      print(index.row())
                      self.model.removeRow(index.row())
                      
              def onCurrentChanged(self,current, previous):
                  #初始化時,previous.row() = -1,不顯示信息
                  if int(previous.row() < 0):
                       return
                  self.statusBar().showMessage('選中第{}行'.format(current.row()+1))
              
          if __name__ == '__main__':
              app = QApplication(sys.argv)
              window = DemoTableView()
              window.show()
              sys.exit(app.exec()) 

          運(yùn)行結(jié)果如下圖:

          測試QTableView

          本文知識點

          • QTableView的行和列的屬性設(shè)置;
          • QTableView的選擇屬性的設(shè)置;
          • QTableView水平和垂直表頭信息設(shè)置;
          • 在QTableView中添加或者刪除一行。

          喜歡本文內(nèi)容就關(guān)注, 收藏,點贊,評論和轉(zhuǎn)發(fā)。

          Gartner最新的對商務(wù)智能軟件的專業(yè)分析報告中,Tableau持續(xù)領(lǐng)跑。Microsoft因為PowerBI表現(xiàn)出色也處于領(lǐng)導(dǎo)者象限。而昔日的領(lǐng)導(dǎo)者像SAP,SAS,IBM,MicroStrategy等逐漸被拉開了差距。

          Tableau因為其靈活,出色的數(shù)據(jù)表現(xiàn)已經(jīng)成為BI領(lǐng)域里無可爭議的領(lǐng)頭羊。而其數(shù)據(jù)驅(qū)動的可視化和核心思想是來自于Leland Wilkinson的The Grammar Of Graphics ,同樣受到該思想影響的還有R的圖形庫ggplot。

          在數(shù)據(jù)可視化開源領(lǐng)域里,大家對百度開發(fā)的echarts可謂耳熟能詳,echarts經(jīng)過多年的發(fā)展,其功能確實非常強(qiáng)大,可用出色來形容。但是螞蟻金服開源的基于The Grammar Of Graphics的語法驅(qū)動的可視化庫G2,讓人眼前一亮。那我們就看看如何利用G2和500行左右的純前端代碼來實現(xiàn)一個的類似Tableau的數(shù)據(jù)分析功能。

          • 演示參見 https://codepen.io/gangtao/full/OZvedx/
          • 代碼參見 https://gist.github.com/gangtao/e053cf9722b64ef8544afa371c2daaee

          數(shù)據(jù)加載

          第一步是加載數(shù)據(jù):

          數(shù)據(jù)加載主要用到了三個庫:

          • axios 基于Promise的HTTP客戶端
          • alasql 基于JS的開源SQL數(shù)據(jù)庫
          • jquery datatable JQuery的數(shù)據(jù)表格插件

          數(shù)據(jù)通過我存放在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 });
           });
           });
           }
          };

          封裝好后,我們就可以用request.get()方法發(fā)送REST請求,獲取csv文件。

          let csv = await request.get(url);

          這一步可能會遇到跨域請求的問題,github上的文件支持跨域。

          把數(shù)據(jù)存儲在一個SQL數(shù)據(jù)庫中,這樣做的好處是為了下一步做數(shù)據(jù)準(zhǔn)備的時候,可以方便的利用SQL來進(jìn)行查詢和分析。

          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是一個對數(shù)據(jù)表的封裝,把csv數(shù)據(jù)存在SQL數(shù)據(jù)庫表中,提供一個query()方法。這里要做的是把SQL查詢個從 "SELECT * FROM table" 變成 "SELECT * FROM CSV(?)" 表示查詢參數(shù)是CSV數(shù)據(jù)。因為codepen的安全性限制,運(yùn)行前向查找的replace語句(這里的regex表示把前面是“FROM ”詞的替換為CSV(?)的)在full page view下是不能執(zhí)行的,所以我用了一個更簡單的假定,用戶的表名就是table,這樣做有很多問題,大家如果在codepen之外的環(huán)境,可以用注釋掉的代碼。

          然后把"SELECT * FROM table"的查詢結(jié)果(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. 數(shù)據(jù)中,如果列的名字中有包含點,空格等字符,例如Iris數(shù)據(jù)集中的Sepal.Length,datatable是無法正常顯示的,這里要調(diào)用sanitizeData()方法把列名,也就是JsonArray中Json對象的屬性名中的點和空格去掉。
          2. sanitizeData()方法會改變輸入對象,所以在傳入之前做了一個深度拷貝,這里利用JSON的stringfy和parse方法可以對JSON兼容的對象有效的拷貝。

          這里要注意,Iris數(shù)據(jù)集中在datatable中的列名都不顯示點,但實際數(shù)據(jù)并沒有改變。

          數(shù)據(jù)準(zhǔn)備

          數(shù)據(jù)加載完畢,我們來到第二步的數(shù)據(jù)準(zhǔn)備階段。數(shù)據(jù)準(zhǔn)備是數(shù)據(jù)科學(xué)項目最花時間的一步,通常需要對數(shù)據(jù)進(jìn)行大量的清洗,變形,抽取等工作,使得數(shù)據(jù)變得可用。

          在這一步我們做了兩件事:

          一是顯示數(shù)據(jù)的一個摘要,讓我們初步了解數(shù)據(jù)的概貌,為進(jìn)一步的數(shù)據(jù)變形和處理做好準(zhǔn)備。

          這個是Iris數(shù)據(jù)集的摘要:

          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;
          }

          這里我們利用數(shù)據(jù)的類型判斷出每一個字段是數(shù)值型還是字符型。對于字符型的字段,我們利用JS6的Set來獲得所有的Unique數(shù)據(jù)。對于數(shù)值型,我們利用d3的max,min,mean,median,deviation方法計算出對應(yīng)的最大值,最小值,平均數(shù),中位數(shù)和偏差。

          另一個就是利用SQL查詢來對數(shù)據(jù)進(jìn)行進(jìn)一步的加工。

          上圖的例子中我們利用限制條件得到一個Iris數(shù)據(jù)的子集。

          另外G2還提供了Dataset的功能:

          源數(shù)據(jù)的解析,將csv, dsv,geojson 轉(zhuǎn)成標(biāo)準(zhǔn)的JSON,查看Connector加工數(shù)據(jù),包括 filter,map,fold(補(bǔ)數(shù)據(jù)) 等操作,查看 Transform統(tǒng)計函數(shù),匯總統(tǒng)計、百分比、封箱 等統(tǒng)計函數(shù),查看 Transform特殊數(shù)據(jù)處理,包括 地理數(shù)據(jù)、矩形樹圖、桑基圖、文字云 的數(shù)據(jù)處理,查看 Transform

          數(shù)據(jù)處理是一個比較大的話題,我們的目標(biāo)是利用盡可能少的代碼完成一個數(shù)據(jù)分析的工具,所以這一步僅僅是利用alasql提供的SQL查詢來處理數(shù)據(jù)。

          數(shù)據(jù)展示

          數(shù)據(jù)處理好后就是我們的核心內(nèi)容,數(shù)據(jù)展示了。

          這一步主要是利用select2提供的選擇控件構(gòu)建圖形語法來驅(qū)動數(shù)據(jù)展示。如上圖所示,對應(yīng)的G2代碼圖形語法為:

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

          圖形語法主要包含以下幾個主要的元素:

          幾何標(biāo)記 Geometry

          幾何標(biāo)記定義了使用什么樣的幾何圖形來表征數(shù)據(jù)。G2現(xiàn)在支持如下這些幾何標(biāo)記:

          geom 類型描述point點,用于繪制各種點圖。path路徑,無序的點連接而成的一條線,常用于路徑圖的繪制。line線,點按照 x 軸連接成一條線,構(gòu)成線圖。area填充線圖跟坐標(biāo)系之間構(gòu)成區(qū)域圖,也可以指定上下范圍。interval使用矩形或者弧形,用面積來表示大小關(guān)系的圖形,一般構(gòu)成柱狀圖、餅圖等圖表。polygon多邊形,可以用于構(gòu)建色塊圖、地圖等圖表類型。edge兩個點之間的鏈接,用于構(gòu)建樹圖和關(guān)系圖中的邊、流程圖中的連接線。schema自定義圖形,用于構(gòu)建箱型圖(或者稱箱須圖)、蠟燭圖(或者稱 K 線圖、股票圖)等圖表。heatmap用于熱力圖的繪制。

          這里要注意,intervalstack是官方支持的,但是文檔沒有提到,在閱讀G2的API文檔的時候,我也發(fā)現(xiàn)文檔講的不是很清楚,有很多地方?jīng)]有講清楚如何使用API。這也是開源軟件值得改進(jìn)的地方。

          圖形屬性 Attributes

          圖形屬性對應(yīng)視覺編碼中的不同元素,大家可以參考我的另一博客 數(shù)據(jù)可視化中的視覺屬性 。

          圖形屬性主要有以下幾種。

          1. position:位置,二維坐標(biāo)系內(nèi)映射至 x 軸、y 軸;
          2. color:顏色,包含了色調(diào)、飽和度和亮度;
          3. size:大小,不同的幾何標(biāo)記對大小的定義有差異;
          4. shape:形狀,幾何標(biāo)記的形狀決定了某個具體圖表類型的表現(xiàn)形式,例如點圖,可以使用圓點、三角形、圖片表示;線圖可以有折線、曲線、點線等表現(xiàn)形式;
          5. opacity:透明度,圖形的透明度,這個屬性從某種意義上來說可以使用顏色代替,需要使用 'rgba' 的形式,所以在 G2 中我們獨(dú)立出來。

          在構(gòu)建語法的時候,我們把圖形屬性綁定一個或者多個數(shù)據(jù)字段。

          坐標(biāo)系 Coordinates

          坐標(biāo)系是將兩種位置標(biāo)度結(jié)合在一起組成的 2 維定位系統(tǒng),描述了數(shù)據(jù)是如何映射到圖形所在的平面。

          G2提供了以下幾種坐標(biāo)系:



          coordType說明rect直角坐標(biāo)系,目前僅支持二維,由 x, y 兩個互相垂直的坐標(biāo)軸構(gòu)成。polar極坐標(biāo)系,由角度和半徑 2 個維度構(gòu)成。theta一種特殊的極坐標(biāo)系,半徑長度固定,僅僅將數(shù)據(jù)映射到角度,常用于餅圖的繪制。helix螺旋坐標(biāo)系,基于阿基米德螺旋線。

          分面 Facet

          分面,將一份數(shù)據(jù)按照某個維度分隔成若干子集,然后創(chuàng)建一個圖表的矩陣,將每一個數(shù)據(jù)子集繪制到圖形矩陣的窗格中。分面其實提供了兩個功能:

          1. 按照指定的維度劃分?jǐn)?shù)據(jù)集;
          2. 對圖表進(jìn)行排版。

          G2支持以下的分面類型:



          分面類型說明rect默認(rèn)類型,指定 2 個維度作為行列,形成圖表的矩陣。list指定一個維度,可以指定一行有幾列,超出自動換行。circle指定一個維度,沿著圓分布。tree指定多個維度,每個維度作為樹的一級,展開多層圖表。mirror指定一個維度,形成鏡像圖表。matrix指定一個維度,形成矩陣分面。

          注意,在我的代碼中,為了簡化使用,只支持list和rect,當(dāng)綁定一個字段的時候用list,綁定兩個字段的時候用rect。

          除了上面提到的元素,當(dāng)然還有許多其它的元素我們沒有包含和支持,例如:坐標(biāo)軸,圖例,提示等等。

          關(guān)于圖形的語法的更多內(nèi)容,請參考這里。

          生成圖形語法的核心代碼如下:

          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的模版字符串可以有效的構(gòu)造代碼片段
          • 使用eval執(zhí)行構(gòu)造好的語法驅(qū)動的代碼來響應(yīng)select的change事件,以獲得良好的交互性。在生產(chǎn)環(huán)境,要注意該方法的安全性隱患,因為純前端,eval能帶來的威脅比較小,生產(chǎn)中,可以把這個執(zhí)行放在安全的沙箱中運(yùn)行
          • 你需要理解圖形語法,并不是任意的組合都能驅(qū)動出有效的圖形。

          這里對于select2的多選,有一個小的提示,在缺省情況下,多選的順序是固定的順序,并不依賴選擇的順序,然而許多圖形語法和字段的順序有關(guān),所以我們使用如下的方法來相應(yīng)select的選擇事件。

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

          這樣做就是每次選中后,把當(dāng)前選中的項目移到數(shù)據(jù)最后的位置。

          一些例子

          好了,下面我們就來看一些例子,了解一下如何使用圖形語法來分析和探索數(shù)據(jù)。

          Iris數(shù)據(jù)集散點圖

          圖形語法:

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

          Car數(shù)據(jù)集折線圖

          圖形語法:

          g2chart.line().position('id*speed');

          切換到極坐標(biāo):

          圖形語法:

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

          Berkeley數(shù)據(jù)柱狀圖

          數(shù)據(jù)處理:

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

          圖形語法:

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

          Berkeley數(shù)據(jù)堆疊柱狀圖

          數(shù)據(jù)處理:

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

          圖形語法:

          g2chart.intervalStack().position('Gender*f').color('Admit')

          Berkeley數(shù)據(jù)餅圖

          數(shù)據(jù)處理:

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

          圖形語法:

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

          Berkeley數(shù)據(jù)分面的應(yīng)用

          圖形語法:

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

          更多的分析圖形留給大家去嘗試

          總結(jié)

          本文分享了一個利用純前端技術(shù)構(gòu)建一個類似Tableau的BI應(yīng)用的例子,整個代碼統(tǒng)計:

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

          總計474 行,用這么少的代碼就能完成一個看上去還不錯的BI工具,還算不錯吧。當(dāng)然這里主要是由于開源社區(qū)提供了這么多好的前端庫以供應(yīng)用,我要做的僅僅是讓它們有效的工作在一起。這個只能算是個原型,從功能和質(zhì)量上來說都不成熟,但是能在瀏覽器中不借助任何的服務(wù)器來實現(xiàn)BI的數(shù)據(jù)分析功能,應(yīng)該會有很多人想要在自己的應(yīng)用中嵌一個吧?

          結(jié)合我之前分享的TensorflowJS的文章,下面一步可能是加入預(yù)測功能,為數(shù)據(jù)分析加入智能,前端應(yīng)用的前景,不可限量!

          參考

          • axios 基于Promise的HTTP客戶端
          • alasql 基于JS的開源SQL數(shù)據(jù)庫
          • jquery datatable JQuery的數(shù)據(jù)表格插件
          • select2 JQuery的選擇控件插件
          • 相關(guān)文章 再談使用開源軟件搭建數(shù)據(jù)分析平臺
          • 相關(guān)文章 使用開源軟件快速搭建數(shù)據(jù)分析平臺
          • 相關(guān)文章 高維數(shù)據(jù)可視化圖形語法指南

          主站蜘蛛池模板: 久久精品国产一区二区三区日韩| 日本一区二区不卡在线| 亚洲AV无码一区二区三区系列| 精品国产一区二区三区香蕉事 | 视频一区二区在线观看| 国产精品电影一区| 午夜精品一区二区三区在线观看| 久夜色精品国产一区二区三区| 亚洲一区二区三区91| 东京热无码一区二区三区av| 精品一区二区三区波多野结衣 | 无码人妻精品一区二区三区99仓本 | 在线精品国产一区二区| 国产精品av一区二区三区不卡蜜 | 国产乱码精品一区二区三区四川 | 国产精品无码一区二区三区不卡| 亚洲熟妇成人精品一区| 一区二区三区日本电影| 无码少妇丰满熟妇一区二区 | 日韩在线视频一区二区三区 | 日韩精品一区二区三区大桥未久| 亚洲AV无码一区二区三区牛牛| 亚洲AV无码一区二区二三区入口| 欧洲精品码一区二区三区免费看| 国产高清在线精品一区| 一区二区三区观看免费中文视频在线播放 | 一本一道波多野结衣一区| 国产午夜精品一区二区三区嫩草 | 亚洲AV日韩AV一区二区三曲| 国产精品一区二区久久精品无码| 怡红院一区二区三区| 天天综合色一区二区三区| 毛片一区二区三区无码| 一区二区不卡在线| 午夜AV内射一区二区三区红桃视| 在线|一区二区三区| 亚洲av色香蕉一区二区三区蜜桃 | 无码人妻一区二区三区免费手机| 国产午夜精品一区二区三区嫩草| 国产乱人伦精品一区二区 | 日韩av片无码一区二区不卡电影|