From 97e59331bb8965edf526ee3f2da1f854731bd227 Mon Sep 17 00:00:00 2001 From: voisine nicolas <93999116+n-voisine@users.noreply.github.com> Date: Thu, 23 Jan 2025 10:27:03 +0100 Subject: [PATCH] Extend json report for regression trees (#541) calcul d'une datagridstats de la target discretise passage de datagridstats dans la DTDecisionTreeSpec ecriture de datagridstats quand la target est continuous "targetPartition": { "variable": "PetalLength", "type": "Numerical", "partitionType": "Intervals", "partition": [ [1,2.4], [2.4,4.75], [4.75,4.85], [4.85,6.9] ], "frequencies": [38,27,3,37] }, --- .../DTForest/DTDecisionTreeCreationTask.cpp | 93 ++++++++++-- .../DTForest/DTDecisionTreeCreationTask.h | 9 +- src/Learning/DTForest/DTDecisionTreeSpec.cpp | 133 ++++++++++++++++++ src/Learning/DTForest/DTDecisionTreeSpec.h | 34 +++++ 4 files changed, 253 insertions(+), 16 deletions(-) diff --git a/src/Learning/DTForest/DTDecisionTreeCreationTask.cpp b/src/Learning/DTForest/DTDecisionTreeCreationTask.cpp index a6d47ac7f..de68e6bcf 100644 --- a/src/Learning/DTForest/DTDecisionTreeCreationTask.cpp +++ b/src/Learning/DTForest/DTDecisionTreeCreationTask.cpp @@ -2,7 +2,6 @@ // This software is distributed under the BSD 3-Clause-clear License, the text of which is available // at https://spdx.org/licenses/BSD-3-Clause-Clear.html or see the "LICENSE" file for more details. -#include "DTDecisionTreeCreationTask.h" #include "DTDecisionTreeCreationTask.h" #include "DTDecisionTree.h" #include "DTDecisionTreeSpec.h" @@ -11,7 +10,6 @@ #include "DTDecisionBinaryTreeCost.h" #include "DTStat.h" #include "DTForestAttributeSelection.h" -#include "DTDecisionTreeSpec.h" #include "DTGlobalTag.h" #include "DTDiscretizerMODL.h" #include "DTGrouperMODL.h" @@ -645,6 +643,7 @@ boolean DTDecisionTreeCreationTask::SlaveProcess() ALString sMessage; KWAttribute* attribute = NULL; KWAttributeStats* attributeStats = NULL; + KWDataGridStats targetStats; ObjectArray oaObjects; ObjectArray oatupletable; ObjectArray oaOrigineAttributs; @@ -856,12 +855,13 @@ boolean DTDecisionTreeCreationTask::SlaveProcess() InitializeMODLDiscretization( &slaveTupleTableLoader, slaveLearningSpec, *input_cvIntervalValues.GetConstContinuousVector(), - attributegenerator->GetIndex()); + attributegenerator->GetIndex(), &targetStats); else InitializeBinaryEQFDiscretization( &slaveTupleTableLoader, slaveLearningSpec, *input_cvIntervalValues.GetConstContinuousVector(), - input_ivSplitValues.GetAt(attributegenerator->GetIndex())); + input_ivSplitValues.GetAt(attributegenerator->GetIndex()), + &targetStats); blOrigine.Initialize(slaveLearningSpec, &slaveTupleTableLoader, &oaObjects); @@ -934,6 +934,23 @@ boolean DTDecisionTreeCreationTask::SlaveProcess() // Creation de la spec de l'arbre a partir de l'arbre calcule reportTreeSpec = new DTDecisionTreeSpec; reportTreeSpec->InitFromDecisionTree(dttree); + reportTreeSpec->SetTargetType( + shared_learningSpec.GetLearningSpec()->GetTargetAttributeType()); + //prise en compte de la discretisation de la cible pour la regression + if (bRegressionWithMODLDiscretization) + { + reportTreeSpec->SetTargetStats(targetStats.Clone()); + reportTreeSpec->SetTargetMin( + cast(KWDescriptiveContinuousStats*, + shared_learningSpec.GetLearningSpec() + ->GetTargetDescriptiveStats()) + ->GetMin()); + reportTreeSpec->SetTargetMax( + cast(KWDescriptiveContinuousStats*, + shared_learningSpec.GetLearningSpec() + ->GetTargetDescriptiveStats()) + ->GetMax()); + } // detection des doublons key = reportTreeSpec->ComputeHashValue(); // filtre les arbre qui @@ -2202,7 +2219,8 @@ KWLearningSpec* DTDecisionTreeCreationTask::InitializeRegressionLearningSpec(con newClass->SetName(newClass->GetName() + "_classification"); newTarget = new KWAttribute; - newTarget->SetName(learningSpec->GetTargetAttributeName() + "_categorical"); + newTarget->SetName( + learningSpec->GetClass()->BuildAttributeName(learningSpec->GetTargetAttributeName() + "_categorical")); newTarget->SetType(KWType::Symbol); newClass->InsertAttribute(newTarget); KWClassDomain::GetCurrentDomain()->InsertClass(newClass); @@ -2266,7 +2284,8 @@ void DTDecisionTreeCreationTask::InitializeEqualFreqDiscretization(KWTupleTableL void DTDecisionTreeCreationTask::InitializeMODLDiscretization(KWTupleTableLoader* tupleTableLoader, KWLearningSpec* learningSpec, - const ContinuousVector& cvIntervalValues, int nSplitIndex) + const ContinuousVector& cvIntervalValues, int nSplitIndex, + KWDataGridStats* targetStat) { // on transforme la cible continue en cible categorielle, en effectuant au prealable une dicretisation MODL sur // la cible continue @@ -2276,12 +2295,14 @@ void DTDecisionTreeCreationTask::InitializeMODLDiscretization(KWTupleTableLoader DTBaseLoader bl; SymbolVector* svTargetValues = NULL; + require(targetStat != NULL); assert(learningSpec != NULL); assert(tupleTableLoader != NULL); assert(randomForestParameter.GetDiscretizationTargetMethod() == DTForestParameter::DISCRETIZATION_MODL); - svTargetValues = MODLDiscretizeContinuousTarget( - tupleTableLoader, randomForestParameter.GetMaxIntervalsNumberForTarget(), cvIntervalValues, nSplitIndex); + svTargetValues = + MODLDiscretizeContinuousTarget(tupleTableLoader, randomForestParameter.GetMaxIntervalsNumberForTarget(), + cvIntervalValues, nSplitIndex, targetStat); assert(svTargetValues != NULL); tupleTableLoader->SetInputExtraAttributeName(learningSpec->GetTargetAttributeName()); @@ -2314,7 +2335,7 @@ void DTDecisionTreeCreationTask::InitializeMODLDiscretization(KWTupleTableLoader void DTDecisionTreeCreationTask::InitializeBinaryEQFDiscretization(KWTupleTableLoader* tupleTableLoader, KWLearningSpec* learningSpec, const ContinuousVector& cvIntervalValues, - int nSplitIndex) + int nSplitIndex, KWDataGridStats* targetStat) { // on transforme la cible continue en cible categorielle, en effectuant au prealable une dicretisation MODL sur // la cible continue @@ -2329,8 +2350,9 @@ void DTDecisionTreeCreationTask::InitializeBinaryEQFDiscretization(KWTupleTableL assert(randomForestParameter.GetDiscretizationTargetMethod() == DTForestParameter::DISCRETIZATION_BINARY_EQUAL_FREQUENCY); - svTargetValues = MODLDiscretizeContinuousTarget( - tupleTableLoader, randomForestParameter.GetMaxIntervalsNumberForTarget(), cvIntervalValues, nSplitIndex); + svTargetValues = + MODLDiscretizeContinuousTarget(tupleTableLoader, randomForestParameter.GetMaxIntervalsNumberForTarget(), + cvIntervalValues, nSplitIndex, targetStat); assert(svTargetValues != NULL); tupleTableLoader->SetInputExtraAttributeName(learningSpec->GetTargetAttributeName()); @@ -2419,20 +2441,27 @@ SymbolVector* DTDecisionTreeCreationTask::EqualFreqDiscretizeContinuousTarget(KW SymbolVector* DTDecisionTreeCreationTask::MODLDiscretizeContinuousTarget(KWTupleTableLoader* tupleTableLoader, const int nMaxIntervalsNumber, const ContinuousVector& cvInput, - int nSplitIndex) const + int nSplitIndex, + KWDataGridStats* targetStat) const { SymbolVector* svTargetValues = NULL; SymbolVector svTargetIn; ContinuousVector cvChosenIntervals; + IntVector ivFrequencyIntervals; ContinuousVector cvInputIntervalValues; ObjectDictionary odIntervals; Object o; + int nBound, nPartNumber; boolean bDisplayValues = false; Continuous cValue; ALString s; + KWDGSAttributeDiscretization* attribute; int oldseed; - //initilisation de random seed 2001 + index de l'arbre + require(cvInput.GetSize() > 0); + require(targetStat != NULL); + + //initialisation de random seed 2001 + index de l'arbre oldseed = GetRandomSeed(); SetRandomSeed(2001 + nSplitIndex); @@ -2466,9 +2495,27 @@ SymbolVector* DTDecisionTreeCreationTask::MODLDiscretizeContinuousTarget(KWTuple if (bDisplayValues) cvChosenIntervals.Write(cout); + //initialisation de target Stat d'un arbre + targetStat->DeleteAll(); + + // Creation de l'attribut + attribute = new KWDGSAttributeDiscretization; + attribute->SetAttributeName(shared_learningSpec.GetLearningSpec()->GetTargetAttributeName()); + svTargetValues = new SymbolVector; if (randomForestParameter.GetDiscretizationTargetMethod() == DTForestParameter::DISCRETIZATION_MODL) { + + // Creation des bornes des intervalles + nPartNumber = cvChosenIntervals.GetSize() - 1; + attribute->SetInitialValueNumber(nPartNumber); + attribute->SetGranularizedValueNumber(nPartNumber); + attribute->SetPartNumber(nPartNumber); + for (nBound = 1; nBound < nPartNumber; nBound++) + attribute->SetIntervalBoundAt(nBound - 1, cvChosenIntervals.GetAt(nBound)); + ensure(attribute->Check()); + ivFrequencyIntervals.SetSize(nPartNumber); + for (int nInterval = 0; nInterval < cvChosenIntervals.GetSize(); nInterval++) { s = "I" + ALString(IntToString(nInterval)); @@ -2486,6 +2533,8 @@ SymbolVector* DTDecisionTreeCreationTask::MODLDiscretizeContinuousTarget(KWTuple cValue < cvChosenIntervals.GetAt(nInterval + 1))) { svTargetValues->Add(svTargetIn.GetAt(nInterval)); + ivFrequencyIntervals.SetAt(nInterval, + ivFrequencyIntervals.GetAt(nInterval) + 1); break; } } @@ -2493,6 +2542,16 @@ SymbolVector* DTDecisionTreeCreationTask::MODLDiscretizeContinuousTarget(KWTuple } else { + // Creation des bornes des intervalles + nPartNumber = 2; + attribute->SetInitialValueNumber(nPartNumber); + attribute->SetGranularizedValueNumber(nPartNumber); + attribute->SetPartNumber(nPartNumber); + for (nBound = 1; nBound < nPartNumber; nBound++) + attribute->SetIntervalBoundAt(nBound, cvChosenIntervals.GetAt(nSplitIndex)); + ensure(attribute->Check()); + ivFrequencyIntervals.SetSize(nPartNumber); + Symbol sI0("I0"); Symbol sI1("I1"); @@ -2504,16 +2563,24 @@ SymbolVector* DTDecisionTreeCreationTask::MODLDiscretizeContinuousTarget(KWTuple if (cValue <= cvChosenIntervals.GetAt(nSplitIndex)) { svTargetValues->Add(sI0); + ivFrequencyIntervals.SetAt(0, ivFrequencyIntervals.GetAt(0) + 1); } else { svTargetValues->Add(sI1); + ivFrequencyIntervals.SetAt(1, ivFrequencyIntervals.GetAt(1) + 1); } } } assert(svTargetValues != NULL); assert(tupleTableLoader->GetInputExtraAttributeContinuousValues()->GetSize() == svTargetValues->GetSize()); + //initialisation du gridstat + targetStat->AddAttribute(attribute); + targetStat->CreateAllCells(); + for (int nInterval = 0; nInterval < nPartNumber; nInterval++) + targetStat->SetUnivariateCellFrequencyAt(nInterval, ivFrequencyIntervals.GetAt(nInterval)); + // restitution de l'etat initial : SetRandomSeed(oldseed); diff --git a/src/Learning/DTForest/DTDecisionTreeCreationTask.h b/src/Learning/DTForest/DTDecisionTreeCreationTask.h index 1560f504a..f166f8d36 100644 --- a/src/Learning/DTForest/DTDecisionTreeCreationTask.h +++ b/src/Learning/DTForest/DTDecisionTreeCreationTask.h @@ -128,14 +128,17 @@ class DTDecisionTreeCreationTask : public KDDataPreparationAttributeCreationTask // Discretisation 'MODL' d'une target continue SymbolVector* MODLDiscretizeContinuousTarget(KWTupleTableLoader*, int nMaxIntervalsNumber, - const ContinuousVector& cvIntervalValues, int nSplitIndex) const; + const ContinuousVector& cvIntervalValues, int nSplitIndex, + KWDataGridStats* targetStat) const; // transforme une regression en classification en effectuant au prealable une discretisation MODL void InitializeMODLDiscretization(KWTupleTableLoader*, KWLearningSpec*, - const ContinuousVector& cvIntervalValues, int nSplitIndex); + const ContinuousVector& cvIntervalValues, int nSplitIndex, + KWDataGridStats* targetStat); void InitializeBinaryEQFDiscretization(KWTupleTableLoader*, KWLearningSpec*, - const ContinuousVector& cvIntervalValues, int nSplitIndex); + const ContinuousVector& cvIntervalValues, int nSplitIndex, + KWDataGridStats* targetStat); /////////////////////////////////////////////////////////////////////////////////////////////////// // Reimplementation des methodes virtuelles de tache diff --git a/src/Learning/DTForest/DTDecisionTreeSpec.cpp b/src/Learning/DTForest/DTDecisionTreeSpec.cpp index 5c6c6f5de..8f8f41fd1 100644 --- a/src/Learning/DTForest/DTDecisionTreeSpec.cpp +++ b/src/Learning/DTForest/DTDecisionTreeSpec.cpp @@ -18,6 +18,10 @@ DTDecisionTreeSpec::DTDecisionTreeSpec() nInternalNodesNumber = 0; dTreeLevel = -1; nVariablesNumber = 0; + dgsTargetStats = NULL; + dTargetMin = 0.0; + dTargetMax = 0.0; + nTargetType = KWType::Symbol; } DTDecisionTreeSpec::~DTDecisionTreeSpec() @@ -31,6 +35,9 @@ void DTDecisionTreeSpec::Clean() // destruction du tableau des noeuds (y compris le noeud root) oaTreeNodes.DeleteAll(); + + if (dgsTargetStats != NULL) + delete dgsTargetStats; } DTDecisionTreeNodeSpec* DTDecisionTreeSpec::AddNodeSpec(const DTDecisionTreeNode* nNode, @@ -380,9 +387,88 @@ KWDerivationRule* DTDecisionTreeSpec::CreateGroupIndexRule(const DTDecisionTreeN void DTDecisionTreeSpec::WriteJSONArrayFields(JSONFile* fJSON, boolean bSummary) const { + ContinuousVector cvAttributeDomainLowerBounds; + ContinuousVector cvAttributeDomainUpperBounds; + + cvAttributeDomainLowerBounds.SetSize(1); + cvAttributeDomainUpperBounds.SetSize(1); + fJSON->WriteKeyString("name", sTreeVariableName); fJSON->WriteKeyInt("variableNumber", nVariablesNumber); fJSON->WriteKeyInt("depth", nDepth); + if (dgsTargetStats != NULL) + { + //initialisation des borne du rapport json + cvAttributeDomainLowerBounds.SetAt(0, dTargetMin); + cvAttributeDomainUpperBounds.SetAt(0, dTargetMax); + dgsTargetStats->SetJSONAttributeDomainLowerBounds(&cvAttributeDomainLowerBounds); + dgsTargetStats->SetJSONAttributeDomainUpperBounds(&cvAttributeDomainUpperBounds); + + fJSON->BeginKeyObject("targetPartition"); + + WriteTargetJSONFields(fJSON); + dgsTargetStats->SetJSONAttributeDomainLowerBounds(NULL); + dgsTargetStats->SetJSONAttributeDomainUpperBounds(NULL); + + fJSON->EndObject(); + } +} + +void DTDecisionTreeSpec::WriteTargetJSONFields(JSONFile* fJSON) const +{ + const KWDGSAttributeDiscretization attributeDiscretization; + KWDGSAttributePartition* attribute; + KWDGSAttributePartition* attribute1; + IntVector ivPartIndexes; + boolean bShowCellInterest; + ObjectArray oaNonEmptyCells; + int nPart1; + int nFrequency; + ALString sTmp; + + require(dgsTargetStats != NULL); + require(dgsTargetStats->GetTargetAttributeNumber() == 1 or + dgsTargetStats->GetTargetAttributeNumber() == dgsTargetStats->GetAttributeNumber()); + require(dgsTargetStats->GetJSONAttributeDomainLowerBounds() != NULL and + dgsTargetStats->GetJSONAttributeDomainUpperBounds() != NULL); + require(dgsTargetStats->GetJSONAttributeDomainLowerBounds()->GetSize() == + dgsTargetStats->GetJSONAttributeDomainUpperBounds()->GetSize()); + require(dgsTargetStats->GetJSONAttributeDomainLowerBounds()->GetSize() == dgsTargetStats->GetAttributeNumber()); + + // On determine s'il faut afficher les interets des cellules + bShowCellInterest = + dgsTargetStats->GetTargetAttributeNumber() == 1 and dgsTargetStats->GetSourceAttributeNumber() >= 1; + + // Ecriture de la partition de la target + + attribute = cast(KWDGSAttributePartition*, dgsTargetStats->GetAttributeAt(0)); + + // Cas specifique de la discretisation + if (attribute->GetClassLabel() == attributeDiscretization.GetClassLabel()) + { + // On utilise les bornes pour ecrire les intervalles extremes avec leur vraies bornes + + cast(KWDGSAttributeDiscretization*, attribute) + ->WriteJSONFieldsWithBounds(fJSON, dgsTargetStats->GetJSONAttributeDomainLowerBounds()->GetAt(0), + dgsTargetStats->GetJSONAttributeDomainUpperBounds()->GetAt(0)); + } + // Cas general + else + attribute->WriteJSONReport(fJSON); + + // Effectifs de la target + if (dgsTargetStats->GetAttributeNumber() == 1) + { + assert(not bShowCellInterest); + attribute1 = cast(KWDGSAttributePartition*, dgsTargetStats->GetAttributeAt(0)); + fJSON->BeginKeyList("frequencies"); + for (nPart1 = 0; nPart1 < attribute1->GetPartNumber(); nPart1++) + { + nFrequency = dgsTargetStats->GetUnivariateCellFrequencyAt(nPart1); + fJSON->WriteInt(nFrequency); + } + fJSON->EndList(); + } } const ALString& DTDecisionTreeSpec::GetTreeVariableName() const @@ -414,6 +500,24 @@ double DTDecisionTreeSpec::GetLevel() const return dLevel; } +void DTDecisionTreeSpec::SetTargetMin(const double d) +{ + dTargetMin = d; +} +double DTDecisionTreeSpec::GetTargetMin() const +{ + return dTargetMin; +} + +void DTDecisionTreeSpec::SetTargetMax(const double d) +{ + dTargetMax = d; +} +double DTDecisionTreeSpec::GetTargetMax() const +{ + return dTargetMax; +} + void DTDecisionTreeSpec::SetVariablesNumber(const int i) { nVariablesNumber = i; @@ -440,6 +544,17 @@ int DTDecisionTreeSpec::GetLeavesNumber() const { return nLeavesNumber; } + +void DTDecisionTreeSpec::SetTargetType(const int i) +{ + nTargetType = i; +} + +int DTDecisionTreeSpec::GetTargetType() const +{ + return nTargetType; +} + int DTDecisionTreeSpec::GetDepth() const { return nDepth; @@ -689,12 +804,14 @@ PLShared_DecisionTreeSpec::PLShared_DecisionTreeSpec() { // shared_oaTreeNodes = new PLShared_ObjectArray(new PLShared_DecisionTreeNodeSpec); shared_nsRootNode = new PLShared_DecisionTreeNodeSpec; + shared_dgsTargetStats = new PLShared_DataGridStats; } PLShared_DecisionTreeSpec::~PLShared_DecisionTreeSpec() { // delete shared_oaTreeNodes; delete shared_nsRootNode; + delete shared_dgsTargetStats; } void PLShared_DecisionTreeSpec::DeserializeObject(PLSerializer* serializer, Object* object) const @@ -714,6 +831,14 @@ void PLShared_DecisionTreeSpec::DeserializeObject(PLSerializer* serializer, Obje tree->nLeavesNumber = serializer->GetInt(); tree->nDepth = serializer->GetInt(); tree->dConstructionCost = serializer->GetDouble(); + tree->dTargetMin = serializer->GetDouble(); + tree->dTargetMax = serializer->GetDouble(); + tree->nTargetType = serializer->GetInt(); + if (tree->nTargetType == KWType::Continuous) + { + tree->dgsTargetStats = new KWDataGridStats; + shared_dgsTargetStats->DeserializeObject(serializer, tree->dgsTargetStats); + } tree->nsRootNode = new DTDecisionTreeNodeSpec; // la deserialisation du noeud racine entraine la deserialisation de tous les noeuds qui en dependent // on procede de cette maniere afin d'eviter une recursivite infinie et un debordement de pile, lors de la @@ -743,6 +868,14 @@ void PLShared_DecisionTreeSpec::SerializeObject(PLSerializer* serializer, const serializer->PutInt(tree->nLeavesNumber); serializer->PutInt(tree->nDepth); serializer->PutDouble(tree->dConstructionCost); + serializer->PutDouble(tree->dTargetMin); + serializer->PutDouble(tree->dTargetMax); + serializer->PutInt(tree->nTargetType); + if (tree->nTargetType == KWType::Continuous) + { + assert(tree->dgsTargetStats != NULL); + shared_dgsTargetStats->SerializeObject(serializer, tree->dgsTargetStats); + } assert(tree->nsRootNode != NULL); // la serialisation du noeud racine entraine la serialisation de tous les noeuds qui en dependent // on procede de cette maniere afin d'eviter une recursivite infinie et un debordement de pile, lors de la diff --git a/src/Learning/DTForest/DTDecisionTreeSpec.h b/src/Learning/DTForest/DTDecisionTreeSpec.h index af72a928a..a4b8dbe92 100644 --- a/src/Learning/DTForest/DTDecisionTreeSpec.h +++ b/src/Learning/DTForest/DTDecisionTreeSpec.h @@ -48,6 +48,12 @@ class DTDecisionTreeSpec : public Object void SetLevel(const double); double GetLevel() const; + void SetTargetMin(const double); + double GetTargetMin() const; + + void SetTargetMax(const double); + double GetTargetMax() const; + void SetVariablesNumber(const int); int GetVariablesNumber() const; @@ -57,13 +63,21 @@ class DTDecisionTreeSpec : public Object void SetLeavesNumber(const int); int GetLeavesNumber() const; + void SetTargetType(const int); + int GetTargetType() const; + void SetDepth(const int); int GetDepth() const; + KWDataGridStats* GetTargetStats() const; + void SetTargetStats(KWDataGridStats* a); + const ObjectArray& GetTreeNodes() const; // ecriture du rapport json void WriteJSONArrayFields(JSONFile* fJSON, boolean bSummary) const; + //ecriture rapport de la discretisation de la target en cas de regression + void WriteTargetJSONFields(JSONFile* fJSON) const; protected: friend class PLShared_DecisionTreeSpec; @@ -82,16 +96,22 @@ class DTDecisionTreeSpec : public Object ALString sTreeVariableName; double dTreeLevel; double dLevel; + double dTargetMin; + double dTargetMax; int nVariablesNumber; int nInternalNodesNumber; int nLeavesNumber; int nDepth; double dConstructionCost; + int nTargetType; // adresse du noeud racine DTDecisionTreeNodeSpec* nsRootNode; // tableau de l'ensemble des DTDecisionTreeNodeSpec ObjectArray oaTreeNodes; + + // stat de la target si regression + KWDataGridStats* dgsTargetStats; }; inline const ObjectArray& DTDecisionTreeSpec::GetTreeNodes() const @@ -99,6 +119,19 @@ inline const ObjectArray& DTDecisionTreeSpec::GetTreeNodes() const return oaTreeNodes; } +inline KWDataGridStats* DTDecisionTreeSpec::GetTargetStats() const +{ + return dgsTargetStats; +} + +inline void DTDecisionTreeSpec::SetTargetStats(KWDataGridStats* a) +{ + if (dgsTargetStats != NULL) + delete dgsTargetStats; + + dgsTargetStats = a; +} + //////////////////////////////////////////////////////////////////////////////// // Classe PLShared_DecisionTreeSpec // Serialisation de la classe DTDecisionTreeSpec @@ -120,6 +153,7 @@ class PLShared_DecisionTreeSpec : public PLSharedObject // noeud racine PLShared_DecisionTreeNodeSpec* shared_nsRootNode; + PLShared_DataGridStats* shared_dgsTargetStats; // liste d'objets PLShared_DecisionTreeNodeSpec // PLShared_ObjectArray* shared_oaTreeNodes;