#10358 Regression Analysis: Allow selecting subset of time range for regression

This commit is contained in:
Kristian Bendiksen 2023-06-07 11:23:42 +02:00
parent 5bd492dc56
commit f1794abff2
9 changed files with 188 additions and 37 deletions

View File

@ -90,6 +90,7 @@ RimSummaryRegressionAnalysisCurve*
summaryPlot->addCurveAndUpdate( newCurve );
newCurve->updateDefaultValues();
newCurve->loadDataAndUpdate( true );
newCurve->updateConnectedEditors();

View File

@ -708,6 +708,8 @@ void RimSummaryCurve::onLoadDataAndUpdate( bool updateParentPlot )
{
shouldPopulateViewWithEmptyData = true;
}
updateTimeAnnotations();
}
if ( shouldPopulateViewWithEmptyData )
@ -1293,3 +1295,10 @@ void RimSummaryCurve::calculateCurveInterpolationFromAddress()
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryCurve::updateTimeAnnotations()
{
}

View File

@ -115,6 +115,8 @@ protected:
virtual std::vector<time_t> timeStepsX() const;
virtual void updateTimeAnnotations();
// Overridden PDM methods
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
QList<caf::PdmOptionItemInfo> calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override;

View File

@ -1345,29 +1345,29 @@ void RimSummaryPlot::updateCaseNameHasChanged()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlot::addTimeAnnotation( time_t time )
RimTimeAxisAnnotation* RimSummaryPlot::addTimeAnnotation( time_t time )
{
RimSummaryTimeAxisProperties* axisProps = timeAxisProperties();
{
auto* annotation = new RimTimeAxisAnnotation;
annotation->setTime( time );
axisProps->appendAnnotation( annotation );
}
auto* annotation = new RimTimeAxisAnnotation;
annotation->setTime( time );
axisProps->appendAnnotation( annotation );
return annotation;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlot::addTimeRangeAnnotation( time_t startTime, time_t endTime )
RimTimeAxisAnnotation* RimSummaryPlot::addTimeRangeAnnotation( time_t startTime, time_t endTime )
{
RimSummaryTimeAxisProperties* axisProps = timeAxisProperties();
{
auto* annotation = new RimTimeAxisAnnotation;
annotation->setTimeRange( startTime, endTime );
axisProps->appendAnnotation( annotation );
}
auto* annotation = new RimTimeAxisAnnotation;
annotation->setTimeRange( startTime, endTime );
axisProps->appendAnnotation( annotation );
return annotation;
}
//--------------------------------------------------------------------------------------------------
@ -1379,6 +1379,15 @@ void RimSummaryPlot::removeAllTimeAnnotations()
axisProps->removeAllAnnotations();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryPlot::removeTimeAnnotation( RimTimeAxisAnnotation* annotation )
{
RimSummaryTimeAxisProperties* axisProps = timeAxisProperties();
axisProps->removeAnnotation( annotation );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -60,6 +60,7 @@ class RimSummaryNameHelper;
class RimSummaryPlotNameHelper;
class RimPlotTemplateFileItem;
class RimSummaryPlotSourceStepping;
class RimTimeAxisAnnotation;
class RiaSummaryCurveDefinition;
class QwtInterval;
@ -122,9 +123,10 @@ public:
void reattachAllCurves() override;
void updateCaseNameHasChanged();
void addTimeAnnotation( time_t time );
void addTimeRangeAnnotation( time_t startTime, time_t endTime );
void removeAllTimeAnnotations();
RimTimeAxisAnnotation* addTimeAnnotation( time_t time );
RimTimeAxisAnnotation* addTimeRangeAnnotation( time_t startTime, time_t endTime );
void removeTimeAnnotation( RimTimeAxisAnnotation* annotation );
void removeAllTimeAnnotations();
void updateAxes() override;

View File

@ -22,8 +22,11 @@
#include "RiaTimeTTools.h"
#include "RimSummaryPlot.h"
#include "RimTimeAxisAnnotation.h"
#include "cafPdmUiDateEditor.h"
#include "cafPdmUiLineEditor.h"
#include "cafPdmUiSliderEditor.h"
#include "cafPdmUiTextEditor.h"
#include "ExponentialRegression.hpp"
@ -78,6 +81,14 @@ RimSummaryRegressionAnalysisCurve::RimSummaryRegressionAnalysisCurve()
CAF_PDM_InitFieldNoDefault( &m_forecastUnit, "ForecastUnit", "Unit" );
CAF_PDM_InitField( &m_polynominalDegree, "PolynominalDegree", 3, "Degree" );
CAF_PDM_InitFieldNoDefault( &m_minTimeStep, "MinTimeStep", "From" );
m_minTimeStep.uiCapability()->setUiEditorTypeName( caf::PdmUiSliderEditor::uiEditorTypeName() );
CAF_PDM_InitFieldNoDefault( &m_maxTimeStep, "MaxTimeStep", "To" );
m_maxTimeStep.uiCapability()->setUiEditorTypeName( caf::PdmUiSliderEditor::uiEditorTypeName() );
CAF_PDM_InitField( &m_showTimeSelectionInPlot, "ShowTimeSelectionInPlot", false, "Show In Plot" );
CAF_PDM_InitFieldNoDefault( &m_expressionText, "ExpressionText", "Expression" );
m_expressionText.uiCapability()->setUiEditorTypeName( caf::PdmUiTextEditor::uiEditorTypeName() );
m_expressionText.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
@ -90,6 +101,8 @@ RimSummaryRegressionAnalysisCurve::RimSummaryRegressionAnalysisCurve()
//--------------------------------------------------------------------------------------------------
RimSummaryRegressionAnalysisCurve::~RimSummaryRegressionAnalysisCurve()
{
auto plot = firstAncestorOrThisOfType<RimSummaryPlot>();
if ( plot && m_timeRangeAnnotation ) plot->removeTimeAnnotation( m_timeRangeAnnotation );
}
//--------------------------------------------------------------------------------------------------
@ -148,29 +161,38 @@ std::tuple<std::vector<time_t>, std::vector<double>, QString>
{
if ( values.empty() || timeSteps.empty() ) return { timeSteps, values, "" };
std::vector<double> timeStepsD = convertToDouble( timeSteps );
auto [timeStepsInRange, valuesInRange] = getInRangeValues( timeSteps, values, m_minTimeStep, m_maxTimeStep );
std::vector<time_t> outputTimeSteps = getOutputTimeSteps( timeSteps, m_forecastBackward(), m_forecastForward(), m_forecastUnit() );
if ( timeStepsInRange.empty() || valuesInRange.empty() ) return {};
std::vector<double> timeStepsD = convertToDouble( timeStepsInRange );
std::vector<time_t> outputTimeSteps = getOutputTimeSteps( timeStepsInRange, m_forecastBackward(), m_forecastForward(), m_forecastUnit() );
std::vector<double> outputTimeStepsD = convertToDouble( outputTimeSteps );
if ( timeStepsD.empty() || valuesInRange.empty() )
{
return {};
}
if ( m_regressionType == RegressionType::LINEAR )
{
regression::LinearRegression linearRegression;
linearRegression.fit( timeStepsD, values );
linearRegression.fit( timeStepsD, valuesInRange );
std::vector<double> predictedValues = linearRegression.predict( outputTimeStepsD );
return { outputTimeSteps, predictedValues, generateRegressionText( linearRegression ) };
}
else if ( m_regressionType == RegressionType::POLYNOMINAL )
{
regression::PolynominalRegression polynominalRegression;
polynominalRegression.fit( timeStepsD, values, m_polynominalDegree );
polynominalRegression.fit( timeStepsD, valuesInRange, m_polynominalDegree );
std::vector<double> predictedValues = polynominalRegression.predict( outputTimeStepsD );
return { outputTimeSteps, predictedValues, generateRegressionText( polynominalRegression ) };
}
else if ( m_regressionType == RegressionType::POWER_FIT )
{
auto [filteredTimeSteps, filteredValues] = getPositiveValues( timeStepsD, values );
auto [filteredTimeSteps, filteredValues] = getPositiveValues( timeStepsD, valuesInRange );
regression::PowerFitRegression powerFitRegression;
powerFitRegression.fit( filteredTimeSteps, filteredValues );
std::vector<double> predictedValues = powerFitRegression.predict( outputTimeStepsD );
@ -178,7 +200,7 @@ std::tuple<std::vector<time_t>, std::vector<double>, QString>
}
else if ( m_regressionType == RegressionType::EXPONENTIAL )
{
auto [filteredTimeSteps, filteredValues] = getPositiveValues( timeStepsD, values );
auto [filteredTimeSteps, filteredValues] = getPositiveValues( timeStepsD, valuesInRange );
regression::ExponentialRegression exponentialRegression;
exponentialRegression.fit( filteredTimeSteps, filteredValues );
std::vector<double> predictedValues = exponentialRegression.predict( outputTimeStepsD );
@ -186,7 +208,7 @@ std::tuple<std::vector<time_t>, std::vector<double>, QString>
}
else if ( m_regressionType == RegressionType::LOGARITHMIC )
{
auto [filteredTimeSteps, filteredValues] = getPositiveValues( timeStepsD, values );
auto [filteredTimeSteps, filteredValues] = getPositiveValues( timeStepsD, valuesInRange );
regression::LogarithmicRegression logarithmicRegression;
logarithmicRegression.fit( filteredTimeSteps, filteredValues );
std::vector<double> predictedValues = logarithmicRegression.predict( outputTimeStepsD );
@ -195,7 +217,7 @@ std::tuple<std::vector<time_t>, std::vector<double>, QString>
else if ( m_regressionType == RegressionType::LOGISTIC )
{
regression::LogisticRegression logisticRegression;
logisticRegression.fit( timeStepsD, values );
logisticRegression.fit( timeStepsD, valuesInRange );
std::vector<double> predictedValues = logisticRegression.predict( outputTimeStepsD );
return { convertToTimeT( outputTimeStepsD ), predictedValues, generateRegressionText( logisticRegression ) };
}
@ -220,6 +242,11 @@ void RimSummaryRegressionAnalysisCurve::defineUiOrdering( QString uiConfigName,
regressionCurveGroup->add( &m_expressionText );
caf::PdmUiGroup* timeSelectionGroup = uiOrdering.addNewGroup( "Time Selection" );
timeSelectionGroup->add( &m_minTimeStep );
timeSelectionGroup->add( &m_maxTimeStep );
timeSelectionGroup->add( &m_showTimeSelectionInPlot );
caf::PdmUiGroup* forecastingGroup = uiOrdering.addNewGroup( "Forecasting" );
forecastingGroup->add( &m_forecastForward );
forecastingGroup->add( &m_forecastBackward );
@ -235,11 +262,23 @@ void RimSummaryRegressionAnalysisCurve::fieldChangedByUi( const caf::PdmFieldHan
const QVariant& oldValue,
const QVariant& newValue )
{
if ( &m_minTimeStep == changedField && m_minTimeStep > m_maxTimeStep )
{
m_maxTimeStep = m_minTimeStep;
}
if ( &m_maxTimeStep == changedField && m_maxTimeStep < m_minTimeStep )
{
m_minTimeStep = m_maxTimeStep;
}
RimSummaryCurve::fieldChangedByUi( changedField, oldValue, newValue );
if ( changedField == &m_regressionType || changedField == &m_polynominalDegree || changedField == &m_forecastBackward ||
changedField == &m_forecastForward || changedField == &m_forecastUnit )
changedField == &m_forecastForward || changedField == &m_forecastUnit || changedField == &m_minTimeStep ||
changedField == &m_maxTimeStep || changedField == &m_showTimeSelectionInPlot )
{
loadAndUpdateDataAndPlot();
auto plot = firstAncestorOrThisOfTypeAsserted<RimSummaryPlot>();
if ( plot ) plot->zoomAll();
}
@ -270,6 +309,19 @@ void RimSummaryRegressionAnalysisCurve::defineEditorAttribute( const caf::PdmFie
lineEditorAttr->validator = new QIntValidator( 0, 50, nullptr );
}
}
else if ( field == &m_minTimeStep || field == &m_maxTimeStep )
{
if ( auto* myAttr = dynamic_cast<caf::PdmUiSliderEditorAttribute*>( attribute ) )
{
auto timeSteps = RimSummaryCurve::timeStepsY();
if ( !timeSteps.empty() )
{
myAttr->m_minimum = *timeSteps.begin();
myAttr->m_maximum = *timeSteps.rbegin();
}
myAttr->m_showSpinBox = false;
}
}
else if ( field == &m_expressionText )
{
auto myAttr = dynamic_cast<caf::PdmUiTextEditorAttribute*>( attribute );
@ -480,3 +532,55 @@ std::pair<std::vector<double>, std::vector<double>>
return std::make_pair( filteredTimeSteps, filteredValues );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<std::vector<time_t>, std::vector<double>> RimSummaryRegressionAnalysisCurve::getInRangeValues( const std::vector<time_t>& timeSteps,
const std::vector<double>& values,
time_t minTimeStep,
time_t maxTimeStep )
{
CAF_ASSERT( timeSteps.size() == values.size() );
std::vector<time_t> filteredTimeSteps;
std::vector<double> filteredValues;
for ( size_t i = 0; i < timeSteps.size(); i++ )
{
time_t timeStep = timeSteps[i];
if ( timeStep >= minTimeStep && timeStep <= maxTimeStep )
{
filteredTimeSteps.push_back( timeStep );
filteredValues.push_back( values[i] );
}
}
return std::make_pair( filteredTimeSteps, filteredValues );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryRegressionAnalysisCurve::updateTimeAnnotations()
{
auto plot = firstAncestorOrThisOfTypeAsserted<RimSummaryPlot>();
if ( m_timeRangeAnnotation ) plot->removeTimeAnnotation( m_timeRangeAnnotation );
if ( m_showTimeSelectionInPlot )
{
m_timeRangeAnnotation = plot->addTimeRangeAnnotation( m_minTimeStep, m_maxTimeStep );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryRegressionAnalysisCurve::updateDefaultValues()
{
auto timeSteps = RimSummaryCurve::timeStepsY();
if ( !timeSteps.empty() )
{
m_minTimeStep = timeSteps.front();
m_maxTimeStep = timeSteps.back();
}
}

View File

@ -18,14 +18,12 @@
#pragma once
#include "cafAppEnum.h"
#include "cafPdmField.h"
#include "cafPdmObject.h"
#include "RimSummaryCurve.h"
#include "cafAppEnum.h"
#include "regression-analysis/src/PowerFitRegression.hpp"
#include <utility>
namespace regression
@ -38,6 +36,8 @@ class PolynominalRegression;
class PowerFitRegression;
} // namespace regression
class RimTimeAxisAnnotation;
//==================================================================================================
///
///
@ -77,6 +77,11 @@ public:
static std::vector<time_t>
getOutputTimeSteps( const std::vector<time_t>& timeSteps, int forecastBackward, int forecastForward, ForecastUnit forecastUnit );
void updateDefaultValues();
protected:
void updateTimeAnnotations() override;
private:
void onLoadDataAndUpdate( bool updateParentPlot ) override;
@ -97,6 +102,9 @@ private:
static std::pair<std::vector<double>, std::vector<double>> getPositiveValues( const std::vector<double>& timeSteps,
const std::vector<double>& values );
static std::pair<std::vector<time_t>, std::vector<double>>
getInRangeValues( const std::vector<time_t>& timeSteps, const std::vector<double>& values, time_t minTimeStep, time_t maxTimeStep );
static QString generateRegressionText( const regression::LinearRegression& reg );
static QString generateRegressionText( const regression::PolynominalRegression& reg );
static QString generateRegressionText( const regression::PowerFitRegression& reg );
@ -109,14 +117,19 @@ private:
static void appendTimeSteps( std::vector<time_t>& destinationTimeSteps, const std::set<QDateTime>& sourceTimeSteps );
caf::PdmField<caf::AppEnum<RegressionType>> m_regressionType;
caf::PdmField<int> m_polynominalDegree;
caf::PdmField<QString> m_expressionText;
caf::PdmField<int> m_forecastForward;
caf::PdmField<int> m_forecastBackward;
caf::PdmField<caf::AppEnum<ForecastUnit>> m_forecastUnit;
caf::PdmField<time_t> m_minTimeStep;
caf::PdmField<time_t> m_maxTimeStep;
caf::PdmField<bool> m_showTimeSelectionInPlot;
std::vector<double> m_valuesX;
std::vector<time_t> m_timeStepsX;
std::vector<double> m_valuesY;
std::vector<time_t> m_timeStepsY;
caf::PdmField<int> m_polynominalDegree;
caf::PdmField<QString> m_expressionText;
caf::PdmField<int> m_forecastForward;
caf::PdmField<int> m_forecastBackward;
caf::PdmField<caf::AppEnum<ForecastUnit>> m_forecastUnit;
caf::PdmPointer<RimTimeAxisAnnotation> m_timeRangeAnnotation;
std::vector<double> m_valuesX;
std::vector<time_t> m_timeStepsX;
std::vector<double> m_valuesY;
std::vector<time_t> m_timeStepsY;
};

View File

@ -150,6 +150,7 @@ RimSummaryTimeAxisProperties::RimSummaryTimeAxisProperties()
CAF_PDM_InitFieldNoDefault( &m_annotations, "Annotations", "" );
m_annotations.uiCapability()->setUiTreeHidden( true );
m_annotations.uiCapability()->setUiTreeChildrenHidden( true );
}
//--------------------------------------------------------------------------------------------------
@ -758,6 +759,14 @@ void RimSummaryTimeAxisProperties::removeAllAnnotations()
m_annotations.deleteChildren();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryTimeAxisProperties::removeAnnotation( RimTimeAxisAnnotation* annotation )
{
m_annotations.removeChild( annotation );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -101,7 +101,9 @@ public:
std::vector<RimPlotAxisAnnotation*> annotations() const override;
void appendAnnotation( RimPlotAxisAnnotation* annotation ) override;
void removeAllAnnotations() override;
void removeAnnotation( RimTimeAxisAnnotation* annotation );
void removeAllAnnotations() override;
const QString& dateFormat() const;
const QString& timeFormat() const;