예제 #1
0
 /// <summary>
 /// Create a new identity match result
 /// </summary>
 public MdmIdentityMatchResult(T input, T record, string configurationName, RecordMatchClassification classification = RecordMatchClassification.Match, float score = 1.0f)
 {
     this.Record            = record;
     this.Method            = RecordMatchMethod.Identifier;
     this.Score             = this.Strength = score;
     this.Classification    = classification;
     this.ConfigurationName = configurationName;
     if (input is IHasIdentifiers aIdentity && record is IHasIdentifiers bIdentity)
     {
         this.Vectors = new IRecordMatchVector[] { new MdmIdentityMatchAttribute(classification, string.Join(",", aIdentity.Identifiers.Select(o => $"{o.Value} [{o.Authority.DomainName}]")), string.Join(",", bIdentity.Identifiers.Select(o => $"{o.Value} [{o.Authority.DomainName}]"))) };
     }
 }
예제 #2
0
        /// <summary>
        /// Create a dummy match
        /// </summary>
        public DummyMatchResult(T input, T record)
        {
            this.m_record = record;

            // Patient?
            if (input is Patient)
            {
                var pInput  = (Patient)(object)input;
                var pRecord = (Patient)(object)record;
                // Classify
                if (pInput.MultipleBirthOrder.HasValue && pInput.MultipleBirthOrder != pRecord.MultipleBirthOrder)
                {
                    this.Classification = RecordMatchClassification.Probable;
                }
                else
                {
                    this.Classification = RecordMatchClassification.Match;
                }
            }
            else
            {
                this.Classification = RecordMatchClassification.Match;
            }
        }
예제 #3
0
 /// <summary>
 /// Creates a new match result
 /// </summary>
 /// <param name="record">The record that was classified</param>
 /// <param name="score">The assigned score</param>
 /// <param name="classification">The classification</param>
 /// <param name="method">The method that was used to establish the match</param>
 /// <param name="strength">The relative strength (0 .. 1) of the match given the maximum score the match could have gotten</param>
 /// <param name="vectors">The attributes + scores</param>
 /// <param name="configurationName">The name of the configuration used to match</param>
 public MatchResult(IdentifiedData record, double score, double strength, String configurationName, RecordMatchClassification classification, RecordMatchMethod method, IEnumerable <IRecordMatchVector> vectors)
 {
     this.Strength          = strength;
     this.Record            = record;
     this.Score             = score;
     this.Classification    = classification;
     this.ConfigurationName = configurationName;
     this.Vectors           = vectors.Select(o => o is MatchVector mv ? mv : new MatchVector(o)).ToList();
     this.Method            = method;
 }
