StdDevOutlierAnalysisService implements DataAnalysisService { @Override public final List<DeflatedDataValue> analyse( Collection<OrganisationUnit> parents, Collection<DataElement> dataElements, Collection<Period> periods, Double stdDevFactor, Date from ) { log.info( "Starting std dev analysis, no of org units: " + parents.size() + ", factor: " + stdDevFactor + ", from: " + from ); List<DeflatedDataValue> outlierCollection = new ArrayList<>(); List<String> parentsPaths = parents.stream().map( OrganisationUnit::getPath ).collect( Collectors.toList() ); loop : for ( DataElement dataElement : dataElements ) { if ( dataElement.getValueType().isNumeric() && stdDevFactor != null ) { Set<CategoryOptionCombo> categoryOptionCombos = dataElement.getCategoryOptionCombos(); List<DataAnalysisMeasures> measuresList = dataAnalysisStore.getDataAnalysisMeasures( dataElement, categoryOptionCombos, parentsPaths, from ); MapMap<Long, Long, Integer> lowBoundMapMap = new MapMap<>(); MapMap<Long, Long, Integer> highBoundMapMap = new MapMap<>(); for ( DataAnalysisMeasures measures : measuresList ) { int lowBound = (int) Math.round( MathUtils.getLowBound( measures.getStandardDeviation(), stdDevFactor, measures.getAverage() ) ); int highBound = (int) Math.round( MathUtils.getHighBound( measures.getStandardDeviation(), stdDevFactor, measures.getAverage() ) ); lowBoundMapMap.putEntry( measures.getCategoryOptionComboId(), measures.getOrgUnitId(), lowBound ); highBoundMapMap.putEntry( measures.getCategoryOptionComboId(), measures.getOrgUnitId(), highBound ); } for ( CategoryOptionCombo categoryOptionCombo : categoryOptionCombos ) { Map<Long, Integer> lowBoundMap = lowBoundMapMap.get( categoryOptionCombo.getId() ); Map<Long, Integer> highBoundMap = highBoundMapMap.get( categoryOptionCombo.getId() ); outlierCollection.addAll( dataAnalysisStore.getDeflatedDataValues( dataElement, categoryOptionCombo, periods, lowBoundMap, highBoundMap ) ); if ( outlierCollection.size() > MAX_OUTLIERS ) { break loop; } } } } return outlierCollection; } StdDevOutlierAnalysisService( DataAnalysisStore dataAnalysisStore ); @Override final List<DeflatedDataValue> analyse( Collection<OrganisationUnit> parents,
        Collection<DataElement> dataElements, Collection<Period> periods, Double stdDevFactor, Date from );  }
@Test public void testGetFindOutliers() { dataValueA = createDataValue( dataElementSingleQuoteName, periodI, organisationUnitA, "71", categoryOptionCombo ); dataValueB = createDataValue( dataElementSingleQuoteName, periodJ, organisationUnitA, "-71", categoryOptionCombo ); dataValueService.addDataValue( createDataValue( dataElementSingleQuoteName, periodA, organisationUnitA, "5", categoryOptionCombo ) ); dataValueService.addDataValue( createDataValue( dataElementSingleQuoteName, periodB, organisationUnitA, "-5", categoryOptionCombo ) ); dataValueService.addDataValue( createDataValue( dataElementSingleQuoteName, periodC, organisationUnitA, "5", categoryOptionCombo ) ); dataValueService.addDataValue( createDataValue( dataElementSingleQuoteName, periodD, organisationUnitA, "-5", categoryOptionCombo ) ); dataValueService.addDataValue( createDataValue( dataElementSingleQuoteName, periodE, organisationUnitA, "10", categoryOptionCombo ) ); dataValueService.addDataValue( createDataValue( dataElementSingleQuoteName, periodF, organisationUnitA, "-10", categoryOptionCombo ) ); dataValueService.addDataValue( createDataValue( dataElementSingleQuoteName, periodG, organisationUnitA, "13", categoryOptionCombo ) ); dataValueService.addDataValue( createDataValue( dataElementSingleQuoteName, periodH, organisationUnitA, "-13", categoryOptionCombo ) ); dataValueService.addDataValue( dataValueA ); dataValueService.addDataValue( dataValueB ); double stdDevFactor = 2.0; List<Period> periods = new ArrayList<>(); periods.add( periodI ); periods.add( periodJ ); periods.add( periodA ); periods.add( periodE ); List<DeflatedDataValue> values = stdDevOutlierAnalysisService.analyse( Lists.newArrayList( organisationUnitA ), dataElementsA, periods, stdDevFactor, from ); double lowerBound = -34.51 * stdDevFactor; double upperBound = 34.51 * stdDevFactor; DeflatedDataValue valueA = new DeflatedDataValue( dataValueA ); DeflatedDataValue valueB = new DeflatedDataValue( dataValueB ); assertEquals( 1, values.size() ); assertTrue( values.contains( valueA ) ); assertFalse( values.contains( valueB ) ); }