Might as well show all the FR outcomes now
This commit is contained in:
parent
3e6191af82
commit
875fa24359
|
@ -1,6 +1,6 @@
|
||||||
import { useMemo, useRef } from 'react'
|
import { useMemo, useRef } from 'react'
|
||||||
import { sum, sortBy, groupBy } from 'lodash'
|
import { sum, sortBy, groupBy } from 'lodash'
|
||||||
import { scaleTime, scaleLinear, schemeCategory10 } from 'd3'
|
import { scaleTime, scaleLinear } from 'd3'
|
||||||
|
|
||||||
import { Bet } from 'common/bet'
|
import { Bet } from 'common/bet'
|
||||||
import { FreeResponseContract, MultipleChoiceContract } from 'common/contract'
|
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 { MultiPoint, MultiValueHistoryChart } from '../generic-charts'
|
||||||
import { useElementWidth } from 'web/hooks/use-element-width'
|
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 = (
|
const getMultiChartData = (
|
||||||
contract: FreeResponseContract | MultipleChoiceContract,
|
contract: FreeResponseContract | MultipleChoiceContract,
|
||||||
bets: Bet[],
|
bets: Bet[],
|
||||||
|
@ -22,12 +80,8 @@ const getMultiChartData = (
|
||||||
const sortedBets = sortBy(bets, (b) => b.createdTime)
|
const sortedBets = sortBy(bets, (b) => b.createdTime)
|
||||||
const betsByOutcome = groupBy(sortedBets, (bet) => bet.outcome)
|
const betsByOutcome = groupBy(sortedBets, (bet) => bet.outcome)
|
||||||
const validAnswers = answers.filter((answer) => {
|
const validAnswers = answers.filter((answer) => {
|
||||||
const maxProb = Math.max(
|
|
||||||
...betsByOutcome[answer.id].map((bet) => bet.probAfter)
|
|
||||||
)
|
|
||||||
return (
|
return (
|
||||||
(answer.id !== '0' || outcomeType === 'MULTIPLE_CHOICE') &&
|
(answer.id !== '0' || outcomeType === 'MULTIPLE_CHOICE') &&
|
||||||
maxProb > 0.02 &&
|
|
||||||
totalBets[answer.id] > 0.000000001
|
totalBets[answer.id] > 0.000000001
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -82,7 +136,7 @@ export const ChoiceContractChart = (props: {
|
||||||
const { contract, bets } = props
|
const { contract, bets } = props
|
||||||
const [start, end] = useMemo(() => getDateRange(contract), [contract])
|
const [start, end] = useMemo(() => getDateRange(contract), [contract])
|
||||||
const data = useMemo(
|
const data = useMemo(
|
||||||
() => getMultiChartData(contract, bets, start, end, 6),
|
() => getMultiChartData(contract, bets, start, end, CATEGORY_COLORS.length),
|
||||||
[contract, bets, start, end]
|
[contract, bets, start, end]
|
||||||
)
|
)
|
||||||
const isMobile = useIsMobile(800)
|
const isMobile = useIsMobile(800)
|
||||||
|
@ -100,7 +154,7 @@ export const ChoiceContractChart = (props: {
|
||||||
xScale={xScale}
|
xScale={xScale}
|
||||||
yScale={yScale}
|
yScale={yScale}
|
||||||
data={data.points}
|
data={data.points}
|
||||||
colors={schemeCategory10}
|
colors={CATEGORY_COLORS}
|
||||||
labels={data.labels}
|
labels={data.labels}
|
||||||
pct
|
pct
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
ScaleContinuousNumeric,
|
ScaleContinuousNumeric,
|
||||||
SeriesPoint,
|
SeriesPoint,
|
||||||
} from 'd3'
|
} from 'd3'
|
||||||
import { range } from 'lodash'
|
import { range, sortBy } from 'lodash'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -203,7 +203,7 @@ export const MultiValueHistoryChart = (props: {
|
||||||
const py1 = useCallback((p: SP) => yScale(p[1]), [yScale])
|
const py1 = useCallback((p: SP) => yScale(p[1]), [yScale])
|
||||||
const xBisector = bisector((p: MultiPoint) => p[0])
|
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 [start, end] = xScale.domain()
|
||||||
const fmtX = getFormatterForDateRange(start, end)
|
const fmtX = getFormatterForDateRange(start, end)
|
||||||
const fmtY = (n: number) => (pct ? formatPct(n, 0) : formatLargeNumber(n))
|
const fmtY = (n: number) => (pct ? formatPct(n, 0) : formatLargeNumber(n))
|
||||||
|
@ -215,13 +215,16 @@ export const MultiValueHistoryChart = (props: {
|
||||||
.tickValues(tickValues)
|
.tickValues(tickValues)
|
||||||
.tickFormat(fmtY)
|
.tickFormat(fmtY)
|
||||||
|
|
||||||
|
return { fmtX, fmtY, xAxis, yAxis }
|
||||||
|
}, [h, pct, xScale, yScale])
|
||||||
|
|
||||||
|
const series = useMemo(() => {
|
||||||
const d3Stack = stack<MultiPoint, number>()
|
const d3Stack = stack<MultiPoint, number>()
|
||||||
.keys(range(0, labels.length))
|
.keys(range(0, labels.length))
|
||||||
.value(([_date, probs], o) => probs[o])
|
.value(([_date, probs], o) => probs[o])
|
||||||
.order(stackOrderReverse)
|
.order(stackOrderReverse)
|
||||||
const series = d3Stack(data)
|
return d3Stack(data)
|
||||||
return { fmtX, fmtY, xAxis, yAxis, series }
|
}, [data, labels.length])
|
||||||
}, [h, pct, xScale, yScale, data, labels.length])
|
|
||||||
|
|
||||||
const onSelect = useEvent((ev: D3BrushEvent<MultiPoint>) => {
|
const onSelect = useEvent((ev: D3BrushEvent<MultiPoint>) => {
|
||||||
if (ev.selection) {
|
if (ev.selection) {
|
||||||
|
@ -247,19 +250,23 @@ export const MultiValueHistoryChart = (props: {
|
||||||
setMouseState(undefined)
|
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 (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{mouseState && (
|
{mouseState && (
|
||||||
<ChartTooltip {...mouseState}>
|
<ChartTooltip {...mouseState}>
|
||||||
{fmtX(mouseState.p[0])}
|
{fmtX(mouseState.p[0])}
|
||||||
<Legend
|
<Legend className="text-sm" items={legendItems} />
|
||||||
className="text-sm"
|
|
||||||
items={mouseState.p[1].map((p, i) => ({
|
|
||||||
color: colors[i],
|
|
||||||
label: labels[i],
|
|
||||||
value: fmtY(p),
|
|
||||||
}))}
|
|
||||||
/>
|
|
||||||
</ChartTooltip>
|
</ChartTooltip>
|
||||||
)}
|
)}
|
||||||
<SVGChart
|
<SVGChart
|
||||||
|
|
Loading…
Reference in New Issue
Block a user