VI. Annexes▲
VniClusterMetadata.cs▲
Expose les caractéristiques de l'algorithme ClusterKMeans.
using
System;
using
System.
Collections.
Generic;
using
System.
Text;
using
System.
Runtime.
InteropServices;
using
Microsoft.
SqlServer.
DataMining.
PluginAlgorithms;
namespace
VNI
{
/* doit créer un numéro GUID en exécutant
* Tools->Create GUID puis utilise Copier-Coller ici
* Ne copiez que le numéro unique et ignorez les autres
* nombres
*/
[ComVisible(true)]
[Guid(
"9BC1DB7D-52B9-46aa-9469-FF7B5A2B3F88"
)]
[MiningAlgorithmClass(
typeof
(VniClusterKMeansAlgorithm))]
public
class
VniClusterMetadata :
AlgorithmMetadataBase
{
// Paramètres
protected
MiningParameterCollection parameters;
// modélisation de registre
internal
static
MiningModelingFlag
MainAttributeFlag =
MiningModelingFlag.
CustomBase +
1
;
/* initialisation de collecte de paramètres */
public
VniClusterMetadata
(
)
{
parameters =
DeclareParameters
(
);
}
static
public
MiningParameterCollection DeclareParameters
(
)
{
MiningParameterCollection parameters
=
new
MiningParameterCollection
(
);
MiningParameter param;
// Exemple de population complète d'un paramètre dans le
// constructeur
param =
new
MiningParameter
(
"CLUSTER_COUNT"
,
"Number of Clusters"
,
"3"
,
"(0.0, ...)"
,
true
,
true
,
typeof
(
System.
Int32));
parameters.
Add
(
param);
// Exemple de population d'un paramètre après construction
// Quand on utilise ce constructeur, les réglages suivants
// sont générés :
// - isRequired = false
// - isExposed = true
// - description = ""
// - defaultValue = ""
// - valueEnum = ""
// parameters.Add(param);
return
parameters;
}
public
override
string
GetDisplayName
(
)
{
return
"VNI Cluster K Means"
;
}
public
override
string
GetServiceName
(
)
{
return
"VNI_ClusterKMeans"
;
}
public
override
string
GetServiceDescription
(
)
{
// Description d'Arma
return
"computes K-means (centroid) Euclidean metric clusters for an input"
+
"data starting with initial estimates of the K cluster means."
;
}
/* la valeur d'énumération de type service retournée par cette fonction
* décrit la classe d'algorithmes qui inclut votre algorithme, s'il existe.
* Par exemple, des classes populaires d'algorithmes incluent Association * Rules, Classification, and Clustering.
* L'exemple retourne ServiceTypeOther, parce qu'il n'appartient
* réellement à aucune de ces classes.
*
*/
public
override
PlugInServiceType GetServiceType
(
)
{
return
PlugInServiceType.
ServiceTypeClustering;
}
/* La chaîne type de visionneuse retournée par cette fonction indique les
* outils dont l'objet visionneuse devrait afficher le contenu des
* modèles entraînés avec votre algorithme. Si le contenu de votre
* algorithme est similaire au contenu d'algorithmes incorporés, vous
* pouvez utiliser l'une des chaînes prédéfinies (commentées). Vous
* pouvez aussi construire votre propre visionneuse personnalisée et
* retourner son identifiant. Pour tout détail sur comment faire cela,
* cf. "Un tutoriel pour construire une visionneuse plug-in" sur
* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsql90/html/TutConPIV.asp
*/
public
override
string
GetViewerType
(
)
{
//return MiningViewerType.MicrosoftAssociationRules;
//return MiningViewerType.MicrosoftCluster;
//return MiningViewerType.MicrosoftNaiveBayesian;
//return MiningViewerType.MicrosoftNeuralNetwork;
//return MiningViewerType.MicrosoftSequenceCluster;
//return MiningViewerType.MicrosoftTimeSeries;
//return MiningViewerType.MicrosoftTree;
//return string.Empty;
return
MiningViewerType.
MicrosoftCluster;
}
/* Ceci n'est pas utilisé par le AS mais exposé dans le rowset MINING_ALGORITHMS */
public
override
MiningScaling GetScaling
(
)
{
return
MiningScaling.
Medium;
}
/* utilisé par le rowset mining_algorithm */
public
override
MiningTrainingComplexity GetTrainingComplexity
(
)
{
return
MiningTrainingComplexity.
Low;
}
public
override
MiningPredictionComplexity GetPredictionComplexity
(
)
{
return
MiningPredictionComplexity.
Low;
}
public
override
MiningExpectedQuality GetExpectedQuality
(
)
{
return
MiningExpectedQuality.
Low;
}
/* Un algorithme supporte les dimensions de data mining si le contenu des
* modèles entraînés avec cet algorithme peut être organisé comme une
* dimension de data mining.
* Cet exemple retourne false.
*/
public
override
bool
GetSupportsDMDimensions
(
)
{
return
false
;
}
/* Le support pour les opérations drill-through est décrit au chapitre 10
* de ce document.*/
public
override
bool
GetSupportsDrillThrough
(
)
{
return
false
;
}
public
override
bool
GetDrillThroughMustIncludeChildren
(
)
{
return
false
;
}
/* Retourne true si votre modèle traite le case ID comme une variable
* séparée.*/
/* Cet exemple retourne false.*/
public
override
bool
GetCaseIdModeled
(
)
{
return
false
;
}
/*
* Ceci informe le serveur des statistiques qu'il faut construire avant de
* lancer l'entraînement d'algorithme. les champs d'énumération
* MarginalRequirements peuvent décrire toutes les statistiques
* (cas les plus courants), les statistiques pour attributs d'entrée
* seulement, pour attributs de sortie seulement, ou aucune statistique du
* tout.
*/
public
override
MarginalRequirements GetMarginalRequirements
(
)
{
return
MarginalRequirements.
AllStats;
}
/*
* Cette méthode retourne les types de contenu qui sont supportés par cet
* algorithme pour attributs d'entrée.
* Tous les types courants sont supportés par le plug-in managé.
*/
public
override
MiningColumnContent[]
GetSupInputContentTypes
(
)
{
MiningColumnContent[]
arInputContentTypes =
new
MiningColumnContent[]
{
MiningColumnContent.
Discrete,
MiningColumnContent.
Continuous,
MiningColumnContent.
Discretized,
MiningColumnContent.
NestedTable,
MiningColumnContent.
Key
};
return
arInputContentTypes;
}
/* Cette méthode retourne les types de contenu qui sont supportés par cet
* algorithme pour les attributs prévisibles. Tous les types courants sont
* supportés par le plug-in managé.
*/
public
override
MiningColumnContent[]
GetSupPredictContentTypes
(
)
{
MiningColumnContent[]
arPredictContentTypes =
new
MiningColumnContent[]
{
MiningColumnContent.
Discrete,
MiningColumnContent.
Continuous,
MiningColumnContent.
Discretized,
MiningColumnContent.
NestedTable,
MiningColumnContent.
Key
};
return
arPredictContentTypes;
}
/* Cette méthode retourne la liste des fonctions standards Data Mining
* Extensions (DMX) supportées par cet algorithme. La plupart des
* fonctions standards peuvent être supportées sans aucun effort du
* développeur, une fois que la fonction AlgorithmBase.Predict est
* implémentée correctement.
*/
public
override
SupportedFunction[]
GetSupportedStandardFunctions
(
)
{
SupportedFunction[]
arFuncs =
new
SupportedFunction[]
{
// Fonctions générales de prédiction
SupportedFunction.
PredictSupport,
SupportedFunction.
PredictHistogram,
SupportedFunction.
PredictProbability,
SupportedFunction.
PredictAdjustedProbability,
SupportedFunction.
PredictAssociation,
SupportedFunction.
PredictStdDev,
SupportedFunction.
PredictVariance,
SupportedFunction.
RangeMax,
SupportedFunction.
RangeMid,
SupportedFunction.
RangeMin,
SupportedFunction.
DAdjustedProbability,
SupportedFunction.
DProbability,
SupportedFunction.
DStdDev,
SupportedFunction.
DSupport,
SupportedFunction.
DVariance,
// fonctions liées au contenu
SupportedFunction.
IsDescendent,
SupportedFunction.
PredictNodeId,
SupportedFunction.
IsInNode,
SupportedFunction.
DNodeId,
// fonctions spécifiques du cluster
SupportedFunction.
Cluster,
SupportedFunction.
ClusterDistance,
SupportedFunction.
ClusterPredictHistogram,
SupportedFunction.
ClusterProbability,
SupportedFunction.
PredictCaseLikelihood,
SupportedFunction.
DCluster,
};
return
arFuncs;
}
/* Cette méthode effectue une validation du jeu d'attributs avant de lancer
* l'entraînement.
* Par exemple, cette méthode peut garantir qu'au moins un attribut est
* prévisible, dans un algorithme de classification.
*/
public
override
void
ValidateAttributeSet
(
AttributeSet attributeSet)
{
uint
nCount =
attributeSet.
GetAttributeCount
(
);
int
mainAttrs =
0
;
int
inputAttrs =
0
;
for
(
uint
nIndex =
0
;
nIndex <
nCount;
nIndex++
)
{
bool
thisAttIsInput =
false
;
if
((
attributeSet.
GetAttributeFlags
(
nIndex) &
AttributeFlags.
Input) !=
0
)
{
inputAttrs++;
thisAttIsInput =
true
;
}
MiningModelingFlag[]
modelingFlags =
attributeSet.
GetModelingFlags
(
nIndex);
for
(
int
flagIndex =
0
;
flagIndex <
modelingFlags.
Length;
flagIndex++
)
{
if
(
modelingFlags[
flagIndex]
==
MainAttributeFlag)
{
if
(!
thisAttIsInput)
{
string
strMessage =
string
.
Format
(
"{0} ne peut être appliqué qu'à un attribut d'entrée"
,
GetModelingFlagName
(
MainAttributeFlag));
throw
new
System.
Exception
(
strMessage);
}
mainAttrs++;
}
}
}
}
public
override
AlgorithmBase CreateAlgorithm
(
ModelServices model)
{
return
new
VniClusterKMeansAlgorithm
(
);
}
public
override
MiningParameterCollection GetParametersCollection
(
)
{
if
(
parameters ==
null
)
{
DeclareParameters
(
);
}
return
parameters;
}
public
override
object
ParseParameterValue
(
int
parameterIndex,
string
parameterValue)
{
// Cette fonction devrait retourner un objet contenant la valeur du
// paramètre
// NOTE!! Le type de l'objet doit correspondre exactement au type déclaré
// du paramètre paramIndex
object
retVal =
null
;
if
(
parameterIndex ==
0
)
{
// Ceci est une valeur pour PARAM1, qui est Int32,
// voyez l'implémentation de DeclareParameters
int
dVal =
System.
Convert.
ToInt32
(
parameterValue);
retVal =
dVal;
}
/* else if (parameterIndex == 1)
{
// Ceci est une valeur pour PARAM2, qui est String,
// voyez l'implémentation de DeclareParameters
string strVal = parameterValue;
retVal = strVal;
}*/
else
{
throw
new
System.
ArgumentOutOfRangeException
(
"paramIndex"
);
}
return
retVal;
}
/* Principal registre d'attribut ou tout registre personnalisé*/
public
override
MiningModelingFlag[]
GetSupModelingFlags
(
)
{
MiningModelingFlag[]
arModelingFlags =
new
MiningModelingFlag[
1
];
arModelingFlags[
0
]
=
MainAttributeFlag;
// new MiningModelingFlag[] {
// MainAttributeFlag
// };
return
arModelingFlags;
}
/* nom du principal registre d'attribut ou tout registre personnalisé*/
public
override
string
GetModelingFlagName
(
MiningModelingFlag flag)
{
if
(
flag ==
MainAttributeFlag)
{
return
"VNI_MAIN"
;
}
else
{
throw
new
System.
Exception
(
"Unknown VNI modeling flag : "
+
flag.
ToString
(
));
}
}
}
}
VniClusterKmeansAlgorithm.cs▲
Cette classe implémente des tâches spécifiques de l'algorithme.
using
System;
using
System.
Collections.
Generic;
using
System.
Text;
using
Microsoft.
SqlServer.
DataMining.
PluginAlgorithms;
using
VNI;
using
System.
Diagnostics;
using
Imsl.
Stat;
using
Imsl.
Math;
using
System.
Collections;
/* L'algorithme shell plug-in fonctionne de la façon suivante :
* - Pendant l'entraînement, il traverse tous les cas une fois et envoie des
* notifications de traitement.
* - Le contenu ne se compose que du nombre de cas et de la durée du
* traitement.
* Ces informations ne constituent pas des modèles utiles, mais c'est un
* exemple assez simple de comment utiliser les objets de persistance.
* - Le contenu a un noeud unique, étiqueté "All", qui a les statistiques
d'entraînement fixées comme distribution nodale.
* - La prédiction ignore les entrées et se base seulement sur les statistiques
* d'entraînement fixées.
*/
namespace
VNI
{
///
<
summary
>
/// Persistence
///
<
/summary
>
enum
VNIClusterPersistenceMarker
{
MainAttribute,
Parameters,
ClusterCount,
ClusterDescription,
ClusterDistribution
}
///
<
summary
>
/// énumération contenant des délimitateurs dans le contenu
/// persisté
///
<
/summary
>
enum
MyPersistenceTag
{
ShellAlgorithmContent,
NumberOfCases
};
public
class
MyCaseProcessor :
ICaseProcessor
{
protected
VniClusterKMeansAlgorithm algo;
public
MyCaseProcessor
(
VniClusterKMeansAlgorithm algo)
{
this
.
algo =
algo;
}
public
void
ProcessCase
(
long
caseID,
MiningCase inputCase)
{
// Cherche une annulation toutes les 100 rangées
// Lance aussi une notification de traitement toutes les 100 rangées pour éviter de surcharger le tracerowset
if
(
caseID %
100
==
0
)
{
algo.
Context.
CheckCancelled
(
);
algo.
trainingProgress.
Progress
(
);
}
algo.
trainingProgress.
Current++;
// C'est la condition de clustering triviale, voyez le sommet du // fichier pour les détails
//int destinationCluster = algo.InternalClusterMembership(inputCase);
// Obtention du l'appartenance au cluster
switch
(
algo.
ProcessingPhase)
{
case
VniClusterKMeansAlgorithm.
MainProcessingPhase:
algo.
vniStore.
addCase
(
inputCase);
//algo.Clusters[destinationCluster].PushCase(inputCase);
break
;
//case VniClusterKMeansAlgorithm.UpdateSupportPhase:
//algo.Clusters[destinationCluster].UpdateStats(inputCase);
//algo.vniStore.fillClusters(algo.Clusters);
// break;
}
}
}
public
class
VniClusterKMeansAlgorithm :
AlgorithmBase
{
// Paramètres de mining. Saisit les paramètres d'entraînement avec
// leurs valeurs.
protected
MiningParameterCollection algorithmParams;
// notifications de trace pendant le traitement
public
TaskProgressNotification trainingProgress;
// "principal" attribut (utilisé en partition)
protected
System.
UInt32 MainAttribute;
protected
double
MainMean;
// moyenne de l'attribut principal, s'il est continu
protected
bool
MainContinuous;
// vrai si l'attribut principal est continu
// Représentation interne de cluster
public
InternalCluster[]
Clusters;
public
int
ProcessingPhase =
0
;
public
const
int
MainProcessingPhase =
1
;
public
const
int
UpdateSupportPhase =
2
;
public
const
int
FinalPhase =
3
;
public
VNIStore vniStore;
// maintenant, fixez-le à 3 mais passez un paramètre pour déterminer le nombre de clusters
public
int
num_clusters =
0
;
public
VniClusterKMeansAlgorithm
(
)
{
algorithmParams =
VNI.
VniClusterMetadata.
DeclareParameters
(
);
MainAttribute =
0
;
MainContinuous =
false
;
MainMean =
0
.
0
;
vniStore =
new
VNIStore
(
this
);
}
// Annulation facultative : on n'est PAS OBLIGE d'annuler ceci.
// L'implémentation base.Initialize ne fait rien, aussi n'est-il pas
// nécessaire de l'invoquer
protected
override
void
Initialize
(
)
{
// Initialise les paramètres avec les valeurs par défaut
this
.
algorithmParams[
"CLUSTER_COUNT"
].
Value =
3
;
}
/*
a. La valeur spécifiée par l'utilisateur en déploiement.
b. la valeur par défaut (si aucune n'a été spécifiée par l'utilisateur pendant l'entraînement).
c. La meilleure valeur détectée automatiquement (heuristiquement) par l'algorithme pour le jeu d'entraînement actuel.
*/
protected
override
object
GetTrainingParameterActualValue
(
int
paramOrdinal)
{
return
algorithmParams[
paramOrdinal].
Value;
}
public
void
ProcessCase
(
long
caseId,
MiningCase currentCase)
{
// Assurze-vous que le traitement n'a pas été annulé
this
.
Context.
CheckCancelled
(
);
// Augmente la valeur actuelle de la notification de trace
trainingProgress.
Current++;
if
(
caseId %
100
==
0
)
{
// lance la trace tous les 100 cas, pour éviter tout
// impact sur les performances
trainingProgress.
Progress
(
);
}
// utilise MiningCase ici pour l'entraînement
}
/* Load/Save content est utilisé pour la persistance des modèles détectés */
protected
override
void
LoadContent
(
PersistenceReader reader)
{
// Charge l'attribut principal
reader.
OpenScope
((
PersistItemTag)VNIClusterPersistenceMarker.
MainAttribute);
reader.
GetValue
(
out
this
.
MainAttribute);
reader.
GetValue
(
out
this
.
MainContinuous);
reader.
GetValue
(
out
this
.
MainMean);
reader.
CloseScope
(
);
// Charge les paramètres
reader.
OpenScope
((
PersistItemTag)VNIClusterPersistenceMarker.
Parameters);
foreach
(
MiningParameter param in
this
.
algorithmParams)
{
string
name;
reader.
GetValue
(
out
name);
if
(
name !=
param.
Name)
{
throw
new
System.
Exception
(
"Corrupted file -- unrecognized parameter name : "
+
name);
}
if
(
param.
Name ==
"CLUSTER_COUNT"
)
{
int
dVal =
0
;
reader.
GetValue
(
out
dVal);
param.
Value =
dVal;
}
/*if (param.Name == "PARAM2")
{
string sVal;
reader.GetValue(out sVal);
param.Value = sVal;
}*/
}
reader.
CloseScope
(
);
// Charge les clusters
reader.
OpenScope
((
PersistItemTag)VNIClusterPersistenceMarker.
ClusterCount);
int
clusterCount =
0
;
reader.
GetValue
(
out
clusterCount);
reader.
CloseScope
(
);
Clusters =
new
InternalCluster[
clusterCount];
for
(
int
nIndex =
0
;
nIndex <
clusterCount;
nIndex++
)
{
Clusters[
nIndex]
=
new
InternalCluster
(
this
);
Clusters[
nIndex].
ClusterID =
(
ulong
)nIndex;
Clusters[
nIndex].
Description =
BuildClusterDescription
(
nIndex);
Clusters[
nIndex].
Load
(
ref
reader);
}
}
protected
override
void
SaveContent
(
PersistenceWriter writer)
{
// Sauvegarde l'attribut principal
writer.
OpenScope
((
PersistItemTag)VNIClusterPersistenceMarker.
MainAttribute);
writer.
SetValue
(
this
.
MainAttribute);
writer.
SetValue
(
this
.
MainContinuous);
writer.
SetValue
(
this
.
MainMean);
writer.
CloseScope
(
);
// Sauvegarde les valeurs des paramètres connus
writer.
OpenScope
((
PersistItemTag)VNIClusterPersistenceMarker.
Parameters);
foreach
(
MiningParameter param in
this
.
algorithmParams)
{
writer.
SetValue
(
param.
Name);
if
(
param.
Name ==
"CLUSTER_COUNT"
)
{
int
nVal =
System.
Convert.
ToInt32
(
param.
Value);
writer.
SetValue
(
nVal);
}
}
writer.
CloseScope
(
);
// Sauvegarde les clusters
writer.
OpenScope
((
PersistItemTag)VNIClusterPersistenceMarker.
ClusterCount);
writer.
SetValue
(
Clusters.
Length);
writer.
CloseScope
(
);
for
(
int
iIndex =
0
;
iIndex <
Clusters.
Length;
iIndex++
)
{
Clusters[
iIndex].
Save
(
ref
writer);
}
}
protected
override
AlgorithmNavigationBase GetNavigator
(
bool
forDMDimensionContent)
{
return
new
AlgorithmNavigator
(
this
,
forDMDimensionContent);
}
private
void
PrepareForProcessing
(
int
numClusters)
{
/*////////////////////////////////////////////////////////
* Détecte l'attribut principal
* Cherche l'attribut d'entrée qui a le registre MainAttributeFlag
*/
UInt32 nAtt =
0
;
MainAttribute =
AttributeSet.
Unspecified;
for
(
nAtt =
0
;
nAtt <
this
.
AttributeSet.
GetAttributeCount
(
);
nAtt++
)
{
MiningModelingFlag[]
flags =
this
.
AttributeSet.
GetModelingFlags
(
nAtt);
for
(
int
flagIndex =
0
;
flagIndex <
flags.
Length;
flagIndex++
)
{
if
(
flags[
flagIndex]
==
VniClusterMetadata.
MainAttributeFlag)
{
MainAttribute =
nAtt;
Debug.
Assert
((
AttributeSet.
GetAttributeFlags
(
nAtt) &
AttributeFlags.
Input) !=
0
);
break
;
}
}
}
if
(
MainAttribute ==
AttributeSet.
Unspecified)
{
for
(
nAtt =
0
;
nAtt <
this
.
AttributeSet.
GetAttributeCount
(
);
nAtt++
)
{
if
((
AttributeSet.
GetAttributeFlags
(
nAtt) &
AttributeFlags.
Input) !=
0
)
{
MainAttribute =
nAtt;
}
}
}
Debug.
Assert
(
MainAttribute !=
AttributeSet.
Unspecified);
MainContinuous =
(
AttributeSet.
GetAttributeFlags
(
MainAttribute) &
AttributeFlags.
Continuous) !=
0
;
if
(
MainContinuous)
{
// Extraît la moyenne
AttributeStatistics stats =
this
.
MarginalStats.
GetAttributeStats
(
MainAttribute);
// gardez à l'esprit que, pour les attributs continus, le premier état manque et le deuxième état contient la moyenne de l'attribut
Debug.
Assert
(
stats.
StateStatistics.
Count ==
2
);
Debug.
Assert
(
stats.
StateStatistics[
1
].
Value.
IsDouble);
MainMean =
stats.
StateStatistics[
1
].
Value.
Double;
}
// Utilisez trainingParams et les statistiques marginales ici pour en déduire le meilleur nombre de clusters
// Cet exemple le fixe This sample à 2
Clusters =
new
InternalCluster[
numClusters];
for
(
int
nIndex =
0
;
nIndex <
numClusters;
nIndex++
)
{
// crée les clusters
Clusters[
nIndex]
=
new
InternalCluster
(
this
);
// fixe les propriétés id du noeud interne
Clusters[
nIndex].
ClusterID =
(
ulong
)nIndex;
// Généralement, le cluster devrait construire sa propre description
// Dans ce cas, l'algorithme connaît l'attribut principal, par
// conséquent il construira la description
Clusters[
nIndex].
Description =
BuildClusterDescription
(
nIndex);
}
}
// Généralement, le cluster devrait construire sa propre description
// Dans ce cas, l'algorithme connaît l'attribut principal, par
// conséquent il construira la description
private
string
BuildClusterDescription
(
int
nIndex)
{
string
strRet =
string
.
Empty;
//retourne "VNI Cluster " + nIndex.ToString();
string
attName =
AttributeSet.
GetAttributeDisplayName
(
MainAttribute,
false
);
if
(
MainContinuous)
{
StateValue sVal =
new
StateValue
(
);
sVal.
SetDouble
(
MainMean);
object
val =
AttributeSet.
UntokenizeAttributeValue
(
MainAttribute,
sVal);
if
(
nIndex ==
0
)
{
strRet =
string
.
Format
(
"{0} < {1}"
,
attName,
"99999"
);
}
else
{
strRet =
string
.
Format
(
"{0} >= {1} OR {0} = Manquant"
,
attName,
val.
ToString
(
));
}
}
else
{
StateValue sVal =
new
StateValue
(
);
sVal.
SetIndex
(
1
);
object
val =
AttributeSet.
UntokenizeAttributeValue
(
MainAttribute,
sVal);
if
(
nIndex ==
0
)
{
strRet =
string
.
Format
(
"{0} = {1}"
,
attName,
val.
ToString
(
));
}
else
{
strRet =
string
.
Format
(
"{0} NOT = {1}"
,
attName,
val.
ToString
(
));
}
}
return
strRet;
}
public
int
InternalClusterMembership
(
MiningCase mcase)
{
/* Vérifie les erreurs pour faire que mCase ait les mêmes attributs que les attributs de cluster entraîné
*/
int
member =
-
1
;
double
[]
varr =
new
double
[
this
.
AttributeSet.
GetAttributeCount
(
)];
bool
mcontinue =
mcase.
MoveFirst
(
);
while
(
mcontinue)
{
UInt32 attribute =
mcase.
Attribute;
StateValue value
=
mcase.
Value;
if
(
value
.
IsDouble) /*continu */
{
varr[
attribute]
=
value
.
Double;
//attrList.Add(value.Double);
}
/* pour toute colonne discrète, il y aura un index
* représentant un état. Par exemple, une colonne avec
* des valeurs A,B,C aura 3 indices
* A=1, B=2, c=3
*/
if
(
value
.
IsIndex) /*discrète */
{
varr[
attribute]
=
value
.
Double;
//attrList.Add((double)value.Index);
}
if
(
value
.
IsMissing) /* missing values */
{
//attrList.Add(null);
}
mcontinue =
mcase.
MoveNext
(
);
}
// double[] vals = (double[])attrList.ToArray(typeof(double));
// utilise la distance euclidienne pour calculer le cluster
// On présume que le cas d'entrée a autant d'attributs que le
// modèle entraîné.
double
[,]
centers =
this
.
vniStore.
getCenters
(
);
double
[]
distance =
new
double
[
Clusters.
Length];
double
esum =
0
.
0
;
for
(
int
i =
0
;
i <
distance.
Length;
i++
)
{
esum =
0
.
0
;
for
(
int
j =
0
;
j <
varr.
Length;
j++
)
{
esum +=
(
varr[
j]
-
centers[
i,
j]
) *
(
varr[
j]
-
centers[
i,
j]
);
}
distance[
i]
=
Math.
Sqrt
(
esum);
}
double
[]
distcopy =
new
double
[
distance.
Length];
Array.
Copy
(
distance,
distcopy,
distance.
Length);
Array.
Sort
(
distcopy);
for
(
int
m =
0
;
m <
distance.
Length;
m++
)
{
if
(
distcopy[
0
]
==
distance[
m]
)
{
member =
m;
break
;
}
}
return
member;
}
///
<
summary
>
/// Méthode de pseudo-clustering
/// Retourne 0 pour le premier cluster, 1 pour le deuxième
///
<
/summary
>
/*public int InternalClusterMembership(MiningCase inputCase)
{
int nRet = 1;
bool bContinue = inputCase.MoveFirst();
while (bContinue)
{
if (inputCase.Attribute == MainAttribute)
{
if (MainContinuous)
{
// Safety check
Debug.Assert(inputCase.Value.IsDouble || inputCase.Value.IsMissing);
if (inputCase.Value.IsDouble && (inputCase.Value.Double < MainMean))
{
// Appartient au premier cluster
nRet = 0;
}
}
else
{
// Contrôle de sécurité
Debug.Assert(inputCase.Value.IsIndex || inputCase.Value.IsMissing);
if (inputCase.Value.IsIndex && (inputCase.Value.Index == 1))
{
// Appartient au premier cluster
nRet = 0;
}
}
break;
}
else
{
bContinue = inputCase.MoveNext();
}
}
return nRet;
}*/
/* Début du traitement de Cas. L'objet PushCaseSet nous permet
* d'interagir avec CaseProcessor
*/
protected
override
void
InsertCases
(
PushCaseSet caseSet,
MiningParameterCollection trainingParams)
{
// Initialise le jeu de cluster interne et les paramètres
LoadTrainingParameters
(
trainingParams);
/* obtient le nombre de clusters spécifié par l'utilisateur */
num_clusters =
(
int
)GetTrainingParameterActualValue
(
0
);
if
(
num_clusters ==
0
)
{
throw
new
System.
ArgumentOutOfRangeException
(
"num_clusters"
);
}
// préparation du traitement (2 clusters)
PrepareForProcessing
(
num_clusters);
// passe à la phase 1
ProcessingPhase =
MainProcessingPhase;
// Boucle d'entraînement principale
while
(
ProcessingPhase !=
FinalPhase)
{
// Crée un objet de notification de progression de tâche pour envoyer des évènements de trace
trainingProgress =
this
.
Model.
CreateTaskNotification
(
);
trainingProgress.
Total =
(
int
)this
.
MarginalStats.
GetTotalCasesCount
(
);
trainingProgress.
Current =
0
;
switch
(
ProcessingPhase)
{
case
MainProcessingPhase:
trainingProgress.
Format =
"MainProcessingPhase: processing {0} out of {1}"
;
bool
bSuccess =
true
;
try
{
trainingProgress.
Start
(
);
MyCaseProcessor processor =
new
MyCaseProcessor
(
this
);
caseSet.
StartCases
(
processor);
}
catch
{
bSuccess =
false
;
throw
;
}
finally
{
trainingProgress.
End
(
bSuccess);
}
break
;
case
UpdateSupportPhase:
trainingProgress.
Format =
"Updating support: processing {0} out of {1}"
;
this
.
vniStore.
fillClusters
(
this
.
Clusters);
break
;
}
// passe à la phase de traitement suivante
ProcessingPhase++;
}
// Traitement terminé, appelle PostProcess sur chaque cluster
for
(
int
nIndex =
0
;
nIndex <
Clusters.
Length;
nIndex++
)
{
Clusters[
nIndex].
UpdateStats
(
);
}
}
private
void
LoadTrainingParameters
(
MiningParameterCollection trainingParams)
{
// Copie les valeurs des paramètres dans cette collection de paramètres
foreach
(
MiningParameter param in
trainingParams)
{
if
(
this
.
algorithmParams[
param.
Name]
!=
null
)
{
this
.
algorithmParams[
param.
Name].
Value =
param.
Value;
}
}
}
protected
override
void
Predict
(
MiningCase inputCase,
PredictionResult predictionResult)
{
// Prédiction signifie
// - déterminer le bon cluster
// - effectuer la prédiction de cluster
int
nCaseCluster =
InternalClusterMembership
(
inputCase);
Clusters[
nCaseCluster].
Predict
(
ref
predictionResult);
}
protected
override
ClusterMembershipInfo[]
ClusterMembership
(
long
caseID,
MiningCase inputCase,
string
targetCluster)
{
// Lance une notification de progression
Model.
EmitSingleTraceNotification
(
"ClusterMembership ... "
);
int
clIndex =
InternalClusterMembership
(
inputCase);
string
caption =
Clusters[
clIndex].
Caption;
ClusterMembershipInfo[]
ret =
null
;
if
(
targetCluster.
Length >
0
)
{
int
cltargetCluster =
-
1
;
for
(
int
nIndex =
0
;
nIndex <
Clusters.
Length;
nIndex++
)
{
if
(
Clusters[
nIndex].
Caption.
CompareTo
(
targetCluster) ==
0
)
{
cltargetCluster =
nIndex;
break
;
}
}
if
(
cltargetCluster ==
-
1
)
return
null
;
ret =
new
ClusterMembershipInfo[
1
];
ret[
0
]
=
new
ClusterMembershipInfo
(
);
ret[
0
].
Caption =
Clusters[
cltargetCluster].
Caption;
ret[
0
].
ClusterId =
Clusters[
cltargetCluster].
ClusterID;
ret[
0
].
Distance =
(
cltargetCluster ==
clIndex) ?
0
.
0
:
1
.
0
;
ret[
0
].
Membership =
1
.
0
-
ret[
0
].
Distance;
ret[
0
].
NodeUniqueName =
Clusters[
cltargetCluster].
NodeUniqueName;
return
ret;
}
ret =
new
ClusterMembershipInfo[
Clusters.
Length];
for
(
int
nIndex =
0
;
nIndex <
Clusters.
Length;
nIndex++
)
{
ret[
nIndex]
=
new
ClusterMembershipInfo
(
);
ret[
nIndex].
Caption =
Clusters[
nIndex].
Caption;
ret[
nIndex].
ClusterId =
Clusters[
nIndex].
ClusterID;
ret[
nIndex].
Distance =
(
nIndex ==
clIndex) ?
0
.
0
:
1
.
0
;
ret[
nIndex].
Membership =
1
.
0
-
ret[
nIndex].
Distance;
ret[
nIndex].
NodeUniqueName =
Clusters[
nIndex].
NodeUniqueName;
}
return
ret;
}
protected
override
double
CaseLikelihood
(
long
caseID,
MiningCase inputCase,
bool
normalized)
{
// cet exemple ne calcule la distance de cluster,
// aussi tous les cases sont-ils équiprobables
return
1
.
0
;
}
}
}
AlgorithmNavigator.cs▲
Expose les modèles détectés par l'algorithme ClusterKMeans.
using
System;
using
System.
Collections.
Generic;
using
System.
Text;
using
Microsoft.
SqlServer.
DataMining.
PluginAlgorithms;
using
VNI;
namespace
VNI
{
class
AlgorithmNavigator :
AlgorithmNavigationBase
{
VniClusterKMeansAlgorithm algorithm;
bool
forDMDimension;
int
currentNode;
public
AlgorithmNavigator
(
VniClusterKMeansAlgorithm currentAlgorithm,
bool
dmDimension)
{
algorithm =
currentAlgorithm;
forDMDimension =
dmDimension;
currentNode =
0
;
}
protected
override
bool
MoveToNextTree
(
)
{
// Arbre unique pour cet algorithme
return
false
;
}
protected
override
int
GetCurrentNodeId
(
)
{
return
currentNode;
}
protected
override
bool
ValidateNodeId
(
int
nodeId)
{
return
(
nodeId >=
0
&&
nodeId <=
algorithm.
Clusters.
Length);
}
protected
override
bool
LocateNode
(
int
nodeId)
{
// Le seul noeud valide est 0
if
(!
ValidateNodeId
(
nodeId))
return
false
;
currentNode =
nodeId;
return
true
;
}
protected
override
int
GetNodeIdFromUniqueName
(
string
nodeUniqueName)
{
int
nNode =
System.
Convert.
ToInt32
(
nodeUniqueName);
return
nNode;
}
protected
override
string
GetUniqueNameFromNodeId
(
int
nodeId)
{
return
nodeId.
ToString
(
"D3"
);
}
protected
override
uint
GetParentCount
(
)
{
switch
(
currentNode)
{
case
0
:
return
0
;
default
:
return
1
;
}
}
protected
override
void
MoveToParent
(
uint
parentIndex)
{
currentNode =
0
;
}
protected
override
int
GetParentNodeId
(
uint
parentIndex)
{
return
0
;
}
protected
override
uint
GetChildrenCount
(
)
{
switch
(
currentNode)
{
case
0
:
return
(
uint
)algorithm.
Clusters.
Length;
default
:
return
0
;
}
}
protected
override
void
MoveToChild
(
uint
childIndex)
{
if
(
currentNode ==
0
)
{
currentNode =
(
int
)(
childIndex +
1
);
}
}
protected
override
int
GetChildNodeId
(
uint
childIndex)
{
if
(
currentNode ==
0
)
{
return
(
int
)(
childIndex +
1
);
}
return
-
1
;
}
protected
override
NodeType GetNodeType
(
)
{
// Root est un modèle, tout le reste est un cluster
if
(
currentNode ==
0
)
return
NodeType.
Model;
else
return
NodeType.
Cluster;
}
protected
override
string
GetNodeUniqueName
(
)
{
return
GetUniqueNameFromNodeId
(
currentNode);
}
protected
override
uint
[]
GetNodeAttributes
(
)
{
// Il n'y a pas d'association entre un noeud et un attribut
return
null
;
// new uint[] { 1, 2 };
}
protected
override
double
GetDoubleNodeProperty
(
NodeProperty property)
{
double
dRet =
0
;
double
dTotalSupport =
algorithm.
MarginalStats.
GetTotalCasesCount
(
);
double
dNodeSupport =
0
.
0
;
switch
(
currentNode)
{
case
0
:
dNodeSupport =
dTotalSupport;
break
;
default
:
dNodeSupport =
algorithm.
Clusters[
currentNode -
1
].
Support;
break
;
}
switch
(
property)
{
case
NodeProperty.
Support:
dRet =
dNodeSupport;
break
;
case
NodeProperty.
Score:
dRet =
0
;
break
;
case
NodeProperty.
Probability:
dRet =
dNodeSupport /
dTotalSupport;
break
;
case
NodeProperty.
MarginalProbability:
dRet =
dNodeSupport /
dTotalSupport;
break
;
}
return
dRet;
}
protected
override
string
GetStringNodeProperty
(
NodeProperty property)
{
string
strRet =
""
;
switch
(
property)
{
case
NodeProperty.
Caption:
{
// IMPORTANT : L'en-tête d'un noeud peut être modifié par
// admin avec une déclaration telle que
// UPDATE Model.CONTENT SET NODE_CAPTION = 'Some cluster label'
// WHERE NODE_UNIQUE_NAME = '000001'
// Le listing des changements est immédiatement sauvegardé
// dans le modèle. Voici comment y accéder via model
// services
strRet =
algorithm.
Model.
FindNodeCaption
(
GetNodeUniqueName
(
));
if
(
strRet.
Length ==
0
)
{
// si c'est vide, ça ne figure pas dans le listing
// génère la description
switch
(
currentNode)
{
case
0
:
strRet =
"All"
;
break
;
default
:
strRet =
algorithm.
Clusters[
currentNode -
1
].
Caption;
break
;
}
}
}
break
;
case
NodeProperty.
ConditionXml:
// La condition pour qu'un cas corresponde à un noeud doit être
// représentée ici
strRet =
""
;
break
;
case
NodeProperty.
Description:
switch
(
currentNode)
{
case
0
:
strRet =
"All"
;
break
;
default
:
strRet =
algorithm.
Clusters[
currentNode -
1
].
Description;
break
;
}
break
;
case
NodeProperty.
ModelColumnName:
strRet =
""
;
break
;
case
NodeProperty.
RuleXml:
switch
(
currentNode)
{
case
0
:
strRet =
"<Rule>All</Rule>"
;
break
;
default
:
strRet =
"<Cluster>"
+
algorithm.
Clusters[
currentNode -
1
].
Caption +
"</Cluster>"
;
break
;
}
break
;
case
NodeProperty.
ShortCaption:
switch
(
currentNode)
{
case
0
:
strRet =
"All"
;
break
;
default
:
strRet =
algorithm.
Clusters[
currentNode -
1
].
Caption;
break
;
}
break
;
}
return
strRet;
}
protected
override
AttributeStatistics[]
GetNodeDistribution
(
)
{
switch
(
currentNode)
{
case
0
:
{
// Pour le noeud racine, retourne les statistiques
// marginales de tout le modèle d'extraction
int
attStats =
(
int
)algorithm.
AttributeSet.
GetAttributeCount
(
);
AttributeStatistics[]
marginalStats =
new
AttributeStatistics[
attStats +
2
];
for
(
uint
nIndex =
0
;
nIndex <
attStats;
nIndex++
)
{
marginalStats[
nIndex]
=
algorithm.
MarginalStats.
GetAttributeStats
(
nIndex);
}
// Ajoute des informations supplémentaires dans
// NODE_DISTRIBUTION, pas de chaîne
AttributeStatistics extraInfo =
new
AttributeStatistics
(
);
extraInfo.
Attribute =
AttributeSet.
Unspecified;
StateStatistics state =
new
StateStatistics
(
);
state.
ValueType =
MiningValueType.
Intercept;
state.
Value.
SetDouble
(
2
.
0
);
extraInfo.
StateStatistics.
Add
(
state);
marginalStats[
attStats]
=
extraInfo;
// Ajoute des informations supplémentaires dans
// NODE_DISTRIBUTION : valeur et nom d'attribut
extraInfo =
new
AttributeStatistics
(
);
extraInfo.
Attribute =
AttributeSet.
Unspecified;
extraInfo.
NodeId =
"Any string here"
;
state =
new
StateStatistics
(
);
state.
ValueType =
MiningValueType.
Other;
state.
Value.
SetIndex
(
124
);
extraInfo.
StateStatistics.
Add
(
state);
marginalStats[
attStats +
1
]
=
extraInfo;
return
marginalStats;
}
default
:
// pour les noeuds clusters, retourne la distribution du cluster
return
algorithm.
Clusters[
currentNode -
1
].
Distribution;
}
}
}
}
Cluster.cs▲
Un objet utilisé pour représenter le modèle détecté (cluster).
using
System;
using
System.
Collections.
Generic;
using
System.
Text;
using
System.
Diagnostics;
using
Microsoft.
SqlServer.
DataMining.
PluginAlgorithms;
using
System.
Collections;
namespace
VNI
{
// Représentation interne d'un cluster
// Une instance de cette classe représentera un cluster détecté par
// l'algorithme plug-in.
public
class
InternalCluster
{
private
string
nodeUniqueName;
private
string
description;
/* Chaque cluster maintiendra la distribution des attributs pour
* tous les cas d'entraînement qui finissent dans ce cluster.
*/
private
AttributeStatistics[]
clusterDistribution;
public
VNIPatternAttribute[]
vniatts;
/* référence vers l'objet Algorithm qui a détecté ce cluster */
private
VniClusterKMeansAlgorithm algo;
// ID interne du cluster
private
int
clusterID;
private
int
casesCount;
ArrayList clusterValues;
public
InternalCluster
(
VniClusterKMeansAlgorithm parent)
{
algo =
parent;
// Alloue de l'espace pour toutes les statistiques
// ainsi que pour le cluster Prediction
clusterDistribution =
new
AttributeStatistics[
algo.
AttributeSet.
GetAttributeCount
(
)];
/* pour chaque modèle, nous trouvons dans les données qu'il y
* aura des attributs qui appartiennent à ces modèles.
* VNIPatternattribute garde la trace de chaque attribut dans le
* modèle et ses valeurs et statistiques
*/
vniatts =
new
VNIPatternAttribute[
algo.
AttributeSet.
GetAttributeCount
(
)];
for
(
uint
nIndex =
0
;
nIndex <
algo.
AttributeSet.
GetAttributeCount
(
);
nIndex++
)
{
//////////////////////////////////////
// Distribution pour ce cluster
clusterDistribution[
nIndex]
=
new
AttributeStatistics
(
);
vniatts[
nIndex]
=
new
VNIPatternAttribute
(
);
// détermine le nombre d'états
uint
statCount =
algo.
AttributeSet.
GetAttributeStateCount
(
nIndex);
// détermine si l'attribut est continu
bool
bContinuous =
(
algo.
AttributeSet.
GetAttributeFlags
(
nIndex) &
AttributeFlags.
Continuous) !=
0
;
clusterDistribution[
nIndex].
Attribute =
nIndex;
clusterDistribution[
nIndex].
Support =
0
;
clusterDistribution[
nIndex].
Min =
0
.
0
;
clusterDistribution[
nIndex].
Max =
0
.
0
;
clusterDistribution[
nIndex].
NodeId =
string
.
Empty;
clusterDistribution[
nIndex].
Probability =
0
.
0
;
for
(
int
nStatIndex =
0
;
nStatIndex <
statCount;
nStatIndex++
)
{
StateStatistics stateStat =
new
StateStatistics
(
);
if
(
nStatIndex ==
0
)
stateStat.
Value.
SetMissing
(
);
else
{
if
(
bContinuous)
{
Debug.
Assert
(
nStatIndex ==
1
);
stateStat.
Value.
SetDouble
(
0
.
0
);
}
else
stateStat.
Value.
SetIndex
((
uint
)nStatIndex);
}
stateStat.
Probability =
0
.
0
;
stateStat.
AdjustedProbability =
0
.
0
;
stateStat.
ProbabilityVariance =
0
.
0
;
stateStat.
Support =
0
.
0
;
stateStat.
Variance =
0
.
0
;
clusterDistribution[
nIndex].
StateStatistics.
Add
(
stateStat);
}
}
}
// Fait entrer les cas dans le cluster
// Pour les attributs discrets, il suffit d'incrémenter le
// support d'état
// Pour les attributs continus, incrémentez le support d'état et
// actualisez Min et Max
// Additionnez temporairement les valeurs dans le champ de
// valeurs AttributeStatistics
public
void
PushCase
(
MiningCase inputCase)
{
bool
bContinue =
inputCase.
MoveFirst
(
);
casesCount++;
while
(
bContinue)
{
UInt32 attribute =
inputCase.
Attribute;
StateValue stateVal =
inputCase.
Value;
AttributeStatistics attStat =
this
.
clusterDistribution[
attribute];
bool
bContinuous =
(
algo.
AttributeSet.
GetAttributeFlags
(
attribute) &
AttributeFlags.
Continuous) !=
0
;
if
(
bContinuous)
{
Debug.
Assert
(
attStat.
StateStatistics.
Count ==
2
);
// Attribut continu
bool
first =
attStat.
StateStatistics[
1
].
Support ==
0
.
0
;
if
(
stateVal.
IsMissing)
{
attStat.
StateStatistics[
0
].
Support +=
1
.
0
;
}
else
{
Debug.
Assert
(
stateVal.
IsDouble);
double
thisValue =
stateVal.
Double;
double
dSumSoFar =
attStat.
StateStatistics[
1
].
Value.
Double;
// Incrémente le support pour l'état non
manquant
attStat.
StateStatistics[
1
].
Support +=
1
.
0
;
attStat.
StateStatistics[
1
].
Value.
SetDouble
(
dSumSoFar +
thisValue);
// Le support non manquant pour l'attribut est également incrémenté
attStat.
Support +=
1
.
0
;
if
(
first)
{
attStat.
Min =
thisValue;
attStat.
Max =
thisValue;
}
else
{
if
(
attStat.
Min >
thisValue)
attStat.
Min =
thisValue;
if
(
attStat.
Max <
thisValue)
attStat.
Max =
thisValue;
}
}
}
else
{
// attribut discret
if
(
stateVal.
IsMissing)
{
attStat.
StateStatistics[
0
].
Support +=
1
.
0
;
}
else
{
// Incrémente le support pour l'état non manquant
Debug.
Assert
(
stateVal.
IsIndex);
attStat.
StateStatistics[
stateVal.
Index].
Support +=
1
.
0
;
// et aussi pour l'attribut
attStat.
Support +=
1
.
0
;
}
}
bContinue =
inputCase.
MoveNext
(
);
}
}
public
void
UpdateStats
(
)
{
// détermine le nombre d'états
//casesCount = algo.vniStore.getCaseCount();
for
(
int
i =
0
;
i <
this
.
clusterDistribution.
Length;
i++
)
{
uint
statCount =
algo.
AttributeSet.
GetAttributeStateCount
((
uint
)i);
AttributeStatistics attStat =
this
.
clusterDistribution[
i];
bool
bContinuous =
(
algo.
AttributeSet.
GetAttributeFlags
((
uint
)i) &
AttributeFlags.
Continuous) !=
0
;
if
(
bContinuous)
{
casesCount =
this
.
vniatts[
i].
getCount
(
);
attStat.
StateStatistics[
1
].
Support =
0
.
0
;
Debug.
Assert
(
attStat.
StateStatistics.
Count ==
2
);
double
ExistingSupport =
this
.
vniatts[
i].
getCount
(
);
attStat.
StateStatistics[
1
].
Support =
ExistingSupport;
/* sum of values in the cluster */
attStat.
StateStatistics[
1
].
Value.
SetDouble
(
vniatts[
i].
getSum
(
));
attStat.
StateStatistics[
1
].
Variance =
vniatts[
i].
getVariance
(
);
attStat.
Support =
ExistingSupport;
attStat.
Min =
vniatts[
i].
getMin
(
);
attStat.
Max =
vniatts[
i].
getMax
(
);
//double ExistingSupport = attStats.StateStatistics[1].Support;
//double sumValues = attStats.StateStatistics[1].Value.Double;
//double dExistingMiu = sumValues / this.casesCount;
// Fixe la valeur pour un état existant. C'est Miu (SUM/ExistingSupport)
attStat.
StateStatistics[
1
].
Value.
SetDouble
(
vniatts[
i].
getSum
(
) /
ExistingSupport);
// Fixe Prob/AdjProb pour l'état existant
attStat.
StateStatistics[
1
].
Probability =
(
ExistingSupport +
1
.
0
) /
(
ExistingSupport +
attStat.
StateStatistics.
Count);
// Lisse adjustProb
attStat.
StateStatistics[
1
].
AdjustedProbability =
attStat.
StateStatistics[
1
].
Probability;
// Fixe Prob/AdjProb pour un état manquant ??
double
MissingSupport =
attStat.
StateStatistics[
0
].
Support;
attStat.
StateStatistics[
0
].
Probability =
(
MissingSupport +
1
.
0
) /
(
ExistingSupport +
attStat.
StateStatistics.
Count);
// Lisse adjustProb
attStat.
StateStatistics[
0
].
AdjustedProbability =
attStat.
StateStatistics[
0
].
Probability;
// Fixe Prob/AdjProb pour tout l'attribut
attStat.
Probability =
attStat.
StateStatistics[
1
].
Probability;
attStat.
AdjustedProbability =
attStat.
StateStatistics[
1
].
AdjustedProbability;
}
else
/* discrete */
{
/* divise encore le support suivant des vars discrètes
* red =1 , blue = 2, green =3. Décide combien de reds,
* blues ou greens il y a dans un cluster
*/
ArrayList vals =
this
.
vniatts[
i].
getDataValues
(
);
int
max =
(
int
)vniatts[
i].
getMax
(
);
/* démarrage d'états discrets à 1 */
for
(
int
k =
1
;
k <=
max;
k++
)
{
/*boucle à travers toutes les valeurs de vniatts pour régler le support en fonction de la valeur*/
foreach
(
Object attrobj in
vals)
{
/* null signifie valeur manquante */
if
(
attrobj !=
null
)
{
if
(
k ==
(
int
)(
double
)attrobj)
{
Page 40
attStat.
StateStatistics[(
uint
)k].
Support +=
1
.
0
;
attStat.
Support +=
1
.
0
;
}
}
}
}
// attribut discret, détecte l'état le plus populaire et calcule les probabilités
double
ExistingSupport =
0
.
0
;
for
(
uint
nStateIndex =
0
;
nStateIndex <
statCount;
nStateIndex++
)
{
double
dStateSupport =
attStat.
StateStatistics[
nStateIndex].
Support;
attStat.
StateStatistics[
nStateIndex].
Probability =
(
dStateSupport +
1
.
0
) /
(
this
.
casesCount +
statCount);
attStat.
StateStatistics[
nStateIndex].
AdjustedProbability =
attStat.
StateStatistics[
nStateIndex].
Probability;
if
(
nStateIndex >
0
)
ExistingSupport +=
dStateSupport;
}
// fixe les statistiques globales d'attribut
attStat.
Probability =
(
ExistingSupport +
statCount -
1
.
0
) /
(
ExistingSupport +
statCount);
attStat.
AdjustedProbability =
attStat.
Probability;
}
}
}
// Actualisation des statistiques
// Rien à faire pour discret ou pour continu manquant
// Pour continu, nécessite de calculer StdDev et Variance
// Variance = SUM( Xi - Miu)^2 / N
// Nous avons SUM( Xi) dans Value, d'où Miu = Value/N
// Ici nous incrémentons la Variance avec (Xi - Miu)^2/N
// donc, nous actualisons Value
public
void
UpdateStats
(
MiningCase inputCase)
{
// Actualisation des statistiques
bool
bContinue =
inputCase.
MoveFirst
(
);
while
(
bContinue)
{
UInt32 attribute =
inputCase.
Attribute;
StateValue stateVal =
inputCase.
Value;
AttributeStatistics attStat =
this
.
clusterDistribution[
attribute];
bool
bContinuous =
(
algo.
AttributeSet.
GetAttributeFlags
(
attribute) &
AttributeFlags.
Continuous) !=
0
;
if
(
bContinuous)
{
if
(!
stateVal.
IsMissing)
{
double
ExistingSupport =
attStat.
StateStatistics[
1
].
Support;
double
Miu =
attStat.
StateStatistics[
1
].
Value.
Double /
ExistingSupport;
double
thisValue =
stateVal.
Double;
attStat.
StateStatistics[
1
].
Variance +=
((
thisValue -
Miu) *
(
thisValue -
Miu) /
ExistingSupport);
}
}
bContinue =
inputCase.
MoveNext
(
);
}
}
// Post-traitement des clusters pour les attributs continus :
// - état manquant : rien à faire
// - état non manquant : Value est actuellement SUM(Xi), divise par le support existant pour obtenir Miu
// - décide l'état le plus probable, manquant ou existant, pour prédiction
// - copie la probabilité existante, variance, etc. dans les statistiques de l'attribut
// pour les attributs discrets :
// - détecte l'état le plus probable pour prédiction
// - calcule la probabilité d'attribut (ExistingSupport/NumCases)
public
void
PostProcess
(
)
{
for
(
uint
nIndex =
0
;
nIndex <
algo.
AttributeSet.
GetAttributeCount
(
);
nIndex++
)
{
// détermine le nombre d'états
uint
statCount =
algo.
AttributeSet.
GetAttributeStateCount
(
nIndex);
// détermine si l'attribut est continu
bool
bContinuous =
(
algo.
AttributeSet.
GetAttributeFlags
(
nIndex) &
AttributeFlags.
Continuous) !=
0
;
AttributeStatistics attStats =
this
.
clusterDistribution[
nIndex];
if
(
bContinuous)
{
double
ExistingSupport =
attStats.
StateStatistics[
1
].
Support;
double
sumValues =
attStats.
StateStatistics[
1
].
Value.
Double;
double
dExistingMiu =
sumValues /
this
.
casesCount;
// Fixe la valeur pour un état existant. C'est Miu (SUM/ExistingSupport)
attStats.
StateStatistics[
1
].
Value.
SetDouble
(
dExistingMiu);
// Fixe Prob/AdjProb pour un état existant
attStats.
StateStatistics[
1
].
Probability =
(
ExistingSupport +
1
.
0
) /
(
this
.
casesCount +
attStats.
StateStatistics.
Count);
// Lisse adjustProb
attStats.
StateStatistics[
1
].
AdjustedProbability =
attStats.
StateStatistics[
1
].
Probability;
// Fixe Prob/AdjProb pour un état manquant
double
MissingSupport =
attStats.
StateStatistics[
0
].
Support;
attStats.
StateStatistics[
0
].
Probability =
(
MissingSupport +
1
.
0
) /
(
this
.
casesCount +
attStats.
StateStatistics.
Count);
// Lisse adjustProb
attStats.
StateStatistics[
0
].
AdjustedProbability =
attStats.
StateStatistics[
0
].
Probability;
// Fixe Prob/AdjProb pour tout l'attribut
attStats.
Probability =
attStats.
StateStatistics[
1
].
Probability;
attStats.
AdjustedProbability =
attStats.
StateStatistics[
1
].
AdjustedProbability;
}
else
{
// attribut discret, détecte l'état le plus populaire et calcule les probabilités
double
ExistingSupport =
0
.
0
;
for
(
uint
nStateIndex =
0
;
nStateIndex <
statCount;
nStateIndex++
)
{
double
dStateSupport =
attStats.
StateStatistics[
nStateIndex].
Support;
attStats.
StateStatistics[
nStateIndex].
Probability =
(
dStateSupport +
1
.
0
) /
(
this
.
casesCount +
statCount);
attStats.
StateStatistics[
nStateIndex].
AdjustedProbability =
attStats.
StateStatistics[
nStateIndex].
Probability;
if
(
nStateIndex >
0
)
ExistingSupport +=
dStateSupport;
}
// Fixe les statistiques globales de l'attribut
attStats.
Probability =
(
ExistingSupport +
statCount -
1
.
0
) /
(
this
.
casesCount +
statCount);
attStats.
AdjustedProbability =
attStats.
Probability;
}
}
}
public
string
NodeUniqueName
{
get
{
return
nodeUniqueName;
}
}
public
ulong
ClusterID
{
get
{
return
(
ulong
)clusterID;
}
set
{
clusterID =
(
int
)value
;
// Le nom de noeud unique est 1-based, 0 est la racine
nodeUniqueName =
(
clusterID +
1
).
ToString
(
"D3"
);
}
}
public
string
Description
{
get
{
return
description;
}
set
{
description =
value
;
}
}
public
string
Caption
{
get
{
return
"Cluster "
+
(
clusterID +
1
).
ToString
(
);
}
}
public
int
Support
{
get
{
return
casesCount;
}
}
public
void
Load
(
ref
PersistenceReader reader)
{
// Charge informations sur le cluster
reader.
OpenScope
((
PersistItemTag)VNIClusterPersistenceMarker.
ClusterDescription);
reader.
GetValue
(
out
nodeUniqueName);
reader.
GetValue
(
out
description);
reader.
GetValue
(
out
clusterID);
reader.
GetValue
(
out
casesCount);
int
distLength =
0
;
reader.
GetValue
(
out
distLength);
reader.
CloseScope
(
);
clusterDistribution =
new
AttributeStatistics[
distLength];
for
(
int
nIndex =
0
;
nIndex <
distLength;
nIndex++
)
{
// Sauvegarde chaque dist
reader.
OpenScope
((
PersistItemTag)VNIClusterPersistenceMarker.
ClusterDistribution);
clusterDistribution[
nIndex]
=
new
AttributeStatistics
(
);
AttributeStatistics attStats =
clusterDistribution[
nIndex];
double
dVal;
uint
uVal;
reader.
GetValue
(
out
dVal);
attStats.
AdjustedProbability =
dVal;
reader.
GetValue
(
out
uVal);
attStats.
Attribute =
uVal;
reader.
GetValue
(
out
dVal);
attStats.
Max =
dVal;
reader.
GetValue
(
out
dVal);
attStats.
Min =
dVal;
reader.
GetValue
(
out
dVal);
attStats.
Probability =
dVal;
reader.
GetValue
(
out
dVal);
attStats.
Support =
dVal;
int
statCount;
reader.
GetValue
(
out
statCount);
for
(
int
nState =
0
;
nState <
statCount;
nState++
)
{
StateStatistics stateStat =
new
StateStatistics
(
);
reader.
GetValue
(
out
dVal);
stateStat.
AdjustedProbability =
dVal;
reader.
GetValue
(
out
dVal);
stateStat.
Probability =
dVal;
reader.
GetValue
(
out
dVal);
stateStat.
ProbabilityVariance =
dVal;
reader.
GetValue
(
out
dVal);
stateStat.
Support =
dVal;
reader.
GetValue
(
out
dVal);
stateStat.
Variance =
dVal;
bool
bIsMissing =
false
;
reader.
GetValue
(
out
bIsMissing);
if
(
bIsMissing)
{
stateStat.
Value.
SetMissing
(
);
}
else
{
bool
bIsIndex =
false
;
reader.
GetValue
(
out
bIsIndex);
if
(
bIsIndex)
{
uint
indexVal;
reader.
GetValue
(
out
indexVal);
stateStat.
Value.
SetIndex
(
indexVal);
}
else
{
double
dblVal;
reader.
GetValue
(
out
dblVal);
stateStat.
Value.
SetDouble
(
dblVal);
}
}
attStats.
StateStatistics.
Add
(
stateStat);
}
}
}
public
void
Save
(
ref
PersistenceWriter writer)
{
// Sauvegarde les informations sur le cluster
writer.
OpenScope
((
PersistItemTag)VNIClusterPersistenceMarker.
ClusterDescription);
writer.
SetValue
(
nodeUniqueName);
writer.
SetValue
(
description);
writer.
SetValue
(
clusterID);
writer.
SetValue
(
casesCount);
writer.
SetValue
(
clusterDistribution.
Length);
writer.
CloseScope
(
);
for
(
int
nIndex =
0
;
nIndex <
clusterDistribution.
Length;
nIndex++
)
{
// Sauvegarde chaque dist
writer.
OpenScope
((
PersistItemTag)VNIClusterPersistenceMarker.
ClusterDistribution);
AttributeStatistics attStats =
clusterDistribution[
nIndex];
writer.
SetValue
(
attStats.
AdjustedProbability);
writer.
SetValue
(
attStats.
Attribute);
writer.
SetValue
(
attStats.
Max);
writer.
SetValue
(
attStats.
Min);
writer.
SetValue
(
attStats.
Probability);
writer.
SetValue
(
attStats.
Support);
writer.
SetValue
(
attStats.
StateStatistics.
Count);
for
(
int
nState =
0
;
nState <
attStats.
StateStatistics.
Count;
nState++
)
{
StateStatistics stateStat =
attStats.
StateStatistics[(
uint
)nState];
writer.
SetValue
(
stateStat.
AdjustedProbability);
writer.
SetValue
(
stateStat.
Probability);
writer.
SetValue
(
stateStat.
ProbabilityVariance);
writer.
SetValue
(
stateStat.
Support);
writer.
SetValue
(
stateStat.
Variance);
writer.
SetValue
(
stateStat.
Value.
IsMissing);
if
(!
stateStat.
Value.
IsMissing)
{
writer.
SetValue
(
stateStat.
Value.
IsIndex);
if
(
stateStat.
Value.
IsIndex)
{
writer.
SetValue
(
stateStat.
Value.
Index);
}
else
{
writer.
SetValue
(
stateStat.
Value.
Double);
}
}
}
}
}
// Predict : retourne la prédiction la plus probable dans ce cluster
public
void
Predict
(
ref
PredictionResult predictionResult)
{
// predictionResult contient les options de prédiction et devrait être
// rempli avec les valeurs/statistiques prédites
AttributeGroup outputAttrs =
predictionResult.
OutputAttributes;
outputAttrs.
Reset
(
);
uint
nAtt =
AttributeSet.
Unspecified;
while
(
outputAttrs.
Next
(
out
nAtt))
{
// Vérfie périodiquement si le traitement a été annulé
algo.
Context.
CheckCancelled
(
);
// Construit la prédiction
AttributeStatistics attStats =
new
AttributeStatistics
(
);
if
(
predictionResult.
IncludeNodeId)
{
attStats.
NodeId =
this
.
NodeUniqueName;
}
attStats.
Attribute =
nAtt;
attStats.
Min =
clusterDistribution[
nAtt].
Min;
attStats.
Max =
clusterDistribution[
nAtt].
Max;
attStats.
Support =
clusterDistribution[
nAtt].
Support;
attStats.
Probability =
clusterDistribution[
nAtt].
Probability;
attStats.
AdjustedProbability =
clusterDistribution[
nAtt].
AdjustedProbability;
uint
nStatesCount =
(
uint
)clusterDistribution[
nAtt].
StateStatistics.
Count;
for
(
uint
index =
0
;
index <
nStatesCount;
index++
)
{
StateStatistics clusterStateStat =
clusterDistribution[
nAtt].
StateStatistics[
index];
StateStatistics stateStat =
new
StateStatistics
(
);
stateStat.
AdjustedProbability =
clusterStateStat.
AdjustedProbability;
stateStat.
Probability =
clusterStateStat.
Probability;
stateStat.
Support =
clusterStateStat.
Support;
stateStat.
Variance =
clusterStateStat.
Variance;
stateStat.
ProbabilityVariance =
clusterStateStat.
ProbabilityVariance;
stateStat.
Value =
clusterStateStat.
Value;
attStats.
StateStatistics.
Add
(
stateStat);
}
predictionResult.
AddPrediction
(
attStats);
}
}
public
AttributeStatistics[]
Distribution
{
get
{
return
clusterDistribution;
}
}
public
void
addValues
(
ArrayList values)
{
clusterValues =
values;
}
public
ArrayList getValues
(
)
{
return
clusterValues;
}
public
VNIPatternAttribute[]
getVNIAtts
(
)
{
return
vniatts;
}
}
}
VniStore.cs▲
Cette classe aide aux traductions de données entre Analysis Services et la routine IMSL cluster K Means.
using
System;
using
System.
Collections.
Generic;
using
System.
Text;
using
System.
Diagnostics;
using
Microsoft.
SqlServer.
DataMining.
PluginAlgorithms;
using
System.
Collections;
using
Imsl.
Stat;
using
Imsl.
Math;
namespace
VNI
{
/* Ceci est une classe d'aide qui vous aidera à traduire les données
* entre Analysis Services et les bibliothèques IMSL C#
*/
public
class
VNIStore
{
private
ArrayList caseList;
/* référence vers l'objet Algorithm qui a détecté ce cluster */
private
VniClusterKMeansAlgorithm algo;
private
ClusterKMeans kmean;
private
double
[,]
cases;
private
double
[,]
centers;
public
VNIStore
(
VniClusterKMeansAlgorithm parent)
{
caseList =
new
ArrayList
(
);
algo =
parent;
}
/* Fonction à exécuter. Cela dépend du choix de l'utilisateur à
* partir de la liste des algorithmes disponibles
*/
private
void
execute
(
String function,
int
cluster_count)
{
int
m =
0
;
int
seeds_inc =
caseList.
Count /
cluster_count;
ArrayList list =
translateData
(
0
);
cases =
(
double
[,]
)list[
0
];
double
[,]
cluster_seeds =
new
double
[
3
,
((
double
[]
)caseList[
0
]
).
Length];
for
(
int
i =
0
;
i <
cluster_count;
i++
)
{
for
(
int
j =
0
;
j <
((
double
[]
)caseList[
0
]
).
Length;
j++
)
{
cluster_seeds[
m,
j]
=
cases[
i *
seeds_inc,
j];
}
m++;
}
kmean =
new
ClusterKMeans
(
cases,
cluster_seeds);
// Traduit les données en ce qui est attendu par la fonction
// Initialement, nous utilisons ClusterKMeans.
}
public
void
addCase
(
MiningCase mcase)
{
ArrayList attrList =
new
ArrayList
(
);
double
[]
varr =
new
double
[
algo.
AttributeSet.
GetAttributeCount
(
)];
bool
mcontinue =
mcase.
MoveFirst
(
);
while
(
mcontinue)
{
/* utiliser attribute pour indexer dans les valeurs correctes*/
UInt32 attribute =
mcase.
Attribute;
StateValue value
=
mcase.
Value;
if
(
value
.
IsDouble) /*continous */
{
varr[
attribute]
=
value
.
Double;
//attrList.Add(value.Double) ;
}
/* pour toutes les colonnes discrètes, il y aura un index
* représentant un état. Par exemple, une colonne avec les
* valeurs A,B,C aura 3 indices
* A=1, B=2, C=3
*/
if
(
value
.
IsIndex) /*discrete */
{
varr[
attribute]
=
value
.
Index;
//attrList.Add((double)value.Index);
}
if
(
value
.
IsMissing) /* missing values */
{
//varr[attribute] = ;
//attrList.Add(null);
}
mcontinue =
mcase.
MoveNext
(
);
}
caseList.
Add
(
varr);
}
/* traduit ArrayList des cas d'entrées en des ensembles pour des
* structures pour la routine IMSL C#.
* Retourne un Arraylist d'un élément qui contient l'ensemble/objet
* qui doit être utilisé par la routine C#.
* 0 : utilise la CaseList pour calculer la dimension de l'ensemble
* 1-8 : utilise la CaseList et la crée en dimensions variant de 1 à * 8
* 9 : utilisation pour données spéciales.
* *
*/
private
ArrayList translateData
(
int
dim)
{
switch
(
dim)
{
case
0
:
return
getArrayFromCaseList
(
);
//break;
case
1
:
case
2
:
case
3
:
case
4
:
case
5
:
case
6
:
case
7
:
case
8
:
case
9
:
break
;
}
return
null
;
}
private
ArrayList getArrayFromCaseList
(
)
{
int
rows =
caseList.
Count;
if
(
caseList.
Count ==
0
) return
null
;
double
[]
attrlist;
/* Vérifiez le premier élément. Il devrait y avoir une autre
* arraylist avec une taille égale au nombre d'attributs dans
* MiningCase.
* En d'autres termes, si le tableau a 10 rangées et 5 colonnes,
* alors cet ensemble doit avoir 5 éléments.
*
*/
/* Maintenant, créez un ensemble 2d dans ce code, mais nous
* devrions avoir des objets qui convertissent cette datalist en
* 2D, 3D, structure, etc. qui sont nécessaires à la routine CNL. * Il peut exister une classe parente qui traite de la
* conversation principale puis certaines sous-classes qui
* effectuent des conversions spécifiques de certaines tâches
*/
double
[,]
data =
new
double
[
caseList.
Count,
((
double
[]
)caseList[
0
]
).
Length];
for
(
int
i =
0
;
i <
caseList.
Count;
i++
)
{
attrlist =
(
double
[]
)caseList[
i];
for
(
int
j =
0
;
j <
attrlist.
Length;
j++
)
{
/* null signifie valeur manquante */
data[
i,
j]
=
(
double
)attrlist[
j];
}
}
ArrayList rlist =
new
ArrayList
(
);
rlist.
Add
(
data);
return
rlist;
}
public
void
fillClusters
(
InternalCluster[]
clusters)
{
execute
(
"ClusterKMeans"
,
algo.
num_clusters);
centers =
kmean.
Compute
(
);
int
[]
cmember =
kmean.
GetClusterMembership
(
);
int
[]
nc =
kmean.
GetClusterCounts
(
);
// élimine les valeurs de cluster pour chaque cluster
// fondamentalement, initialise les modèles avec leurs valeurs
// initiales
// cela servira à initialiser les statistiques d'attributs
// utilisées dans la prédiction.
for
(
int
i =
0
;
i <=
nc.
Length;
i++
)
{
// [] indices = new int[nc[i]];
// int m = 0;
for
(
int
j =
0
;
j <
cmember.
Length;
j++
)
{
if
(
cmember[
j]
==
i +
1
)
{
double
[]
data =
(
double
[]
)caseList[
j];
/* ajoute des valeurs pour chaque attribut */
for
(
int
m =
0
;
m <
data.
Length;
m++
)
{
clusters[
i].
vniatts[
m].
addDataValues
(
data[
m]
);
clusters[
i].
vniatts[
m].
setCount
(
nc[
i]
);
}
}
}
}
/* initialise les statistiques pour chaque cluster selon les attributs */
for
(
int
i =
0
;
i <
nc.
Length;
i++
)
{
}
}
public
double
[,]
getCenters
(
)
{
return
centers;
}
public
int
getCaseCount
(
)
{
return
caseList.
Count;
}
}
}
VniPatternAttribute.cs▲
Cette classe sert à représenter un attribut dans le motif détecté. Un motif peut consister en un ou plusieurs attributs.
using
System;
using
System.
Collections.
Generic;
using
System.
Text;
using
System.
Collections;
namespace
VNI
{
/* Microsoft possède le concept de Cas. Par exemple, un tableau de DB est
* un cas. L'enregistrement dans le Cas ou le tableau sont appelés jeu
* d'attributs. Chaque colonne dans un Case ou un tableau est appelé
* attribut. En data mining, le travail consiste à trouver des modèles
* dans vos données. Un motif est constitué de jeux d'attributs.
* Par exemple, en analyse de cluster, nous pourrions trouver 3 clusters,
* et chaque cluster aura différents jeux d'attributs.
* Pour chaque attribut dans le modèle, il nous faut initialiser quelques
* statistiques de base (min, max, variance, etc.)
* Cette classe gardera trace des statistiques de base
*/
public
class
VNIPatternAttribute
{
ArrayList dataValues;
int
count =
0
;
public
VNIPatternAttribute
(
)
{
dataValues =
new
ArrayList
(
);
}
public
double
getMin
(
)
{
if
(
dataValues.
Count >
0
)
{
double
[]
vals =
(
double
[]
)dataValues.
ToArray
(
typeof
(
double
));
Array.
Sort
(
vals);
return
vals[
0
];
}
return
0
;
}
public
double
getSum
(
)
{
double
sum =
0
.
0
;
foreach
(
Object attrobj in
dataValues)
{
/* null signifie valeur manquante */
if
(
attrobj !=
null
)
{
sum +=
(
double
)attrobj;
}
}
return
sum;
}
public
double
getMax
(
)
{
if
(
dataValues.
Count >
0
)
{
double
[]
vals =
(
double
[]
)dataValues.
ToArray
(
typeof
(
double
));
Array.
Sort
(
vals);
return
vals[
vals.
Length -
1
];
}
return
0
;
}
public
double
getVariance
(
)
{
double
variance =
0
.
0
;
if
(
getCount
(
) ==
0
)
{
return
0
;
}
double
ExistingSupport =
getCount
(
);
double
Miu =
this
.
getSum
(
) /
ExistingSupport;
foreach
(
Object attrobj in
dataValues)
{
/* null signifie valeur manquante */
if
(
attrobj !=
null
)
{
double
thisValue =
(
double
)attrobj;
variance +=
(
thisValue -
Miu) *
(
thisValue -
Miu);
}
}
return
variance /
ExistingSupport;
}
public
void
addDataValues
(
double
value
)
{
dataValues.
Add
(
value
);
}
public
int
getCount
(
)
{
return
count;
}
public
void
setCount
(
int
count)
{
this
.
count =
count;
}
public
ArrayList getDataValues
(
)
{
return
dataValues;
}
}
}