diff --git a/common/stats.ts b/common/stats.ts index 258eec7c..9221e10e 100644 --- a/common/stats.ts +++ b/common/stats.ts @@ -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[] diff --git a/functions/src/update-stats.ts b/functions/src/update-stats.ts index 046d0dc9..98b03931 100644 --- a/functions/src/update-stats.ts +++ b/functions/src/update-stats.ts @@ -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, diff --git a/web/components/analytics/charts.tsx b/web/components/analytics/charts.tsx index e078ce21..131ce2a0 100644 --- a/web/components/analytics/charts.tsx +++ b/web/components/analytics/charts.tsx @@ -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]) => ({ - x: date, - y: 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 }) { }} > {point.serieId}{' '} - {isPercent ? formatPercent(+point.data.y) : point.data.yFormatted} + {isPercent ? formatPercent(+point.data.y) : Math.round(+point.data.y)}
- 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?
-- 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?
- What fraction of active users are still active after the given time - period? -
-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} /> ), },