예제 #4
0
 /// <summary>
 /// Create MDM match attribute
 /// </summary>
 public MdmIdentityMatchAttribute(RecordMatchClassification classification, object aValue, object bValue)
 {
     this.Score = classification == RecordMatchClassification.Match ? 1.0f : 0.0f;
     this.A     = aValue;
     this.B     = bValue;
 }
        /// <summary>
        /// Classifies the individual record against the input and score it based on similarity rules
        /// </summary>
        /// <typeparam name="T">The type of record being classified</typeparam>
        /// <param name="input">The input being classified</param>
        /// <param name="block">The block which is being classified</param>
        /// <param name="attributes">The match attributes to classify on</param>
        /// <returns>The match classification</returns>
        /// <param name="configurationName">The name of the configuration used</param>
        /// <param name="evaluationType">The evaluation type</param>
        /// <param name="matchThreshold">The matching threshold</param>
        /// <param name="collector">The diagnostics collector to use</param>
        private IRecordMatchResult <T> ClassifyInternal <T>(T input, T block, List <MatchAttribute> attributes, string configurationName, ThresholdEvaluationType evaluationType, double matchThreshold, double nonMatchThreshold, IRecordMatchingDiagnosticSession collector = null) where T : IdentifiedData
        {
            try
            {
                collector?.LogStartAction(block);
                var attributeResult = attributes.Select(v =>
                {
                    try
                    {
                        collector?.LogStartAction(v);
                        this.m_tracer.TraceVerbose("Initializing attribute {0}", v);
                        // Initialize the weights and such for the attribute
                        var attributeScores = v.GetPropertySelectors <T>().Select(selector =>
                        {
                            Func <T, dynamic> selectorExpression = (Func <T, dynamic>)selector.Value;
                            object aValue       = selectorExpression(input),
                            bValue              = selectorExpression(block);
                            var defaultInstance = selectorExpression.Method.ReturnType.GetConstructors().Any(c => c.GetParameters().Length == 0) ?
                                                  Activator.CreateInstance(selectorExpression.Method.ReturnType) :
                                                  null;
                            var result = AssertionUtil.ExecuteAssertion(selector.Key, v.Assertion, v, aValue, bValue);
                            return(result);
                        });
                        var bestScore = attributeScores.OrderByDescending(o => o.CalculatedScore).FirstOrDefault();

                        if (bestScore == null || !bestScore.CalculatedScore.HasValue)
                        {
                            return(null);
                        }
                        else
                        {
                            var result = new MatchVector(v, v.Id ?? bestScore.PropertyName, bestScore.CalculatedScore.Value, bestScore.Evaluated, bestScore.A, bestScore.B);
                            return(result);
                        }
                    }
                    finally
                    {
                        collector?.LogEndAction();
                    }
                }).OfType <MatchVector>().ToList();

                // Throw out attributes which are dependent however the dependent attribute was unsuccessful
                // So if for example: If the scoring for CITY is only counted when STATE is successful, but STATE was
                // unsuccessful, we want to exclude CITY.
                attributeResult.RemoveAll(o => !o.Attribute.When.All(w =>
                {
                    var attScore = attributeResult.First(r => r.Attribute.Id == w.AttributeRef);
                    // TODO: Allow cascaded operators to specify a value
                    //switch (w.Operator)
                    //{
                    //    case BinaryOperatorType.NotEqual:
                    //        return attScore != w.Value;

                    //    case BinaryOperatorType.Equal:
                    //        return attScore == w.Value;

                    //    case BinaryOperatorType.GreaterThan:
                    //        return attScore > w.Value;

                    //    case BinaryOperatorType.GreaterThanOrEqual:
                    //        return attScore >= w.Value;

                    //    case BinaryOperatorType.LessThan:
                    //        return attScore < w.Value;

                    //    case BinaryOperatorType.LessThanOrEqual:
                    //        return attScore <= w.Value;

                    //    default:
                    //        throw new InvalidOperationException($"Cannot use operator {w.Operator} on when");
                    //}
                    return(attScore?.Evaluated == true && attScore?.Score > 0);
                })); // Remove all failed attributes
                var score = attributeResult.Sum(v => v.Score);

                // The attribute scores which are produced will be from SUM(NonMatchWeight) .. SUM(MatchWeight)
                double maxScore = attributeResult.Sum(o => o.Attribute.MatchWeight),
                       minScore = attributeResult.Sum(o => o.Attribute.NonMatchWeight);
                // This forms a number line between -MIN .. MAX , our probability is the distance that our score
                // is on that line, for example: -30.392 .. 30.392
                // Then the strength is 0.5 of a score of 0 , and 1.0 for a score of 30.392
                var strength = (double)(score + -minScore) / (double)(maxScore + -minScore);
                if (Double.IsNaN(strength))
                {
                    strength = 0;
                }
                RecordMatchClassification classification = RecordMatchClassification.NonMatch;
                if (evaluationType == ThresholdEvaluationType.AbsoluteScore)
                {
                    classification = score > matchThreshold ? RecordMatchClassification.Match : score <= nonMatchThreshold ? RecordMatchClassification.NonMatch : RecordMatchClassification.Probable;
                }
                else
                {
                    classification = strength > matchThreshold ? RecordMatchClassification.Match : strength <= nonMatchThreshold ? RecordMatchClassification.NonMatch : RecordMatchClassification.Probable;
                }
                var retVal = new MatchResult <T>(block, score, strength, configurationName, classification, RecordMatchMethod.Weighted, attributeResult);

                return(retVal);
            }
            catch (Exception e)
            {
                this.m_tracer.TraceError("Error classifying result set (mt={0}, nmt={1}) - {2}", matchThreshold, nonMatchThreshold, e.Message);
                throw new MatchingException($"Error classifying result set", e);
            }
            finally
            {
                collector?.LogEndAction();
            }
        }