Showing posts with label charts. Show all posts
Showing posts with label charts. Show all posts

Monday, April 30, 2018

Google Charts Legend Position Top Not Working

Leave a Comment

You can use this jsfiddle for testing

Full Code:

google.load('visualization', '1.1', {     'packages': ['bar'] }); google.setOnLoadCallback(drawStuff);  function drawStuff() {     var data = new google.visualization.DataTable();     data.addColumn('string', 'Topping');     data.addColumn('number', 'Nescafe Instant');     data.addColumn('number', 'Folgers Instant');     data.addColumn('number', 'Nescafe Beans');     data.addColumn('number', 'Folgers Beans');     data.addRows([         ['2001', 500, 1200, 816, 200],         ['2002', 163, 231, 539, 594],         ['2003', 125, 819, 200, 578],         ['2004', 197, 536, 613, 500]     ]);      // Set chart options     var options = {         isStacked: true,         width: 800,         height: 600,         legend: {             position: "top",             alignment: "",             textStyle: {                     color: "#999"                 }             },         chart: {             title: 'Year-by-year coffee consumption',             subtitle: 'This data is not real'         },          series: {             2: {                 targetAxisIndex: 1             },             3: {                 targetAxisIndex: 1             }         }     };  }; 

Question :

I want to display legend above of the chart. I have used legend position : top. But it is not working. I can move legend either left side or right side without a problem changing legend position value.

But I can't move legend top or bottom. Why is that? What was the my mistake?

1 Answers

Answers 1

First, you need to use the correct library, as jsapi is out of date and should no longer be used,
according to the release notes.

The version of Google Charts that remains available via the jsapi loader is no longer being updated consistently. Please use the new gstatic loader.js from now on.

This will only change the load statement:

old load statement...

google.load('visualization', '1.1', {   packages: ['bar'] }); 

new load statement...

google.charts.load('current', {   packages: ['corechart'] }); 

Next, as seen above, for classic charts, use --> packages: ['corechart']
instead of 'bar'

Google has provided a chart option, to style classic charts similar to material:

theme: 'material' 

When creating a classic chart, use the following namespace:

google.visualization.ColumnChart 

vs. material

google.charts.Bar 

See the following working snippet:

google.charts.load('current', {    packages: ['corechart']  }).then(function () {    var data = new google.visualization.DataTable();    data.addColumn('string', 'Topping');    data.addColumn('number', 'Nescafe Instant');    data.addColumn('number', 'Folgers Instant');    data.addColumn('number', 'Nescafe Beans');    data.addColumn('number', 'Folgers Beans');    data.addRows([      ['2001', 500, 1200, 816, 200],      ['2002', 163, 231, 539, 594],      ['2003', 125, 819, 200, 578],      ['2004', 197, 536, 613, 500]    ]);      var options = {      chartArea: {        height: '100%',        width: '100%',        top: 64,        left: 64,        right: 32,        bottom: 32      },      height: '100%',      width: '100%',      isStacked: true,      legend: {        position: 'top',        textStyle: {          color: '#999'        }      },      title: 'Year-by-year coffee consumption'    };      var container = document.getElementById('chart_div');    var chart = new google.visualization.ColumnChart(container);      drawChart();    window.addEventListener('resize', drawChart, false);    function drawChart() {      chart.draw(data, options);    }  });
html, body {    height: 100%;    margin: 0px 0px 0px 0px;    overflow: hidden;    padding: 0px 0px 0px 0px;  }    .chart {    height: 100%;  }
<script src="https://www.gstatic.com/charts/loader.js"></script>  <div class="chart" id="chart_div"></div>

Note: the only advantage to material is having multiple stacks,
in classic, you cannot group multiple stacks.


UPDATE

There are no options you can change to position the legend to top or bottom, it is simply not supported.

The only solution would be to manually build a custom legend; following is an example of how this could be done:

google.charts.load('current', {    packages: ['bar']  }).then(function () {    var data = new google.visualization.DataTable();    data.addColumn('string', 'Topping');    data.addColumn('number', 'Nescafe Instant');    data.addColumn('number', 'Folgers Instant');    data.addColumn('number', 'Nescafe Beans');    data.addColumn('number', 'Folgers Beans');    data.addRows([      ['2001', 500, 1200, 816, 200],      ['2002', 163, 231, 539, 594],      ['2003', 125, 819, 200, 578],      ['2004', 197, 536, 613, 500]    ]);      var options = {      chart: {        title: 'coffee consumption',        subtitle: 'This data is not real'      },      height: '100%',      isStacked: true,      legend: {        position: 'none'      },      series: {        2: {          targetAxisIndex: 1        },        3: {          targetAxisIndex: 1        }      },      width: '100%'    };      var container = document.getElementById('chart_div');    var chart = new google.charts.Bar(container);      drawChart();    window.addEventListener('resize', drawChart, false);    function drawChart() {      chart.draw(data, google.charts.Bar.convertOptions(options));    }      // add legend marker    function addLegendMarker(markerProps) {      var legendMarker = document.getElementById('template-legend-marker').innerHTML;      for (var handle in markerProps) {        if (markerProps.hasOwnProperty(handle)) {          legendMarker = legendMarker.replace('{{' + handle + '}}', markerProps[handle]);        }      }      document.getElementById('legend_div').insertAdjacentHTML('beforeEnd', legendMarker);    }      // chart ready event    google.visualization.events.addListener(chart, 'ready', function () {      var legend = document.getElementById('legend_div');        // get colors from chart      var colorPallette = [];      var colorsBottom = [];      var colorsTop = [];      var stacks = container.getElementsByTagName('*');      Array.prototype.forEach.call(stacks, function(stack) {        switch (stack.tagName) {          case 'path':            if ((stack.getAttribute('fill') !== null) && (stack.getAttribute('fill') !== '#ffffff')) {              if (colorsTop.indexOf(stack.getAttribute('fill')) === -1) {                colorsTop.push(stack.getAttribute('fill'));              }            }            break;            case 'rect':            if ((stack.getAttribute('fill') !== null) && (stack.getAttribute('fill') !== '#ffffff')) {              if (colorsBottom.indexOf(stack.getAttribute('fill')) === -1) {                colorsBottom.push(stack.getAttribute('fill'));              }            }            break;        }      });      for (var i = 0; i < colorsBottom.length; i++) {        colorPallette.push(colorsBottom[i]);        colorPallette.push(colorsTop[i]);      }        // clear previous legend      legend.innerHTML = '';        // add legend marker for each Y axis column - skip X axis --> i = 1      for (var i = 1; i < data.getNumberOfColumns(); i++) {        var markerProps = {};        markerProps.index = i;        markerProps.color = colorPallette[i - 1];        markerProps.label = data.getColumnLabel(i);        addLegendMarker(markerProps);      }        // add "hover" event to each legend marker      var currentSelection;      var markers = legend.getElementsByTagName('DIV');      Array.prototype.forEach.call(markers, function(marker) {        marker.addEventListener('mouseover', function (e) {          currentSelection = chart.getSelection();          var marker = e.target || e.srcElement;          if (marker.tagName.toUpperCase() !== 'DIV') {            marker = marker.parentNode;          }          var columnIndex = parseInt(marker.getAttribute('data-columnIndex'));          chart.setSelection([{column: columnIndex}]);        }, false);        marker.addEventListener('mouseout', function (e) {          chart.setSelection([]);        }, false);      });    });  });
html, body {    height: 100%;    margin: 0px 0px 0px 0px;    overflow: hidden;    padding: 0px 0px 0px 0px;  }    .chart {    height: 100%;  }    #legend_div {    color: #999;    font-family: Roboto;    position: absolute;    right: 0px;    text-align: right;    top: 0px;    width: 60%;    z-index: 1000;  }    .legend-marker {    display: inline-block;    padding: 6px 6px 6px 6px;  }    .legend-marker-color {    border-radius: 25%;    display: inline-block;    height: 12px;    width: 12px;  }
<script src="https://www.gstatic.com/charts/loader.js"></script>  <div id="legend_div"></div>  <div class="chart" id="chart_div"></div>    <!-- template for building marker -->  <script id="template-legend-marker" type="text/html">    <div class="legend-marker" data-columnIndex="{{index}}">      <div class="legend-marker-color" style="background-color: {{color}}"></div>      <span>{{label}}</span>    </div>  </script>

Read More

Tuesday, April 24, 2018

Charts lib lags on scroll

Leave a Comment

I've just implemented the library Charts (https://github.com/danielgindi/Charts) in a tableview, but I experience pretty heavy lag during scrolling, when my charts hold a lot of data.

I have a method inside the ChartTableViewCell where I draw the chart based on the data I pass and call from my viewcontroller.

func updateCell(chartData: ChartData) {      DispatchQueue.global(qos: .background).async {         print("This is run on the background queue")     self.readings = (readings != nil) ? readings!.readings : []         self.period = period          if (chartData.isKind(of: LineChartData.self)) {             data.lineData = chartData as! LineChartData         }         else if (chartData.isKind(of: BarChartData.self)) {             data.barData = chartData as! BarChartData         }     }      DispatchQueue.main.async {     self.chartView.data = data     } } 

In my tableViewController I call the function after parsing the data:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {          let meta = chartMetas[indexPath.row]         let cell = tableView.dequeueReusableCell(withIdentifier: "chartCell", for: indexPath) as! ChartTableViewCell         cell.readings = currentReadings         cell.updateCell()          return cell     } 

What did I miss? Since the view is lagging so hard when scrolling.

UPDATE:

I tried, as suggested, to prepare the chart data in the viewcontroller and pass it to the cell. However it seems like the problem in the resuseable cells, the lags appears when I scroll and a new cell enters the screen. How can I fix this? It is pretty annoying.

UPDATE 2: It looks like the Charts aren't supposed to be used in a tableview... https://github.com/danielgindi/Charts/issues/3395

3 Answers

Answers 1

To get a serious performance boost, you'll likely need to implement UITableViewDataSourcePrefecting. By doing this, the table view will call into your delegate to let you know that a cell will be needed ahead of time. This will give your code a chance to prepare and render any data it needs.

From the documentation:

You use a prefetch data source object in conjunction with your table view’s data source to begin loading data for cells before the tableView(_:cellForRowAt:) data source method is called.The following steps are required to add a prefetch data source to your table view:

  • Create the table view and its data source.

  • Create an object that adopts the UITableViewDataSourcePrefetching protocol, and assign it to the prefetchDataSource property on the table view.

  • Initiate asynchronous loading of the data required for the cells at the specified index paths in your implementation of tableView(_:prefetchRowsAt:).

  • Prepare the cell for display using the prefetched data in your tableView(_:cellForRowAt:) data source method.

  • Cancel pending data load operations when the table view informs you that the data is no longer required in the tableView(_:cancelPrefetchingForRowsAt:) method.

Answers 2

Try to prepare all the chart data before you pass it to the cell. I mean make all the stuff you do I the updateCell() func in your tableViewController probably in viewDidLoad and pass the generated chart Datas to an array.

Then in tableView(tableView: , cellForRowAt indexPath: ) just pass the previously generated data to your cell.

So your updateCell func should looks like so:

func updateCell(lineChartData: LineChartData) {     // SET DATA AND RELOAD     chartView.data = lineChartData     chartView.fitScreen() } 

And you tableView

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {      let meta = chartMetas[indexPath.row]     let cell = tableView.dequeueReusableCell(withIdentifier: "chartCell", for: indexPath) as! ChartTableViewCell     cell.readings = currentReadings     cell.updateCell(lineChartData: arrayOfGeneratedChartData[indexPath.row])      return cell } 

Seems that you keeps entries in the cell, so don't do this. Keep them in the tableViewController. Pass only required data to the cell and don't do hard tasks in the cell methods.

Answers 3

You chart library is probably doing some pretty heavy processing when there are large data sets. If the author of this library didn't account for this, you are executing this heavy processing on the main thread every time you scroll and a new cell appears on the screen. This will certainly cause stuttering.

I would look to change the cell setup method to simply display a spinner or placeholder table, and then kick off the loading of data and building of the chart on a background thread. This should cause the UITableView scrolling to be smooth, but you will see the cells with placeholders as you scroll through the table, where the charts get populated after the background thread processing completes.

Running things on a background thread in Swift is pretty easy.

Just make sure when you are ready to update the UI with the chart, you do that on the main thread (Al UI updates should be executed on the main thread or you will see weird visual oddities or delays in the UI being updated). So maybe hide the placeholder image and show the chart using animations on the main thread after the heavy processing is done on the background thread.

What if you did something like this?

func updateCell(chartData: ChartData) {      DispatchQueue.global(qos: .background).async {         print("This is run on the background queue")     self.readings = (readings != nil) ? readings!.readings : []         self.period = period          if (chartData.isKind(of: LineChartData.self)) {             data.lineData = chartData as! LineChartData         }         else if (chartData.isKind(of: BarChartData.self)) {             data.barData = chartData as! BarChartData         }               self.chartView.data = data          DispatchQueue.main.async {             // something that triggers redrawing of the chartView.            chartView.fitScreen()         }     } } 

Note that the main thread call is inside the background thread execution block. This means all the stuff will run on the background thread, and when that background thread stuff is done, it will call something in the chart library that will do the UI refresh. Again, this won't help if your chart library is written to force long running operations to happen on the main thread.

Read More

Sunday, January 7, 2018

Set custom dataset values - Charts 3.0.4 and Using Swift 4.0

Leave a Comment

I want to show some random values in position of (1,2,3,4,5,6) like (16,23,323,63,8,66) in graph points. Im using line chart in charts framework.

Is there any formatter available to get this done?

enter image description here

The above image shows an example graph which I wants to plot.

1 Answers

Answers 1

Create a custom formatter:

class RandomCustomFormatter: NSObject, IValueFormatter {      convenience init(lineChart: LineChartView, xArray: [Double], yArray: [Double]) {         self.init()          var y = yArray         y.shuffle(count: y.count)         var dataEntries = [ChartDataEntry]()         var c = 0         for _ in xArray {         dataEntries.append(ChartDataEntry(x: xArray[c], y: y[c]))         c+=1         }         let theDataSet = LineChartDataSet(values: dataEntries, label: "Test Data")         print("dataentries shuffled: \(dataEntries)")          lineChart.data = LineChartData(dataSet: theDataSet)       }       public func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String {         let valueToUse = Int(value)         print("valuetouse: \(valueToUse)")         return String(valueToUse)     } } 

add array extension:

extension Array {     mutating func shuffle(count: Int) {         for _ in 0...count-1 {             sort { (_,_) in arc4random() < arc4random() }         }     } } 

set the formatter:

//x datapoints let x = [1.0,2.0,3.0,4.0,5.0,6.0] //y datapoints var y = [8.0,16.0,23.0,63.0,66.0,323.0] let formatter = RandomCustomFormatter(lineChart: lineChart, xArray: x, yArray: y) self.lineChart?.data?.setValueFormatter(formatter) 

result 1:

result 1

result 2:

result 2

Read More

Thursday, October 5, 2017

Android: mpandroidchart not shown properly

Leave a Comment

I am working on a groupped Barchart using mpandroidchart library:

Code:

public void drawing_length(String length)     {         ArrayList<String> xx = new ArrayList<String>();         ArrayList<String> yy = new ArrayList<String>();         ArrayList<String> zz = new ArrayList<String>();          int L = 7;         if (length.equals("7days"))         {             L = 7;         }         if (length.equals("14days"))         {             L = 14;         }         if (length.equals("30days"))         {             L = 30;         }         if (L > x.size())         {             L = x.size();         }         for (int g=0; g<L; g++)         {             xx.add(x.get(g));       // newest first, oldest last             yy.add(y.get(g));             zz.add(z.get(g));         }          Collections.reverse(xx);        //oldest first, newest last, i.e. sequential         Collections.reverse(yy);         Collections.reverse(zz);         setupGrouppedChart(xx,yy,zz);     }  public void setupGrouppedChart(ArrayList<String> x, ArrayList<String> y1, ArrayList<String> y2)     {         float barWidth;         float barSpace;         float groupSpace;          barWidth = 0.3f;         barSpace = 0f;         groupSpace = 0.4f;          ll_combined_chart.setVisibility(View.GONE);         ll_scatteredchart.setVisibility(View.GONE);         ll_groupped_barchart.setVisibility(View.VISIBLE);          chart_grouppedBar = (BarChart) findViewById(R.id.chart_grouppedBar);         chart_grouppedBar.getDescription().setEnabled(false);         chart_grouppedBar.setBackgroundColor(Color.TRANSPARENT);         chart_grouppedBar.setDrawGridBackground(false);         chart_grouppedBar.setPinchZoom(false);               chart_grouppedBar.setScaleEnabled(false);         chart_grouppedBar.setDrawBarShadow(false);         chart_grouppedBar.setDrawValueAboveBar(false);         chart_grouppedBar.setHighlightFullBarEnabled(false);         chart_grouppedBar.setHighlightPerDragEnabled(false);         chart_grouppedBar.setHighlightPerTapEnabled(false);          chart_grouppedBar.setDoubleTapToZoomEnabled(false);         chart_grouppedBar.getXAxis().setDrawGridLines(false);         chart_grouppedBar.getAxisLeft().setDrawGridLines(false);         chart_grouppedBar.getAxisRight().setDrawGridLines(false);         chart_grouppedBar.getAxisRight().setEnabled(false);         chart_grouppedBar.animateXY(800, 800);         chart_grouppedBar.getDescription().setText("This is testing Description");         chart_grouppedBar.setVisibleXRangeMaximum(14f);          Utilities.custom_toast(Stat.this, "Xsize=" + x.size(), "gone", "short");         final String[] x_name = new String[x.size()];         for (int j = 0;  j <x.size(); j++)         {             String[] temp = x.get(j).split("-");             x_name[j] = temp[0] + "/" + temp[1];         }         XAxis xLabels = chart_grouppedBar.getXAxis();         xLabels.setPosition(XAxis.XAxisPosition.BOTTOM);         xLabels.setGranularity(1f);          xLabels.setValueFormatter(new IAxisValueFormatter()         {             @Override             public String getFormattedValue(float value, AxisBase axis)             {                 return x_name[(int) value % x_name.length];             }         });          BarDataSet set1, set2;         ArrayList<BarEntry> valueSet1 = new ArrayList<>();         ArrayList<BarEntry> valueSet2 = new ArrayList<>();         for (int i = 0; i < x_name.length; i++)         {             float val1 = Float.parseFloat(y1.get(i));             BarEntry v1e1 = new BarEntry(i, val1);             valueSet1.add(v1e1);              float val2 = Float.parseFloat(y2.get(i));             BarEntry v1e2 = new BarEntry(i, val2);             valueSet2.add(v1e2);         }          if (chart_grouppedBar.getData() != null && chart_grouppedBar.getData().getDataSetCount() > 0)         {             set1 = (BarDataSet) chart_grouppedBar.getData().getDataSetByIndex(0);             set2 = (BarDataSet) chart_grouppedBar.getData().getDataSetByIndex(1);              set1.setValues(valueSet1);             set2.setValues(valueSet2);              chart_grouppedBar.getData().setHighlightEnabled(false);             chart_grouppedBar.groupBars(0, groupSpace, barSpace);             chart_grouppedBar.getData().notifyDataChanged();             chart_grouppedBar.notifyDataSetChanged();         }         else         {             set1 = new BarDataSet(valueSet1, "A");             set1.setColor(getResources().getColor(R.color.pink_light));             set2 = new BarDataSet(valueSet2, "B");             set2.setColor(getResources().getColor(R.color.purple1));              BarData data = new BarData(set1, set2);             //data.setValueFormatter(new LargeValueFormatter());             //data.setValueTypeface(mTfLight);              data.setValueFormatter(new MyValueFormatter());             chart_grouppedBar.setData(data);             chart_grouppedBar.getBarData().setBarWidth(barWidth);             chart_grouppedBar.getXAxis().setAxisMaximum(0 + chart_grouppedBar.getBarData().getGroupWidth(groupSpace, barSpace) * x_name.length);             chart_grouppedBar.getAxisLeft().setAxisMinimum(0);             chart_grouppedBar.getAxisLeft().setValueFormatter(new MyYAxisValueFormatter());             chart_grouppedBar.getData().setHighlightEnabled(false);             chart_grouppedBar.groupBars(0, groupSpace, barSpace);             chart_grouppedBar.invalidate(); // refresh         }     } 

Screen capture:

enter image description here

Question:

  1. The legend for the x-axis is shifted. How can it be adjusted?
  2. There should be 9 entries but it is now only showing 7 entries. I suspect it is on the right but the screen cannot be scrolled to the right. The toast report the size is 9 correctly. How could the chart be scrollable?
  3. The Y-axis is also wrong. The height of the bars do not match with the Y-axis.

Thank you.

2 Answers

Answers 1

Hard-Coded bar width is the problem.Find bar width dynamically like below

((1.00f- 0.20f) / barEntryCount) - 0.05f 

where

0.05f -> Bar Spacing; 0.20f -> Group Spacing; 

Please read this thread.

Regarding alignment of X Axis Labels (Question 1)

do like following

if (dataSets.size() > 1) {    barChart.getXAxis().setCenterAxisLabels(true); else    barChart.getXAxis().setCenterAxisLabels(false); 

Regarding the 2nd question

Please verify that your dataset count is 9.If you given 9 elements in dataset surly ,it will plot.Please check other view properties including view clipping.

Answers 2

For your 2nd question

How could the chart be scrollable?

chart_grouppedBar.setDragEnabled(true);  

Reference link

For 3rd question

The Y-axis is also wrong. The height of the bars do not match with the Y-axis.

try to set Min & Max value of axis and label count. For example:

    chart_grouppedBar.getAxisLeft().setAxisMaxValue(MAX VALUE FROM YOUR DATASET);     chart_grouppedBar.getAxisLeft().setLabelCount(8, true); 

reference link

Read More

Wednesday, August 23, 2017

Create intraday chart with R using chartSeries, candleChart, or barChart?

Leave a Comment

Can chartSeries, candleChart, or barChart be used to create an intraday chart in R?

chartSeries, candleChart, and barChart are part of the quantmod package for R.

2 Answers

Answers 1

First we need some example intraday trading data, which you can get for free from a variety of sites including Google's Undocumented Finance API.

Get some example data (hourly intervals)

query_addr <- 'https://www.google.com/finance/getprices' stock_symb <- 'GOOG' stock_exch <- 'NASD' intvl_size <- 60*60 # 1 hr interval (in seconds) -- use 24*this for daily period_len <- '90d' output_fmt <- 'd,o,h,l,c,v' # date, open, high, low, close, volume  library(httr) resp <-    POST(url = query_addr,         query = list(q = stock_symb,                      x = stock_exch,                      i = intvl_size,                      p = period_len,                     f = output_fmt) )  df <-    read.csv(text = content(resp),            skip = 7,            header = FALSE,            stringsAsFactors = FALSE)  # we need a function to munge the date convention used by google finance API g_fin_date <- function(dstr, intvl_size){   unix_dates <- numeric(length(dstr))   date_is_unix <- grepl('^a',dstr)   unix_dates[date_is_unix] <- as.numeric(sub('^a','',dstr[date_is_unix]))   for(i in 2L:length(dstr)){     if(!date_is_unix[i]){       unix_dates[i] <- unix_dates[i-1] + intvl_size     }   }   return(as.POSIXct(unix_dates,origin="1970-01-01",tz="GMT" )) }  # see header of resp text for column order names(df) <- c('close_date','Close','High','Low','Open','Volume')  df[,'close_date'] <- g_fin_date(df[,'close_date'], intvl_size=intvl_size) 

Here I have just chosen hourly open (i.e. beginning price), high, low, close (i.e. ending price)-- but you can specify a finer level of detail if you desire -- it will still roll up to a larger period with quantmod::to.period().

Make an xts

Once we have a data frame (such as you might obtain from an API or flat file) then you need to convert the data to xts. Note that for xts the timestamp must be the row name (and can be dropped from the columns).

library(xts) rownames(df) <- df$close_date df$close_date <- NULL 

Convert to OHLC (Open, High, Low, Close) using xts

This is straightforward aggregation -- see ?to.period

GOOG <- to.hourly(as.xts(df)) # for daily use to.daily(as.xts(df)) 

More chart examples available at quantmod.com.

Make some charts using quantmod

There are great charts already built into quantmod, including the ones you mentioned.

library(quantmod) chartSeries(GOOG) barChart(GOOG, theme='white.mono',bar.type='hlc') candleChart(GOOG,multi.col=TRUE,theme='white')  

Enjoy your charts

enter image description here

enter image description here

enter image description here

Answers 2

Me: "I'll take intra-day time series charts in R for 100 Alex!" :D

Alex: "THIS popular format for financial time series can be used by quantmod functions chartSeries, candleChart, and barChart to create intraday charts in R"

Me: "What is an xts object, indexed by data/time stamp, containing prices for the Open, High, Low, and Close?

Alex: "Right you are!"

Read More

Wednesday, June 7, 2017

Add section to annotation chart?

Leave a Comment

I'm using Google Charts' Annotation Chart to display data. Everything's working but it's not showing the volume section, as seen in this google finance chart that, I believe, uses the same chart.

Here's what I have so far, but I don't know how to include that section:

      google.charts.load('current', {'packages':['annotationchart']});        google.charts.setOnLoadCallback(drawChart);          function drawChart() {          var data = new google.visualization.DataTable();          data.addColumn('date', 'Date');          data.addColumn('number', 'Col1');          data.addColumn('string', 'Col2');          data.addColumn('string', 'Col3');          data.addColumn('number', 'Col4');          data.addColumn('string', 'Col5');          data.addColumn('string', 'Col6');          data.addRows([            [new Date(2017, 2, 15), 85, 'More', 'Even More',                                    91, undefined, undefined],            [new Date(2017, 2, 16), 93, 'Sales', 'First encounter',                                    99, undefined, undefined],            [new Date(2017, 2, 17), 75, 'Sales', 'Reached milestone',                                    96, 'Att', 'Good'],            [new Date(2017, 2, 18), 60, 'Sales', 'Low',                                    80, 'HR', 'Absences'],            [new Date(2017, 2, 19), 95, 'Sales', 'Goals',                                    85, 'HR', 'Vacation'],            [new Date(2017, 2, 20), 40, 'Sales', 'Training',                                    67, 'HR', 'PTO']          ]);            var chart = new google.visualization.AnnotationChart(document.getElementById('chart_div'));            var options = {            displayAnnotations: true          };            chart.draw(data, options);        }
	<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>  	<div id='chart_div' style='width: 900px; height: 500px;'></div>

This is what the google finance chart looks like, but I can't seem to include the volume section marked in red: enter image description here

1 Answers

Answers 1

the annotation chart does not include an option for the middle chart / volume section

this could be added manually by drawing another, separate chart

however, the second chart cannot be placed in between the annotation chart and it's range filter

as such, you would need to turn off the annotation's range filter
and draw your own ChartRangeFilter

typically, custom filters are bound to charts using a dashboard

however, while building the example for this answer,
i noticed the annotation chart doesn't re-draw properly

after the filter has been applied, and then removed,
the annotation chart does not return to the original state
to correct, need to create the annotation chart every time it is drawn

see following working snippet,

a column chart is used for the volume section

the range filter is bound manually using the 'statechange' event

google.charts.load('current', {    callback: drawDashboard,    packages: ['annotationchart', 'controls', 'corechart']  });    function drawDashboard() {    var data = new google.visualization.DataTable();    data.addColumn('date', 'Date');    data.addColumn('number', 'Col1');    data.addColumn('string', 'Col2');    data.addColumn('string', 'Col3');    data.addColumn('number', 'Col4');    data.addColumn('string', 'Col5');    data.addColumn('string', 'Col6');    data.addRows([      [new Date(2017, 2, 15), 85, 'More', 'Even More',                              91, undefined, undefined],      [new Date(2017, 2, 16), 93, 'Sales', 'First encounter',                              99, undefined, undefined],      [new Date(2017, 2, 17), 75, 'Sales', 'Reached milestone',                              96, 'Att', 'Good'],      [new Date(2017, 2, 18), 60, 'Sales', 'Low',                              80, 'HR', 'Absences'],      [new Date(2017, 2, 19), 95, 'Sales', 'Goals',                              85, 'HR', 'Vacation'],      [new Date(2017, 2, 20), 40, 'Sales', 'Training',                              67, 'HR', 'PTO']    ]);      var rangeFilter = new google.visualization.ControlWrapper({      controlType: 'ChartRangeFilter',      containerId: 'control_div',      dataTable: data,      options: {        filterColumnLabel: 'Date',        ui: {          chartOptions: {            height: 60,            width: '100%',            chartArea: {              width: '100%'            },            chartType: 'AreaChart'          }        }      },      view: {        columns: [0, 1, 4]      }    });      google.visualization.events.addListener(rangeFilter, 'ready', drawCharts);    google.visualization.events.addListener(rangeFilter, 'statechange', drawCharts);      rangeFilter.draw();      function drawCharts() {      var filterState = rangeFilter.getState();      var filterRows = data.getFilteredRows([{        column: 0,        minValue: filterState.range.start,        maxValue: filterState.range.end      }]);      var viewAnn = new google.visualization.DataView(data);      viewAnn.setRows(filterRows);        var chartAnn = new google.visualization.AnnotationChart(document.getElementById('chart_ann'));      var optionsAnn = {        displayAnnotations: false,        displayRangeSelector: false      };      chartAnn.draw(viewAnn, optionsAnn);        var viewCol = new google.visualization.DataView(data);      viewCol.setColumns([0, 1, 4]);      viewCol.setRows(filterRows);        var chartCol = new google.visualization.ColumnChart(document.getElementById('chart_col'));      var optionsCol = {        hAxis: {          textStyle: {            color: 'transparent'          }        },        height: 72,        legend: 'none',        theme: 'maximized',        vAxis: {          textStyle: {            color: 'transparent'          }        }      };      chartCol.draw(viewCol, optionsCol);    }  }
<script src="https://www.gstatic.com/charts/loader.js"></script>  <div id="chart_ann"></div>  <div id="chart_col"></div>  <div id="control_div"></div>

Read More

Thursday, May 11, 2017

Chartjs animate x-axis

Leave a Comment

I want to use a chartjs linechart to visualize my data points. Chartjs seems to animate the graph by default, but it does not animate the values on the x-axis. The x-axis only move in discrete steps.

Is there any way to enable animation on the axis also?

Thanks!

3 Answers

Answers 1

As far as I am aware, ChartJS does not support x-axis animation out-of-the-box. So you'll have to hack it. There are several ways to possibly do this, but the following method seems to work.

When a chart is updated, the following steps occur: 1) The axes are drawn, and then 2) a draw() function is called to draw the data. There are different draw() functions for different chart types, and the function for line charts is Chart.controllers.line.prototype.draw. The draw() functions take one argument, which I will call animationFraction, that indicates how complete the animation is as a fraction. For instance, if an animation is 5% complete, animationFraction will be 0.05, and if an animation is 100% complete (i.e. if the chart is in its final form), animationFraction=1. The draw() function is called at each step of the animation to update the data display.

One hack to animate the x-axis then is to monkey-patch the line chart draw() function to translate the canvas in the horizontal dimension at every draw step:

var hShift = (1-animationFraction)*ctx.canvas.width; 

hShift is the horizontal shift in pixels of the chart. As defined above, the data will sweep in from the right; if you want it to sweep in from the left, you can make the above negative. You then save the canvas context state, transform the canvas using hShift, draw the chart data, and then restore the canvas to its original state so that on the next animation frame the axes will be drawn in the correct spot:

ctx.save(); ctx.setTransform(1, 0, 0, 1, hShift, 0); ctx.oldDraw.call(this, animationFraction); ctx.restore(); 

In the above, this refers to the chart object, and oldDraw refers to the original line chart drawing function that was saved previously:

var oldDraw = Chart.controllers.line.prototype.draw; 

You can additionally setup your new draw() function to read new animation options that allow you to set whether the x-axis and y-axis are animated:

var oldDraw = Chart.controllers.line.prototype.draw; Chart.controllers.line.prototype.draw = function(animationFraction) {     var animationConfig = this.chart.options.animation;     if (animationConfig.xAxis === true) {         var ctx = this.chart.chart.ctx;         var hShift = (1-animationFraction)*ctx.canvas.width;         ctx.save();         ctx.setTransform(1, 0, 0, 1, hShift,0);         if (animationConfig.yAxis === true) {             oldDraw.call(this, animationFraction);         } else {             oldDraw.call(this, 1);         }         ctx.restore();     } else if (animationConfig.yAxis === true) {         oldDraw.call(this, animationFraction);     } else {         oldDraw.call(this, 1);     } } 

You can then create a line chart with both axes animated with:

var lineChart = new Chart(ctx, {     type: 'line',     data: data,     options: {          animation: {              duration: 5000,             xAxis: true,             yAxis: true,         }     } }); 

See https://jsfiddle.net/16L8sk2p/ for a demo.

Answers 2

I am no expert in javascript but I found an example for Chartjs that, when inserted a new data point, updates the x-axis via animation as it seems, maybe it helps you: example.

Example source: sitepoint.com

Answers 3

You can loop through the points / bars onAnimationComplete and display the values


Preview

enter image description here


HTML

<canvas id="myChart1" height="300" width="500"></canvas> <canvas id="myChart2" height="300" width="500"></canvas> 

Script

var chartData = {     labels: ["January", "February", "March", "April", "May", "June"],     datasets: [         {             fillColor: "#79D1CF",             strokeColor: "#79D1CF",             data: [60, 80, 81, 56, 55, 40]         }     ] };  var ctx = document.getElementById("myChart1").getContext("2d"); var myLine = new Chart(ctx).Line(chartData, {     showTooltips: false,     onAnimationComplete: function () {          var ctx = this.chart.ctx;         ctx.font = this.scale.font;         ctx.fillStyle = this.scale.textColor         ctx.textAlign = "center";         ctx.textBaseline = "bottom";          this.datasets.forEach(function (dataset) {             dataset.points.forEach(function (points) {                 ctx.fillText(points.value, points.x, points.y - 10);             });         })     } });  var ctx = document.getElementById("myChart2").getContext("2d"); var myBar = new Chart(ctx).Bar(chartData, {     showTooltips: false,     onAnimationComplete: function () {          var ctx = this.chart.ctx;         ctx.font = this.scale.font;         ctx.fillStyle = this.scale.textColor         ctx.textAlign = "center";         ctx.textBaseline = "bottom";          this.datasets.forEach(function (dataset) {             dataset.bars.forEach(function (bar) {                 ctx.fillText(bar.value, bar.x, bar.y - 5);             });         })     } }); 

Fiddle - http://jsfiddle.net/uh9vw0ao/

Read More

Friday, April 14, 2017

excel radar chart - change axes limit values

Leave a Comment

with data like below

+---------+-------+-------+-------+ |         | test1 | test2 | test3 | +---------+-------+-------+-------+ | metricA | -87.1 | -87.3 | -87.6 | | metricB | 12.35 |  12.2 | 12.25 | | metricC |   2.2 |   2.1 |  2.05 | | metricD |   7.7 |   7.9 |   7.8 | | metricE |  3.61 |  3.36 |  3.48 | +---------+-------+-------+-------+ 

I'm trying to create a radar chart in excel - I get the following chart - however since the values are very close the results from the three tests are overlapping each other. How do I adjust the axis limits such that the differences are displayed in the chart ? I was able to change it only for one axis - the one corresponding to metricA.

enter image description here

6 Answers

Answers 1

You didn't specify the application domain, so I don't know what these numbers mean.

The first best solution is what others also wrote: to change the range of values.

A) Differences instead of absolute values (if the essential question is their difference).

B) Ratios. In other cases their ratio to each other or to the average of the group or to an external standard value is more important, like the industry standard is -85, so test1 is at 102% - this differences will not be bigger but all metrics will have the same data range, so the scale can be adjusted to show the differences better.

C) Compare to an industry average and a standard deviation (e.g. test1 is 2.5 sigma from the standard regarding metricA)

The second best solution is to use a clustered column chart or multiple charts.

The third best solution is to improve somehow this radar thing, and make visible that all the three are essentially at the same place. For this, you can change the thickness and style of the lines (as below) or of the markers.

Radar Chart

Answers 2

To compare the three tests without fiddling with the axis scales, you can try some sort of standardization - I got an OK result with subtracting the difference between a test score and the mean average of the test scores for that metric e.g. =B2-AVERAGE($B2:$D2)

So if your test data is in B2:D7 like this:

             test1  test2    test3   metricA    -87.1   -87.3   -87.6  metricB     12.35   12.2    12.25  metricC     2.2      2.1    2.05  metricD     7.7      7.9     7.8  metricE     3.61    3.36    3.48 

Then put that formula in and copy across and down to get:

              test1   test2   test3   metricA     0.2333  0.0333 -0.2667  metricB     0.0833 -0.0667 -0.0167  metricC     0.0833 -0.0167 -0.0667  metricD    -0.1000  0.1000  0.0000  metricE     0.1267 -0.1233 -0.0033 

Gives this chart:

enter image description here

Other formulas I tried with:

  • =(B2-MIN($B2:$D2))/(MAX($B2:$D2)-MIN($B2:$D2)) which is a normalization giving a number between 0 and 1

  • =STANDARDIZE(B2,AVERAGE($B2:$D2),STDEV.P($B2:$D2)) which leverages Excel's STANDARDIZE function.

Answers 3

Instead of displaying the absolute values why not display the differences?

Perhaps two charts, one with the absolute as shown and another with the variance.

Answers 4

Multiply the values with Hundred or More And then change the Axis unit to None.

Answers 5

by using the differences between the three tests I got much more variance in values. Basically, I took the difference between:

  • Test 1- Test 2
  • Test 2 - Test 3
  • Test 1 - Test 3

I don't know if this is what you are looking for, but it clearly shows the differences of values between the tests. I hope this helps!

Answers 6

It is not possible to change the axis scale for different angles on a radar chart. However, we can create a scatter plot that recreates a radar chart where you can specify the range of values for each angle, essentially allowing you to specify the axes limits as per your question.

We'll create a scatter series for each test. For each metric, you'll specify the axis range you want. We'll place each score along the axis range you specify, and then point the values at different angles based on the metric, simulating a radar chart. Each series will then loop back to metricA again to close the loop.

Setting up the data:

Angle (360 degrees split up into 5 angles):

  • metricA: 0
  • metricB: =2*PI()/5
  • metricC: =2*2*PI()/5
  • metricD: =3*2*PI()/5
  • metricE: =4*2*PI()/5

Scale lower and Scale upper: Freely set the axis range for each metric

x1: (x-coordinate for each scatter point)

In cell F2: =SIN($B2)*(E2-$C2)/($D2-$C2)

y1: (y-coordinate for each scatter point)

In cell G2: =COS($B2)*(E2-$C2)/($D2-$C2)

The formulas for x and y can be filled down for all metrics, and then copied across for each test.

Finally, duplicate metricA in the bottom row: In cell A7: =A2 And fill this across all columns

Then, create a scatter plot with lines between the points:

  • Add each series one at a time
  • Set the x and y axes to fixed ranges from -1 to 1
  • Resize the plot area to be square-shaped
  • Hide the major gridlines, tick marks and axis labels to make the scatter plot look more like a radar chart

enter image description here

You manually enter any values you want for scale lower and scale upper values. For my chart, I set the lower limit of each metric as being the lowest value minus 10% of the difference between highest and lowest value, and I set the upper limit as being the highest value plus 10% of the difference between highest and lowest value. This result in all lowest points being the same distance from the middle and all highest points being the same distance from the middle. The middle value for each metric is scaled based on it's value compared to the highest and lowest values.

Scale lower: =MIN(E2,H2,K2)-(MAX(E2,H2,K2)-MIN(E2,H2,K2))/10

Scale upper: =MAX(E2,H2,K2)+(MAX(E2,H2,K2)-MIN(E2,H2,K2))/10

If you needed, it would also be possible to add pseudo axes and gridlines to make the scatter plot look even more like a radar chart. It would just be necessary to add additional series to plot the axes and gridlines and format them as thin gray lines to mimic normal axes and gridlines.

Read More

Wednesday, April 12, 2017

find the overlapping points of two series in devexpress chart

Leave a Comment

As you can see , I have a winform chart ( DevExpress Chart ) with two series . enter image description here
Each series has their points.
And what I want is to find the overlapping points of those two series ( pointed by green circle in picture ) .

For example ( 3.4 for the first overlapping point and 7.3 for the second overlapping point ) .
Thanks .

3 Answers

Answers 1

If you have direct access to the Series collection, you could:

var intersection = Series[0].Points.Intersect(Series[1].Points);

If you are creating discrete Point objects along the way, you may have to define the matching behavior, as well.

Answers 2

unfortunately, you will not find a direct answer, I faced the same problem once before and it ended up to find a workaround (which mostly will be a mathematic solution based on your business logic of the series).

Answers 3

As I understood each series in your case is just collection of sequantial points. All you need is to find intersection between line segments from each collection. I suggest to use for such purpose Bentley-Ottmann algorithm, which allows to find answer in (N log N) way (N is count of line segments from both series). Unfortunately I don't know whether there is implementation in c# (anytime you can use c++ implementation, if you will not find c# solution)

If you have always two series, defined by some two different functions, you can optimize algorithm in O (N) way (use "merge" logic)

Read More

Saturday, June 11, 2016

Piping timechart into streamstats

Leave a Comment

We have splunk index for certain events. Events are categorized by event type.

I need to find fixed size (let say, 5 min) windows where frequency (events per second) of any events drops/rises more than a preset percentage (let say, 50%) as compared to a preceding window.

I, unsuccessfully, tried something like this:

 index=index_of_events | eval cnt=1 | timechart span=20s limit=40 per_second(cnt) as ev  by ev_type useother=f usenull=f |  streamstats window=40 global=false first(ev) as start last(ev) as end by ev_type |   eval diff=abs(start-end) | eval max_val=max(start, end) |   where diff > 0 AND max > 0 | eval prc=100*diff/max_val | where prc > 50 

Is it approach doable? Can I pipe timechart directly to streamstats or do I need something like untable between them?

Is there a better way to accomplish such task?

If possible I would also like to exclude low frequency events (do not care if 2/sec becomes 1/sec).

0 Answers

Read More

Friday, April 22, 2016

Why will a change on $scope.data on a onChange callback not re-plot chart.js?

Leave a Comment

I have an application that is written around the MEAN web stack. I have created an API that depending upon the URL a JSON data set is returned for given weights. This is interconnected with my Mongo database. There are two returned JSON types, one for all weights and another for weights that fall between two dates. Not only am I displaying all of these weights in the first instance on one page(home/index), but I am also displaying by a filtered set(by date) on an independent page. This independent page utilizes both angular-chart.js and ngDaterangepicker. These are both Angular directives that are injected into my code.

When a date range is selected a ng-change function runs (change()) which grabs a new set of filtered data(by date). This data is then added back into $scope.data the property, which when changed, should theoretically cause the chart to re-plot. Unfortunately, my application is not watching for new changes to this property inside of a on-change function.

If a timeout is set outside of this function the graph will re-plot. So I am wondering if there is a better way or a different way to solve this problem.

According to the documentation from angular-chart if there is any change to the value of $scope.data the chart will re-plot the new values, however anything inside of an on change event listener/function will cause any new values set to $scope.data to not be bound and cause the graph not to replot the new values. Why is the ng-change directive so special? Below is my HTML and JavaScript.

=== HTML View ===

<div class="container-fluid"> <h1 class="text-center">Graphs</h1>     <div class="container"> <div class="col-sm-12" ng-controller="weights">     <input type="text" ng-change="change()" ng-model="model"/>         <input type="daterange" ng-change="change({{myDateRange | json}})" ng-model="myDateRange" />         {{myDateRange | json}}         {{data}} </div>     <div class="col-sm-12">     <div class="col-sm-2"></div>     <div class="col-sm-8 center-block">         <div ng-controller="weights">     <canvas id="line" class="chart chart-line" chart-data="data"   chart-labels="labels" chart-legend="true" chart-series="series"   chart-click="onClick"> </canvas>  </div> </div> <div class="col-sm-2"></div> </div>     </div> </div> 

=== Angular Controller ===

angular.module('app').controller('weights', ['$scope', 'weightsFactory','$filter','$http', '$timeout', function($scope, weightsFactory, $filter, $http, $timeout){     //need to get weights from /weights     //$scope.labels = [1, 2, 3, 4];      $scope.series = ['Weights Over Time'];     $http.get('/weights').success(function(data){ //all data     var dates = [];      var weights = [];      $scope.weights = data;       angular.forEach(data, function(value, key){         dates.push($filter('date')(value.date, 'MMM dd, yyyy - hh:m:ss a'));         weights.push(value.weight);      });         $scope.labels = dates;      $scope.data = [ weights ];     });  $scope.change = function(data){      //2016-04-08T04:00:00.000Z/2016-04-09T03:59:59.999Z      var startTime = '2016-04-08T04:00:00.000Z';       var endTime = '2016-04-09T03:59:59.999Z';      $http.get('/weight/range/'+startTime+'/'+endTime).success(function(response){         $timeout(function() {             $scope.data = [['100', '200', '300']]; //this data is being set but is not causing a change on chart.js         }, 10);      });         } 

3 Answers

Answers 1

I wasted 50+ bounty on this question, however it is safe to assume that you can have the same controller in an application, however if you nest two controllers together then yes the application will work, however you will have essentially two different $scope variables in memory. I am not going to remove my error only to prove that if all else fails, look for identical controllers. Be careful with your HTML.

Answers 2

As far as i can see your $scope.change function resides outside of the weights controller scope, hence unreachable by ng-change

Answers 3

i would imaging your controller is being loaded twice.

<div ng-controller="weights"> <div class="col-sm-12" ng-controller="weights"> 

if you were to remove them both and add the controller to the container-fluid div, you should be able to share the state of the controller with the ng-change directive.

<div class="container-fluid" ng-controller="weights">     <h1 class="text-center">Graphs</h1>     <div class="container">         <div class="col-sm-12">             <input type="daterange" ng-change="change({{myDateRange | json}})" ng-model="myDateRange" />                  {{myDateRange | json}} {{data}}         </div>     </div>     <div class="col-sm-12">         <div class="col-sm-2"></div>         <div class="col-sm-8 center-block"> ...             <div>                 <canvas></canvas>             </div>         </div>         <div class="col-sm-2"></div>     </div> </div> 

angular-charts ng-change example

Read More

Monday, April 18, 2016

Displaying the state in a geochart

Leave a Comment

I am displaying a GeoChart the state level. When configuring with the Brazil region works, as you can see below, but not with the state code, for example: BR-SP

google.load('visualization', '1', {    'packages': ['geochart', 'table']  });  google.setOnLoadCallback(drawRegionsMap);    function drawRegionsMap() {      var data = google.visualization.arrayToDataTable([      ['State', 'Views'],      ['BR-AL', 300],      ['BR-SP', 300],      ['BR-RJ', 400]      ]);      var geochart = new google.visualization.GeoChart(document.getElementById('chart_div'));    var options = {      region: 'BR',      resolution: 'provinces',      width: 800,      height: 600,      colorAxis: {        colors: ['#acb2b9', '#2f3f4f']      }    };      geochart.draw(data, options);    }
<script src="https://www.gstatic.com/charts/loader.js"></script>  <script src="https://www.google.com/jsapi"></script>    <div id="chart_div"></div>

A nível de estado:

google.load('visualization', '1', {    'packages': ['geochart', 'table']  });  google.setOnLoadCallback(drawRegionsMap);    function drawRegionsMap() {      var data = google.visualization.arrayToDataTable([      ['State', 'Views'],      ['Sao Paulo', 300],      ['Campinas', 300],      ]);      var geochart = new google.visualization.GeoChart(document.getElementById('chart_div'));    var options = {      region: 'BR-SP',      width: 800,      height: 600,      colorAxis: {        colors: ['#acb2b9', '#2f3f4f']      }    };      geochart.draw(data, options);    }
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>   	<script type="text/javascript" src="https://www.google.com/jsapi"></script>    <div id="chart_div"></div>

2 Answers

Answers 1

It seems that you can't divide a country region (e.g. São Paulo) in subregions (e.g. Campinas) using options.displayMode: 'regions' but you can show them as markers with options.displayMode: 'markers'

google.load('visualization', '1', {    'packages': ['geochart', 'table']  });  google.setOnLoadCallback(drawRegionsMap);    function drawRegionsMap() {      var data = google.visualization.arrayToDataTable([      ['State', 'Views'],      ['São Paulo', 300],      ['Campinas', 200],      ['Araras', 400],      ['Buri', 100],      ]);      var geochart = new google.visualization.GeoChart(document.getElementById('chart_div'));    var options = {      region: 'BR',      displayMode: 'markers',      resolution: 'provinces',      width: 800,      height: 600,      colorAxis: {        colors: ['#acb2b9', '#2f3f4f']      }    };      geochart.draw(data, options);    }
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>   	<script type="text/javascript" src="https://www.google.com/jsapi"></script>    <div id="chart_div"></div>

Answers 2

The problem comes from your options.region

var options = {     region: 'BR-SP',     width: 800,     height: 600,     colorAxis: {       colors: ['#acb2b9', '#2f3f4f']     } } 

Region subcodes don't seem to be allowed. According to the Configuration Options, the region code can only be

The area to display on the map. (Surrounding areas will be displayed as well.) Can be either a country code (in uppercase ISO-3166 format), or a one of the following strings: ...

If you look at the Wikipedia Article List of Country Codes, BR-SP is not included as it's a subdivision and doesn't have a numerical code attached (https://en.wikipedia.org/wiki/ISO_3166-2:BR).

If you change the region to BR it'll work, but unfortunately It doesn't look like you can zoom in any further.

Check out Leaflet Maps (an open sourced alternative). You can use GeoJSON to set the regions and then do your own zoom level.

Read More