Skip to content

Commit

Permalink
convert funnel correlation details
Browse files Browse the repository at this point in the history
  • Loading branch information
thmsobrmlr committed Apr 5, 2023
1 parent 5e20263 commit cb7bde6
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 102 deletions.
85 changes: 85 additions & 0 deletions frontend/src/scenes/funnels/funnelCorrelationDetailsLogic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { expectLogic } from 'kea-test-utils'
import { initKeaTests } from '~/test/init'
import { FunnelCorrelationResultsType, FunnelCorrelationType, InsightLogicProps, InsightType } from '~/types'

import { funnelCorrelationDetailsLogic } from './funnelCorrelationDetailsLogic'

const funnelResults = [
{
action_id: '$pageview',
count: 19,
name: '$pageview',
order: 0,
type: 'events',
},
{
action_id: '$pageview',
count: 7,
name: '$pageview',
order: 1,
type: 'events',
},
{
action_id: '$pageview',
count: 4,
name: '$pageview',
order: 2,
type: 'events',
},
]

describe('funnelCorrelationDetailsLogic', () => {
let logic: ReturnType<typeof funnelCorrelationDetailsLogic.build>

beforeEach(() => {
initKeaTests(false)
})

const defaultProps: InsightLogicProps = {
dashboardItemId: undefined,
cachedInsight: {
short_id: undefined,
filters: {
insight: InsightType.FUNNELS,
actions: [
{ id: '$pageview', order: 0 },
{ id: '$pageview', order: 1 },
],
},
result: funnelResults,
},
}

beforeEach(async () => {
logic = funnelCorrelationDetailsLogic(defaultProps)
logic.mount()
})

describe('correlationMatrixAndScore', () => {
it('returns calculated values based on selected details', async () => {
await expectLogic(logic, () =>
logic.actions.setFunnelCorrelationDetails({
event: { event: 'some event', elements: [], properties: {} },
success_people_url: '',
failure_people_url: '',
success_count: 2,
failure_count: 4,
odds_ratio: 3,
correlation_type: FunnelCorrelationType.Success,
result_type: FunnelCorrelationResultsType.Events,
})
).toMatchValues({
correlationMatrixAndScore: {
correlationScore: expect.anything(),
correlationScoreStrength: 'weak',
truePositive: 2,
falsePositive: 2,
trueNegative: 11,
falseNegative: 4,
},
})

expect(logic.values.correlationMatrixAndScore.correlationScore).toBeCloseTo(0.204)
})
})
})
105 changes: 105 additions & 0 deletions frontend/src/scenes/funnels/funnelCorrelationDetailsLogic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { kea, props, key, path, connect, selectors, reducers, actions } from 'kea'
import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils'
import { FunnelCorrelation, InsightLogicProps } from '~/types'

import { insightLogic } from 'scenes/insights/insightLogic'
import { funnelLogic } from './funnelLogic'
import { funnelDataLogic } from './funnelDataLogic'

import type { funnelCorrelationDetailsLogicType } from './funnelCorrelationDetailsLogicType'

