Search code examples
androidjodatimeandroidplot

Time series data not being interpreted properly


I'm trying to create a simple line chart using a time series. The problem is that androidplot is not properly displaying the time values from the array, i.e. the dates are off. The dates should be:

Jan 1 2001, Feb 1 2001, Mar 1 2001, Apr 1 2001, May 1 2001

but I am getting:

Jan 1 2001, Jan 30 2001, Mar 1 2001, Mar 31 2001, May 1 2001

You can see from the the debug info in the formatted what data androidplot has interpreted. I am using JodaTime for time. Code is here:

public class VisualizeFragment extends Fragment {

    public static final String TAG = Config.PRE + VisualizeFragment.class.getSimpleName();

    private XYPlot plot;

    public VisualizeFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View v = inflater.inflate(R.layout.simple_xy_plot_example, container, false);

        // initialize our XYPlot reference:
        plot = (XYPlot) v.findViewById(R.id.mySimpleXYPlot);

        // Create a couple arrays of y-values to plot:
        Number[] weights = {150, 180, 179, 170, 175};
        Number[] xyears = {
                978307200,  // 2001
                1009843200, // 2002
                1041379200, // 2003
                1072915200, // 2004
                1104537600  // 2005
        };
        Number[] years = {
                new DateTime(2001, 1, 1, 0, 0).getMillis() / 1000,
                new DateTime(2001, 2, 1, 0, 0).getMillis() / 1000,
                new DateTime(2001, 3, 1, 0, 0).getMillis() / 1000,
                new DateTime(2001, 4, 1, 0, 0).getMillis() / 1000,
                new DateTime(2001, 5, 1, 0, 0).getMillis() / 1000,
        };

        XYSeries series1 = new SimpleXYSeries(
                Arrays.asList(years),
                Arrays.asList(weights),
                "Weight");

        // Create a formatter to use for drawing a series using LineAndPointRenderer
        // and configure it from xml:
        LineAndPointFormatter series1Format = new LineAndPointFormatter();
        series1Format.setPointLabelFormatter(new PointLabelFormatter());
        series1Format.configure(getActivity().getApplicationContext(), R.xml.line_point_formatter_with_plf1);

        // add a new series' to the xyplot:
        plot.addSeries(series1, series1Format);

        // reduce the number of range labels
        //plot.setTicksPerRangeLabel(3);
        plot.getGraphWidget().setDomainLabelOrientation(-45);

        // draw a domain tick for each year:
        plot.setDomainStep(XYStepMode.SUBDIVIDE, years.length);


        plot.setDomainValueFormat(new Format() {

            // create a simple date format that draws on the year portion of our timestamp.
            // see http://download.oracle.com/javase/1.4.2/docs/api/java/text/SimpleDateFormat.html
            // for a full description of SimpleDateFormat.
            private SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd");

            @Override
            public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {

                // because our timestamps are in seconds and SimpleDateFormat expects milliseconds
                // we multiply our timestamp by 1000:
                long timestamp = ((Number) obj).longValue() * 1000;
                Date date = new Date(timestamp);

                Log.d(TAG, "formatting date=" + new SimpleDateFormat("MMMM dd, yyyy").format(date));

                return dateFormat.format(date, toAppendTo, pos);
            }

            @Override
            public Object parseObject(String source, ParsePosition pos) {
                return null;

            }
        });


        return v;

    }


}

Solution

  • I believe the problem is that you are uniformly subdividing your domain (XYStepMode.SUBDIVIDE) but because your xVals are epoch values they are not uniformly distributed; the milliseconds between Jan 1, 2001 and Feb 1, 2001 is not the same as the milliseconds between Feb 1, 2001 and Mar 1, 2001, etc. which causes the discrepancy.

    What I would suggest is to use i as your domain values instead of epoch values and then use i to lookup the correct epoch value in your domain value format code. This would involve making 2 changes. First, to use i as your f(x):

    // I've just removed the 'years' param here, which tells SimpleXYSeries
    // to use an implicit value of 'i' for getY(i).
    XYSeries series1 = new SimpleXYSeries(Arrays.asList(weights),"Weight");
    

    And then change your format method to this:

    @Override
    public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
        int i = ((Number) obj).intValue();
        Date date = years[i];
        Log.d(TAG, "formatting date=" + new SimpleDateFormat("MMMM dd, yyyy").format(date));
        return dateFormat.format(date, toAppendTo, pos);
    }