Fix Boxplot labels, spacing + remove hardcoded values
continuous-integration/drone/push Build is passing Details

This commit is contained in:
e26chiu 2023-02-12 14:56:48 -05:00
parent 9170034b21
commit e7d6d682f3
2 changed files with 119 additions and 130 deletions

View File

@ -47,16 +47,10 @@ export type StatsPlotProps = {
strokeDashArray?: string; strokeDashArray?: string;
/** Number of ticks for the value (y-)axis */ /** Number of ticks for the value (y-)axis */
numTicksLeftAxis?: number; numTicksLeftAxis?: number;
/** Distance between the boxplot and the top of the grid, in px. */ /** Left margin before the start of the graph for the left axis labels, in px. */
plotTopOffset?: number; valueAxisLeftMargin?: number;
/** Distance between the left axis labels and the start of the lines of the graph, in px. */ /** Distance between the left axis labels and the start of the graph */
valueAxisLeftOffset?: number; valueAxisLabelOffset?: number;
/** Distance between the top and the first label of the y axis, in px. */
valueAxisLabelTopOffset?: number;
/** Distance between the left and the labels of the y axis, in px. */
valueAxisLabelLeftOffset?: number;
/** Distance between the left and the start of the first label of the x axis, in px. */
categoryAxisLabelLeftOffset?: number;
/** Distance between the top and the column lines of the grid of the graph, in px. */ /** Distance between the top and the column lines of the grid of the graph, in px. */
gridColumnTopOffset?: number; gridColumnTopOffset?: number;
/** Distance between the top of the point in the boxplot and the start of the tooltip box, in px. */ /** Distance between the top of the point in the boxplot and the start of the tooltip box, in px. */
@ -73,6 +67,8 @@ export type StatsPlotProps = {
boxPlotWidthFactor?: number; boxPlotWidthFactor?: number;
/** Factor multiplied with the compressed width to determine the distance between boxes, in px. */ /** Factor multiplied with the compressed width to determine the distance between boxes, in px. */
boxPlotLeftOffset?: number; boxPlotLeftOffset?: number;
/** Boxplot padding */
boxPlotPadding?: number;
}; };
export const BoxPlot = withTooltip<StatsPlotProps, TooltipData>( export const BoxPlot = withTooltip<StatsPlotProps, TooltipData>(
@ -90,18 +86,16 @@ export const BoxPlot = withTooltip<StatsPlotProps, TooltipData>(
strokeWidth = 2.5, strokeWidth = 2.5,
strokeDashArray = "10,4", strokeDashArray = "10,4",
numTicksLeftAxis = 6, numTicksLeftAxis = 6,
plotTopOffset = 10, valueAxisLeftMargin = 40,
valueAxisLeftOffset = 40,
gridColumnTopOffset = -20, gridColumnTopOffset = -20,
valueAxisLabelTopOffset = 5, valueAxisLabelOffset = -10,
valueAxisLabelLeftOffset = 10,
categoryAxisLabelLeftOffset = 30,
toolTipTopOffset = 0, toolTipTopOffset = 0,
toolTipLeftOffset = 0, toolTipLeftOffset = 0,
categoryAxisLabelSize = DEFAULT_LABEL_SIZE, categoryAxisLabelSize = DEFAULT_LABEL_SIZE,
valueAxisLabelSize = DEFAULT_LABEL_SIZE, valueAxisLabelSize = DEFAULT_LABEL_SIZE,
boxPlotWidthFactor = 0.4, boxPlotWidthFactor = 0.4,
boxPlotLeftOffset = 0.3, boxPlotLeftOffset = 0.3,
boxPlotPadding = 0.3,
}: StatsPlotProps & WithTooltipProvidedProps<TooltipData>) => { }: StatsPlotProps & WithTooltipProvidedProps<TooltipData>) => {
// bounds // bounds
const xMax = width; const xMax = width;
@ -128,23 +122,22 @@ export const BoxPlot = withTooltip<StatsPlotProps, TooltipData>(
// scales // scales
const xScale = scaleBand<string>({ const xScale = scaleBand<string>({
range: [18, xMax - 80], // scaling is needed due to the left offset range: [margin.left, xMax - 2 * margin.left - valueAxisLeftMargin], // scaling is needed due to left margin
round: true, round: true,
domain: plotData.map(getX), domain: plotData.map(getX),
padding: 0.3, padding: boxPlotPadding,
}); });
const values = plotData.reduce((allValues, { boxPlot }) => { const values = plotData.reduce((allValues, { boxPlot }) => {
allValues.push(boxPlot.min, boxPlot.max); allValues.push(boxPlot.min, boxPlot.max);
return allValues; return allValues;
}, [] as number[]); }, [] as number[]);
const minYValue = Math.min(...values);
const maxYValue = Math.max(...values); const maxYValue = Math.max(...values);
const yScale = scaleLinear<number>({ const yScale = scaleLinear<number>({
range: [yMax, 0], range: [yMax - margin.top, 0],
round: true, round: true,
domain: [minYValue, maxYValue], domain: [0, maxYValue],
}); });
const constrainedWidth = Math.min(200, xScale.bandwidth()); const constrainedWidth = Math.min(200, xScale.bandwidth());
@ -168,35 +161,65 @@ export const BoxPlot = withTooltip<StatsPlotProps, TooltipData>(
<div> <div>
<svg width={width} height={height}> <svg width={width} height={height}>
<Group top={margin.top} left={margin.left}> <Group top={margin.top} left={margin.left}>
<GridRows <Group left={valueAxisLeftMargin}>
top={plotTopOffset} <GridRows
left={valueAxisLeftOffset} scale={yScale}
scale={yScale} width={xMax}
width={xMax} numTicks={numTicksLeftAxis}
numTicks={numTicksLeftAxis} stroke={Color.tertiaryBackground}
stroke={Color.tertiaryBackground} strokeWidth={strokeWidth}
strokeWidth={strokeWidth} strokeDasharray={strokeDashArray}
strokeDasharray={strokeDashArray} />
/> <GridColumns
<GridColumns scale={xScale}
scale={xScale} height={yMax - gridColumnTopOffset}
height={yMax + plotTopOffset - gridColumnTopOffset} top={gridColumnTopOffset}
top={gridColumnTopOffset} stroke={Color.tertiaryBackground}
left={valueAxisLeftOffset} strokeWidth={strokeWidth}
stroke={Color.tertiaryBackground} strokeDasharray={strokeDashArray}
strokeWidth={strokeWidth} />
strokeDasharray={strokeDashArray} <AxisBottom
/> top={yMax - gridColumnTopOffset}
<Line scale={xScale}
fill={Color.tertiaryBackground} hideAxisLine
to={new Point({ x: valueAxisLeftOffset, y: gridColumnTopOffset })} hideTicks
from={ labelProps={{
new Point({ x: valueAxisLeftOffset, y: yMax + plotTopOffset }) fontSize: `${categoryAxisLabelSize / 16}rem`,
} }}
stroke={Color.tertiaryBackground} tickLabelProps={() => {
strokeWidth={strokeWidth} return {
strokeDasharray={strokeDashArray} fill: Color.label,
/> fontWeight: TICK_LABEL_FONT_WEIGHT,
textAnchor: "middle",
};
}}
/>
<AxisLeft
scale={yScale}
left={valueAxisLabelOffset}
numTicks={numTicksLeftAxis}
hideAxisLine
labelProps={{
fontSize: `${valueAxisLabelSize / 16}rem`,
}}
tickLabelProps={() => {
return {
fill: Color.label,
fontWeight: TICK_LABEL_FONT_WEIGHT,
textAnchor: "end",
verticalAnchor: "middle",
};
}}
/>
<Line
fill={Color.tertiaryBackground}
to={new Point({ x: 0, y: gridColumnTopOffset })}
from={new Point({ x: 0, y: yMax })}
stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray}
/>
</Group>
<Line <Line
fill={Color.tertiaryBackground} fill={Color.tertiaryBackground}
to={ to={
@ -208,92 +231,58 @@ export const BoxPlot = withTooltip<StatsPlotProps, TooltipData>(
from={ from={
new Point({ new Point({
x: xMax - margin.left - strokeWidth, x: xMax - margin.left - strokeWidth,
y: yMax + plotTopOffset, y: yMax,
}) })
} }
stroke={Color.tertiaryBackground} stroke={Color.tertiaryBackground}
strokeWidth={strokeWidth} strokeWidth={strokeWidth}
strokeDasharray={strokeDashArray} strokeDasharray={strokeDashArray}
/> />
<AxisBottom {plotData.map((d: Stats, i) => (
top={yMax + plotTopOffset - gridColumnTopOffset} <Group key={i}>
left={categoryAxisLabelLeftOffset} <VisxBoxPlot
scale={xScale} className={styles.boxplot}
hideAxisLine min={getMin(d)}
hideTicks max={getMax(d)}
labelProps={{ left={
fontSize: `${categoryAxisLabelSize / 16}rem`, xScale(getX(d))! +
}} boxPlotLeftOffset * constrainedWidth +
tickLabelProps={() => { valueAxisLeftMargin
return { }
fill: Color.label, firstQuartile={getFirstQuartile(d)}
fontWeight: TICK_LABEL_FONT_WEIGHT, thirdQuartile={getThirdQuartile(d)}
}; median={getMedian(d)}
}} boxWidth={constrainedWidth * boxPlotWidthFactor}
/> rx={0}
<AxisLeft ry={0}
scale={yScale} stroke={Color.label}
top={plotTopOffset + valueAxisLabelTopOffset} strokeWidth={strokeWidth}
left={valueAxisLabelLeftOffset} valueScale={yScale}
numTicks={numTicksLeftAxis} minProps={{
hideAxisLine onMouseMove: mouseOverEventHandler(d),
labelProps={{ onMouseLeave: hideTooltip,
fontSize: `${valueAxisLabelSize / 16}rem`, }}
}} maxProps={{
tickLabelProps={() => { onMouseMove: mouseOverEventHandler(d),
return { onMouseLeave: hideTooltip,
fill: Color.label, }}
fontWeight: TICK_LABEL_FONT_WEIGHT, boxProps={{
}; onMouseMove: mouseOverEventHandler(d),
}} strokeWidth: 0,
/> onMouseLeave: hideTooltip,
<Group top={plotTopOffset}> }}
{plotData.map((d: Stats, i) => ( medianProps={{
<Group key={i}> style: {
<VisxBoxPlot stroke: Color.label,
className={styles.boxplot} },
min={getMin(d)} onMouseMove: mouseOverEventHandler(d),
max={getMax(d)} onMouseLeave: () => {
left={ hideTooltip();
xScale(getX(d))! + },
boxPlotLeftOffset * constrainedWidth + }}
valueAxisLeftOffset />
} </Group>
firstQuartile={getFirstQuartile(d)} ))}
thirdQuartile={getThirdQuartile(d)}
median={getMedian(d)}
boxWidth={constrainedWidth * boxPlotWidthFactor}
rx={0}
ry={0}
stroke={Color.label}
strokeWidth={strokeWidth}
valueScale={yScale}
minProps={{
onMouseMove: mouseOverEventHandler(d),
onMouseLeave: hideTooltip,
}}
maxProps={{
onMouseMove: mouseOverEventHandler(d),
onMouseLeave: hideTooltip,
}}
boxProps={{
onMouseMove: mouseOverEventHandler(d),
strokeWidth: 0,
onMouseLeave: hideTooltip,
}}
medianProps={{
style: {
stroke: Color.label,
},
onMouseMove: mouseOverEventHandler(d),
onMouseLeave: () => {
hideTooltip();
},
}}
/>
</Group>
))}
</Group>
</Group> </Group>
</svg> </svg>

View File

@ -212,7 +212,7 @@ export default function CoopPage() {
width={isMobile ? pageWidth / 1.5 : 600} width={isMobile ? pageWidth / 1.5 : 600}
height={DefaultProp.graphHeight} height={DefaultProp.graphHeight}
data={C7iv} data={C7iv}
valueAxisLeftOffset={75} valueAxisLeftMargin={75}
margin={{ margin={{
top: 20, top: 20,
left: 20, left: 20,