具有双y轴谷歌图表的多个基线(Multiple baselines with dual y-axis Google Chart)

其他开发 IT屋
问 题

I'm using the Google Visualization API for a simple sales chart that has two series, number of sales and sales value, that I'm showing on a column chart with two vetical axes. The sales value can be negative, such as for returns, but this is causing the graph to show two different baselines. The zero baseline for number of sales is in line with the lowest sales value figure. Here's an example of the code with some sample data:

google.load('visualization', '1.0', { 'packages': ['corechart'] });
google.setOnLoadCallback(drawSalesChart);

function drawSalesChart() {
    var dataTable = new google.visualization.DataTable();
    dataTable.addColumn('string', 'Order Source');
    dataTable.addColumn('number', 'Num Sales');
    dataTable.addColumn('number', 'Sales Value');

    dataTable.addRows([
        ['Web (Order)', 300, 31000],
        ['Call Centre (Order)', 700, 61000],
        ['Call Centre (Return)', 50, -4100],
        ['Call Centre (Exchange)', 10, 800]
    ]);

    var options = {
        title: 'Sales by Order Source',
        hAxis: { title: 'Order Source' },
        series: {
            0: { targetAxisIndex: 0 },
            1: { targetAxisIndex: 1 },
        },
        vAxes: {
            0: { title: 'Num Sales' },
            1: { title: 'Sales Value' }
        }
    };

    new google.visualization.ColumnChart(
        document.getElementById('livesales-chart-container')).draw(dataTable, options);
}

I've been through the API documentation as there's information on setting the baseline but there doesn't seem to be a way to tie the zero of each vAxis to the same point. I've tried searching Google and StackOverflow and there are similar questions but I can't see that anyone has had this problem.

How can I, or even can I, show a single baseline at zero for both series?

解决方案

From a visualization perspective, it may be a lot better to create two separate charts on top of each other since the data provided is very different both in scope and in what it is explaining.

