Skip to content

Commit

Permalink
Make sure non-normalized data integrate well with categorical probes (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Iinh authored Dec 6, 2023
1 parent fba4f81 commit 11f55c2
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 24 deletions.
20 changes: 18 additions & 2 deletions src/components/explore/AggregationsOverTimeGraph.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import {
getPercentileName,
getTransformedPercentileName,
getProportionName,
getCountName,
} from '../../config/shared';
export let title;
Expand Down Expand Up @@ -233,12 +235,26 @@
if ($store.activeBuckets.length === 10) return yDomain;
if ($store.proportionMetricType === 'proportions') {
buckets.forEach((bucket) => {
yData = yData.concat([...data.map((arr) => arr.proportions[bucket])]);
yData = yData.concat([
...data.map(
(arr) =>
arr[
getProportionName($store.productDimensions.normalizationType)
][bucket]
),
]);
});
}
if ($store.proportionMetricType === 'counts') {
buckets.forEach((bucket) => {
yData = yData.concat([...data.map((arr) => arr.counts[bucket])]);
yData = yData.concat([
...data.map(
(arr) =>
arr[getCountName($store.productDimensions.normalizationType)][
bucket
]
),
]);
});
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/explore/ProbeExplorer.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script>
import { isEmpty } from 'lodash';
import { writable } from 'svelte/store';
import { window1D } from '@graph-paper/core/utils/window-functions';
Expand Down Expand Up @@ -48,7 +49,7 @@
// data which caused the graph to break.
// so, we filter out these empty data points.
normType === 'non_normalized'
? normData.filter((d) => d.non_norm_histogram !== '')
? normData.filter((d) => !isEmpty(d.non_norm_histogram))
: normData;
let data = filterData(
Expand Down
57 changes: 41 additions & 16 deletions src/components/explore/ProportionExplorerView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@
gatherAggregationTypes,
} from '../../utils/probe-utils';
import { numericStringsSort } from '../../utils/sort';
import { getHistogramName, numHighlightedBuckets } from '../../config/shared';
import {
getHistogramName,
getProportionName,
getCountName,
numHighlightedBuckets,
} from '../../config/shared';
export let aggregationLevel = 'build_id';
export let data;
Expand All @@ -52,28 +57,27 @@
let currentKey = probeKeys[0];
let currentAggregation = aggregationTypes[0];
function filterResponseData(d, agg, key) {
return d.filter(
(di) => di.client_agg_type === agg && di.metric_key === key
);
}
$: selectedData = filterResponseData(data, currentAggregation, currentKey);
// set the audience size when the reference updates.
let ref;
const movingAudienceSize = tweened(0, { duration: 500, easing });
$: if (ref) movingAudienceSize.set(ref.audienceSize);
$: if (currentKey && ref) {
if (data[currentKey] !== undefined) {
const r = data[currentKey][currentAggregation].find(
if (selectedData[currentKey] !== undefined) {
const r = selectedData[currentKey][currentAggregation].find(
(d) => d.label.toString() === ref.label.toString()
);
ref = r;
}
}
function filterResponseData(d, agg, key) {
return d.filter(
(di) => di.client_agg_type === agg && di.metric_key === key
);
}
$: selectedData = filterResponseData(data, currentAggregation, currentKey);
let showOptionMenu = false;
let coloredBuckets = [];
let everActiveBuckets = [];
Expand All @@ -84,7 +88,9 @@
showOptionMenu = true;
const lastDataset = data[data.length - 1];
coloredBuckets = Object.entries(lastDataset.counts)
coloredBuckets = Object.entries(
lastDataset[getCountName($store.productDimensions.normalizationType)]
)
.sort(([bucketAName, bucketAValue], [bucketBName, bucketBValue]) => {
const bucketValueDifference = bucketAValue - bucketBValue;
if (bucketValueDifference === 0) {
Expand Down Expand Up @@ -154,7 +160,12 @@
{/if}
</div>

<CategoricalMenu {data} {activeBuckets} {bucketColorMap} {bucketOptions} />
<CategoricalMenu
data={selectedData}
{activeBuckets}
{bucketColorMap}
{bucketOptions}
/>
</div>

<div class="body-control-row body-control-row--stretch">
Expand Down Expand Up @@ -195,7 +206,9 @@
binColorMap={bucketColorMap}
showViolins={false}
{aggregationLevel}
pointMetricType={metricType}
pointMetricType={getProportionName(
$store.productDimensions.normalizationType
)}
{densityMetricType}
yTickFormatter={metricType === 'proportions'
? formatPercent
Expand All @@ -208,7 +221,19 @@
0,
Math.max(
...selectedData
.map((d) => Object.values(d[metricType]))
.map((d) =>
Object.values(
d[
metricType === 'proportions'
? getProportionName(
$store.productDimensions.normalizationType
)
: getCountName(
$store.productDimensions.normalizationType
)
]
)
)
.flat()
),
]}
Expand Down
4 changes: 2 additions & 2 deletions src/components/table/ProbeTableView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
gatherAggregationTypes,
} from '../../utils/probe-utils';
import { PERCENTILES } from '../../utils/constants';
import { getPercentileName } from '../../config/shared';
import { getProportionName, getPercentileName } from '../../config/shared';
import { store } from '../../state/store';
export let data;
Expand Down Expand Up @@ -84,7 +84,7 @@
? formatPercentDecimal
: formatCount}
key={probeType === 'categorical'
? 'proportions'
? getProportionName($store.productDimensions.normalizationType)
: getPercentileName($store.productDimensions.normalizationType)}
tooltipFormatter={probeType === 'categorical'
? () => undefined
Expand Down
4 changes: 2 additions & 2 deletions src/components/table/TableView.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script>
import { Button, ButtonGroup } from '@graph-paper/button';
import { isEmpty } from 'lodash';
import DataTable from './DataTable.svelte';
import Row from './Row.svelte';
import Cell from './Cell.svelte';
Expand Down Expand Up @@ -49,7 +49,7 @@
// empty non-normalized data
let filtered =
$store.productDimensions.normalizationType === 'non_normalized'
? data.filter((d) => d.non_norm_histogram !== '')
? data.filter((d) => !isEmpty(d.non_norm_histogram))
: data;
updatedData = filtered.map((d) => ({
...d,
Expand Down
11 changes: 11 additions & 0 deletions src/config/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ const dataNormalizationNameMap = {
non_normalized: 'non_norm_proportions',
normalized: 'proportions',
},
counts: {
non_normalized: 'non_norm_counts',
normalized: 'counts',
},
};

export const numHighlightedBuckets = 10;
Expand Down Expand Up @@ -63,6 +67,13 @@ export function getProportionName(type = 'proportions') {
return dataNormalizationNameMap.proportions[type];
}

export function getCountName(type = 'counts') {
if (!Object.hasOwn(dataNormalizationNameMap.counts, type)) {
throw new Error(`Unknown normalization type: ${type}`);
}
return dataNormalizationNameMap.counts[type];
}

export function extractBucketMetadata(transformedData) {
const etc = {};
const options = getBucketKeys(transformedData);
Expand Down
12 changes: 12 additions & 0 deletions src/utils/probe-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,15 @@ export function convertValueToPercentage(data) {
const sum = data.reduce((a, b) => a + b.value, 0);
return data.map((a) => ({ bin: a.bin, value: a.value / sum }));
}

export function convertValueToProportions(obj) {
const newObj = { ...obj };

// Calculate the total of all values
const total = Object.values(newObj).reduce((a, b) => a + b, 0);
// Convert each value to a proportion of the total
Object.keys(newObj).forEach((key) => {
newObj[key] /= total;
});
return newObj;
}
13 changes: 12 additions & 1 deletion src/utils/transform-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Some transform functions are checks that, if they fail, throw an error.
import produce from 'immer';
import { fullBuildIDToDate, buildDateStringToDate } from './build-id-utils';
import { nearestBelow } from './stats';
import { convertValueToProportions } from './probe-utils';

const errors = {
MISSING_PERCENTILES: {
Expand Down Expand Up @@ -92,7 +93,12 @@ export function checkForTotalUsers(draft) {
export function addProportion(draft) {
// requires point.histogram.
draft.proportions = { ...draft.histogram };
draft.non_norm_proportions = { ...draft.non_norm_histogram };

// draft.non_norm_histogram is not in proportion format
// like draft.histogram so we need to convert it here
draft.non_norm_proportions = convertValueToProportions(
draft.non_norm_histogram
);
}

export function changeBooleanHistogramResponse(draft) {
Expand All @@ -109,6 +115,11 @@ export function proportionsToCounts(draft) {
Object.keys(draft.proportions).forEach((p) => {
draft.counts[p] = draft.proportions[p] * draft.total_users;
});
draft.non_norm_counts = {};
Object.keys(draft.non_norm_proportions).forEach((p) => {
draft.non_norm_counts[p] =
draft.non_norm_proportions[p] * draft.total_users;
});
}

export function toAudienceSize(draft) {
Expand Down

0 comments on commit 11f55c2

Please sign in to comment.