All Downloads are FREE. Search and download functionalities are using the official Maven repository.

timeBench.data.io.RConnector Maven / Gradle / Ivy

Go to download

TimeBench, a flexible, easy-to-use, and reusable software library written in Java that provides foundational data structures and algorithms for time- oriented data in Visual Analytics.

The newest version!
package timeBench.data.io;

import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

import org.apache.log4j.Logger;
import org.rosuda.REngine.REXP;
import org.rosuda.REngine.REXPDouble;
import org.rosuda.REngine.REXPMismatchException;
import org.rosuda.REngine.REngine;
import org.rosuda.REngine.REngineException;
import org.rosuda.REngine.REngineStdOutput;

import timeBench.calendar.Calendar;
import timeBench.calendar.CalendarFactory;
import timeBench.calendar.Granularity;
import timeBench.calendar.JavaDateCalendarManager;
import timeBench.data.TemporalDataException;
import timeBench.data.TemporalDataset;
import timeBench.data.TemporalElement;
import timeBench.data.TemporalElementStore;
import timeBench.data.TemporalObject;

/**
 * XXX Should we (a) encapsulate REngine or (b) provide some static helper methods?
 * 
 * TODO How handle R Exceptions?
 * 
 * @author Rind
 */
public class RConnector {

    public static final String RENGINE_IMPL = "org.rosuda.REngine.JRI.JRIEngine";
    private static Logger logger = Logger.getLogger(RConnector.class);

    private static RConnector instance;
    
    private Calendar calendar = JavaDateCalendarManager.getSingleton().getDefaultCalendar();

    // could be replaced by REngine.getLastEngine()
    private REngine engine;  
    
    private RConnector() {
        try {
            if (logger.isInfoEnabled())
                // show R console output in System.out
                engine = REngine.engineForClass(RENGINE_IMPL, new String[] {
                        "--no-save", "--no-restore" }, new REngineStdOutput(),
                        false);
            else
                engine = REngine.engineForClass(RENGINE_IMPL);
        } catch (ClassNotFoundException e) {
            logger.error("unexpected exception", e);
        } catch (NoSuchMethodException e) {
            logger.error("unexpected exception", e);
        } catch (IllegalAccessException e) {
            logger.error("unexpected exception", e);
        } catch (InvocationTargetException e) {
            logger.error("unexpected exception", e);
        }
    }

    public static synchronized RConnector getInstance(Calendar calendar) {
    	RConnector instance = getInstance();
    	instance.calendar = calendar;
    	return instance;
    }
    
    public static synchronized RConnector getInstance() {
        if (null == instance)
            instance = new RConnector();
        return instance;
    }

    public double getDouble(String name) throws REXPMismatchException,
            REngineException {
        REXP x = engine.parseAndEval(name, null, true);
        return x.asDouble();
    }