<!--
You are free to copy and use this sample in accordance with the terms of the
Apache license (http://www.apache.org/licenses/LICENSE-2.0.html)
-->

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <title>
      Google Visualization API Sample
    </title>
    <script type="text/javascript" src="http://www.google.com/jsapi"></script>
    <script type="text/javascript">
      google.load('visualization', '1', {packages: ['corechart']});
    </script>
    <script type="text/javascript">

      function drawSalesChart() {
        var dataTable = new google.visualization.DataTable();
        dataTable.addColumn('string', 'Order Source');
        dataTable.addColumn('number', 'Num Sales');
        dataTable.addColumn('number', 'Sales Value');

        dataTable.addRows([
          ['Web (Order)', 300, 31000],
          ['Call Centre (Order)', 700, 61000],
          ['Call Centre (Return)', 50, -4100],
          ['Call Centre (Exchange)', 10, 800]
        ]);

        var dataView1 = new google.visualization.DataView(dataTable);
        dataView1.setColumns([0,1]);

        var dataView2 = new google.visualization.DataView(dataTable);
        dataView2.setColumns([0,2]);

        var options1 = {
          title: 'Sales by Order Source',
          hAxis: { title: 'Order Source' },
          vAxis: { title: 'Num Sales' }
        };

        var options2 = {
          title: null,
          hAxis: { title: null, textPosition: 'none' },
          vAxis: { title: 'Sales Value' }
        };

        new google.visualization.ColumnChart(
          document.getElementById('chart1')).draw(dataView1, options1);
        new google.visualization.ColumnChart(
          document.getElementById('chart2')).draw(dataView2, options2);
      }

      google.setOnLoadCallback(drawSalesChart);

    </script>
  </head>
  <body style="font-family: Arial;border: 0 none;">
    <div id="chart1" style="width: 600px; height: 300px;"></div>
    <div id="chart2" style="width: 600px; height: 100px;"></div>
  </body>
</html>

Of course, this would need some prettying-up to make the graphs line up properly, and to make the colors work as you'd like, but this way you can focus on the main data you want to show, while keeping the other info nearby as a reference.

If you are insistent on doing them on the same graph, you will need to write a function to be able to calculate where the grid lines should lie (or figure out how Google does it, but I couldn't find it on a web search).

To figure out what the max/min values should be on a graph, an "easy" way is to take the difference between the minimum and maximum values, count the number of grid lines you will have (default for google is 5), round up to the nearest significant digit of your biggest number, and use those as your grid line dividers.

e.g. Taking your first column: 300, 700, 50, 10

Max Value: 700
Min Value: 10
Exponent: LEN(Max)-1 = 2 = 10^2, nearest 100
Grid Lines: 5 - 1 = 4 (assuming you want the bottom value to serve as the floor at the same rounding, you need 4 more iterations to go over the top value)
Difference Between Max and Min: 690
Required Interval: 690 / 4 = 172.5
Rounded up to the nearest 100: 200

Min Value: FLOOR(Min,200) = 0
Max Value: CEILING(Max,200) = 800

Grid Line 1: 0
Grid Line 2: 200
Grid Line 3: 400
Grid Line 4: 600
Grid Line 5: 800

Note, this matches what your chart shows. However, it won't work for negative values because the math gets a bit more complicated.

First you need to figure out the ratio of negative values to the total difference in min and max values.

e.g. Given your Column 2 data: 31000, 61000, -4100, 800

Min Value: -4100
Max Value: 61000
Difference: 65100
Negative Ratio: 6.3%

So 6.3% of your range is in the negative portion. Given 5 grid lines, that means that one grid line will need to be below 0, and you only have 4 grid lines for the positive portion. Since the negative portion is smaller than the positive portion, the positive portion will determine the grid line spacings.

So now you have 4 grid lines to cover the positive portion (0 - 61000), which means you have 3 segments from 0 to reach 61000.

That means 61000 / 3, rounded up to 4 significant digits, or 30,000.

That makes your gridlines:

-30,000
0
30,000
60,000
90,000

Coincidentally, this is what you got in your chart.

Now that you know the second series has 1 negative gridline, you'd have to readjust your first series to match the second one. So instead of having 5 gridlines (0 and 4 above), you now have 1 negative gridline, 1 0, and then 3 above zero that need to reach 700. So you take the 700 positive value you have, divide by 3, for 233.333.

Round that up to the nearest 100, and you get 300.

So your first chart max/min would be readjusted to -300, and 900 for the following gridlines:

-300
0
300
600
900

This will set the same baseline.

That will solve your problem -- all you need to do is code that logic in to Javascript! Let us know if you do it, I'm sure someone else will have the same issue down the line.

本文地址:IT屋 » Multiple baselines with dual y-axis Google Chart

问 题

我正在使用Google Visualization API制作一个简单的销售图表,该图表有两个系列,即销售和销售价值数量,我将在带有两个光轴的柱状图上展示。销售价值可能是负值,例如退货,但这会导致图表显示两个不同的基线。销售数量的零基线符合最低销售价值数字。以下是一些示例数据的代码示例:

  google.load('visualization','1.0',{'packages ':['corechart']}); 
google.setOnLoadCallback(drawSalesChart);

函数drawSalesChart(){
var dataTable = new google.visualization.DataTable();
dataTable.addColumn('string','Order Source');
dataTable.addColumn('number','Num Sales');
dataTable.addColumn('number','Sales Value');

dataTable.addRows([
['Web(Order)',300,31000],
['Call Center(Order)',700,61000],
['Call Center(Return)',50,-4100],
['Call Center(Exchange)',10,800]
]);

var options = {
title:'通过订单来源销售',
hAxis:{title:'订单来源'},
系列:{
0:{targetAxisIndex:0},
1:{targetAxisIndex:1},
},
vAxes:{
0:{title:'Num Sales'},
1:{title:'Sales Value'}
}
};

new google.visualization.ColumnChart(
document.getElementById('livesales-chart-container'))。draw(dataTable,options);
}


我已经通过了API文档,因为有设置基线的信息,但似乎没有办法将每个vAxis的零点绑定到同一点。我尝试过搜索谷歌和StackOverflow,也有类似的问题,但我看不出有人有这个问题。



我怎么能,在两个系列中显示一个零基线?


解决方案

从可视化的角度来看,创建两个单独的因为所提供的数据在范围和解释内容上都有很大的不同。



 < ;! -  
您可以根据
Apache许可条款(http://www.apache.org/licenses/LICENSE-2.0.html)$ b $自由复制和使用本示例b - >

<!DOCTYPE html PUBLIC“ - // W3C // DTD XHTML 1.0 Strict // EN”“http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict。 DTD“>
< html xmlns =“http://www.w3.org/1999/xhtml”>
< head>
< meta http-equiv =“content-type”content =“text / html; charset = utf-8”/>
< title>
Google Visualization API示例
< / title>
< script type =“text / javascript”src =“http://www.google.com/jsapi”>< / script>
< script type =“text / javascript”>
google.load('visualization','1',{packages:['corechart']});
< / script>
< script type =“text / javascript”>

函数drawSalesChart(){
var dataTable = new google.visualization.DataTable();
dataTable.addColumn('string','Order Source');
dataTable.addColumn('number','Num Sales');
dataTable.addColumn('number','Sales Value');

dataTable.addRows([
['Web(Order)',300,31000],
['Call Center(Order)',700,61000],
['Call Center(Return)',50,-4100],
['Call Center(Exchange)',10,800]
]);

var dataView1 = new google.visualization.DataView(dataTable);
dataView1.setColumns([0,1]);

var dataView2 = new google.visualization.DataView(dataTable);
dataView2.setColumns([0,2]);

var options1 = {
title:'通过订单来源销售',
hAxis:{title:'订单来源'},
vAxis:{title:' Num Sales'}
};

var options2 = {
title:null,
hAxis:{title:null,textPosition:'none'},
vAxis:{title:'Sales Value '}
};

google.visualization.ColumnChart(
document.getElementById('chart1'))。draw(dataView1,options1);
new google.visualization.ColumnChart(
document.getElementById('chart2'))。draw(dataView2,options2);
}

google.setOnLoadCallback(drawSalesChart);

< / script>
< / head>
< body style =“font-family:Arial; border:0 none;”>
< div id =“chart1”style =“width:600px; height:300px;”>< / div>
< div id =“chart2”style =“width:600px; height:100px;”>< / div>
< / body>
< / html>


当然,这需要一些修改,以使图形正确排列,并且使颜色可以随意使用,但这样您可以专注于您想要显示的主要数据,同时将其他信息作为参考。

你坚持要在同一个图上做它们,你需要编写一个函数来计算网格线应该在哪里(或者弄清楚Google如何去做,但是我在网络搜索中找不到它) 。

为了找出图表上最大/最小值应该是什么,一个“简单”的方法是取最小值和最大值之间的差值,您将拥有的网格线数量(默认为谷歌为5),四舍五入到最大数字的最接近的有效数字,并将它们用作网格线分隔线。

如第一栏:300,700,50和10

 最大值:700 
最小值:10
指数:LEN(最大)-1 = 2 = 10 ^ 2,最接近100
网格线:5 - 1 = 4(假设您希望底部值作为同一舍入处的底面,则需要4个以上的迭代超过最高值)
最大值和最小值之差:690
所需时间间隔:690/4 = 172.5
舍入到最接近的100:200

最小值:FLOOR(Min,200)= 0
最大值:CEILING(Max,200)= 800

网格线1:0
网格线2: 200
网格线3:400
网格线4:600
网格线5:800


请注意,这与您的图表显示的内容相符。然而,它不适用于负值,因为数学有点复杂。



首先,您需要计算负值与总差值的比率最小值和最大值。

例如给定您的第2列数据:31000,61000,-4100,800

 最小值:-4100 
最大值: 61000
差异:65100
负比率:6.3%


你的范围是负面的部分。给定5个网格线,这意味着一个网格线将需要低于0,并且只有4个网格线用于正部分。由于负值部分小于正值部分,所以正值部分将确定网格线的间距。所以现在你有4条网格线来覆盖正值部分(0 - 61000),这意味着你有3个部分从0到61000.

这意味着61000/3,四舍五入有效数字,或30,000。



这使得你的网格线:

  -30,000 
0
30,000
60,000
90,000


巧合的是,这是您得到的你的图表。



现在你知道第二个系列有1个负网格线,你必须重新调整你的第一个系列以匹配第二个系列。因此,现在不是有5个网格线(上面的0和4),现在有1个负网格线,1 0,然后是3以上零需要达到700.因此,您获得700的正值,除以3,为233.333 。



圆到最接近的100,然后你得到300。 min将被重新调整为-300,以及900用于以下网格线:

  -300 
0
300
600
900


这将设定相同的基线。



这将解决您的问题 - 您需要做的就是将这些逻辑编码到Javascript中!让我们知道如果你这样做,我相信别人会有同样的问题。


本文地址:IT屋 » 具有双y轴谷歌图表的多个基线