Skip to content

Commit

Permalink
feat(experiments): secondary metrics table (#21164)
Browse files Browse the repository at this point in the history
  • Loading branch information
jurajmajerik authored Apr 1, 2024
1 parent 92e17ce commit db30fef
Show file tree
Hide file tree
Showing 13 changed files with 422 additions and 266 deletions.
37 changes: 26 additions & 11 deletions frontend/src/scenes/experiments/ExperimentNext.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import './Experiment.scss'

import { LemonDivider } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'

import { ExperimentForm } from './ExperimentForm'
import { ExperimentImplementationDetails } from './ExperimentImplementationDetails'
import { experimentLogic } from './experimentLogic'
import { ExperimentLoader, ExperimentLoadingAnimation, PageHeaderCustom } from './ExperimentView/components'
import {
ExperimentLoader,
ExperimentLoadingAnimation,
NoResultsEmptyState,
PageHeaderCustom,
} from './ExperimentView/components'
import { DistributionTable } from './ExperimentView/DistributionTable'
import { ExperimentExposureModal, ExperimentGoalModal, Goal } from './ExperimentView/Goal'
import { Info } from './ExperimentView/Info'
import { NoResultsEmptyState } from './ExperimentView/NoResultsEmptyState'
import { Overview } from './ExperimentView/Overview'
import { ProgressBar } from './ExperimentView/ProgressBar'
import { ReleaseConditionsTable } from './ExperimentView/ReleaseConditionsTable'
Expand All @@ -35,16 +40,20 @@ export function ExperimentView(): JSX.Element {
<ExperimentLoadingAnimation />
) : experimentResults && experimentResults.insight ? (
<>
<Overview />
<ProgressBar />
<Goal />
<div>
<Overview />
<LemonDivider className="mt-4" />
</div>
<div className="xl:flex">
<div className="w-1/2 pr-2">
<Goal />
</div>

<div className="w-1/2 xl:pl-2 mt-8 xl:mt-0">
<ProgressBar />
</div>
</div>
<Results />
<SecondaryMetricsTable
experimentId={experiment.id}
onMetricsChange={(metrics) => updateExperimentSecondaryMetrics(metrics)}
initialMetrics={experiment.secondary_metrics}
defaultAggregationType={experiment.parameters?.aggregation_group_type_index}
/>
<ExperimentGoalModal experimentId={experimentId} />
<ExperimentExposureModal experimentId={experimentId} />
</>
Expand All @@ -55,6 +64,12 @@ export function ExperimentView(): JSX.Element {
{experiment.start_date && <NoResultsEmptyState />}
</>
)}
<SecondaryMetricsTable
experimentId={experiment.id}
onMetricsChange={(metrics) => updateExperimentSecondaryMetrics(metrics)}
initialMetrics={experiment.secondary_metrics}
defaultAggregationType={experiment.parameters?.aggregation_group_type_index}
/>
<DistributionTable />
<ReleaseConditionsTable />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import '../Experiment.scss'

import { LemonTable, LemonTableColumns, Link } from '@posthog/lemon-ui'
import { useValues } from 'kea'
import { getSeriesColor } from 'lib/colors'
import { capitalizeFirstLetter } from 'lib/utils'
import { urls } from 'scenes/urls'

import { MultivariateFlagVariant } from '~/types'

import { experimentLogic } from '../experimentLogic'
import { VariantTag } from './components'

export function DistributionTable(): JSX.Element {
const { experiment } = useValues(experimentLogic)
Expand All @@ -18,17 +17,8 @@ export function DistributionTable(): JSX.Element {
className: 'w-1/3',
key: 'key',
title: 'Variant',
render: function Key(_, item, index): JSX.Element {
return (
<div className="flex items-center">
<div
className="w-2 h-2 rounded-full mr-2"
// eslint-disable-next-line react/forbid-dom-props
style={{ backgroundColor: getSeriesColor(index + 1) }}
/>
<span className="font-semibold">{capitalizeFirstLetter(item.key)}</span>
</div>
)
render: function Key(_, item): JSX.Element {
return <VariantTag variantKey={item.key} />
},
},
{
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/scenes/experiments/ExperimentView/Goal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,8 @@ export function Goal(): JSX.Element {

return (
<div>
<h2 className="font-semibold text-lg mb-1">Experiment goal</h2>
<div>
<h2 className="font-semibold text-lg mb-0">Experiment goal</h2>
<div className="text-muted text-xs">
This <b>{experimentInsightType === InsightType.FUNNELS ? 'funnel' : 'trend'}</b>{' '}
{experimentInsightType === InsightType.FUNNELS
? 'experiment measures conversion through each step of the user journey.'
Expand Down

This file was deleted.

106 changes: 49 additions & 57 deletions frontend/src/scenes/experiments/ExperimentView/Overview.tsx
Original file line number Diff line number Diff line change
@@ -1,95 +1,87 @@
import '../Experiment.scss'

import { LemonDivider } from '@posthog/lemon-ui'
import { useValues } from 'kea'
import { getSeriesColor } from 'lib/colors'
import { capitalizeFirstLetter } from 'lib/utils'

import { InsightType } from '~/types'

import { experimentLogic } from '../experimentLogic'
import { VariantTag } from './components'

export function Overview(): JSX.Element {
const {
experimentResults,
getIndexForVariant,
experimentInsightType,
sortedConversionRates,
highestProbabilityVariant,
getHighestProbabilityVariant,
areResultsSignificant,
conversionRateForVariant,
} = useValues(experimentLogic)

function SignificanceText(): JSX.Element {
return (
<>
<span>Your results are&nbsp;</span>
<span className="font-semibold">{`${areResultsSignificant ? 'significant' : 'not significant'}`}.</span>
</>
)
}
function WinningVariantText(): JSX.Element {
const winningVariant = getHighestProbabilityVariant(experimentResults)

if (experimentInsightType === InsightType.FUNNELS) {
const winningVariant = sortedConversionRates[0]
const secondBestVariant = sortedConversionRates[1]
const difference = winningVariant.conversionRate - secondBestVariant.conversionRate
if (experimentInsightType === InsightType.FUNNELS) {
const winningConversionRate = conversionRateForVariant(experimentResults, winningVariant || '')
const controlConversionRate = conversionRateForVariant(experimentResults, 'control')
const difference = parseFloat(winningConversionRate) - parseFloat(controlConversionRate)

return (
<div>
<h2 className="font-semibold text-lg">Summary</h2>
if (difference === 0) {
return (
<span>
<b>No variant is winning</b> at this moment.&nbsp;
</span>
)
}

return (
<div className="items-center inline-flex flex-wrap">
<div
className="w-2 h-2 rounded-full mr-1"
// eslint-disable-next-line react/forbid-dom-props
style={{ backgroundColor: getSeriesColor(winningVariant.index + 1) }}
/>
<span className="font-semibold">{capitalizeFirstLetter(winningVariant.key)}</span>
<VariantTag variantKey={winningVariant || ''} />
<span>&nbsp;is winning with a conversion rate&nbsp;</span>
<span className="font-semibold text-success items-center">
increase of {`${difference.toFixed(2)}%`}
</span>
<span>&nbsp;percentage points (vs&nbsp;</span>
<div
className="w-2 h-2 rounded-full mr-1"
// eslint-disable-next-line react/forbid-dom-props
style={{
backgroundColor: getSeriesColor(secondBestVariant.index + 1),
}}
/>
<span className="font-semibold">{capitalizeFirstLetter(secondBestVariant.key)}</span>
<VariantTag variantKey="control" />
<span>).&nbsp;</span>
<SignificanceText />
</div>
</div>
)
}
)
}

const index = getIndexForVariant(experimentResults, highestProbabilityVariant || '')
if (highestProbabilityVariant && index !== null && experimentResults) {
const { probability } = experimentResults
const index = getIndexForVariant(experimentResults, winningVariant || '')
if (winningVariant && index !== null && experimentResults) {
const { probability } = experimentResults

return (
<div>
<h2 className="font-semibold text-lg">Overview</h2>
<LemonDivider />
<div className="items-center inline-flex">
<div
className="w-2 h-2 rounded-full mr-1"
// eslint-disable-next-line react/forbid-dom-props
style={{
backgroundColor: getSeriesColor(index + 2),
}}
/>
<span className="font-semibold">{capitalizeFirstLetter(highestProbabilityVariant)}</span>
return (
<div className="items-center inline-flex flex-wrap">
<VariantTag variantKey={winningVariant} />
<span>&nbsp;is winning with a&nbsp;</span>
<span className="font-semibold text-success items-center">
{`${(probability[highestProbabilityVariant] * 100).toFixed(2)}% probability`}&nbsp;
{`${(probability[winningVariant] * 100).toFixed(2)}% probability`}&nbsp;
</span>
<span>of being best.&nbsp;</span>
<SignificanceText />
</div>
)
}

return <></>
}

function SignificanceText(): JSX.Element {
return (
<div className="flex-wrap">
<span>Your results are&nbsp;</span>
<span className="font-semibold">{`${areResultsSignificant ? 'significant' : 'not significant'}`}.</span>
</div>
)
}

return <></>
return (
<div>
<h2 className="font-semibold text-lg">Summary</h2>
<div className="items-center inline-flex flex-wrap">
<WinningVariantText />
<SignificanceText />
</div>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ export function ProgressBar(): JSX.Element {

return (
<div>
<div className="mb-1 font-semibold">{`${
<h2 className="font-semibold text-lg mb-0">Data collection</h2>
<div className="text-muted text-xs">
Estimated target for the number of participants. Actual data may reveal significance earlier or later
than predicted.
</div>
<div className="mt-4 mb-1 font-semibold">{`${
experimentProgressPercent > 100 ? 100 : experimentProgressPercent.toFixed(2)
}% complete`}</div>
<LemonProgress
Expand Down
32 changes: 2 additions & 30 deletions frontend/src/scenes/experiments/ExperimentView/Results.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,8 @@ import '../Experiment.scss'

import { useValues } from 'kea'

import { filtersToQueryNode } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode'
import { Query } from '~/queries/Query/Query'
import { NodeKind } from '~/queries/schema'
import { InsightShortId } from '~/types'

import { experimentLogic } from '../experimentLogic'
import { transformResultFilters } from '../utils'
import { ResultsTag } from './components'
import { ResultsQuery, ResultsTag } from './components'
import { SummaryTable } from './SummaryTable'

export function Results(): JSX.Element {
Expand All @@ -22,29 +16,7 @@ export function Results(): JSX.Element {
<ResultsTag />
</div>
<SummaryTable />
<Query
query={{
kind: NodeKind.InsightVizNode,
source: filtersToQueryNode(transformResultFilters(experimentResults?.filters ?? {})),
showTable: true,
showLastComputation: true,
showLastComputationRefresh: false,
}}
context={{
insightProps: {
dashboardItemId: experimentResults?.fakeInsightId as InsightShortId,
cachedInsight: {
short_id: experimentResults?.fakeInsightId as InsightShortId,
filters: transformResultFilters(experimentResults?.filters ?? {}),
result: experimentResults?.insight,
disable_baseline: true,
last_refresh: experimentResults?.last_refresh,
},
doNotLoad: true,
},
}}
readOnly
/>
<ResultsQuery targetResults={experimentResults} showTable={true} />
</div>
)
}
Loading

0 comments on commit db30fef

Please sign in to comment.