Reputation: 637
I just started learning Google Apps Script/JavaScript and would like to know how to reshape, manipulate multi-dimensional data from Google Spreadsheets. I've read some posts like this one saying that GAS is not flexible with data manipulation. But the post is a bit dated so I wonder if there are any new changes/additions to GAS functionalities.
Below is the functional code to start with. It can successfully draw the chart below. But what I really want is:
I tried by using PIVOT B
, GROUP BY dayOfWeek(toDate(A)),
but it returned either One or more participants failed to draw()×
error message or unexpected format.
<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>
Upvotes: 1
Views: 764
Reputation: 637
I think I had an epiphany moment this morning. After learning and borrowing codes from other posts (noted below in the script), I was able to produce the chart I want.
I think this is a work-around to my problem. If you have better suggestions, I would be grateful to learn.
<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 http://stackoverflow.com/questions/17924826/add-tooltips-to-a-google-line-chart-with-multiple-data-series-with-simplified
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>
Upvotes: 0
Reputation: 45720
You can add multiple filters. To do so with your source data:
You need to SELECT
the additional text column B
and include it in the GROUP BY
and optionally LABEL
statements.
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' ");
Notice that we have B
first. If we don't, we get the errors you mentioned. That is because LineChart
expects a label column then columns of related sequences. Moving the text column to the start of the SELECT
side-steps the errors.
Add a filter for "Issue".
var issueFilter = new google.visualization.ControlWrapper({
'controlType': 'CategoryFilter',
'containerId': 'issue-div',
'options': {
'filterColumnLabel': 'Issue'
}
});
Bind the new filter to the dashboard.
dashboard.bind(issueFilter, dailyChart);
And that's it!
... No, it's not. Because Issue
was our first column, it's been picked up as the major axis, which isn't so useful.
Your challenge at this point is to select an appropriate visualization that matches your data - because the LineChart doesn't.
Note: This snippet is runnable.
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>
Upvotes: 1