Use objects instead of tuples for chart data
This commit is contained in:
parent
7f7e7acd61
commit
c7221d1026
|
@ -17,9 +17,10 @@ import { SingleValueHistoryChart } from '../generic-charts'
|
||||||
import { useElementWidth } from 'web/hooks/use-element-width'
|
import { useElementWidth } from 'web/hooks/use-element-width'
|
||||||
|
|
||||||
const getBetPoints = (bets: Bet[]) => {
|
const getBetPoints = (bets: Bet[]) => {
|
||||||
return sortBy(bets, (b) => b.createdTime).map(
|
return sortBy(bets, (b) => b.createdTime).map((b) => ({
|
||||||
(b) => [new Date(b.createdTime), b.probAfter] as const
|
x: new Date(b.createdTime),
|
||||||
)
|
y: b.probAfter,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BinaryContractChart = (props: {
|
export const BinaryContractChart = (props: {
|
||||||
|
@ -34,16 +35,16 @@ export const BinaryContractChart = (props: {
|
||||||
const betPoints = useMemo(() => getBetPoints(bets), [bets])
|
const betPoints = useMemo(() => getBetPoints(bets), [bets])
|
||||||
const data = useMemo(
|
const data = useMemo(
|
||||||
() => [
|
() => [
|
||||||
[startDate, startP] as const,
|
{ x: startDate, y: startP },
|
||||||
...betPoints,
|
...betPoints,
|
||||||
[endDate ?? MAX_DATE, endP] as const,
|
{ x: endDate ?? MAX_DATE, y: endP },
|
||||||
],
|
],
|
||||||
[startDate, startP, endDate, endP, betPoints]
|
[startDate, startP, endDate, endP, betPoints]
|
||||||
)
|
)
|
||||||
|
|
||||||
const rightmostDate = getRightmostVisibleDate(
|
const rightmostDate = getRightmostVisibleDate(
|
||||||
endDate,
|
endDate,
|
||||||
last(betPoints)?.[0],
|
last(betPoints)?.x,
|
||||||
new Date(Date.now())
|
new Date(Date.now())
|
||||||
)
|
)
|
||||||
const visibleRange = [startDate, rightmostDate]
|
const visibleRange = [startDate, rightmostDate]
|
||||||
|
|
|
@ -93,7 +93,7 @@ const getTrackedAnswers = (
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStartPoint = (answers: Answer[], start: Date) => {
|
const getStartPoint = (answers: Answer[], start: Date) => {
|
||||||
return [start, answers.map((_) => 0)] as const
|
return { x: start, y: answers.map((_) => 0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
const getEndPoint = (
|
const getEndPoint = (
|
||||||
|
@ -101,10 +101,10 @@ const getEndPoint = (
|
||||||
contract: FreeResponseContract | MultipleChoiceContract,
|
contract: FreeResponseContract | MultipleChoiceContract,
|
||||||
end: Date
|
end: Date
|
||||||
) => {
|
) => {
|
||||||
return [
|
return {
|
||||||
end,
|
x: end,
|
||||||
answers.map((a) => getOutcomeProbability(contract, a.id)),
|
y: answers.map((a) => getOutcomeProbability(contract, a.id)),
|
||||||
] as const
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getBetPoints = (answers: Answer[], bets: Bet[]) => {
|
const getBetPoints = (answers: Answer[], bets: Bet[]) => {
|
||||||
|
@ -121,10 +121,10 @@ const getBetPoints = (answers: Answer[], bets: Bet[]) => {
|
||||||
const sharesSquared = sum(
|
const sharesSquared = sum(
|
||||||
Object.values(sharesByOutcome).map((shares) => shares ** 2)
|
Object.values(sharesByOutcome).map((shares) => shares ** 2)
|
||||||
)
|
)
|
||||||
points.push([
|
points.push({
|
||||||
new Date(bet.createdTime),
|
x: new Date(bet.createdTime),
|
||||||
answers.map((answer) => sharesByOutcome[answer.id] ** 2 / sharesSquared),
|
y: answers.map((a) => sharesByOutcome[a.id] ** 2 / sharesSquared),
|
||||||
])
|
})
|
||||||
}
|
}
|
||||||
return points
|
return points
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ export const ChoiceContractChart = (props: {
|
||||||
)
|
)
|
||||||
const rightmostDate = getRightmostVisibleDate(
|
const rightmostDate = getRightmostVisibleDate(
|
||||||
contractEnd,
|
contractEnd,
|
||||||
last(betPoints)?.[0],
|
last(betPoints)?.x,
|
||||||
new Date(Date.now())
|
new Date(Date.now())
|
||||||
)
|
)
|
||||||
const visibleRange = [contractStart, rightmostDate]
|
const visibleRange = [contractStart, rightmostDate]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { useMemo, useRef } from 'react'
|
import { useMemo, useRef } from 'react'
|
||||||
import { max, range } from 'lodash'
|
import { range } from 'lodash'
|
||||||
import { scaleLinear } from 'd3-scale'
|
import { scaleLinear } from 'd3-scale'
|
||||||
|
|
||||||
import { getDpmOutcomeProbabilities } from 'common/calculate-dpm'
|
import { getDpmOutcomeProbabilities } from 'common/calculate-dpm'
|
||||||
|
@ -14,9 +14,10 @@ const getNumericChartData = (contract: NumericContract) => {
|
||||||
const { totalShares, bucketCount, min, max } = contract
|
const { totalShares, bucketCount, min, max } = contract
|
||||||
const step = (max - min) / bucketCount
|
const step = (max - min) / bucketCount
|
||||||
const bucketProbs = getDpmOutcomeProbabilities(totalShares)
|
const bucketProbs = getDpmOutcomeProbabilities(totalShares)
|
||||||
return range(bucketCount).map(
|
return range(bucketCount).map((i) => ({
|
||||||
(i) => [min + step * (i + 0.5), bucketProbs[`${i}`]] as const
|
x: min + step * (i + 0.5),
|
||||||
)
|
y: bucketProbs[`${i}`],
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NumericContractChart = (props: {
|
export const NumericContractChart = (props: {
|
||||||
|
@ -24,16 +25,14 @@ export const NumericContractChart = (props: {
|
||||||
height?: number
|
height?: number
|
||||||
}) => {
|
}) => {
|
||||||
const { contract } = props
|
const { contract } = props
|
||||||
|
const { min, max } = contract
|
||||||
const data = useMemo(() => getNumericChartData(contract), [contract])
|
const data = useMemo(() => getNumericChartData(contract), [contract])
|
||||||
const isMobile = useIsMobile(800)
|
const isMobile = useIsMobile(800)
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
const width = useElementWidth(containerRef) ?? 0
|
const width = useElementWidth(containerRef) ?? 0
|
||||||
const height = props.height ?? (isMobile ? 150 : 250)
|
const height = props.height ?? (isMobile ? 150 : 250)
|
||||||
const maxY = max(data.map((d) => d[1])) as number
|
const maxY = Math.max(...data.map((d) => d.y))
|
||||||
const xScale = scaleLinear(
|
const xScale = scaleLinear([min, max], [0, width - MARGIN_X])
|
||||||
[contract.min, contract.max],
|
|
||||||
[0, width - MARGIN_X]
|
|
||||||
)
|
|
||||||
const yScale = scaleLinear([0, maxY], [height - MARGIN_Y, 0])
|
const yScale = scaleLinear([0, maxY], [height - MARGIN_Y, 0])
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef}>
|
<div ref={containerRef}>
|
||||||
|
|
|
@ -29,9 +29,10 @@ const getScaleP = (min: number, max: number, isLogScale: boolean) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const getBetPoints = (bets: Bet[], scaleP: (p: number) => number) => {
|
const getBetPoints = (bets: Bet[], scaleP: (p: number) => number) => {
|
||||||
return sortBy(bets, (b) => b.createdTime).map(
|
return sortBy(bets, (b) => b.createdTime).map((b) => ({
|
||||||
(b) => [new Date(b.createdTime), scaleP(b.probAfter)] as const
|
x: new Date(b.createdTime),
|
||||||
)
|
y: scaleP(b.probAfter),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PseudoNumericContractChart = (props: {
|
export const PseudoNumericContractChart = (props: {
|
||||||
|
@ -51,15 +52,15 @@ export const PseudoNumericContractChart = (props: {
|
||||||
const betPoints = useMemo(() => getBetPoints(bets, scaleP), [bets, scaleP])
|
const betPoints = useMemo(() => getBetPoints(bets, scaleP), [bets, scaleP])
|
||||||
const data = useMemo(
|
const data = useMemo(
|
||||||
() => [
|
() => [
|
||||||
[startDate, startP] as const,
|
{ x: startDate, y: startP },
|
||||||
...betPoints,
|
...betPoints,
|
||||||
[endDate ?? MAX_DATE, endP] as const,
|
{ x: endDate ?? MAX_DATE, y: endP },
|
||||||
],
|
],
|
||||||
[betPoints, startDate, startP, endDate, endP]
|
[betPoints, startDate, startP, endDate, endP]
|
||||||
)
|
)
|
||||||
const rightmostDate = getRightmostVisibleDate(
|
const rightmostDate = getRightmostVisibleDate(
|
||||||
endDate,
|
endDate,
|
||||||
last(betPoints)?.[0],
|
last(betPoints)?.x,
|
||||||
new Date(Date.now())
|
new Date(Date.now())
|
||||||
)
|
)
|
||||||
const visibleRange = [startDate, rightmostDate]
|
const visibleRange = [startDate, rightmostDate]
|
||||||
|
|
|
@ -25,9 +25,9 @@ import { formatLargeNumber } from 'common/util/format'
|
||||||
import { useEvent } from 'web/hooks/use-event'
|
import { useEvent } from 'web/hooks/use-event'
|
||||||
import { Row } from 'web/components/layout/row'
|
import { Row } from 'web/components/layout/row'
|
||||||
|
|
||||||
export type MultiPoint = readonly [Date, number[]] // [time, [ordered outcome probs]]
|
export type MultiPoint = { x: Date; y: number[] }
|
||||||
export type HistoryPoint = readonly [Date, number] // [time, number or percentage]
|
export type HistoryPoint = { x: Date; y: number }
|
||||||
export type DistributionPoint = readonly [number, number] // [outcome amount, prob]
|
export type DistributionPoint = { x: number; y: number }
|
||||||
export type PositionValue<P> = TooltipPosition & { p: P }
|
export type PositionValue<P> = TooltipPosition & { p: P }
|
||||||
|
|
||||||
const formatPct = (n: number, digits?: number) => {
|
const formatPct = (n: number, digits?: number) => {
|
||||||
|
@ -118,10 +118,10 @@ export const SingleValueDistributionChart = (props: {
|
||||||
useState<PositionValue<DistributionPoint>>()
|
useState<PositionValue<DistributionPoint>>()
|
||||||
const xScale = viewXScale ?? props.xScale
|
const xScale = viewXScale ?? props.xScale
|
||||||
|
|
||||||
const px = useCallback((p: DistributionPoint) => xScale(p[0]), [xScale])
|
const px = useCallback((p: DistributionPoint) => xScale(p.x), [xScale])
|
||||||
const py0 = yScale(yScale.domain()[0])
|
const py0 = yScale(yScale.domain()[0])
|
||||||
const py1 = useCallback((p: DistributionPoint) => yScale(p[1]), [yScale])
|
const py1 = useCallback((p: DistributionPoint) => yScale(p.y), [yScale])
|
||||||
const xBisector = bisector((p: DistributionPoint) => p[0])
|
const xBisector = bisector((p: DistributionPoint) => p.x)
|
||||||
|
|
||||||
const { fmtX, fmtY, xAxis, yAxis } = useMemo(() => {
|
const { fmtX, fmtY, xAxis, yAxis } = useMemo(() => {
|
||||||
const fmtX = (n: number) => formatLargeNumber(n)
|
const fmtX = (n: number) => formatLargeNumber(n)
|
||||||
|
@ -154,8 +154,8 @@ export const SingleValueDistributionChart = (props: {
|
||||||
// so your queryX is out of bounds
|
// so your queryX is out of bounds
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const [_x, y] = item
|
const p = { x: queryX, y: item.y }
|
||||||
setMouseState({ top: mouseY - 10, left: mouseX + 60, p: [queryX, y] })
|
setMouseState({ top: mouseY - 10, left: mouseX + 60, p })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ export const SingleValueDistributionChart = (props: {
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{mouseState && (
|
{mouseState && (
|
||||||
<ChartTooltip className="text-sm" {...mouseState}>
|
<ChartTooltip className="text-sm" {...mouseState}>
|
||||||
<strong>{fmtY(mouseState.p[1])}</strong> {fmtX(mouseState.p[0])}
|
<strong>{fmtY(mouseState.p.y)}</strong> {fmtX(mouseState.p.x)}
|
||||||
</ChartTooltip>
|
</ChartTooltip>
|
||||||
)}
|
)}
|
||||||
<SVGChart
|
<SVGChart
|
||||||
|
@ -209,10 +209,10 @@ export const MultiValueHistoryChart = (props: {
|
||||||
const xScale = viewXScale ?? props.xScale
|
const xScale = viewXScale ?? props.xScale
|
||||||
|
|
||||||
type SP = SeriesPoint<MultiPoint>
|
type SP = SeriesPoint<MultiPoint>
|
||||||
const px = useCallback((p: SP) => xScale(p.data[0]), [xScale])
|
const px = useCallback((p: SP) => xScale(p.data.x), [xScale])
|
||||||
const py0 = useCallback((p: SP) => yScale(p[0]), [yScale])
|
const py0 = useCallback((p: SP) => yScale(p[0]), [yScale])
|
||||||
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.x)
|
||||||
|
|
||||||
const { fmtX, fmtY, xAxis, yAxis } = useMemo(() => {
|
const { fmtX, fmtY, xAxis, yAxis } = useMemo(() => {
|
||||||
const [start, end] = xScale.domain()
|
const [start, end] = xScale.domain()
|
||||||
|
@ -232,7 +232,7 @@ export const MultiValueHistoryChart = (props: {
|
||||||
const series = useMemo(() => {
|
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(({ y }, o) => y[o])
|
||||||
.order(stackOrderReverse)
|
.order(stackOrderReverse)
|
||||||
return d3Stack(data)
|
return d3Stack(data)
|
||||||
}, [data, labels.length])
|
}, [data, labels.length])
|
||||||
|
@ -260,8 +260,8 @@ export const MultiValueHistoryChart = (props: {
|
||||||
// so your queryX is out of bounds
|
// so your queryX is out of bounds
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const [_x, ys] = item
|
const p = { x: queryX, y: item.y }
|
||||||
setMouseState({ top: mouseY - 10, left: mouseX + 60, p: [queryX, ys] })
|
setMouseState({ top: mouseY - 10, left: mouseX + 60, p })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -269,7 +269,7 @@ export const MultiValueHistoryChart = (props: {
|
||||||
setMouseState(undefined)
|
setMouseState(undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
const mouseProbs = mouseState?.p[1] ?? []
|
const mouseProbs = mouseState?.p.y ?? []
|
||||||
const legendItems = sortBy(
|
const legendItems = sortBy(
|
||||||
mouseProbs.map((p, i) => ({
|
mouseProbs.map((p, i) => ({
|
||||||
color: colors[i],
|
color: colors[i],
|
||||||
|
@ -284,7 +284,7 @@ export const MultiValueHistoryChart = (props: {
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{mouseState && (
|
{mouseState && (
|
||||||
<ChartTooltip {...mouseState}>
|
<ChartTooltip {...mouseState}>
|
||||||
{fmtX(mouseState.p[0])}
|
{fmtX(mouseState.p.x)}
|
||||||
<Legend className="max-w-xs text-sm" items={legendItems} />
|
<Legend className="max-w-xs text-sm" items={legendItems} />
|
||||||
</ChartTooltip>
|
</ChartTooltip>
|
||||||
)}
|
)}
|
||||||
|
@ -328,10 +328,10 @@ export const SingleValueHistoryChart = (props: {
|
||||||
const [mouseState, setMouseState] = useState<PositionValue<HistoryPoint>>()
|
const [mouseState, setMouseState] = useState<PositionValue<HistoryPoint>>()
|
||||||
const xScale = viewXScale ?? props.xScale
|
const xScale = viewXScale ?? props.xScale
|
||||||
|
|
||||||
const px = useCallback((p: HistoryPoint) => xScale(p[0]), [xScale])
|
const px = useCallback((p: HistoryPoint) => xScale(p.x), [xScale])
|
||||||
const py0 = yScale(yScale.domain()[0])
|
const py0 = yScale(yScale.domain()[0])
|
||||||
const py1 = useCallback((p: HistoryPoint) => yScale(p[1]), [yScale])
|
const py1 = useCallback((p: HistoryPoint) => yScale(p.y), [yScale])
|
||||||
const xBisector = bisector((p: HistoryPoint) => p[0])
|
const xBisector = bisector((p: HistoryPoint) => p.x)
|
||||||
|
|
||||||
const { fmtX, fmtY, xAxis, yAxis } = useMemo(() => {
|
const { fmtX, fmtY, xAxis, yAxis } = useMemo(() => {
|
||||||
const [start, end] = xScale.domain()
|
const [start, end] = xScale.domain()
|
||||||
|
@ -370,8 +370,8 @@ export const SingleValueHistoryChart = (props: {
|
||||||
// so your queryX is out of bounds
|
// so your queryX is out of bounds
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const [_x, y] = item
|
const p = { x: queryX, y: item.y }
|
||||||
setMouseState({ top: mouseY - 10, left: mouseX + 60, p: [queryX, y] })
|
setMouseState({ top: mouseY - 10, left: mouseX + 60, p })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -383,7 +383,7 @@ export const SingleValueHistoryChart = (props: {
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{mouseState && (
|
{mouseState && (
|
||||||
<ChartTooltip className="text-sm" {...mouseState}>
|
<ChartTooltip className="text-sm" {...mouseState}>
|
||||||
<strong>{fmtY(mouseState.p[1])}</strong> {fmtX(mouseState.p[0])}
|
<strong>{fmtY(mouseState.p.y)}</strong> {fmtX(mouseState.p.x)}
|
||||||
</ChartTooltip>
|
</ChartTooltip>
|
||||||
)}
|
)}
|
||||||
<SVGChart
|
<SVGChart
|
||||||
|
|
Loading…
Reference in New Issue
Block a user