Might as well show all the FR outcomes now

This commit is contained in:
Marshall Polaris 2022-09-26 23:51:24 -07:00
parent 3e6191af82
commit 875fa24359
2 changed files with 81 additions and 20 deletions

View File

@ -1,6 +1,6 @@
import { useMemo, useRef } from 'react'
import { sum, sortBy, groupBy } from 'lodash'
import { scaleTime, scaleLinear, schemeCategory10 } from 'd3'
import { scaleTime, scaleLinear } from 'd3'
import { Bet } from 'common/bet'
import { FreeResponseContract, MultipleChoiceContract } from 'common/contract'
@ -10,6 +10,64 @@ import { MARGIN_X, MARGIN_Y, getDateRange } from '../helpers'
import { MultiPoint, MultiValueHistoryChart } from '../generic-charts'
import { useElementWidth } from 'web/hooks/use-element-width'
// thanks to https://observablehq.com/@jonhelfman/optimal-orders-for-choosing-categorical-colors
const CATEGORY_COLORS = [
'#00b8dd',
'#eecafe',
'#874c62',
'#6457ca',
'#f773ba',
'#9c6bbc',
'#a87744',
'#af8a04',
'#bff9aa',
'#f3d89d',
'#c9a0f5',
'#ff00e5',
'#9dc6f7',
'#824475',
'#d973cc',
'#bc6808',
'#056e70',
'#677932',
'#00b287',
'#c8ab6c',
'#a2fb7a',
'#f8db68',
'#14675a',
'#8288f4',
'#fe1ca0',
'#ad6aff',
'#786306',
'#9bfbaf',
'#b00cf7',
'#2f7ec5',
'#4b998b',
'#42fa0e',
'#5b80a1',
'#962d9d',
'#3385ff',
'#48c5ab',
'#b2c873',
'#4cf9a4',
'#00ffff',
'#3cca73',
'#99ae17',
'#7af5cf',
'#52af45',
'#fbb80f',
'#29971b',
'#187c9a',
'#00d539',
'#bbfa1a',
'#61f55c',
'#cabc03',
'#ff9000',
'#779100',
'#bcfd6f',
'#70a560',
]
const getMultiChartData = (
contract: FreeResponseContract | MultipleChoiceContract,
bets: Bet[],
@ -22,12 +80,8 @@ const getMultiChartData = (
const sortedBets = sortBy(bets, (b) => b.createdTime)
const betsByOutcome = groupBy(sortedBets, (bet) => bet.outcome)
const validAnswers = answers.filter((answer) => {
const maxProb = Math.max(
...betsByOutcome[answer.id].map((bet) => bet.probAfter)
)
return (
(answer.id !== '0' || outcomeType === 'MULTIPLE_CHOICE') &&
maxProb > 0.02 &&
totalBets[answer.id] > 0.000000001
)
})
@ -82,7 +136,7 @@ export const ChoiceContractChart = (props: {
const { contract, bets } = props
const [start, end] = useMemo(() => getDateRange(contract), [contract])
const data = useMemo(
() => getMultiChartData(contract, bets, start, end, 6),
() => getMultiChartData(contract, bets, start, end, CATEGORY_COLORS.length),
[contract, bets, start, end]
)
const isMobile = useIsMobile(800)
@ -100,7 +154,7 @@ export const ChoiceContractChart = (props: {
xScale={xScale}
yScale={yScale}
data={data.points}
colors={schemeCategory10}
colors={CATEGORY_COLORS}
labels={data.labels}
pct
/>

View File

@ -13,7 +13,7 @@ import {
ScaleContinuousNumeric,
SeriesPoint,
} from 'd3'
import { range } from 'lodash'
import { range, sortBy } from 'lodash'
import dayjs from 'dayjs'
import {
@ -203,7 +203,7 @@ export const MultiValueHistoryChart = (props: {
const py1 = useCallback((p: SP) => yScale(p[1]), [yScale])
const xBisector = bisector((p: MultiPoint) => p[0])
const { fmtX, fmtY, xAxis, yAxis, series } = useMemo(() => {
const { fmtX, fmtY, xAxis, yAxis } = useMemo(() => {
const [start, end] = xScale.domain()
const fmtX = getFormatterForDateRange(start, end)
const fmtY = (n: number) => (pct ? formatPct(n, 0) : formatLargeNumber(n))
@ -215,13 +215,16 @@ export const MultiValueHistoryChart = (props: {
.tickValues(tickValues)
.tickFormat(fmtY)
return { fmtX, fmtY, xAxis, yAxis }
}, [h, pct, xScale, yScale])
const series = useMemo(() => {
const d3Stack = stack<MultiPoint, number>()
.keys(range(0, labels.length))
.value(([_date, probs], o) => probs[o])
.order(stackOrderReverse)
const series = d3Stack(data)
return { fmtX, fmtY, xAxis, yAxis, series }
}, [h, pct, xScale, yScale, data, labels.length])
return d3Stack(data)
}, [data, labels.length])
const onSelect = useEvent((ev: D3BrushEvent<MultiPoint>) => {
if (ev.selection) {
@ -247,19 +250,23 @@ export const MultiValueHistoryChart = (props: {
setMouseState(undefined)
})
const mouseProbs = mouseState?.p[1] ?? []
const legendItems = sortBy(
mouseProbs.map((p, i) => ({
color: colors[i],
label: labels[i],
value: fmtY(p),
p,
})),
(item) => -item.p
).slice(0, 10)
return (
<div className="relative">
{mouseState && (
<ChartTooltip {...mouseState}>
{fmtX(mouseState.p[0])}
<Legend
className="text-sm"
items={mouseState.p[1].map((p, i) => ({
color: colors[i],
label: labels[i],
value: fmtY(p),
}))}
/>
<Legend className="text-sm" items={legendItems} />
</ChartTooltip>
)}
<SVGChart