处理电子表格数据以实现可视化

Manipulating Spreadsheet data for visualization

我刚开始学习 Google 应用程序 Script/JavaScript,想知道如何重塑、操作 Google 电子表格中的多维数据。我读过一些像 this one 这样的 post 说 GAS 在数据操作方面不灵活。但是 post 有点过时了,所以我想知道 GAS 功能是否有任何新的 changes/additions。

下面是开始的功能代码。可以成功绘制出下图。但我真正想要的是:

  1. 按 "Weekday" (dayOfWeek(toDate(A))) 和 "Item" (column B)
  2. 分组
  3. 使用"Item"作为过滤器

    .

我尝试使用 PIVOT BGROUP BY dayOfWeek(toDate(A)),,但它返回了 One or more participants failed to draw()× 错误消息或意外格式。

<html>
  <head>
    <title>
      Test
    </title>
    <!--Load the AJAX API-->
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript">
      google.load('visualization', '1', {'packages': ['table', 'controls', 'corechart']});
      google.setOnLoadCallback(initialize);

      function initialize() {

              var url = 'https://docs.google.com/spreadsheets/d/1_mSbT87MVWOiX2cfKX_x3dgTnToY5ulCWeGGCVn13iQ/gviz/tq?sheet=Sheet1&tq='   

              var queryStringDaily = encodeURIComponent("SELECT dayOfWeek(toDate(A)), sum(C), sum(D), sum(E), sum(F), sum(G), sum(H) GROUP BY dayOfWeek(toDate(A)) LABEL dayOfWeek(toDate(A)) 'Weekday' " ); 

              var queryDaily = new google.visualization.Query(url+ queryStringDaily);
              queryDaily.send(drawDaily);
      }

      function drawDaily(dailyTicket) {

                //prepare data
                var dailyTicket_table = dailyTicket.getDataTable(firstRowIsHeader = true); 

                // Create a dashboard.
                var dashboard = new google.visualization.Dashboard(
                    document.getElementById('dashboard_div2'));

                // Create a filter
                var categoryFilter = new google.visualization.ControlWrapper({
                  'controlType': 'CategoryFilter',
                  'containerId': 'filter_div2',
                  'options': {
                    'filterColumnLabel': 'Weekday'
                  }
                });

                //create chart
                var dailyChart = new google.visualization.ChartWrapper({
                  'chartType': 'LineChart',
                  'containerId': 'current_day',
                  'options': {
                    'title': 'Tickets by Rep, Item, and Weekday',
                    'legend': {position: 'right'},
                    //reformat x-axis tickmarks
                    'hAxis': {'viewWindow': {'min': 1.5, 'max': 6.5},
                              'ticks': [//{v: 1, f: 'Sunday'}, 
                                        {v: 2, f: 'Monday'}, 
                                        {v: 3, f: 'Tuesday'}, 
                                        {v: 4, f: 'Wednesday'}, 
                                        {v: 5, f: 'Thursday'}, 
                                        {v: 6, f: 'Friday'}, 
                                        //{v: 7, f: 'Saturday'}
                                        ] 
                            },                    
                  }
                });

                // bind charts and controls to dashboard 
                dashboard.bind(categoryFilter, dailyChart); 

                // Draw the dashboard.
                dashboard.draw(dailyTicket_table);

      }


    </script>
  </head>

  <body>
    <!--Div that will hold the dashboard-->
    <div id="dashboard_div2"></div>
    <!--Divs that will hold each control and chart-->
    <div id="filter_div2"></div>
    <div id="current_day" style="align: center; width: 500px; height: 250px;"></div>

  </body>
</html>