    public TemporalDataset getTemporalDataset(
            String name) throws REngineException, REXPMismatchException,
            TemporalDataException {
        // check R class
        REXP x = engine.parseAndEval("class(" + name + ")", null, true);
        if (!x.isString())
            throw new TemporalDataException("Unsupported R class: " + x);
        logger.trace(x.asString());

        // ts ... simplest R time series class
        if ("ts".equals(x.asString())) {
            int[] start = engine
                    .parseAndEval("start(" + name + ")", null, true)
                    .asIntegers();
            double frequency = engine.parseAndEval("frequency(" + name + ")",
                    null, true).asDouble();
            double[] data = engine.parseAndEval("as.vector(" + name + ")",
                    null, true).asDoubles();
            logger.debug("ts with start: " + Arrays.toString(start)
                    + ", frequency: " + frequency + ", obs: " + data.length);

            // get calendar and granularity
            Calendar calendar = JavaDateCalendarManager.getSingleton().getDefaultCalendar();
            Granularity granularity = null;
            if (Math.abs(frequency - 1.0) < 0.005
                    || Math.abs(frequency - 0.1) < 0.005)
            	granularity = CalendarFactory.getSingleton().getGranularity(calendar,"Year","Top");
            else if (Math.abs(frequency - 12.0) < 0.005)
                granularity = CalendarFactory.getSingleton().getGranularity(calendar,"Month","Top");
            else
                throw new TemporalDataException(
                        "Unknown granularity. frequency: " + frequency);

            // prepare a calendar
            GregorianCalendar cal = new GregorianCalendar(
                    TimeZone.getTimeZone("UTC"));
            cal.setTimeInMillis(0);
            cal.set(GregorianCalendar.MONTH, 0);
            cal.set(GregorianCalendar.DAY_OF_MONTH, 1);
            cal.set(GregorianCalendar.HOUR_OF_DAY, 0);
            cal.set(GregorianCalendar.MINUTE, 0);
            cal.set(GregorianCalendar.YEAR, start[0]);
            if (frequency - 12.0 < 0.05 && start.length > 1) {
                // GregorianCalendar.MONTH starts with 0
                cal.set(GregorianCalendar.MONTH, start[1] - 1);
            }

            // relational
            TemporalDataset tmpds = new timeBench.data.TemporalDataset();
            tmpds.addDataColumn(name, double.class, null);

            // time indices are only used in log file to check our code
            double[] time = new double[0];
            DateFormat df = null;
            if (logger.isTraceEnabled()) {
                time = engine.parseAndEval(
                    "as.vector(time(" + name + "))", null, true).asDoubles();
                df = DateFormat.getDateTimeInstance();
            }

            for (int i = 0; i < data.length; i++) {
                long inf = cal.getTimeInMillis();

                if (Math.abs(frequency - 12.0) < 0.005)
                    cal.add(GregorianCalendar.MONTH, 1);
                else if (Math.abs(frequency - 0.1) < 0.005)
                    cal.add(GregorianCalendar.YEAR, 10);
                else
                    cal.add(GregorianCalendar.YEAR, 1);

                long sup = cal.getTimeInMillis() - 1;
                if (logger.isTraceEnabled())
                    logger.trace("time from R: " + time[i] + " inf: "
                            + df.format(new Date(inf)) + " sup: "
                            + df.format(new Date(sup)) + " data: " + data[i]);

                tmpds.addTemporalElement(
                        i,
                        inf,
                        sup,
                        granularity,
                        TemporalElementStore.PRIMITIVE_INTERVAL);
                TemporalObject to = tmpds.addTemporalObject(i, i);
                to.set(name, data[i]);
            }

            return tmpds;
        } else if ("zoo".equals(x.asString())) {
            // zoo ... feature-rich R time series class
            double start = engine.parseAndEval(
                    "as.numeric(start(" + name + "))", null, true).asDouble();
            double[] data = engine.parseAndEval("coredata(" + name + ")", null,
                    true).asDoubles();
            double[] time = engine.parseAndEval(
                    "as.numeric(time(" + name + "))", null, true).asDoubles();
            logger.debug("ts with start: " + start + ", obs: " + data.length);


            Granularity granularity = calendar.getBottomGranularity();
            
            // relational
            TemporalDataset tmpds = new timeBench.data.TemporalDataset();
            tmpds.addDataColumn(name, double.class, null);

            // time indices are only used in log file to check our code
            DateFormat df = null;
            if (logger.isTraceEnabled()) {
                df = DateFormat.getDateTimeInstance();
            }

            for (int i = 0; i < data.length; i++) {
                long inf = Math.round(time[i] * 1000);
                if (logger.isTraceEnabled())
                    logger.trace("time from R: " + time[i] + " inf: "
                            + df.format(new Date(inf)) + " data: " + data[i]);

                tmpds.addTemporalElement(
                        i,
                        inf,
                        inf,
                        granularity,
                        TemporalElementStore.PRIMITIVE_INSTANT);
                TemporalObject to = tmpds.addTemporalObject(i, i);
                to.set(name, data[i]);
            }

            return tmpds;
        } else
            throw new TemporalDataException("Unsupported R class: "
                    + x.asString());
    }

