Newer
Older
#include "MantidKernel/TimeSeriesProperty.h"
#include "MantidKernel/EmptyValues.h"
#include "MantidKernel/Exception.h"
#include "MantidKernel/Logger.h"
#include "MantidKernel/TimeSplitter.h"
#if !(defined __APPLE__ && defined __INTEL_COMPILER)
#else
#include <boost/range/algorithm_ext/is_sorted.hpp>
#endif
namespace Mantid {
namespace Kernel {
namespace {
/// static Logger definition
Logger g_log("TimeSeriesProperty");
}
/**
* Constructor
* @param name :: The name to assign to the property
*/
template <typename TYPE>
TimeSeriesProperty<TYPE>::TimeSeriesProperty(const std::string &name)
: Property(name, typeid(std::vector<TimeValueUnit<TYPE>>)), m_values(),
m_size(), m_propSortedFlag(), m_filterApplied() {}
/// Virtual destructor
template <typename TYPE> TimeSeriesProperty<TYPE>::~TimeSeriesProperty() {}
/**
* "Virtual" copy constructor
*/
template <typename TYPE>
TimeSeriesProperty<TYPE> *TimeSeriesProperty<TYPE>::clone() const {
return new TimeSeriesProperty<TYPE>(*this);
}
/**
* "Virutal copy constructor with a time shift
* @param timeShift :: a time shift in seconds
*/
template <typename TYPE>
Property *
TimeSeriesProperty<TYPE>::cloneWithTimeShift(const double timeShift) const {
auto timeSeriesProperty = this->clone();
auto values = timeSeriesProperty->valuesAsVector();
auto times = timeSeriesProperty->timesAsVector();
// Shift the time
for (auto it = times.begin(); it != times.end(); ++it) {
(*it) += timeShift;
}
timeSeriesProperty->clear();
timeSeriesProperty->addValues(times, values);
return timeSeriesProperty;
}
/** Return time series property, containing time derivative of current property.
* The property itself and the returned time derivative become sorted by time and
* the derivative is calculated in seconds^-1.
* (e.g. dValue/dT where dT=t2-t1 is time difference in seconds
* for subsequent time readings and dValue=Val1-Val2 is difference in
* subsequent values)
*
*/
template <typename TYPE>
std::unique_ptr<TimeSeriesProperty<double>>
TimeSeriesProperty<TYPE>::getDerivative() const {
if (this->m_values.size() < 2) {
throw std::runtime_error("Derivative is not defined for a time-series "
"property with less then two values");
}
this->sort();
auto it = this->m_values.begin();
int64_t t0 = it->time().totalNanoseconds();
TYPE v0 = it->value();
it++;
auto timeSeriesDeriv = std::unique_ptr<TimeSeriesProperty<double>>(
new TimeSeriesProperty<double>(this->name() + "_derivative"));
timeSeriesDeriv->reserve(this->m_values.size() - 1);
for (; it != m_values.end(); it++) {
TYPE v1 = it->value();
int64_t t1 = it->time().totalNanoseconds();
if (t1 != t0) {
double deriv = 1.e+9 * (double(v1 - v0) / double(t1 - t0));
int64_t tm = static_cast<int64_t>((t1 + t0) / 2);
timeSeriesDeriv->addValue(Kernel::DateAndTime(tm), deriv);
}
return timeSeriesDeriv;
}
/** time series derivative specialization for string type */
template <>
std::unique_ptr<TimeSeriesProperty<double>>
TimeSeriesProperty<std::string>::getDerivative() const {
throw std::runtime_error(
"Time series property derivative is not defined for strings");
// return nullptr;
/**
* Return the memory used by the property, in bytes
* */
template <typename TYPE>
size_t TimeSeriesProperty<TYPE>::getMemorySize() const {
// Rough estimate
return m_values.size() * (sizeof(TYPE) + sizeof(DateAndTime));
}
/**
* Just returns the property (*this) unless overridden
* @param rhs a property that is merged in some descendent classes
* @return a property with the value
*/
template <typename TYPE>
TimeSeriesProperty<TYPE> &TimeSeriesProperty<TYPE>::merge(Property *rhs) {
return operator+=(rhs);
}
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* Add the value of another property
* @param right the property to add
* @return the sum
*/
template <typename TYPE>
TimeSeriesProperty<TYPE> &TimeSeriesProperty<TYPE>::
operator+=(Property const *right) {
TimeSeriesProperty<TYPE> const *rhs =
dynamic_cast<TimeSeriesProperty<TYPE> const *>(right);
if (rhs) {
if (this->operator!=(*rhs)) {
m_values.insert(m_values.end(), rhs->m_values.begin(),
rhs->m_values.end());
m_propSortedFlag = TimeSeriesSortStatus::TSUNKNOWN;
} else {
// Do nothing if appending yourself to yourself. The net result would be
// the same anyway
;
}
// Count the REAL size.
m_size = static_cast<int>(m_values.size());
} else
g_log.warning() << "TimeSeriesProperty " << this->name()
<< " could not be added to another property of the same "
"name but incompatible type.\n";
return *this;
}
/**
* Deep comparison.
* @param right The other property to compare to.
* @return true if the are equal.
*/
template <typename TYPE>
bool TimeSeriesProperty<TYPE>::
operator==(const TimeSeriesProperty<TYPE> &right) const {
sort();
if (this->name() != right.name()) // should this be done?
{
return false;
}
if (this->m_size != right.m_size) {
return false;
}
{ // so vectors can go out of scope
std::vector<DateAndTime> lhsTimes = this->timesAsVector();
std::vector<DateAndTime> rhsTimes = right.timesAsVector();
if (!std::equal(lhsTimes.begin(), lhsTimes.end(), rhsTimes.begin())) {
return false;
{
// so vectors can go out of scope
std::vector<TYPE> lhsValues = this->valuesAsVector();
std::vector<TYPE> rhsValues = right.valuesAsVector();
if (!std::equal(lhsValues.begin(), lhsValues.end(), rhsValues.begin())) {
return false;
}
}
/**
* Deep comparison.
* @param right The other property to compare to.
* @return true if the are equal.
*/
template <typename TYPE>
bool TimeSeriesProperty<TYPE>::operator==(const Property &right) const {
auto rhs_tsp = dynamic_cast<const TimeSeriesProperty<TYPE> *>(&right);
if (!rhs_tsp)
return false;
return this->operator==(*rhs_tsp);
}
/**
* Deep comparison (not equal).
* @param right The other property to compare to.
* @return true if the are not equal.
*/
template <typename TYPE>
bool TimeSeriesProperty<TYPE>::
operator!=(const TimeSeriesProperty<TYPE> &right) const {
return !(*this == right);
}
/**
* Deep comparison (not equal).
* @param right The other property to compare to.
* @return true if the are not equal.
*/
template <typename TYPE>
bool TimeSeriesProperty<TYPE>::operator!=(const Property &right) const {
return !(*this == right);
}
/**
* Set name of the property
*/
template <typename TYPE>
void TimeSeriesProperty<TYPE>::setName(const std::string name) {
m_name = name;
}
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
/** Filter out a run by time. Takes out any TimeSeriesProperty log entries
*outside of the given
* absolute time range.
* Be noticed that this operation is not reversible.
*
* Use case 1: if start time of the filter fstart is in between t1 and t2 of
*the TimeSeriesProperty,
* then, the new start time is fstart and the value of the log is
*the log value @ t1
*
* Use case 2: if the start time of the filter in on t1 or before log start
*time t0, then
* the new start time is t1/t0/filter start time.
*
* EXCEPTION: If there is only one entry in the list, it is considered to mean
* "constant" so the value is kept even if the time is outside the range.
*
* @param start :: Absolute start time. Any log entries at times >= to this time
*are kept.
* @param stop :: Absolute stop time. Any log entries at times < than this time
*are kept.
*/
template <typename TYPE>
void TimeSeriesProperty<TYPE>::filterByTime(const Kernel::DateAndTime &start,
const Kernel::DateAndTime &stop) {
// 0. Sort
sort();
// 1. Do nothing for single (constant) value
if (m_values.size() <= 1)
return;
typename std::vector<TimeValueUnit<TYPE>>::iterator iterhead, iterend;
// 2. Determine index for start and remove Note erase is [...)
int istart = this->findIndex(start);
if (istart >= 0 && static_cast<size_t>(istart) < m_values.size()) {
// "start time" is behind time-series's starting time
iterhead = m_values.begin() + istart;
bool useprefiltertime;
if (m_values[istart].time() == start) {
// The filter time is on the mark. Erase [begin(), istart)
useprefiltertime = false;
} else {
// The filter time is larger than T[istart]. Erase[begin(), istart) ...
// filter start(time)
// and move istart to filter startime
useprefiltertime = true;
}
// Remove the series
m_values.erase(m_values.begin(), iterhead);
if (useprefiltertime) {
m_values[0].setTime(start);
}
} else {
// "start time" is before/after time-series's starting time: do nothing
// 3. Determine index for end and remove Note erase is [...)
int iend = this->findIndex(stop);
if (static_cast<size_t>(iend) < m_values.size()) {
if (m_values[iend].time() == stop) {
// Filter stop is on a log. Delete that log
iterend = m_values.begin() + iend;
} else {
// Filter stop is behind iend. Keep iend
iterend = m_values.begin() + iend + 1;
}
// Delete from [iend to mp.end)
m_values.erase(iterend, m_values.end());
}
// 4. Make size consistent
m_size = static_cast<int>(m_values.size());
/**
* Filter by a range of times. If current property has a single value it remains
* unaffected
* @param splittervec :: A list of intervals to split filter on
*/
template <typename TYPE>
void TimeSeriesProperty<TYPE>::filterByTimes(
const std::vector<SplittingInterval> &splittervec) {
// 1. Sort
sort();
// 2. Return for single value
if (m_values.size() <= 1) {
return;
}
std::vector<TimeValueUnit<TYPE>> mp_copy;
g_log.debug() << "DB541 mp_copy Size = " << mp_copy.size()
<< " Original MP Size = " << m_values.size() << "\n";
// 4. Create new
for (size_t isp = 0; isp < splittervec.size(); ++isp) {
Kernel::SplittingInterval splitter = splittervec[isp];
Kernel::DateAndTime t_start = splitter.start();
Kernel::DateAndTime t_stop = splitter.stop();
int tstartindex = findIndex(t_start);
if (tstartindex < 0) {
// The splitter is not well defined, and use the first
tstartindex = 0;
} else if (tstartindex >= int(m_values.size())) {
// The splitter is not well defined, adn use the last
tstartindex = int(m_values.size()) - 1;
int tstopindex = findIndex(t_stop);
if (tstopindex < 0) {
tstopindex = 0;
} else if (tstopindex >= int(m_values.size())) {
tstopindex = int(m_values.size()) - 1;
} else {
if (t_stop == m_values[size_t(tstopindex)].time() &&
size_t(tstopindex) > 0) {
tstopindex--;
/* Check */
if (tstartindex < 0 || tstopindex >= int(m_values.size())) {
g_log.warning() << "Memory Leak In SplitbyTime!\n";
}
if (tstartindex == tstopindex) {
TimeValueUnit<TYPE> temp(t_start, m_values[tstartindex].value());
mp_copy.push_back(temp);
} else {
mp_copy.push_back(
TimeValueUnit<TYPE>(t_start, m_values[tstartindex].value()));
for (size_t im = size_t(tstartindex + 1); im <= size_t(tstopindex);
++im) {
mp_copy.push_back(
TimeValueUnit<TYPE>(m_values[im].time(), m_values[im].value()));
}
}
} // ENDFOR
g_log.debug() << "DB530 Filtered Log Size = " << mp_copy.size()
<< " Original Log Size = " << m_values.size() << "\n";
// 5. Clear
m_values.clear();
m_values = mp_copy;
mp_copy.clear();
m_size = static_cast<int>(m_values.size());
* Split this time series property by time intervals to multiple time series
* property according to number of distinct splitters' indexes, such as 0 and 1
*
* NOTE: If the input TSP has a single value, it is assumed to be a constant
* and so is not split, but simply copied to all outputs.
*
* @param splitter :: a TimeSplitterType object containing the list of intervals
* @param outputs :: A vector of output TimeSeriesProperty pointers of the same
* @param isPeriodic :: whether the log (this TSP) is periodic. For example
* proton-charge is periodic log.
void TimeSeriesProperty<TYPE>::splitByTime(
std::vector<SplittingInterval> &splitter, std::vector<Property *> outputs,
bool isPeriodic) const {
// 0. Sort if necessary
sort();
if (outputs.empty())
return;
std::vector<TimeSeriesProperty<TYPE> *> outputs_tsp;
size_t numOutputs = outputs.size();
// 1. Clear the outputs before you start
for (size_t i = 0; i < numOutputs; i++) {
TimeSeriesProperty<TYPE> *myOutput =
dynamic_cast<TimeSeriesProperty<TYPE> *>(outputs[i]);
if (myOutput) {
outputs_tsp.push_back(myOutput);
if (this->m_values.size() == 1) {
// Special case for TSP with a single entry = just copy.
myOutput->m_values = this->m_values;
myOutput->m_size = 1;
} else {
myOutput->m_values.clear();
myOutput->m_size = 0;
}
} else {
outputs_tsp.push_back(NULL);
// 2. Special case for TSP with a single entry = just copy.
if (this->m_values.size() == 1)
return;
// 3. We will be iterating through all the entries in the the map/vector
// And at the same time, iterate through the splitter
Kernel::TimeSplitterType::iterator itspl = splitter.begin();
g_log.debug() << "[DB] Number of time series entries = " << m_values.size()
<< ", Number of splitters = " << splitter.size() << "\n";
while (itspl != splitter.end() && i_property < m_values.size()) {
// Get the splitting interval times and destination
DateAndTime start = itspl->start();
DateAndTime stop = itspl->stop();
int output_index = itspl->index();
// output workspace index is out of range. go to the next splitter
if (output_index < 0 || output_index >= static_cast<int>(numOutputs))
continue;
TimeSeriesProperty<TYPE> *myOutput = outputs_tsp[output_index];
// skip if the input property is of wrong type
if (!myOutput) {
++itspl;
++counter;
// Skip the events before the start of the time
while (i_property < m_values.size() && m_values[i_property].time() < start)
// i_property is out of the range. Then use the last entry
myOutput->addValue(m_values[i_property - 1].time(),
m_values[i_property - 1].value());
++itspl;
++counter;
break;
}
// The current entry is within an interval. Record them until out
if (m_values[i_property].time() > start && i_property > 0 && !isPeriodic) {
// Record the previous oneif this property is not exactly on start time
// and this entry is not recorded
size_t i_prev = i_property - 1;
if (myOutput->size() == 0 ||
m_values[i_prev].time() != myOutput->lastTime())
myOutput->addValue(m_values[i_prev].time(), m_values[i_prev].value());
}
// Loop through all the entries until out.
while (i_property < m_values.size() && m_values[i_property].time() < stop) {
// Copy the log out to the output
myOutput->addValue(m_values[i_property].time(),
m_values[i_property].value());
// Go to the next interval
++itspl;
// But if we reached the end, then we are done.
if (itspl == splitter.end())
break;
// No need to keep looping through the filter if we are out of events
if (i_property == this->m_values.size())
} // Looping through entries in the splitter vector
// Make sure all entries have the correct size recorded in m_size.
for (std::size_t i = 0; i < numOutputs; i++) {
TimeSeriesProperty<TYPE> *myOutput =
dynamic_cast<TimeSeriesProperty<TYPE> *>(outputs[i]);
myOutput->m_size = myOutput->realSize();
// g_log.notice() << "[DB] Final output size = " << myOutput->size() <<
// "\n";
}
// The makeFilterByValue & expandFilterToRange methods generate a bunch of
// warnings when the template type is the wider integer types
// (when it's being assigned back to a double such as in a call to minValue or
// firstValue)
// However, in reality these methods are only used for TYPE=int or double (they
// are only called from FilterByLogValue) so suppress the warnings
#pragma warning(push)
#pragma warning(disable : 4244)
#pragma warning(disable : 4804) // This one comes about for TYPE=bool - again
// the method is never called for this type
#endif
#if defined(__GNUC__) && !(defined(__INTEL_COMPILER))
#pragma GCC diagnostic ignored "-Wconversion"
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
/**
* Fill a TimeSplitterType that will filter the events by matching
* log values >= min and <= max. Creates SplittingInterval's where
* times match the log values, and going to index==0.
* This method is used by the FilterByLogValue algorithm.
*
* @param split :: Splitter that will be filled.
* @param min :: min value
* @param max :: max value
* @param TimeTolerance :: offset added to times in seconds (default: 0)
* @param centre :: Whether the log value time is considered centred or at the
*beginning (the default).
*/
template <typename TYPE>
void TimeSeriesProperty<TYPE>::makeFilterByValue(
std::vector<SplittingInterval> &split, double min, double max,
double TimeTolerance, bool centre) const {
const bool emptyMin = (min == EMPTY_DBL());
const bool emptyMax = (max == EMPTY_DBL());
if (!emptyMin && !emptyMax && max < min) {
std::stringstream ss;
ss << "TimeSeriesProperty::makeFilterByValue: 'max' argument must be "
"greater than 'min' "
<< "(got min=" << min << " max=" << max << ")";
throw std::invalid_argument(ss.str());
}
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
// If min or max were unset ("empty") in the algorithm, set to the min or max
// value of the log
if (emptyMin)
min = minValue();
if (emptyMax)
max = maxValue();
// Make sure the splitter starts out empty
split.clear();
// Do nothing if the log is empty.
if (m_values.empty())
return;
// 1. Sort
sort();
// 2. Do the rest
bool lastGood(false);
time_duration tol = DateAndTime::durationFromSeconds(TimeTolerance);
int numgood = 0;
DateAndTime t;
DateAndTime start, stop;
for (size_t i = 0; i < m_values.size(); ++i) {
const DateAndTime lastTime = t;
// The new entry
t = m_values[i].time();
TYPE val = m_values[i].value();
// A good value?
const bool isGood = ((val >= min) && (val <= max));
if (isGood)
numgood++;
if (isGood != lastGood) {
// We switched from bad to good or good to bad
if (isGood) {
// Start of a good section. Subtract tolerance from the time if
// boundaries are centred.
start = centre ? t - tol : t;
} else {
// End of the good section. Add tolerance to the LAST GOOD time if
// boundaries are centred.
// Otherwise, use the first 'bad' time.
stop = centre ? lastTime + tol : t;
split.push_back(SplittingInterval(start, stop, 0));
// Reset the number of good ones, for next time
numgood = 0;
}
lastGood = isGood;
}
if (numgood > 0) {
// The log ended on "good" so we need to close it using the last time we
// found
stop = t + tol;
split.push_back(SplittingInterval(start, stop, 0));
}
/** Function specialization for TimeSeriesProperty<std::string>
* @throws Kernel::Exception::NotImplementedError always
*/
template <>
void TimeSeriesProperty<std::string>::makeFilterByValue(
std::vector<SplittingInterval> &, double, double, double, bool) const {
throw Exception::NotImplementedError("TimeSeriesProperty::makeFilterByValue "
"is not implemented for string "
"properties");
}
/** If the first and/or last values in a log are between min & max, expand and
* existing TimeSplitter
* (created by makeFilterByValue) if necessary to cover the full TimeInterval
* given.
* This method is used by the FilterByLogValue algorithm.
* @param split The splitter to modify if necessary
* @param min The minimum 'good' value
* @param max The maximum 'good' value
* @param range The full time range that we want this splitter to cover
*/
template <typename TYPE>
void TimeSeriesProperty<TYPE>::expandFilterToRange(
std::vector<SplittingInterval> &split, double min, double max,
const TimeInterval &range) const {
const bool emptyMin = (min == EMPTY_DBL());
const bool emptyMax = (max == EMPTY_DBL());
if (!emptyMin && !emptyMax && max < min) {
std::stringstream ss;
ss << "TimeSeriesProperty::expandFilterToRange: 'max' argument must be "
"greater than 'min' "
<< "(got min=" << min << " max=" << max << ")";
throw std::invalid_argument(ss.str());
}
// If min or max were unset ("empty") in the algorithm, set to the min or max
// value of the log
if (emptyMin)
min = minValue();
if (emptyMax)
max = maxValue();
// Assume everything before the 1st value is constant
double val = firstValue();
if ((val >= min) && (val <= max)) {
TimeSplitterType extraFilter;
extraFilter.push_back(SplittingInterval(range.begin(), firstTime(), 0));
// Include everything from the start of the run to the first time measured
// (which may be a null time interval; this'll be ignored)
split = split | extraFilter;
}
// Assume everything after the LAST value is constant
val = lastValue();
if ((val >= min) && (val <= max)) {
TimeSplitterType extraFilter;
extraFilter.push_back(SplittingInterval(lastTime(), range.end(), 0));
// Include everything from the start of the run to the first time measured
// (which may be a null time interval; this'll be ignored)
split = split | extraFilter;
}
/** Function specialization for TimeSeriesProperty<std::string>
* @throws Kernel::Exception::NotImplementedError always
*/
template <>
void TimeSeriesProperty<std::string>::expandFilterToRange(
std::vector<SplittingInterval> &, double, double,
const TimeInterval &) const {
throw Exception::NotImplementedError("TimeSeriesProperty::makeFilterByValue "
"is not implemented for string "
"properties");
}
/** Calculates the time-weighted average of a property in a filtered range.
* This is written for that case of logs whose values start at the times given.
* @param filter The splitter/filter restricting the range of values included
* @return The time-weighted average value of the log in the range within the
* filter.
*/
template <typename TYPE>
double TimeSeriesProperty<TYPE>::averageValueInFilter(
const std::vector<SplittingInterval> &filter) const {
// TODO: Consider logs that aren't giving starting values.
// First of all, if the log or the filter is empty, return NaN
if (realSize() == 0 || filter.empty()) {
return std::numeric_limits<double>::quiet_NaN();
}
// If there's just a single value in the log, return that.
if (realSize() == 1) {
return static_cast<double>(m_values.front().value());
}
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
// Sort, if necessary.
sort();
double numerator(0.0), totalTime(0.0);
// Loop through the filter ranges
for (TimeSplitterType::const_iterator it = filter.begin(); it != filter.end();
++it) {
// Calculate the total time duration (in seconds) within by the filter
totalTime += it->duration();
// Get the log value and index at the start time of the filter
int index;
double value = getSingleValue(it->start(), index);
DateAndTime startTime = it->start();
while (index < realSize() - 1 && m_values[index + 1].time() < it->stop()) {
++index;
numerator +=
DateAndTime::secondsFromDuration(m_values[index].time() - startTime) *
value;
startTime = m_values[index].time();
value = static_cast<double>(m_values[index].value());
}
// Now close off with the end of the current filter range
numerator +=
DateAndTime::secondsFromDuration(it->stop() - startTime) * value;
}
// 'Normalise' by the total time
return numerator / totalTime;
}
/** Calculates the time-weighted average of a property.
* @return The time-weighted average value of the log.
*/
template <typename TYPE>
double TimeSeriesProperty<TYPE>::timeAverageValue() const {
double retVal = 0.0;
try {
TimeSplitterType filter;
filter.push_back(SplittingInterval(this->firstTime(), this->lastTime()));
retVal = this->averageValueInFilter(filter);
// just return nan
retVal = std::numeric_limits<double>::quiet_NaN();
}
return retVal;
}
/** Function specialization for TimeSeriesProperty<std::string>
* @throws Kernel::Exception::NotImplementedError always
*/
template <>
double TimeSeriesProperty<std::string>::averageValueInFilter(
const TimeSplitterType &) const {
throw Exception::NotImplementedError("TimeSeriesProperty::"
"averageValueInFilter is not "
"implemented for string properties");
}
// Re-enable the warnings disabled before makeFilterByValue
#ifdef _WIN32
#pragma warning(pop)
#endif
#if defined(__GNUC__) && !(defined(__INTEL_COMPILER))
#pragma GCC diagnostic warning "-Wconversion"
#endif
/**
* Return the time series as a correct C++ map<DateAndTime, TYPE>. All values
* are included.
*
* @return time series property values as map
*/
template <typename TYPE>
std::map<DateAndTime, TYPE>
TimeSeriesProperty<TYPE>::valueAsCorrectMap() const {
// 1. Sort if necessary
sort();
// 2. Data Strcture
std::map<DateAndTime, TYPE> asMap;
if (m_values.size() > 0) {
for (size_t i = 0; i < m_values.size(); i++)
asMap[m_values[i].time()] = m_values[i].value();
}
/**
* Return the time series's values as a vector<TYPE>
* @return the time series's values as a vector<TYPE>
*/
template <typename TYPE>
std::vector<TYPE> TimeSeriesProperty<TYPE>::valuesAsVector() const {
sort();
std::vector<TYPE> out;
out.reserve(m_values.size());
for (size_t i = 0; i < m_values.size(); i++)
out.push_back(m_values[i].value());
/**
* Return the time series as a C++ multimap<DateAndTime, TYPE>. All values.
* This method is used in parsing the ISIS ICPevent log file: different
* commands
* can be recorded against the same time stamp but all must be present.
*/
template <typename TYPE>
std::multimap<DateAndTime, TYPE>
TimeSeriesProperty<TYPE>::valueAsMultiMap() const {
std::multimap<DateAndTime, TYPE> asMultiMap;
if (m_values.size() > 0) {
for (size_t i = 0; i < m_values.size(); i++)
asMultiMap.insert(
std::make_pair(m_values[i].time(), m_values[i].value()));
}
/**
* Return the time series's times as a vector<DateAndTime>
* @return A vector of DateAndTime objects
*/
template <typename TYPE>
std::vector<DateAndTime> TimeSeriesProperty<TYPE>::timesAsVector() const {
sort();
std::vector<DateAndTime> out;
out.reserve(m_values.size());
for (size_t i = 0; i < m_values.size(); i++) {
out.push_back(m_values[i].time());
}
/**
* @return Return the series as list of times, where the time is the number of
* seconds since the start.
*/
template <typename TYPE>
std::vector<double> TimeSeriesProperty<TYPE>::timesAsVectorSeconds() const {
// 1. Sort if necessary
sort();
// 2. Output data structure
std::vector<double> out;
out.reserve(m_values.size());
Kernel::DateAndTime start = m_values[0].time();
for (size_t i = 0; i < m_values.size(); i++) {
out.push_back(DateAndTime::secondsFromDuration(m_values[i].time() - start));
}
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
/** Add a value to the series.
* Added values need not be sequential in time.
* @param time The time
* @param value The associated value
*/
template <typename TYPE>
void TimeSeriesProperty<TYPE>::addValue(const Kernel::DateAndTime &time,
const TYPE value) {
TimeValueUnit<TYPE> newvalue(time, value);
// Add the value to the back of the vector
m_values.push_back(newvalue);
// Increment the separate record of the property's size
m_size++;
// Toggle the sorted flag if necessary
// (i.e. if the flag says we're sorted and the added time is before the prior
// last time)
if (m_size == 1) {
// First item, must be sorted.
m_propSortedFlag = TimeSeriesSortStatus::TSSORTED;
} else if (m_propSortedFlag == TimeSeriesSortStatus::TSUNKNOWN &&
m_values.back() < *(m_values.rbegin() + 1)) {
// Previously unknown and still unknown
m_propSortedFlag = TimeSeriesSortStatus::TSUNSORTED;
} else if (m_propSortedFlag == TimeSeriesSortStatus::TSSORTED &&
m_values.back() < *(m_values.rbegin() + 1)) {
// Previously sorted but last added is not in order
m_propSortedFlag = TimeSeriesSortStatus::TSUNSORTED;
}
/** Add a value to the map
* @param time :: The time as a string in the format: (ISO 8601)
* yyyy-mm-ddThh:mm:ss
* @param value :: The associated value
* @return True if insertion successful (i.e. identical time not already in map
*/
template <typename TYPE>
void TimeSeriesProperty<TYPE>::addValue(const std::string &time,
const TYPE value) {
return addValue(Kernel::DateAndTime(time), value);
}
/**
* Add a value to the map using a time_t
* @param time :: The time as a time_t value
* @param value :: The associated value
* @return True if insertion successful (i.e. identical time not already in map
*/
template <typename TYPE>
void TimeSeriesProperty<TYPE>::addValue(const std::time_t &time,
const TYPE value) {
Kernel::DateAndTime dt;
dt.set_from_time_t(time);
return addValue(dt, value);
}
/** Adds vectors of values to the map. Should be much faster than repeated calls
* to addValue.
* @param times :: The time as a boost::posix_time::ptime value
* @param values :: The associated value
*/
template <typename TYPE>
void TimeSeriesProperty<TYPE>::addValues(
const std::vector<Kernel::DateAndTime> ×,
const std::vector<TYPE> &values) {
for (size_t i = 0; i < times.size(); i++) {
if (i >= values.size())
break;
else {
m_values.push_back(TimeValueUnit<TYPE>(times[i], values[i]));
m_size++;
if (values.size() > 0)
m_propSortedFlag = TimeSeriesSortStatus::TSUNKNOWN;