From f1794abff2cb9a57a6cd3118b225d0391515dc91 Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Wed, 7 Jun 2023 11:23:42 +0200 Subject: [PATCH] #10358 Regression Analysis: Allow selecting subset of time range for regression --- ...icCreateRegressionAnalysisCurveFeature.cpp | 1 + .../Summary/RimSummaryCurve.cpp | 9 ++ .../Summary/RimSummaryCurve.h | 2 + .../Summary/RimSummaryPlot.cpp | 33 +++-- .../ProjectDataModel/Summary/RimSummaryPlot.h | 8 +- .../RimSummaryRegressionAnalysisCurve.cpp | 122 ++++++++++++++++-- .../RimSummaryRegressionAnalysisCurve.h | 37 ++++-- .../Summary/RimSummaryTimeAxisProperties.cpp | 9 ++ .../Summary/RimSummaryTimeAxisProperties.h | 4 +- 9 files changed, 188 insertions(+), 37 deletions(-) diff --git a/ApplicationLibCode/Commands/SummaryPlotCommands/RicCreateRegressionAnalysisCurveFeature.cpp b/ApplicationLibCode/Commands/SummaryPlotCommands/RicCreateRegressionAnalysisCurveFeature.cpp index 0ec985e151..cc583471e4 100644 --- a/ApplicationLibCode/Commands/SummaryPlotCommands/RicCreateRegressionAnalysisCurveFeature.cpp +++ b/ApplicationLibCode/Commands/SummaryPlotCommands/RicCreateRegressionAnalysisCurveFeature.cpp @@ -90,6 +90,7 @@ RimSummaryRegressionAnalysisCurve* summaryPlot->addCurveAndUpdate( newCurve ); + newCurve->updateDefaultValues(); newCurve->loadDataAndUpdate( true ); newCurve->updateConnectedEditors(); diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp index 8f67825b0c..13af485001 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.cpp @@ -708,6 +708,8 @@ void RimSummaryCurve::onLoadDataAndUpdate( bool updateParentPlot ) { shouldPopulateViewWithEmptyData = true; } + + updateTimeAnnotations(); } if ( shouldPopulateViewWithEmptyData ) @@ -1293,3 +1295,10 @@ void RimSummaryCurve::calculateCurveInterpolationFromAddress() } } } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimSummaryCurve::updateTimeAnnotations() +{ +} diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.h b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.h index 8e670eb100..9e7b786244 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryCurve.h @@ -115,6 +115,8 @@ protected: virtual std::vector timeStepsX() const; + virtual void updateTimeAnnotations(); + // Overridden PDM methods void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override; QList calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlot.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlot.cpp index 728f9fc00d..6552582e31 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlot.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlot.cpp @@ -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 ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlot.h b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlot.h index 2dde10aec9..62cefb5013 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlot.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryPlot.h @@ -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; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp index 175d616b07..d9775debe4 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.cpp @@ -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(); + if ( plot && m_timeRangeAnnotation ) plot->removeTimeAnnotation( m_timeRangeAnnotation ); } //-------------------------------------------------------------------------------------------------- @@ -148,29 +161,38 @@ std::tuple, std::vector, QString> { if ( values.empty() || timeSteps.empty() ) return { timeSteps, values, "" }; - std::vector timeStepsD = convertToDouble( timeSteps ); + auto [timeStepsInRange, valuesInRange] = getInRangeValues( timeSteps, values, m_minTimeStep, m_maxTimeStep ); - std::vector outputTimeSteps = getOutputTimeSteps( timeSteps, m_forecastBackward(), m_forecastForward(), m_forecastUnit() ); + if ( timeStepsInRange.empty() || valuesInRange.empty() ) return {}; + + std::vector timeStepsD = convertToDouble( timeStepsInRange ); + + std::vector outputTimeSteps = getOutputTimeSteps( timeStepsInRange, m_forecastBackward(), m_forecastForward(), m_forecastUnit() ); std::vector 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 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 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 predictedValues = powerFitRegression.predict( outputTimeStepsD ); @@ -178,7 +200,7 @@ std::tuple, std::vector, 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 predictedValues = exponentialRegression.predict( outputTimeStepsD ); @@ -186,7 +208,7 @@ std::tuple, std::vector, 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 predictedValues = logarithmicRegression.predict( outputTimeStepsD ); @@ -195,7 +217,7 @@ std::tuple, std::vector, QString> else if ( m_regressionType == RegressionType::LOGISTIC ) { regression::LogisticRegression logisticRegression; - logisticRegression.fit( timeStepsD, values ); + logisticRegression.fit( timeStepsD, valuesInRange ); std::vector 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(); 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( 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( attribute ); @@ -480,3 +532,55 @@ std::pair, std::vector> return std::make_pair( filteredTimeSteps, filteredValues ); } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::pair, std::vector> RimSummaryRegressionAnalysisCurve::getInRangeValues( const std::vector& timeSteps, + const std::vector& values, + time_t minTimeStep, + time_t maxTimeStep ) +{ + CAF_ASSERT( timeSteps.size() == values.size() ); + + std::vector filteredTimeSteps; + std::vector 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(); + 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(); + } +} diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.h b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.h index 64982c8e2b..e640457e95 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryRegressionAnalysisCurve.h @@ -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 namespace regression @@ -38,6 +36,8 @@ class PolynominalRegression; class PowerFitRegression; } // namespace regression +class RimTimeAxisAnnotation; + //================================================================================================== /// /// @@ -77,6 +77,11 @@ public: static std::vector getOutputTimeSteps( const std::vector& 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> getPositiveValues( const std::vector& timeSteps, const std::vector& values ); + static std::pair, std::vector> + getInRangeValues( const std::vector& timeSteps, const std::vector& 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& destinationTimeSteps, const std::set& sourceTimeSteps ); caf::PdmField> m_regressionType; - caf::PdmField m_polynominalDegree; - caf::PdmField m_expressionText; - caf::PdmField m_forecastForward; - caf::PdmField m_forecastBackward; - caf::PdmField> m_forecastUnit; + caf::PdmField m_minTimeStep; + caf::PdmField m_maxTimeStep; + caf::PdmField m_showTimeSelectionInPlot; - std::vector m_valuesX; - std::vector m_timeStepsX; - std::vector m_valuesY; - std::vector m_timeStepsY; + caf::PdmField m_polynominalDegree; + caf::PdmField m_expressionText; + caf::PdmField m_forecastForward; + caf::PdmField m_forecastBackward; + caf::PdmField> m_forecastUnit; + + caf::PdmPointer m_timeRangeAnnotation; + std::vector m_valuesX; + std::vector m_timeStepsX; + std::vector m_valuesY; + std::vector m_timeStepsY; }; diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryTimeAxisProperties.cpp b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryTimeAxisProperties.cpp index 8df599dd45..302b3bce1b 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryTimeAxisProperties.cpp +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryTimeAxisProperties.cpp @@ -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 ); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryTimeAxisProperties.h b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryTimeAxisProperties.h index 9d2e0072f6..926078c5ce 100644 --- a/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryTimeAxisProperties.h +++ b/ApplicationLibCode/ProjectDataModel/Summary/RimSummaryTimeAxisProperties.h @@ -101,7 +101,9 @@ public: std::vector 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;