Add more stats. Fix timezone. Group retention and new user retention

This commit is contained in:
James Grugett 2022-09-19 18:41:24 -05:00
parent d0973de2b4
commit 4dc3eada1f
4 changed files with 126 additions and 65 deletions

View File

@ -1,11 +1,14 @@
export type Stats = {
startDate: number
dailyActiveUsers: number[]
dailyActiveUsersWeeklyAvg: number[]
weeklyActiveUsers: number[]
monthlyActiveUsers: number[]
d1: number[]
d1Weekly: number[]
w1NewUsers: number[]
d1WeeklyAvg: number[]
nd1: number[]
nd1WeeklyAvg: number[]
nw1: number[]
dailyBetCounts: number[]
dailyContractCounts: number[]
dailyCommentCounts: number[]

View File

@ -12,6 +12,7 @@ import { Bet } from '../../common/bet'
import { Contract } from '../../common/contract'
import { Comment } from '../../common/comment'
import { User } from '../../common/user'
import { Stats } from '../../common/stats'
import { DAY_MS } from '../../common/util/time'
import { average } from '../../common/util/math'
@ -109,7 +110,7 @@ export async function getDailyNewUsers(
}
export const updateStatsCore = async () => {
const today = dayjs().tz('America/Pacific').startOf('day').valueOf()
const today = dayjs().tz('America/Los_Angeles').startOf('day').valueOf()
const startDate = today - numberOfDays * DAY_MS
log('Fetching data for stats update...')
@ -145,6 +146,11 @@ export const updateStatsCore = async () => {
)
const dailyActiveUsers = dailyUserIds.map((userIds) => userIds.length)
const dailyActiveUsersWeeklyAvg = dailyUserIds.map((_, i) => {
const start = Math.max(0, i - 6)
const end = i + 1
return average(dailyActiveUsers.slice(start, end))
})
const weeklyActiveUsers = dailyUserIds.map((_, i) => {
const start = Math.max(0, i - 6)
@ -172,14 +178,31 @@ export const updateStatsCore = async () => {
return retainedCount / uniques.size
})
const d1Weekly = d1.map((_, i) => {
const d1WeeklyAvg = d1.map((_, i) => {
const start = Math.max(0, i - 6)
const end = i + 1
return average(d1.slice(start, end))
})
const dailyNewUserIds = dailyNewUsers.map((users) => users.map((u) => u.id))
const w1NewUsers = dailyNewUserIds.map((_userIds, i) => {
const nd1 = dailyUserIds.map((userIds, i) => {
if (i === 0) return 0
const uniques = new Set(userIds)
const yesterday = dailyNewUserIds[i - 1]
const retainedCount = sumBy(yesterday, (userId) =>
uniques.has(userId) ? 1 : 0
)
return retainedCount / uniques.size
})
const nd1WeeklyAvg = nd1.map((_, i) => {
const start = Math.max(0, i - 6)
const end = i + 1
return average(nd1.slice(start, end))
})
const nw1 = dailyNewUserIds.map((_userIds, i) => {
if (i < 13) return 0
const twoWeeksAgo = {
@ -243,6 +266,7 @@ export const updateStatsCore = async () => {
const retainedCount = sumBy(Array.from(activeTwoMonthsAgo), (userId) =>
activeLastMonth.has(userId) ? 1 : 0
)
if (activeTwoMonthsAgo.size === 0) return 0
return retainedCount / activeTwoMonthsAgo.size
})
@ -320,14 +344,17 @@ export const updateStatsCore = async () => {
return total
})
const statsData = {
const statsData: Stats = {
startDate: startDate.valueOf(),
dailyActiveUsers,
dailyActiveUsersWeeklyAvg,
weeklyActiveUsers,
monthlyActiveUsers,
d1,
d1Weekly,
w1NewUsers,
d1WeeklyAvg,
nd1,
nd1WeeklyAvg,
nw1,
dailyBetCounts,
dailyContractCounts,
dailyCommentCounts,

View File

@ -64,18 +64,21 @@ export function DailyPercentChart(props: {
startDate: number
dailyPercent: number[]
small?: boolean
excludeFirstDays?: number
}) {
const { dailyPercent, startDate, small } = props
const { dailyPercent, startDate, small, excludeFirstDays } = props
const { width } = useWindowSize()
const dates = dailyPercent.map((_, i) =>
dayjs(startDate).add(i, 'day').toDate()
)
const points = zip(dates, dailyPercent).map(([date, percent]) => ({
const points = zip(dates, dailyPercent)
.map(([date, percent]) => ({
x: date,
y: percent,
}))
.slice(excludeFirstDays ?? 0)
const data = [{ id: 'Percent', data: points, color: '#11b981' }]
const bottomAxisTicks = width && width < 600 ? 6 : undefined
@ -128,7 +131,7 @@ function Tooltip(props: { point: Point; isPercent?: boolean }) {
}}
>
<strong>{point.serieId}</strong>{' '}
{isPercent ? formatPercent(+point.data.y) : point.data.yFormatted}
{isPercent ? formatPercent(+point.data.y) : Math.round(+point.data.y)}
</div>
<div>{dayjs(point.data.x).format('MMM DD')}</div>
</Col>

View File

@ -49,16 +49,19 @@ export default function Analytics() {
export function CustomAnalytics(props: Stats) {
const {
startDate,
d1,
d1Weekly,
w1NewUsers,
dailyActiveUsers,
dailyActiveUsersWeeklyAvg,
weeklyActiveUsers,
monthlyActiveUsers,
d1,
d1WeeklyAvg,
nd1,
nd1WeeklyAvg,
nw1,
dailyBetCounts,
dailyContractCounts,
dailyCommentCounts,
dailySignups,
weeklyActiveUsers,
monthlyActiveUsers,
weekOnWeekRetention,
monthlyRetention,
weeklyActivationRate,
@ -108,6 +111,16 @@ export function CustomAnalytics(props: Stats) {
/>
),
},
{
title: 'Daily (7d avg)',
content: (
<DailyCountChart
dailyCounts={dailyActiveUsersWeeklyAvg}
startDate={startDate}
small
/>
),
},
{
title: 'Weekly',
content: (
@ -132,13 +145,11 @@ export function CustomAnalytics(props: Stats) {
/>
<Spacer h={8} />
<Title text="D1" />
<Title text="Retention" />
<p className="text-gray-500">
The fraction of users that took an action yesterday that took an action
today.
What fraction of active users are still active after the given time
period?
</p>
<Spacer h={4} />
<Tabs
defaultIndex={1}
tabs={[
@ -149,39 +160,85 @@ export function CustomAnalytics(props: Stats) {
dailyPercent={d1}
startDate={startDate}
small
excludeFirstDays={1}
/>
),
},
{
title: 'D1 weekly average',
title: 'D1 (7d avg)',
content: (
<DailyPercentChart
dailyPercent={d1Weekly}
dailyPercent={d1WeeklyAvg}
startDate={startDate}
small
excludeFirstDays={7}
/>
),
},
{
title: 'W1',
content: (
<DailyPercentChart
dailyPercent={weekOnWeekRetention}
startDate={oneWeekLaterDate}
small
excludeFirstDays={14}
/>
),
},
{
title: 'M1',
content: (
<DailyPercentChart
dailyPercent={monthlyRetention}
startDate={oneWeekLaterDate}
small
excludeFirstDays={60}
/>
),
},
]}
/>
<Spacer h={8} />
<Title text="W1 New users" />
<Spacer h={8} />
<Title text="New user retention" />
<p className="text-gray-500">
The fraction of new users two weeks ago that took an action in the past
week.
What fraction of new users are still active after the given time period?
</p>
<Spacer h={4} />
<Tabs
defaultIndex={0}
defaultIndex={2}
tabs={[
{
title: 'W1',
title: 'ND1',
content: (
<DailyPercentChart
dailyPercent={w1NewUsers}
dailyPercent={nd1}
startDate={startDate}
excludeFirstDays={1}
small
/>
),
},
{
title: 'ND1 (7d avg)',
content: (
<DailyPercentChart
dailyPercent={nd1WeeklyAvg}
startDate={startDate}
excludeFirstDays={7}
small
/>
),
},
{
title: 'NW1',
content: (
<DailyPercentChart
dailyPercent={nw1}
startDate={startDate}
excludeFirstDays={14}
small
/>
),
@ -239,38 +296,6 @@ export function CustomAnalytics(props: Stats) {
<Spacer h={8} />
<Title text="Retention" />
<p className="text-gray-500">
What fraction of active users are still active after the given time
period?
</p>
<Tabs
defaultIndex={0}
tabs={[
{
title: 'Weekly',
content: (
<DailyPercentChart
dailyPercent={weekOnWeekRetention.slice(7)}
startDate={oneWeekLaterDate}
small
/>
),
},
{
title: 'Monthly',
content: (
<DailyPercentChart
dailyPercent={monthlyRetention.slice(7)}
startDate={oneWeekLaterDate}
small
/>
),
},
]}
/>
<Spacer h={8} />
<Title text="Weekly activation rate" />
<p className="text-gray-500">
Out of all new users this week, how many placed at least one bet?
@ -293,6 +318,7 @@ export function CustomAnalytics(props: Stats) {
dailyPercent={dailyDividedByWeekly}
startDate={oneWeekLaterDate}
small
excludeFirstDays={7}
/>
),
},
@ -303,6 +329,7 @@ export function CustomAnalytics(props: Stats) {
dailyPercent={dailyDividedByMonthly}
startDate={oneWeekLaterDate}
small
excludeFirstDays={30}
/>
),
},
@ -313,6 +340,7 @@ export function CustomAnalytics(props: Stats) {
dailyPercent={weeklyDividedByMonthly}
startDate={oneWeekLaterDate}
small
excludeFirstDays={30}
/>
),
},