export const funnelCorrelationDetailsLogic = kea<funnelCorrelationDetailsLogicType>([
props({} as InsightLogicProps),
key(keyForInsightLogicProps('insight_funnel')),
path((key) => ['scenes', 'funnels', 'funnelCorrelationDetailsLogic', key]),
connect((props: InsightLogicProps) => ({
values: [
insightLogic(props),
['isUsingDataExploration'],
funnelLogic(props),
['steps as legacySteps'],
funnelDataLogic(props),
['steps as dataExplorationSteps'],
],
})),

actions({
setFunnelCorrelationDetails: (payload: FunnelCorrelation | null) => ({ payload }),
}),

reducers({
funnelCorrelationDetails: [
null as null | FunnelCorrelation,
{
setFunnelCorrelationDetails: (_, { payload }) => payload,
},
],
}),

selectors({
steps: [
(s) => [s.isUsingDataExploration, s.dataExplorationSteps, s.legacySteps],
(isUsingDataExploration, dataExplorationApiParams, legacyApiParams) => {
return isUsingDataExploration ? dataExplorationApiParams : legacyApiParams
},
],

correlationMatrixAndScore: [
(s) => [s.funnelCorrelationDetails, s.steps],
(
funnelCorrelationDetails,
steps
): {
truePositive: number
falsePositive: number
trueNegative: number
falseNegative: number
correlationScore: number
correlationScoreStrength: 'weak' | 'moderate' | 'strong' | null
} => {
if (!funnelCorrelationDetails) {
return {
truePositive: 0,
falsePositive: 0,
trueNegative: 0,
falseNegative: 0,
correlationScore: 0,
correlationScoreStrength: null,
}
}

const successTotal = steps[steps.length - 1].count
const failureTotal = steps[0].count - successTotal
const success = funnelCorrelationDetails.success_count
const failure = funnelCorrelationDetails.failure_count

const truePositive = success // has property, converted
const falseNegative = failure // has property, but dropped off
const trueNegative = failureTotal - failure // doesn't have property, dropped off
const falsePositive = successTotal - success // doesn't have property, converted

// Phi coefficient: https://en.wikipedia.org/wiki/Phi_coefficient
const correlationScore =
(truePositive * trueNegative - falsePositive * falseNegative) /
Math.sqrt(
(truePositive + falsePositive) *
(truePositive + falseNegative) *
(trueNegative + falsePositive) *
(trueNegative + falseNegative)
)

const correlationScoreStrength =
Math.abs(correlationScore) > 0.5 ? 'strong' : Math.abs(correlationScore) > 0.3 ? 'moderate' : 'weak'

return {
correlationScore,
truePositive,
falsePositive,
trueNegative,
falseNegative,
correlationScoreStrength,
}
},
],
}),
])
32 changes: 0 additions & 32 deletions frontend/src/scenes/funnels/funnelLogic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
AvailableFeature,
CorrelationConfigType,
FunnelCorrelationResultsType,
FunnelCorrelationType,
FunnelsFilterType,
FunnelVizType,
InsightLogicProps,
Expand Down Expand Up @@ -620,37 +619,6 @@ describe('funnelLogic', () => {
})
})

describe('funnel correlation matrix', () => {
beforeEach(async () => {
await initFunnelLogic()
})
it('Selecting a record returns appropriate values', async () => {
await expectLogic(logic, () =>
logic.actions.setFunnelCorrelationDetails({
event: { event: 'some event', elements: [], properties: {} },
success_people_url: '',
failure_people_url: '',
success_count: 2,
failure_count: 4,
odds_ratio: 3,
correlation_type: FunnelCorrelationType.Success,
result_type: FunnelCorrelationResultsType.Events,
})
).toMatchValues({
correlationMatrixAndScore: {
correlationScore: expect.anything(),
correlationScoreStrength: 'weak',
truePositive: 2,
falsePositive: 2,
trueNegative: 11,
falseNegative: 4,
},
})

expect(logic.values.correlationMatrixAndScore.correlationScore).toBeCloseTo(0.204)
})
})