    public void putTemporalDataset(String name,
            TemporalDataset tmpds, String dataField)
            throws TemporalDataException, REngineException {
        double[] time = new double[tmpds.getTemporalObjectCount()];
        double[] data = new double[tmpds.getTemporalObjectCount()];
        int i = 0;
        
        int dataCol = tmpds.getNodeTable().getColumnNumber(dataField);

        for (TemporalObject cur : tmpds.temporalObjects()) { 
            if (cur.getTemporalElement().isAnchored()) {
                time[i] = ((double) cur.getTemporalElement().asGeneric()
                        .getInf()) / 1000.0d;
                data[i] = cur.getDouble(dataCol);
                i++;
            } else {
                logger.debug("skip temp.o. with temp.el. "
                        + cur.getTemporalElement());
            }
        }
        logger.trace(i);

        if (i == 0)
            throw new TemporalDataException(
                    "TemporalDataSet did not contain any anchored objects.");

        // shorten arrays, if some temp.obj. were skipped
        if (i < time.length || i < data.length) {
            double[] temp = new double[i];
            System.arraycopy(time, 0, temp, 0, i);
            time = temp;
            System.arraycopy(data, 0, temp, 0, i);
            data = temp;
        }

        engine.assign(name + ".time", time);
        engine.assign(name + ".data", data);
        this.exec(name + " <- zoo(" + name + ".data)");
        // work around a possible timezone bug in as.POSIXct.numeric
        // cp. http://stackoverflow.com/questions/2457129/converting-unix-seconds-in-milliseconds-to-posixct-posixlt
        // this.exec("time(" + name + ") <- as.POSIXct(" + name +
        // ".time, origin=\"1970-01-01 GMT\")");
        this.exec("time(" + name + ") <- as.POSIXct(as.POSIXlt(" + name
                + ".time, origin=\"1970-01-01\"))");
    }

    public void putTemporalDataset(String name,
            TemporalDataset tmpds, String dataField,
            int startYear, int startMonth, double frequency, int length)
            throws REngineException {
        double[] data = new double[length];
        Arrays.fill(data, REXPDouble.NA);

        if (frequency != 12.0)
            throw new RuntimeException("only months supported");

        Granularity granularity = CalendarFactory.getSingleton().getGranularity(
        		JavaDateCalendarManager.getSingleton().getDefaultCalendar(),
        		"Month","Top");

        int dataCol = tmpds.getNodeTable().getColumnNumber(dataField);

        for (TemporalObject cur : tmpds.temporalObjects()) { 
            TemporalElement elem = cur.getTemporalElement();
            if (!elem.isAnchored()) {
                logger.debug("skip temp.o. with unanchored temp.el. " + elem);
            } else if (elem.getGranularityId() != granularity.getIdentifier()
                    || elem.getGranularityContextId() != granularity.getGranularityContextIdentifier()) {
                logger.debug("skip temp.o. with wrong granularity " + elem);
            } else {
                long inf = elem.asGeneric().getInf();

                GregorianCalendar cal = new GregorianCalendar(
                        TimeZone.getTimeZone("UTC"));
                cal.setTimeInMillis(inf);
                int year = cal.get(GregorianCalendar.YEAR);
                int month = cal.get(GregorianCalendar.MONTH) + 1;
                int index = (year - startYear) * 12 + month - startMonth;

                if (index >= 0 && index < length) {
                    data[index] = cur.getDouble(dataCol);
                } else {
                    logger.debug("skip temp.o. out of temporal bounds " + index
                            + " " + elem);
                }
            }
        }

        // copy data to R and build ts object
        engine.assign(name + ".data", data);
        this.exec(name + " <- ts(" + name + ".data, start = c(" + startYear
                + ", " + startMonth + "), frequency = " + frequency + ")");
    }

    public void exec(String command) throws REngineException {
        try {
            logger.debug("R> " + command);
            engine.parseAndEval(command, null, false);
        } catch (REXPMismatchException e) {
            logger.error("unexpected exception", e);
        }
    }

    public boolean close() {
        return engine.close();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy