package com.linkedin.thirdeye.dashboard.views.heatmap;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math3.distribution.NormalDistribution;
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HeatMap {
private static final Logger LOG = LoggerFactory.getLogger(HeatMap.class);
List<HeatMapCell> heatMapCells;
private String dimensionName;
public HeatMap(String dimensionName, List<HeatMapCell> heatMapCells) {
this.dimensionName = dimensionName;
this.heatMapCells = heatMapCells;
}
public static class Builder {
List<String> dimensionValues = new ArrayList<>();
DescriptiveStatistics baselineStats = new DescriptiveStatistics();
DescriptiveStatistics currentStats = new DescriptiveStatistics();
DescriptiveStatistics numeratorBaselineStats = new DescriptiveStatistics();
DescriptiveStatistics numeratorCurrentStats = new DescriptiveStatistics();
DescriptiveStatistics denominatorBaselineStats = new DescriptiveStatistics();
DescriptiveStatistics denominatorCurrentStats = new DescriptiveStatistics();
DescriptiveStatistics cellSizeStats = new DescriptiveStatistics();
String cellSizeExpression = null;
List<HeatMapCell> heatMapCells = new ArrayList<>();
private String dimensionName;
public Builder(String dimensionName) {
this.dimensionName = dimensionName;
}
public void addCell(String dimensionValue, double baselineValue, double currentValue) {
dimensionValues.add(dimensionValue);
baselineStats.addValue(baselineValue);
currentStats.addValue(currentValue);
}
public void addCell(String dimensionValue, double baselineValue, double currentValue, Double cellSize, String cellSizeExpression,
Double numeratorBaseline, Double denominatorBaseline, Double numeratorCurrent, Double denominatorCurrent) {
addCell(dimensionValue, baselineValue, currentValue);
cellSizeStats.addValue(cellSize);
this.cellSizeExpression = cellSizeExpression;
numeratorBaselineStats.addValue(numeratorBaseline);
numeratorCurrentStats.addValue(numeratorCurrent);
denominatorBaselineStats.addValue(denominatorBaseline);
denominatorCurrentStats.addValue(denominatorCurrent);
}
HeatMap build() {
int numCells = dimensionValues.size();
NormalDistribution baselineDist = getDistribution(baselineStats);
NormalDistribution currentDist = getDistribution(currentStats);
double baselineTotal = baselineStats.getSum();
double currentTotal = currentStats.getSum();
for (int i = 0; i < numCells; i++) {
String dimensionValue = dimensionValues.get(i);
double baselineValue = baselineStats.getElement(i);
double currentValue = currentStats.getElement(i);
double numeratorBaseline = numeratorBaselineStats.getValues().length == 0 ? 0 : numeratorBaselineStats.getElement(i);
double numeratorCurrent = numeratorCurrentStats.getValues().length == 0 ? 0 : numeratorCurrentStats.getElement(i);
double denominatorBaseline = denominatorBaselineStats.getValues().length == 0 ? 0 : denominatorBaselineStats.getElement(i);
double denominatorCurrent = denominatorCurrentStats.getValues().length == 0 ? 0 : denominatorCurrentStats.getElement(i);
// contribution
double baselineContribution = baselineValue *100/ baselineTotal;
double currentContribution = currentValue * 100 / currentTotal;
double baselineCDFValue = 0;
if (baselineDist != null) {
baselineCDFValue = baselineDist.cumulativeProbability(baselineValue);
}
double currentCDFValue = 0;
if (currentDist != null) {
currentCDFValue = currentDist.cumulativeProbability(currentValue);
}
double absoluteChange = (currentValue - baselineValue);
double percentageChange = 0;
if (Double.compare(baselineValue, 0d) != 0) {
percentageChange = ((currentValue - baselineValue) / baselineValue) * 100;
} else {
if (Double.compare(currentValue, 0d) != 0) {
percentageChange = 100;
}
}
double contributionDifference = currentContribution - baselineContribution;
double cellSize = cellSizeStats.getValues().length == 0 ? (currentValue + baselineValue) / 2 : cellSizeStats.getElement(i);
double deltaColor = percentageChange;
double deltaSize = cellSize;
double contributionColor = (currentContribution - baselineContribution);
double contributionSize = cellSize;
double contributionToOverallChange = ((currentValue - baselineValue) / baselineTotal) * 100;
double contributionToOverallColor = contributionToOverallChange;
double contributionToOverallSize = cellSize;
HeatMapCell cell = new HeatMapCell(dimensionValue, baselineValue, currentValue,
numeratorBaseline, denominatorBaseline, numeratorCurrent, denominatorCurrent,
baselineContribution, currentContribution, baselineCDFValue, currentCDFValue,
percentageChange, absoluteChange, contributionDifference, contributionToOverallChange,
deltaColor, deltaSize, contributionColor, contributionSize, contributionToOverallColor,
contributionToOverallSize, cellSizeExpression);
heatMapCells.add(cell);
}
return new HeatMap(dimensionName, heatMapCells);
}
private NormalDistribution getDistribution(DescriptiveStatistics stats) {
NormalDistribution dist = null;
try {
dist = new NormalDistribution(stats.getMean(), stats.getStandardDeviation());
} catch (Exception e) {
LOG.warn("Could not get statistics for dimension:{}, error message:{}", dimensionName,
e.getMessage());
}
return dist;
}
}
}