* @return
* @throws Exception if an error occurs
*/
public Instance remapDateTimeStamp(Instance inst, Instance previous,
String timeStampName) throws Exception {
Instance result = inst;
if (!isDateBased()) {
throw new Exception("This periodicity is not date timestamp-based");
}
int origIndex = result.dataset().attribute(timeStampName).index();
Calendar c = new GregorianCalendar();
boolean applyTrainingSkipAdjust = true;
long localSkipAdjust = 0;
if (!result.isMissing(origIndex)) {
Date d = new Date((long) result.value(origIndex));
double origValue = result.value(origIndex);
if (m_skipList != null && m_skipList.size() > 0 && previous != null) {
// check this instance's date time stamp against the skip list -
// our fundamental assumption (for the training data) is that these
// dates
// are not actually
// present in the data (i.e. sat and sun for stock market data). If
// they
// are in the data (but with missing targets) then the missing value
// interpolation routine will have filled them in, which is the wrong
// thing to do if they are supposed to be skipped over
if (dateInSkipList(d)) {
throw new Exception(
"This instance contains a date time stamp that is "
+ "a member of the skip list - skip list entries are not time "
+ "units with respect to the model and should not be present : "
+ inst.toString());
}
if (!previous.isMissing(origIndex)) {
if (result.value(origIndex) >= previous.value(origIndex)) {
// compared to the previous date are we more than one time unit
// ahead?
double start = previous.value(origIndex);
double end = origValue;
while (start < end) {
start = weka.classifiers.timeseries.core.Utils
.advanceSuppliedTimeValue(start, this);
if (start < end) {
if (dateInSkipList(new Date((long) start))) {
m_trainingRemapSkipAdjust--;
} else {
// oh oh the difference between the current and previous
// instance
// is more than one time step but the intervening step(s)
// are
// not in the skip list!
throw new Exception("There is an increment of more than "
+ "one time step between\n" + previous.toString()
+ "\nand\n" + inst.toString() + "\n but none of the "
+ "intervening time steps are in the " + "skip list.");
}
}
}
} else {
// we have a problem here - data is not sorted in ascending order
// of the date time stamp!
throw new Exception(
"The data does not seem to be sorted in ascending order "
+ "of the date time stamp!");
}
}
}
if (m_skipList != null && m_skipList.size() > 0 && previous == null) {
// this case indicates that we are being invoked in a
// priming/forecasting context
// check that this instance does not occur before the first training
// instance!!
if (origValue < m_dateTimeStampInitialVal) {
throw new Exception(
"The timestamp for this instance occurs before the "
+ "timestamp of the first training instance!");
}
// can't prime/forecast for values that occurred before the training
// data.
double end = result.value(origIndex);
// first advance end until it is not in the skip list (this won't
// be needed for priming instances that are within the training
// date range), but might occur for closed-loop forecasting when
// the date is advanced one time unit for each step
while (dateInSkipList(new Date((long) end))) {
end = weka.classifiers.timeseries.core.Utils
.advanceSuppliedTimeValue(end, this);
}
double start = 0;
if (end < m_dateTimeStampFinalVal) {
// priming/forecasting within the range of the training data -
// will have to recompute all skips from the initial training
// time stamp up to this instance and not apply the pre-computed
// skip total for the full training period
applyTrainingSkipAdjust = false;
start = m_dateTimeStampInitialVal;
} else {
// priming/forecasting beyond the last training date time stamp seen
start = m_dateTimeStampFinalVal;
}
// now compute local skip adjust from start up to end
while (start < end) {
start = weka.classifiers.timeseries.core.Utils
.advanceSuppliedTimeValue(start, this);
if (start < end) {
if (dateInSkipList(new Date((long) start))) {
localSkipAdjust--;
}
}
}
// set end as the current value
d = new Date((long) end);
origValue = end;
}
if (m_handlerPeriodicity == Periodicity.MONTHLY
|| m_handlerPeriodicity == Periodicity.WEEKLY
|| m_handlerPeriodicity == Periodicity.QUARTERLY) {
c.setTime(d);
long year = c.get(Calendar.YEAR);
long month = c.get(Calendar.MONTH);
long week = c.get(Calendar.WEEK_OF_YEAR);
long remapped = 0;
if (m_handlerPeriodicity == Periodicity.MONTHLY) {
remapped = ((year - m_dateTimeStampBaseVal) * 12) + month;
} else if (m_handlerPeriodicity == Periodicity.WEEKLY) {
remapped = ((year - m_dateTimeStampBaseVal) * 52) + week;
// adjust for the case where week 1 of the year actually starts
// in the last week of December
if (month == Calendar.DECEMBER && week == 1) {
remapped += 52;
}
} else if (m_handlerPeriodicity == Periodicity.QUARTERLY) {
remapped = ((year - m_dateTimeStampBaseVal) * 4)
+ ((month / 3L) + 1L);
}
if (m_skipList != null && m_skipList.size() > 0) {
remapped += (applyTrainingSkipAdjust) ? m_trainingRemapSkipAdjust
: 0;
remapped += localSkipAdjust;
}
result.setValue(result.numAttributes() - 1, remapped);
} else {
double remapped = origValue - m_dateTimeStampInitialVal;
remapped /= deltaTime();// m_deltaTime;
// it might (or might not) make sense to take the floor here. For
// daily data
// I have the feeling that data arithmetic (adding 1 to day of the
// year)
// may actually add slightly more than
// a day at certain times (to account for) leap seconds/years
// remapped = Math.floor(remapped);
if (m_skipList != null && m_skipList.size() > 0) {
remapped += (applyTrainingSkipAdjust) ? m_trainingRemapSkipAdjust
: 0;
remapped += localSkipAdjust;
}
result.setValue(result.numAttributes() - 1, remapped);
}
}
return result;
}