How to remove gaps in a time series data chart when data is not available in Flex?
-
19-06-2021 - |
Frage
I'm using Flex for charting time series data. the data range is from 2002 to 2009, however the data is not available for some periods of time (i.e from 4/2004 to 8/2005). The following lines show the tags I'm using for the chart:
<mx:Canvas id="cp" backgroundColor="#ffffff" fontFamily="Verdana" fontSize="12" color="#093A89" fontWeight="bold" width="100%" height="100%" alpha="1" creationComplete="init()">
<mx:LineChart id="cChart" showDataTips="true" paddingRight="40" paddingLeft="30" width="100%" height="85%">
<mx:verticalAxis>
<mx:LinearAxis id="linearAxis" baseAtZero="false" title="{parameterLabel}" minorInterval="0.5" interval="1.0"/>
</mx:verticalAxis>
<mx:verticalAxisRenderers>
<mx:AxisRenderer axis="{linearAxis}" fontSize="10">
<mx:axisStroke>
<mx:SolidColorStroke weight="6" color="#BBCCDD" alpha="1" caps="square"/>
</mx:axisStroke>
</mx:AxisRenderer>
</mx:verticalAxisRenderers>
<mx:horizontalAxis>
<mx:DateTimeAxis id="ca" minimum="{sDate}" maximum="{eDate}" title="Date" dataUnits="days" dataInterval="1" labelUnits="days"/>
</mx:horizontalAxis>
<mx:horizontalAxisRenderers>
<mx:AxisRenderer axis="{ca}" canDropLabels="true" fontSize="10" labelRotation="45">
<mx:axisStroke>
<mx:SolidColorStroke weight="6" color="#BBCCDD" alpha="1" caps="square"/>
</mx:axisStroke>
</mx:AxisRenderer>
</mx:horizontalAxisRenderers>
<mx:series>
<mx:LineSeries id="l1" visible="false"/>
</mx:series>
</mx:LineChart>
<mx:Legend id="mylgn" horizontalCenter="0" bottom="32"/>
<s:Label id="lblChart1" text="{dataType} {parameterLabel} at {streamLabel}" horizontalCenter="0" bottom="20"/>
<s:Label id="lblChart2" text="{optionalText}" horizontalCenter="0" bottom="5"/>
The following image illustrates the chart created by the above code:
As you can see there are gaps for the intervals with no data. Is there any way to remove/cut the intervals with no data? What is the best practice for this type of data?
Any thoughts or recommendation would be much appreciated
Lösung
This is accomplished via an adapter to your dataProvider
.
To show a horizontal line between missing samples, you must add an additional sample to your data provider with value equal to the last sample.
If you had time series data of:
timestamp value
4/2004 3
8/2005 23
You would add an additional sample immediately before 8/2005 equal to the previous value of 3.
timestamp value
4/2004 3
7/2005 3 <-- insert sample
8/2005 23
Instead of interpolating between values 3 and 23, a flat horizontal line is displayed.
Sample data model
package
{
public class TrendData
{
public var timestamp:Date;
public var value:Number;
}
}
Static adapter utility
package
{
import mx.collections.ArrayList;
public class TimeSeriesDataAdapter
{
public static function interpolate(data:ArrayList):ArrayList
{
var set:ArrayList = new ArrayList();
var timespan:Number;
// add first sample:
set.addItem(data[0]);
for (var i:uint = 1; i < data.length; i++)
{
// measure timestamp between current sample and last sample
timespan = TrendData(data[i]).timestamp.time - TrendData(data[i-1]).timestamp.time;
// if the timespan is greater than desired range (1-day), add a sample
if(timespan >= 86400000)
{
var trendData:TrendData = new TrendData();
// set timestamp just before sample
trendData.timestamp = new Date(TrendData(data[i]).timestamp.time - 1);
// set value to previous value
trendData.value = TrendData(data[i-1]).value;
set.addItem(trendData);
}
set.addItem(data[i]);
}
return set;
}
}
}
Data Visualization implementation
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
import mx.collections.ArrayList;
[Bindable]
[ArrayElementType("TrendData")]
public var data:ArrayList
]]>
</fx:Script>
<mx:LineChart dataProvider="{TimeSeriesDataAdapter.interpolate(data)}" />
</s:Application>
Andere Tipps
I used Jason's answer to fix the gap issue in my charts. However to make the code suitable for my application, I applied the following changes. 1- I replaced ArryList to ArrayCollection 2- I didn't use the TrendData class 3- Since my date filed is string, I had to convert it to date first 4- I considered 7 days or more as a gap 5- I had to sort my ArrayCollection before creating the excessive points.
The following shows my version of the TimeSeriesDataAdapter.interpolate function:
public static function interpolate(data:ArrayCollection):ArrayCollection
{
var set:ArrayCollection = new ArrayCollection();
var timespan:Number;
var oneDayTime:Number = 86400000; // 1000*60*60*24 = 1 day
for (var j:uint = 1; j < data.length; j++)
{
// measure timestamp between current sample and last sample
data[j].formattedDate = DateField.stringToDate(data[j].Date,"MM/DD/YYYY");
data[j].time = data[j].formattedDate.time;
}
var dataSortField:SortField = new SortField();
dataSortField.name = "time";
var numericDataSort:Sort = new Sort();
numericDataSort.fields = [dataSortField];
data.sort = numericDataSort;
data.refresh();
// add first sample:
set.addItem(data[0]);
for (var i:uint = 1; i < data.length; i++)
{
// measure timestamp between current sample and last sample
timespan = data[i].time - data[i-1].time;
// if the timespan is greater than desired range (7-days), add a sample
if(timespan >= oneDayTime*7)
{
var trendData:Object = new Object();
var timestamp:Date = new Date();
trendData = data[i-1];
// set timestamp just before sample
timestamp.time = data[i].time - 1;
trendData.Date = DateField.dateToString(timestamp,"MM/DD/YYYY");
set.addItem(trendData);
}
set.addItem(data[i]);
}
return set;
}