describe('funnel correlation properties', () => {
const props = { dashboardItemId: Insight123, syncWithUrl: true }

Expand Down
64 changes: 0 additions & 64 deletions frontend/src/scenes/funnels/funnelLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ export const funnelLogic = kea<funnelLogicType>({
success,
}),
setPropertyCorrelationTypes: (types: FunnelCorrelationType[]) => ({ types }),
setFunnelCorrelationDetails: (payload: FunnelCorrelation | null) => ({ payload }),
setPropertyNames: (propertyNames: string[]) => ({ propertyNames }),
excludePropertyFromProject: (propertyName: string) => ({ propertyName }),
}),
Expand Down Expand Up @@ -264,12 +263,6 @@ export const funnelLogic = kea<funnelLogicType>({
},
},
],
funnelCorrelationDetails: [
null as null | FunnelCorrelation,
{
setFunnelCorrelationDetails: (_, { payload }) => payload,
},
],
isTooltipShown: [
false,
{
Expand Down Expand Up @@ -640,63 +633,6 @@ export const funnelLogic = kea<funnelLogicType>({
(s) => [s.filters, s.aggregationLabel],
(filters, aggregationLabel): Noun => aggregationLabel(filters.aggregation_group_type_index),
],
correlationMatrixAndScore: [
(s) => [s.funnelCorrelationDetails, s.steps],
(
funnelCorrelationDetails,
steps
): {
truePositive: number
falsePositive: number
trueNegative: number
falseNegative: number
correlationScore: number
correlationScoreStrength: 'weak' | 'moderate' | 'strong' | null
} => {
if (!funnelCorrelationDetails) {
return {
truePositive: 0,
falsePositive: 0,
trueNegative: 0,
falseNegative: 0,
correlationScore: 0,
correlationScoreStrength: null,
}
}

const successTotal = steps[steps.length - 1].count
const failureTotal = steps[0].count - successTotal
const success = funnelCorrelationDetails.success_count
const failure = funnelCorrelationDetails.failure_count

const truePositive = success // has property, converted
const falseNegative = failure // has property, but dropped off
const trueNegative = failureTotal - failure // doesn't have property, dropped off
const falsePositive = successTotal - success // doesn't have property, converted

// Phi coefficient: https://en.wikipedia.org/wiki/Phi_coefficient
const correlationScore =
(truePositive * trueNegative - falsePositive * falseNegative) /
Math.sqrt(
(truePositive + falsePositive) *
(truePositive + falseNegative) *
(trueNegative + falsePositive) *
(trueNegative + falseNegative)
)

const correlationScoreStrength =
Math.abs(correlationScore) > 0.5 ? 'strong' : Math.abs(correlationScore) > 0.3 ? 'moderate' : 'weak'

return {
correlationScore,
truePositive,
falsePositive,
trueNegative,
falseNegative,
correlationScoreStrength,
}
},
],
advancedOptionsUsedCount: [
(s) => [s.filters, s.stepReference],
(filters, stepReference): number => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import { Popover } from 'lib/lemon-ui/Popover/Popover'
import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton'
import { IconEllipsis } from 'lib/lemon-ui/icons'
import { funnelCorrelationLogic } from 'scenes/funnels/funnelCorrelationLogic'
import { funnelCorrelationDetailsLogic } from 'scenes/funnels/funnelCorrelationDetailsLogic'

export const EventCorrelationActionsCell = ({ record }: { record: FunnelCorrelation }): JSX.Element => {
const { insightProps } = useValues(insightLogic)
const { setFunnelCorrelationDetails } = useActions(funnelLogic(insightProps))
const { isEventExcluded, isEventPropertyExcluded } = useValues(funnelCorrelationLogic(insightProps))
const { excludeEventFromProject, excludeEventPropertyFromProject } = useActions(
funnelCorrelationLogic(insightProps)
)
const { setFunnelCorrelationDetails } = useActions(funnelCorrelationDetailsLogic(insightProps))
const components = record.event.event.split('::')

const buttons: LemonButtonProps[] = [
Expand Down Expand Up @@ -48,9 +49,9 @@ export const EventCorrelationActionsCell = ({ record }: { record: FunnelCorrelat

export const PropertyCorrelationActionsCell = ({ record }: { record: FunnelCorrelation }): JSX.Element => {
const { insightProps } = useValues(insightLogic)
const logic = funnelLogic(insightProps)
const { excludePropertyFromProject, setFunnelCorrelationDetails } = useActions(logic)
const { isPropertyExcludedFromProject } = useValues(logic)
const { excludePropertyFromProject } = useActions(funnelLogic(insightProps))
const { isPropertyExcludedFromProject } = useValues(funnelLogic(insightProps))
const { setFunnelCorrelationDetails } = useActions(funnelCorrelationDetailsLogic(insightProps))
const propertyName = (record.event.event || '').split('::')[0]

const buttons: LemonButtonProps[] = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@ import { AlertMessage } from 'lib/lemon-ui/AlertMessage'
import clsx from 'clsx'
import { parseDisplayNameForCorrelation } from 'scenes/funnels/funnelUtils'
import { funnelCorrelationLogic } from 'scenes/funnels/funnelCorrelationLogic'
import { funnelCorrelationDetailsLogic } from 'scenes/funnels/funnelCorrelationDetailsLogic'

export function CorrelationMatrix(): JSX.Element {
const { insightProps } = useValues(insightLogic)
const { funnelCorrelationDetails, correlationMatrixAndScore } = useValues(funnelLogic(insightProps))
const { setFunnelCorrelationDetails, openCorrelationPersonsModal } = useActions(funnelLogic(insightProps))
const { openCorrelationPersonsModal } = useActions(funnelLogic(insightProps))
const { correlationsLoading } = useValues(funnelCorrelationLogic(insightProps))
const { funnelCorrelationDetails, correlationMatrixAndScore } = useValues(
funnelCorrelationDetailsLogic(insightProps)
)
const { setFunnelCorrelationDetails } = useActions(funnelCorrelationDetailsLogic(insightProps))

const actor = funnelCorrelationDetails?.result_type === FunnelCorrelationResultsType.Events ? 'event' : 'property'
const action =
Expand Down

0 comments on commit cb7bde6

Please sign in to comment.