您可以添加多个过滤器。要对您的源数据执行此操作:

  1. 您需要 SELECT 附加文本列 B 并将其包含在 GROUP BY 和可选的 LABEL 语句中。

    var queryStringDaily = encodeURIComponent("SELECT B, dayOfWeek(toDate(A)), sum(C),
              sum(D), sum(E), sum(F), sum(G), sum(H)
              GROUP BY dayOfWeek(toDate(A)), B
              LABEL dayOfWeek(toDate(A)) 'Weekday', B 'Issue' ");
    

    请注意,我们首先有 B。如果我们不这样做,我们就会得到您提到的错误。那是因为 LineChart 需要一个标签列,然后是相关序列的列。将文本列移动到 SELECT 的开头可以避免错误。

  2. 为 "Issue" 添加过滤器。

    var issueFilter = new google.visualization.ControlWrapper({
      'controlType': 'CategoryFilter',
      'containerId': 'issue-div',
      'options': {
        'filterColumnLabel': 'Issue'
      }
    });
    
  3. 将新筛选器绑定到仪表板。

    dashboard.bind(issueFilter, dailyChart);
    

就是这样!

... 不,不是。因为 Issue 是我们的第一列,所以它被选为主轴,这不是很有用。

此时您的挑战是 select 与您的数据相匹配的适当可视化 - 因为折线图没有。

注意:此代码段可运行。

google.load('visualization', '1', {
  'packages': ['table', 'controls', 'corechart']
});
google.setOnLoadCallback(initialize);

function initialize() {

  var url = 'https://docs.google.com/spreadsheets/d/1_mSbT87MVWOiX2cfKX_x3dgTnToY5ulCWeGGCVn13iQ/gviz/tq?sheet=Sheet1&tq='

  var queryStringDaily = encodeURIComponent("SELECT B, dayOfWeek(toDate(A)), sum(C), sum(D), sum(E), sum(F), sum(G), sum(H) GROUP BY dayOfWeek(toDate(A)),B LABEL dayOfWeek(toDate(A)) 'Weekday', B 'Issue' ");

  var queryDaily = new google.visualization.Query(url + queryStringDaily);
  queryDaily.send(drawDaily);
}

function drawDaily(dailyTicket) {

  //prepare data
  var dailyTicket_table = dailyTicket.getDataTable(firstRowIsHeader = true);
  console.log(JSON.stringify(dailyTicket_table).replace(/\"/g,"'").replace(/"/g,''));

  // Create a dashboard.
  var dashboard = new google.visualization.Dashboard(
    document.getElementById('dashboard-div'));

  // Create a filter
  var issueFilter = new google.visualization.ControlWrapper({
    'controlType': 'CategoryFilter',
    'containerId': 'issue-div',
    'options': {
      'filterColumnLabel': 'Issue'
    }
  });

  var categoryFilter = new google.visualization.ControlWrapper({
    'controlType': 'CategoryFilter',
    'containerId': 'weekday-div',
    'options': {
      'filterColumnLabel': 'Weekday'
    }
  });

  //create chart
  var dailyChart = new google.visualization.ChartWrapper({
    'chartType': 'LineChart',
    'containerId': 'linechart-div',
    'options': {
      'title': 'Tickets by Rep, Item, and Weekday',
      'legend': { position: 'right'},
      //reformat x-axis tickmarks
      'hAxis': {
        'viewWindow': { 'min': 1.5, 'max': 6.5},
        'ticks': [ 
          //{v: 1, f: 'Sunday'}, 
          { v: 2, f: 'Monday' },
          { v: 3, f: 'Tuesday' },
          { v: 4, f: 'Wednesday'},
          { v: 5, f: 'Thursday' },
          { v: 6, f: 'Friday' },
          //{v: 7, f: 'Saturday'}
        ]
      },
    }
  });

  // bind charts and controls to dashboard 
  dashboard.bind(issueFilter, dailyChart);
  dashboard.bind(categoryFilter, dailyChart);

  // Draw the dashboard.
  dashboard.draw(dailyTicket_table);

}
<html>
  <head>
    <title>
      Test
    </title>
    <!--Load the AJAX API-->
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>

  </head>

  <body>
    <!--Div that will hold the dashboard-->
    <div id="dashboard-div">
      <div id="control-div">
        <div id="issue-div">
        </div>
        <div id="weekday-div">
        </div>
      </div>
      <div id="charts-div">
        <div id="linechart-div" style="align: center; width: 500px; height: 250px;">
        </div>
      </div>
    </div>    

  </body>
</html>

我想今天早上我顿悟了。在从其他帖子中学习和借用代码后(在脚本中注明),我能够生成我想要的图表。

  1. 正如@Mogsdad 所建议的,我将 B 列(问题)移至 "SELECT" 项的开头,然后将此列用于注释和筛选。注释文本疯狂地出现在每个条形图上方,但我可以通过将 annotated.textStyle 的字体大小设置为 0 来隐藏它们。
  2. 我还自定义了工具提示,因此不会显示工作日(数字格式)。

我认为这是解决我的问题的方法。如果大家有更好的建议,不胜感激学习。

<html>
  <head>
    <title>
      Test
    </title>
    <!--Load the AJAX API-->
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript">
      google.load('visualization', '1', {'packages': ['table', 'controls', 'corechart']});
      google.setOnLoadCallback(initialize);

      function initialize() {

              var url = 'https://docs.google.com/spreadsheets/d/1_mSbT87MVWOiX2cfKX_x3dgTnToY5ulCWeGGCVn13iQ/gviz/tq?sheet=Sheet1&tq='   
              
              var queryStringDaily = encodeURIComponent("SELECT B, dayOfWeek(toDate(A)), sum(C), sum(D), sum(E), sum(F), sum(G), sum(H) GROUP BY dayOfWeek(toDate(A)), B LABEL dayOfWeek(toDate(A)) 'Weekday' , B '{role: \"annotation\"}' " ); 

              var queryDaily = new google.visualization.Query(url+ queryStringDaily);
              queryDaily.send(drawDaily);
      }

      function drawDaily(dailyTicket) {

                //prepare data
                var dailyTicket_table = dailyTicket.getDataTable(firstRowIsHeader = true); 
                
                //NEW!!! create tooltip
                //inspired by 
                var columns = [2]; // jump to the value columns
                for (var i = 2; i < dailyTicket_table.getNumberOfColumns(); i++) { 
                    columns.push(i);
                    columns.push({
                        type: 'string',
                        properties: {
                            role: 'tooltip'
                        },
                        calc: (function (j) {
                            return function (dt, row) {
                                return dt.getColumnLabel(j)  //+ ': Weekday:' + dt.getValue(row, 1) 
                                + ' Tickets:' + dt.getValue(row, j)
                            } 
                        })(i) 
                    });

                    columns.push({sourceColumn: 0,
                        //calc: getValueAt.bind(undefined, 1)
                        type: "string",
                        role: "annotation",
                        label: "Issue"} );
                }
                columns = [1].concat(columns);

                var view = new google.visualization.DataView(dailyTicket_table);
                view.setColumns(columns)

                // Create dashboard.
                var dashboard = new google.visualization.Dashboard(
                    document.getElementById('dashboard_div2'));

                // Create filter
                var issueFilter = new google.visualization.ControlWrapper({
                  'controlType': 'CategoryFilter',
                  'containerId': 'issue-div',
                  'options': {
                    'filterColumnLabel': 'Issue',
                    'ui': {
                    'allowMultiple': false,
                    'allowNone': false, 
                    }
                  },
                  //Set default filter value
                  'state': {'selectedValues': [dailyTicket_table.getValue(1, 1)]}
                });

                //create chart
                var dailyChart = new google.visualization.ChartWrapper({
                  'chartType': 'ColumnChart',
                  'containerId': 'current_day',
                  'options': {
                      'legend': {position: 'right'},
                      //Set the fontsize of labels so they don't show up crazily
                      'annotations': {textStyle: {'fontsize': 0},
                                     //use 'line' style so to remove the line pointer
                                      style: 'line'},
                      'hAxis': {'viewWindow': {'min': 1.5, 'max': 6.5},
                                'ticks': [//{v: 1, f: 'Sunday'}, 
                                          {v: 2, f: 'Monday'}, 
                                          {v: 3, f: 'Tuesday'}, 
                                          {v: 4, f: 'Wednesday'}, 
                                          {v: 5, f: 'Thursday'}, 
                                          {v: 6, f: 'Friday'}, 
                                          //{v: 7, f: 'Saturday'}
                                          ] 
                               },  
                  }
                });

                // bind charts and controls to dashboard 
                dashboard.bind(issueFilter, dailyChart);

                // Draw the dashboard.
                dashboard.draw(view);
      }

    </script>
  </head>

  <body>
    <!--Div that will hold the dashboard-->
    <div id="dashboard_div2"></div>
    <!--Divs that will hold each control and chart-->
    <div id="filter_div2"></div>
    <div id="issue-div"></div>
    <div id="current_day" style="align: center; width: 1100px; height: 500px;"></div>

  </body>
</html>