Skip to content

Commit

Permalink
feat: add proper zero bucket
Browse files Browse the repository at this point in the history
Signed-off-by: Manik Rana <[email protected]>
  • Loading branch information
Maniktherana committed May 29, 2024
1 parent d9a66f3 commit 5d33e31
Showing 1 changed file with 79 additions and 40 deletions.
119 changes: 79 additions & 40 deletions web/ui/react-app/src/pages/graph/HistogramChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,52 +12,69 @@ type closestToZeroType = {

const HistogramChart: FC<{ histogram: Histogram; index: number; scale: ScaleType }> = ({ index, histogram, scale }) => {
const { buckets } = histogram;
if (!buckets) {
return <div>No data</div>;
}
const formatter = Intl.NumberFormat('en', { notation: 'compact' });

const rangeMax = buckets ? parseFloat(buckets[buckets.length - 1][2]) : 0;
const rangeMin = buckets ? parseFloat(buckets[0][1]) : 0;

const expBucketWidth = buckets
? Math.abs(
Math.log(Math.abs(parseFloat(buckets[buckets.length - 1][2]))) -
Math.log(Math.abs(parseFloat(buckets[buckets.length - 1][1])))
)
: 0; //bw
const rangeMax = parseFloat(buckets[buckets.length - 1][2]);
const rangeMin = parseFloat(buckets[0][1]);

const countMax = buckets ? buckets.map((b) => parseFloat(b[3])).reduce((a, b) => Math.max(a, b)) : 0;
const expBucketWidth = Math.abs(
Math.log(Math.abs(parseFloat(buckets[buckets.length - 1][2]))) -
Math.log(Math.abs(parseFloat(buckets[buckets.length - 1][1])))
); //bw
const countMax = buckets.map((b) => parseFloat(b[3])).reduce((a, b) => Math.max(a, b));

// The count of a histogram bucket is represented by its area rather than its height. This means it considers
// both the count and the width (range) of the bucket. For this, we can set the height of the bucket proportional
// to its frequency density (fd). The fd is the count of the bucket divided by the width of the bucket.

// Frequency density histograms are necessary when the bucekts are of unequal width. If the buckets are collected in
// intervals of equal width, then there is no difference between frequency and frequency density histograms.
const fds = buckets
? buckets.map((b) => {
return parseFloat(b[3]) / (parseFloat(b[2]) - parseFloat(b[1]));
})
: [];
const fds = buckets.map((b) => {
return parseFloat(b[3]) / (parseFloat(b[2]) - parseFloat(b[1]));
});
const fdMax = fds.reduce((a, b) => Math.max(a, b));
const closestToZero = buckets ? findClosestToZero(buckets.map((b) => parseFloat(b[1]))) : { closest: 0, closestIdx: 0 };

const zeroAxisLeft =
scale === 'linear'
? ((0 - rangeMin) / (rangeMax - rangeMin)) * 100 + '%'
: (closestToZero.closestIdx / (buckets ? buckets.length : 1)) * 100 + '%';
const closestToZero = findClosestToZero(buckets.map((b) => parseFloat(b[1])));
const { zeroBucket, zeroBucketIdx } = findZeroBucket(buckets);
console.log('ZERO BUCKET IS', zeroBucket);

const maxPositive = buckets ? parseFloat(buckets[buckets.length - 1][2]) : 0;
const minPositive = buckets ? parseFloat(buckets[closestToZero.closestIdx + 1][1]) : 0;
const maxNegative = buckets ? parseFloat(buckets[closestToZero.closestIdx - 1][2]) : 0;
const minNegative = buckets ? parseFloat(buckets[0][1]) : 0;
const startNegative = buckets ? -Math.log(Math.abs(minNegative)) : 0; //start_neg
const endNegative = buckets ? -Math.log(Math.abs(maxNegative)) : 0; //end_neg
const startPositive = buckets ? Math.log(minPositive) : 0; //start_pos
const endPositive = buckets ? Math.log(maxPositive) : 0; //end_pos
const maxPositive = parseFloat(buckets[buckets.length - 1][2]);
const minPositive =
zeroBucketIdx !== -1 ? parseFloat(buckets[zeroBucketIdx + 1][1]) : parseFloat(buckets[closestToZero.closestIdx + 1][1]);
const maxNegative =
zeroBucketIdx !== -1 ? parseFloat(buckets[zeroBucketIdx - 1][2]) : parseFloat(buckets[closestToZero.closestIdx][2]);
console.log('MAX NEGATIVE', maxNegative, 'MIN POSITIVE', minPositive, 'MAX POSITIVE', maxPositive);
const minNegative = parseFloat(buckets[0][1]);
const startNegative = -Math.log(Math.abs(minNegative)); //start_neg
const endNegative = -Math.log(Math.abs(maxNegative)); //end_neg
const startPositive = Math.log(minPositive); //start_pos
const endPositive = Math.log(maxPositive); //end_pos

const widthNegative = endNegative - startNegative; //width_neg
const widthPositive = endPositive - startPositive; //width_pos
const widthTotal = widthNegative + expBucketWidth + widthPositive; //width_total

const zeroAxisLeft =
scale === 'linear'
? ((0 - rangeMin) / (rangeMax - rangeMin)) * 100 + '%'
: ((widthNegative + 0.5 * expBucketWidth) / widthTotal) * 100 + '%';

function findZeroBucket(buckets: [number, string, string, string][]): {
zeroBucket: [number, string, string, string];
zeroBucketIdx: number;
} {
for (let i = 0; i < buckets.length; i++) {
const left = parseFloat(buckets[i][1]);
const right = parseFloat(buckets[i][2]);
if (left <= 0 && right >= 0) {
return { zeroBucket: buckets[i], zeroBucketIdx: i };
}
}
return { zeroBucket: [-1, '', '', ''], zeroBucketIdx: -1 };
}

function findClosestToZero(numbers: number[]): closestToZeroType {
let closest = numbers[0];
let closestIdx = 0;
Expand Down Expand Up @@ -182,20 +199,42 @@ const RenderHistogramBars: FC<RenderHistogramProps> = ({
const left = parseFloat(b[1]);
const right = parseFloat(b[2]);
const count = parseFloat(b[3]);
console.log('new stuff');

const bucketIdx = `bucket-${index}-${bIdx}-${Math.ceil(parseFloat(b[3]) * 100)}`;
const bucketWidth =
scale === 'linear' ? ((right - left) / (rangeMax - rangeMin)) * 100 + '%' : (bw / widthTotal) * 100 + '%';
const bucketLeft =
scale === 'linear'
? ((left - rangeMin) / (rangeMax - rangeMin)) * 100 + '%'
: left < 0
? (-(Math.log(Math.abs(left)) + startNegative) / widthTotal) * 100 + '%' // negative buckets boundary
: ((Math.log(left) - startPositive + bw + widthNegative) / widthTotal) * 100 + '%'; // positive buckets boundary
const bucketHeight = scale === 'linear' ? (fds[bIdx] / fdMax) * 100 + '%' : (count / countMax) * 100 + '%';

let bucketWidth = '';
let bucketLeft = '';
let bucketHeight = '';

switch (scale) {
case 'linear':
bucketWidth = ((right - left) / (rangeMax - rangeMin)) * 100 + '%';
bucketLeft = ((left - rangeMin) / (rangeMax - rangeMin)) * 100 + '%';
bucketHeight = (fds[bIdx] / fdMax) * 100 + '%';
break;
case 'exponential':
bucketWidth = (bw / widthTotal) * 100 + '%';
if (left < 0) {
// negative buckets boundary
bucketLeft = (-(Math.log(Math.abs(left)) + startNegative) / widthTotal) * 100 + '%';
} else {
// positive buckets boundary
bucketLeft = ((Math.log(left) - startPositive + bw + widthNegative) / widthTotal) * 100 + '%';
}
bucketHeight = (count / countMax) * 100 + '%';
break;
default:
console.error('Invalid scale type');
}

// zero bucket
if (left < 0 && right > 0) {
bucketLeft = (widthNegative / widthTotal) * 100 + '%';
}

console.log(
'ID',
bucketIdx,
'\n',
'left',
left,
'\n',
Expand Down

0 comments on commit 5d33e31

Please sign in to comment.