From 54b5dc33f43f915fec394c737e2826e90ec4b874 Mon Sep 17 00:00:00 2001 From: Gaute Lindkvist Date: Tue, 29 Jan 2019 16:42:04 +0100 Subject: [PATCH] #4013 Create variation bins for ensemble parameters based on current data * Rather than having a fixed index. * This way the variation is more or less always normal distributed. --- .../Summary/RimEnsembleCurveSet.cpp | 19 +-- .../Summary/RimEnsembleCurveSet.h | 29 ++--- .../Summary/RimSummaryCaseCollection.cpp | 109 ++++++++++++------ .../Summary/RimSummaryCaseCollection.h | 19 ++- .../RimSummaryCaseCollection-Test.cpp | 29 +++-- 5 files changed, 124 insertions(+), 81 deletions(-) diff --git a/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp b/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp index 0770d61629..f3595b31b6 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp +++ b/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.cpp @@ -453,16 +453,13 @@ QString RimEnsembleCurveSet::ensembleParameterUiName(const RimEnsembleCurveSet:: QString variationString; if (paramPair.second.isNumeric()) { - switch (paramPair.second.logarithmicVariationIndex()) + switch (paramPair.second.variationBin) { - case -1: - variationString = QString(" (No variation)"); - case 0: + case EnsembleParameter::LOW_VARIATION: variationString = QString(" (Low variation)"); + case EnsembleParameter::MEDIUM_VARIATION: break; - case 1: - break; - case 2: + case EnsembleParameter::HIGH_VARIATION: variationString = QString(" (High variation)"); break; } @@ -1170,13 +1167,7 @@ std::vector RimEnsembleCurveSet::ensembl { parameterVector.push_back(std::make_pair(parameterName, group->ensembleParameter(parameterName))); } - - // Sort by variation index (highest first) but keep name as sorting parameter when parameters have the same variation index - std::stable_sort(parameterVector.begin(), parameterVector.end(), [](const NameParameterPair& lhs, const NameParameterPair& rhs) - { - return lhs.second.logarithmicVariationIndex() > rhs.second.logarithmicVariationIndex(); - }); - + EnsembleParameter::sortByBinnedVariation(parameterVector); return parameterVector; } diff --git a/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h b/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h index aaefc0b0be..3f6af71fa7 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h +++ b/ApplicationCode/ProjectDataModel/Summary/RimEnsembleCurveSet.h @@ -127,26 +127,27 @@ private: caf::PdmFieldHandle* userDescriptionField() override; caf::PdmFieldHandle* objectToggleField() override; - void defineEditorAttribute(const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute) override; + void defineEditorAttribute(const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute) override; - QList calculateValueOptions(const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly) override; - void defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering) override; - void defineUiTreeOrdering(caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "") override; + QList calculateValueOptions(const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly) override; + void defineUiOrdering(QString uiConfigName, caf::PdmUiOrdering& uiOrdering) override; + void defineUiTreeOrdering(caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "") override; - void fieldChangedByUi(const caf::PdmFieldHandle* changedField, - const QVariant& oldValue, const QVariant& newValue) override; + void fieldChangedByUi(const caf::PdmFieldHandle* changedField, + const QVariant& oldValue, const QVariant& newValue) override; - void appendOptionItemsForSummaryAddresses(QList* options, - RimSummaryCaseCollection* summaryCaseGroup, - RimSummaryFilter* summaryFilter); + void appendOptionItemsForSummaryAddresses(QList* options, + RimSummaryCaseCollection* summaryCaseGroup, + RimSummaryFilter* summaryFilter); - void updateCurveColors(); - void updateQwtPlotAxis(); + void updateCurveColors(); + void updateQwtPlotAxis(); - QString name() const; - QString createAutoName() const; + QString name() const; + QString createAutoName() const; - void updateLegendMappingMode(); + void updateLegendMappingMode(); + void sortParameterVectorByBinnedVariation(std::vector& parameterVector) const; private: caf::PdmField m_showCurves; caf::PdmChildArrayField m_curves; diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.cpp b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.cpp index 610bf04386..4d648adb62 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.cpp +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.cpp @@ -36,44 +36,11 @@ CAF_PDM_SOURCE_INIT(RimSummaryCaseCollection, "SummaryCaseSubCollection"); - -//-------------------------------------------------------------------------------------------------- -/// Return an integer derived from the logarithm of the range. -/// -1 if there is practically no variation and a rising positive integer for non-zero range. -//-------------------------------------------------------------------------------------------------- -int EnsembleParameter::logarithmicVariationIndex() const -{ - const double eps = 1.0e-4; - - double maxAbs = std::max(std::fabs(maxValue), std::fabs(minValue)); - if (maxAbs < eps) - { - return -1; - } - - double normalisedStdDevPercent = 2.0 * m_stdDeviation / maxAbs * 100.0; - if (normalisedStdDevPercent < eps) - { - return -1; - } - - // Should always yield an index from and including -1 to and including 2 - // As the maximum normalisedStdDevPercent is ~282 - // Found with two values symmetric around 0 so min = -X and max = +X - // normalisedStdDevPercent is then 2 * sqrt(2X^2) / X * 100 = sqrt(2) * 100 = ~282. - // And log10 value is ~2.45. - int variationIndex = std::max(-1, (int) std::round(std::log10(normalisedStdDevPercent))); - //CVF_ASSERT(variationIndex >= -1 && variationIndex <= 2); - return variationIndex; -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void EnsembleParameter::calculateStdDeviation() +double EnsembleParameter::stdDeviation() const { - m_stdDeviation = 0.0; - double N = static_cast(values.size()); if (N > 1 && isNumeric()) { @@ -86,10 +53,80 @@ void EnsembleParameter::calculateStdDeviation() sumValuesSquared += value * value; } - m_stdDeviation = std::sqrt((N * sumValuesSquared - sumValues * sumValues) / (N * (N - 1.0))); + return std::sqrt((N * sumValuesSquared - sumValues * sumValues) / (N * (N - 1.0))); } + return 0.0; } +//-------------------------------------------------------------------------------------------------- +/// Standard deviation normalized by max absolute value of min/max values. +/// Produces values between 0.0 and sqrt(2.0). +//-------------------------------------------------------------------------------------------------- +double EnsembleParameter::normalizedStdDeviation() const +{ + const double eps = 1.0e-4; + + double maxAbs = std::max(std::fabs(maxValue), std::fabs(minValue)); + if (maxAbs < eps) + { + return 0.0; + } + + double normalisedStdDev = stdDeviation() / maxAbs; + if (normalisedStdDev < eps) + { + return 0.0; + } + return normalisedStdDev; +} + + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void EnsembleParameter::sortByBinnedVariation(std::vector& parameterVector) +{ + double minStdDev = std::numeric_limits::infinity(); + double maxStdDev = 0.0; + for (const auto& paramPair : parameterVector) + { + minStdDev = std::min(minStdDev, paramPair.second.normalizedStdDeviation()); + maxStdDev = std::max(maxStdDev, paramPair.second.normalizedStdDeviation()); + } + if ((maxStdDev - minStdDev) < 1.0e-8) + { + return; + } + + double delta = (maxStdDev - minStdDev) / NR_OF_VARIATION_BINS; + + std::vector bins; + for (int i = 0; i < NR_OF_VARIATION_BINS - 1; ++i) + { + bins.push_back(minStdDev + (i + 1) * delta); + } + + for (NameParameterPair& nameParamPair : parameterVector) + { + int binNumber = 0; + for (double bin : bins) + { + if (nameParamPair.second.normalizedStdDeviation() >= bin) + { + binNumber++; + } + } + nameParamPair.second.variationBin = binNumber; + } + + // Sort by variation bin (highest first) but keep name as sorting parameter when parameters have the same variation index + std::stable_sort(parameterVector.begin(), parameterVector.end(), + [&bins](const NameParameterPair& lhs, const NameParameterPair& rhs) + { + return lhs.second.variationBin > rhs.second.variationBin; + } + ); +} //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -338,8 +375,6 @@ EnsembleParameter RimSummaryCaseCollection::ensembleParameter(const QString& par } } - eParam.calculateStdDeviation(); - return eParam; } diff --git a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.h b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.h index 3490d86fa0..97a78c1cc0 100644 --- a/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.h +++ b/ApplicationCode/ProjectDataModel/Summary/RimSummaryCaseCollection.h @@ -25,6 +25,9 @@ #include "cafPdmObject.h" #include "cafPdmProxyValueField.h" +#include + +#include #include class RimSummaryCase; @@ -35,31 +38,35 @@ class RimSummaryCase; class EnsembleParameter { public: - enum Type { TYPE_NONE, TYPE_NUMERIC, TYPE_TEXT }; + typedef std::pair NameParameterPair; + enum Type { TYPE_NONE, TYPE_NUMERIC, TYPE_TEXT }; + enum Bins { LOW_VARIATION, MEDIUM_VARIATION, HIGH_VARIATION, NR_OF_VARIATION_BINS }; QString name; Type type; std::vector values; double minValue; double maxValue; + int variationBin; EnsembleParameter() : type(TYPE_NONE), minValue(std::numeric_limits::infinity()), maxValue(-std::numeric_limits::infinity()), - m_stdDeviation(0.0) + variationBin(static_cast(MEDIUM_VARIATION)) {} bool isValid() const { return !name.isEmpty() && type != TYPE_NONE; } bool isNumeric() const { return type == TYPE_NUMERIC; } bool isText() const { return type == TYPE_TEXT; } - double range() const { return std::abs(maxValue - minValue); } - void calculateStdDeviation(); + double range() const { return std::abs(maxValue - minValue); } + double normalizedStdDeviation() const; - int logarithmicVariationIndex() const; + static void sortByBinnedVariation(std::vector& parameterVector); private: - double m_stdDeviation; + double stdDeviation() const; + }; //================================================================================================== diff --git a/ApplicationCode/UnitTests/RimSummaryCaseCollection-Test.cpp b/ApplicationCode/UnitTests/RimSummaryCaseCollection-Test.cpp index 93d2317171..5d57fd2425 100644 --- a/ApplicationCode/UnitTests/RimSummaryCaseCollection-Test.cpp +++ b/ApplicationCode/UnitTests/RimSummaryCaseCollection-Test.cpp @@ -6,7 +6,7 @@ #include -TEST(RimSummaryCaseCollection, logarithmicVariationIndex) +TEST(RimSummaryCaseCollection, EnsembleParameter) { std::random_device rd; std::mt19937 gen(rd()); @@ -14,7 +14,8 @@ TEST(RimSummaryCaseCollection, logarithmicVariationIndex) std::uniform_real_distribution variationDistribution(0.0, 5000.0); std::uniform_int_distribution countDistribution(1u, 1000u); size_t N = 1000; - std::map indexCounts; + + std::vector parameters; for (size_t i = 0; i < N; ++i) { EnsembleParameter param; @@ -34,17 +35,25 @@ TEST(RimSummaryCaseCollection, logarithmicVariationIndex) param.values.push_back(QVariant(value)); } - param.calculateStdDeviation(); param.minValue = minValue; param.maxValue = maxValue; - int variationIndex = param.logarithmicVariationIndex(); - EXPECT_GE(variationIndex, -1); - EXPECT_LE(variationIndex, 2); - indexCounts[variationIndex]++; + + double normStdDev = param.normalizedStdDeviation(); + EXPECT_GE(normStdDev, 0.0); + EXPECT_LE(normStdDev, std::sqrt(2.0)); + parameters.push_back(std::make_pair(QString("%1").arg(i), param)); } - - for (auto countPair : indexCounts) + size_t previousSize = parameters.size(); + EnsembleParameter::sortByBinnedVariation(parameters); + size_t currentSize = parameters.size(); + EXPECT_EQ(previousSize, currentSize); + + int currentVariation = (int)EnsembleParameter::HIGH_VARIATION; + for (const EnsembleParameter::NameParameterPair& nameParamPair : parameters) { - qDebug() << "Variation index " << countPair.first << " count = " << countPair.second; + EXPECT_GE(nameParamPair.second.variationBin, (int) EnsembleParameter::LOW_VARIATION); + EXPECT_LE(nameParamPair.second.variationBin, (int) EnsembleParameter::HIGH_VARIATION); + EXPECT_LE(nameParamPair.second.variationBin, currentVariation); + currentVariation = nameParamPair.second.variationBin; } }