Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(experiments): secondary metrics table #21164

Merged
merged 13 commits into from
Apr 1, 2024
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
Loading