Charts: Efficiently Refreshing Data


#1

I am new to charts and currently evaluating. I am using charts to display real-time data. I have 10 series that are each getting unique data every ~2 seconds. So I need to update the chart every ~2s. I start accumulating a fair bit of data after a few minutes of running, thus reloading the data nd redrawing are expensive operations causing my app to get bogged down. 

The only way I have found to update my chart are:

[chart reloadData];

[chart redrawChartAndGL: YES]

Is there a more efficient way in order to avoid reloading all of my data everytime? Something built into shinobi? An easy way to put the redraw/reload on a different thread?

Thanks, 


#2

Hi,

Currently, the only way to update the chart is by calling reloadData , which causes the chart to request the whole data set from its data-source. We are currently working on improving our support for data streaming, but we don’t have any dates finalised as to when these improvements will be available.

To help you improve your performance, could I ask some questions about your chart?

  • When it is updating, does you need to see all of the old data? You could supply a window of data to the chart.
  • Have you thought about creating a “smart” data-source that actually samples the data, reducing the number of data points that are actually given to the chart. You could make this behaviour kick in when your data set gets so large that the reload lag is noticeable? It is important to note that if you have more data points than pixels horizontally, you wont be seeing them all anyway!

Thanks,

Jan


#3

Jan,

Thanks for the response - I appreciate hearing back. And thanks for the ideas. I’m using core data and a fetchedresultscontroller, so was hoping for a similar cached data approach as a collection or table where only new data is added to the dataset and then the plot refreshed instead of reloading the entire dataset.

A followup Question:

I have proceeding with the window concept. My only concerns are panning/zoom (would like to allow user to pan through full dataset but when new data comes in to snap back to most recent data window). I’ve started just by adjusting the xaxis default range to the most recent 100 datapoints (in delegate method willStartLoadingData) which gives this desired experience, but obviously need to adjust the numberOfDataPointsForSeriesAtIndex and dataPointAtIndex instead to constrain the data loading.

Do you have suggestions for how to handle pan/zoom in this situation to present more of the dataset? If I allow panning outside of range and reload data (using panning delegate), how should I reload/refresh and prevent a jerky user experience?

Thanks,

JP


#4

What you are talking about should be possible. What you need to essentially be doing is forwarding your panning and zooming methods onto an ‘intellegent’ data-source implementation. This datasource will need to be able to detect when your user has panned to a certain bound, and then calculate which datapoints the chart should now be displaying, based of the chart’s axes’ ranges.

A few things to consider:

  • You will need to find a balance between how often you are refreshing and how much data you are showing on your chart to minimise the user’s jerk.
  • You should also display some data +/- offscreen so that the chart is not reloaded every time the user pans, just when they have panned a certain distance into your data. Again, this will be a fine balance as it will obviously require you to have more data points and thus longer loading times.
  • If you reload the data your chart will try to change its axis ranges to fit the data. To avoid this, you would need to store the current axes range in chart.xAxis.currentRange and chart.yAxis.currentRange before you do your reload, and then restore this ranges after you have reloaded.
  • If you are supporting users zooming out, I would be careful that you don’t allow them to zoom out too far, as the number of data-points on screen at once will obviously grow. Zooming in shouldn’t need a reload at all.

I wish you the best of luck with this, it sounds like you have the right idea. Let me know how you get on!

Jan


#5

Thanks for the quick followup and the ideas. Below is a quick overview of current implementation with a question or two.

I’ve implemented a first pass at this and it’s functional with a few hickups. I’m using a fetchedResultsController (FRC) to get fresh data to my chart controller. Then in my FRC delegate method didChangeContent I grab a subset of the fetchedObjects by creating a subarray. I use the subarray as my chart datasource; the subarray contains the most recent data. I window (display) a smaller subset of the subarray using an axis range and use the extra data as an initial pan buffer  (as you mention). Then using the chart delegate method didFinishPanning, I adjust my subarray, adjust my range to be the new range (after panning), and call the chart methods reload and redraw so that I can pan through all of the data. And I can zoom, because you can’t zoom without panning (so the pan delegate methods fire on zoom). And I still keep the functionality of snapping back to most recent data and window when fresh data arrives (I actually like this so most current is always shown and user can pause stream to look more closely at full dataset if needed).

The issue I’m having is related to jerkiness based on the range resolution vs. the pan/zoom resolution and my dataset resolution. I’m hoping you might be able to help come up with a solution or determine if I’m doing something wrong:

  • the pan and zoom are continuously variable, but the range datatype/resolution is an integer. So if when I stop panning, I set my new range, the range is between integers vs. the double that represents the actual range (is this correct? am I missing something). This small amount of jerkiness is a bit of an eyesore. And it becomes a much bigger issue when zoomed in. I need to reset the range after panning if I want to refresh the data. Thoughts to help this small jerkiness? Am I missing somehting?

#6

1 more question:

  • Is there a way to turn off the automatic range setting on dataload? Other than setting the default range? Like the chart behaves when you pan without reload?

Thanks,

John 


#7

If I do the following on an example chart that I opened and zoomed into then I get a range with 6 decimal point accuracy:

chart.yAxis.axisRange

Are you talking about the accuracy of the SChartNumberRange class that is stored in this property?

As for the automatic range setting, the chart will always recalculate its ranges, amongst other calculations, when the chart reloads its data. It is up to you to catch the reload and change the ranges if you wish.

Jan