public void TestTrendRequest_IsValid_True_If_Enough_Valid_Values()
        {
            var dataList = DataList();

            // Add invalid data point after 5 more recent points
            dataList.Add(new CoreDataSet
            {
                Value = ValueData.NullValue,
                Denominator = 4,
                Count = 8,
                Year = 2000
            });

            var trendRequest = new TrendRequest()
            {
                UnitValue = 1,
                ValueTypeId = ValueTypeIds.Proportion,
                YearRange = 1,
                Data = dataList
            };

            string validationMessage = null;
            var isValid = trendRequest.IsValid(ref validationMessage);
            Assert.IsTrue(isValid, validationMessage);
        }
 public void Write(TrendRequest trendRequest, TrendMarkerResult trendMarkerResult)
 {
     foreach (var data in trendRequest.Data)
     {
         _csvWriter.AddLine(data.AreaCode, data.Year, data.Value, data.Denominator, data.Count);
     }
     _csvWriter.AddLine("", "", "", "", "", trendMarkerResult.ChiSquare, trendMarkerResult.Slope, trendMarkerResult.Intercept,
         trendMarkerResult.Marker, trendMarkerResult.NumberOfPointsUsedInCalculation, trendMarkerResult.Message);
 }
        public TrendMarkerResult GetTrendMarkerResult(IndicatorMetadata indicatorMetadata, Grouping grouping, IList<CoreDataSet> dataList)
        {
            var trendRequest = new TrendRequest
            {
                UnitValue = indicatorMetadata.Unit.Value,
                ValueTypeId = indicatorMetadata.ValueTypeId,
                Data = dataList,
                YearRange = grouping.YearRange,
            };

            var result = _trendCalculator.GetResults(trendRequest);
            return result;
        }
        public void TestTrendRequest_IsValid_True_If_Enough_Recent_Values()
        {
            var trendRequest = new TrendRequest()
            {
                UnitValue = 1,
                ValueTypeId = ValueTypeIds.Proportion,
                YearRange = 1,
                Data = DataList()
            };

            string validationMessage = null;
            var isValid = trendRequest.IsValid(ref validationMessage);
            Assert.IsTrue(isValid, validationMessage);
        }
        public void TestTrendRequest_IsValid_False_If_Value_Invalid()
        {
            var dataList = DataList();
            dataList[0].Value = ValueData.NullValue;

            var trendRequest = new TrendRequest()
            {
                UnitValue = 1,
                ValueTypeId = ValueTypeIds.Proportion,
                YearRange = 1,
                Data = dataList
            };

            string validationMessage = null;
            trendRequest.IsValid(ref validationMessage);
            Assert.AreEqual("Not enough data points with valid values to calculate recent trend", validationMessage);
        }
        public void TestTrendRequestIsInvalidForYearRangeGreaterThan1()
        {
            var trendRequest = new TrendRequest()
            {
                UnitValue = 1,
                ValueTypeId = ValueTypeIds.Proportion,
                YearRange = 3
            };

            string validationMessage = null;
            trendRequest.IsValid(ref validationMessage);
            Assert.IsTrue(validationMessage == "The recent trend cannot be calculated for this year range");
        }
        public void TestTrendRequestIsInvalidForNonRelevantValueType()
        {
            var trendRequest = new TrendRequest()
            {
                UnitValue = 1,
                ValueTypeId = ValueTypeIds.Count,
                YearRange = 1
            };

            string validationMessage = null;
            trendRequest.IsValid(ref validationMessage);
            Assert.IsTrue(validationMessage == "The recent trend cannot be calculated for this value type");
        }
 public TrendMarkerResult GetResults(TrendRequest trendRequest)
 {
     return CalculateTrend(trendRequest);
 }
        private bool IsSignificant(TrendRequest trendRequest, ref int pointsUsed, ref double chiSquare, ref string message)
        {
            chiSquare = 0.0;

            if (trendRequest.IsValid(ref message) == false)
            {
                pointsUsed = 0;

                return false;
            }

            // Chi squared confidence 
            const double chiSquared99Point8 = 9.54953570608324; // inExcel =CHIINV(0.002,1)
            double chiSquaredConfidence = chiSquared99Point8;

            var fullDataList = trendRequest.Data.ToList();
            for (int pointsToUse = MinimumNumberOfPoints; pointsToUse <= fullDataList.Count; pointsToUse++)
            {
                pointsUsed = pointsToUse;

                double sumOfr = 0;
                double sumOfn = 0;
                double sumOfrAndt = 0;
                double sumOfnAndt = 0;
                double sumOfnAndtSq = 0;
                double t = pointsUsed;

                var dataList = GetMostRecentDataOrderedByIncreasingYear(fullDataList, pointsToUse);
                foreach (var coreDataSet in dataList)
                {
                    var r = coreDataSet.Count.Value;
                    var n = coreDataSet.Denominator;

                    sumOfr += r;
                    sumOfn += n;
                    sumOfrAndt += r * t;
                    sumOfnAndt += n * t;
                    sumOfnAndtSq += n * (t * t);

                    t--;
                }

                var sumPart1 = ((sumOfn * sumOfrAndt) - (sumOfr * sumOfnAndt));

                chiSquare = (sumOfn * (sumPart1 * sumPart1)) /
                            (sumOfr * (sumOfn - sumOfr) * (sumOfn * sumOfnAndtSq - (sumOfnAndt * sumOfnAndt)));

                if (chiSquare > chiSquaredConfidence)
                {
                    return true;
                }
            }

            return false;
        }
        private TrendMarkerResult CalculateTrend(TrendRequest trendRequest, int pointsUsed = 0)
        {
            var message = string.Empty;
            var chiSquare = 0.0;

            // Is there a significant trend
            var isSignificant = IsSignificant(trendRequest, ref pointsUsed, ref chiSquare, ref message);

            // Trend line
            TrendLine trendLine;
            if (isSignificant)
            {
                var values = GetMostRecentDataOrderedByIncreasingYear(trendRequest.Data, pointsUsed)
                    .Select(x => x.Value)
                    .ToList();
                trendLine = GetSlopeAndIntercept(values, trendRequest.ValueTypeId, trendRequest.UnitValue);
            }
            else
            {
                trendLine = new TrendLine();
            }

            // Trend marker
            TrendMarker marker;
            if (isSignificant)
            {
                marker = trendLine.SlopeBeta > 0 ? TrendMarker.Increasing : TrendMarker.Decreasing;
            }
            else
            {
                marker = pointsUsed > 0 ? TrendMarker.NoChange : TrendMarker.CannotBeCalculated;
            }

            // Return results
            return new TrendMarkerResult()
            {
                Slope = trendLine.SlopeBeta,
                Intercept = trendLine.InterceptAlpha,
                NumberOfPointsUsedInCalculation = pointsUsed,
                Marker = marker,
                IsSignificant = isSignificant,
                ChiSquare = chiSquare,
                Message = message
            };
        }