td-plots 1.10.2 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/BoxPlot.d.ts +2 -0
- package/dist/components/SplitBoxPlot.d.ts +23 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.esm.js +221 -20
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +221 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { BoxPlotDataTrace } from "./BoxPlot";
|
|
3
|
+
export interface SplitBoxPlotProps {
|
|
4
|
+
groups: BoxPlotDataTrace[];
|
|
5
|
+
additionalGroups?: BoxPlotDataTrace[];
|
|
6
|
+
width?: number;
|
|
7
|
+
height?: number;
|
|
8
|
+
title?: string;
|
|
9
|
+
xAxisTitle?: string;
|
|
10
|
+
yAxisTitle?: string;
|
|
11
|
+
secondXAxisTitle?: string;
|
|
12
|
+
secondYAxisTitle?: string;
|
|
13
|
+
containerStyleOverrides?: React.CSSProperties;
|
|
14
|
+
plotId?: string;
|
|
15
|
+
unit?: string;
|
|
16
|
+
xAnnotations?: {
|
|
17
|
+
x: number;
|
|
18
|
+
text?: string;
|
|
19
|
+
color?: string;
|
|
20
|
+
}[];
|
|
21
|
+
}
|
|
22
|
+
declare const SplitBoxPlot: (props: SplitBoxPlotProps) => import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
export default SplitBoxPlot;
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export { default as PairedComparisonsBoxPlot } from "./components/PairedComparis
|
|
|
9
9
|
export type { PairedComparisonsBoxPlotProps } from "./components/PairedComparisonsBoxPlot";
|
|
10
10
|
export { default as BoxPlot } from "./components/BoxPlot";
|
|
11
11
|
export type { BoxPlotProps } from "./components/BoxPlot";
|
|
12
|
+
export { default as SplitBoxPlot } from "./components/SplitBoxPlot";
|
|
13
|
+
export type { SplitBoxPlotProps } from "./components/SplitBoxPlot";
|
|
12
14
|
export { default as SummaryComparisonPlot } from "./components/SummaryComparisonPlot";
|
|
13
15
|
export type { SummaryComparisonPlotProps } from "./components/SummaryComparisonPlot";
|
|
14
16
|
export { SummaryComparisonPlotLegend } from "./components/SummaryComparisonPlot";
|
package/dist/index.esm.js
CHANGED
|
@@ -1512,12 +1512,13 @@ const BoxPlot = (props) => {
|
|
|
1512
1512
|
: {
|
|
1513
1513
|
x: trace.values,
|
|
1514
1514
|
y0: trace.y !== undefined ? trace.y : undefined,
|
|
1515
|
+
showMean: true,
|
|
1515
1516
|
}; // Values map to x because the boxplot is horizontal
|
|
1516
1517
|
return Object.assign({ type: "box", orientation: "h", name: trace.label, fillcolor: trace.fill === "none" ? "rgba(255, 255, 255, 0)" : undefined, line: {
|
|
1517
1518
|
color: trace.color || "#1f77b4", // Default to Plotly's default blue if no color provided
|
|
1518
1519
|
}, boxpoints: false,
|
|
1519
1520
|
// hoverinfo: "none", // Disable default hover
|
|
1520
|
-
width: trace.width }, boxDefinition);
|
|
1521
|
+
width: trace.width, xaxis: trace.xaxis, yaxis: trace.yaxis }, boxDefinition);
|
|
1521
1522
|
});
|
|
1522
1523
|
const layout = Object.assign(Object.assign({}, extraLayoutConfig), { title: {
|
|
1523
1524
|
text: title,
|
|
@@ -1537,6 +1538,7 @@ const BoxPlot = (props) => {
|
|
|
1537
1538
|
staticPlot: false, // Enable interactivity
|
|
1538
1539
|
};
|
|
1539
1540
|
const containerStyles = Object.assign({ width: "100%", height: "100%", position: "relative" }, containerStyleOverrides);
|
|
1541
|
+
console.log("layout:", layout); // Debugging log to inspect the final layout object
|
|
1540
1542
|
return (jsx("div", { ref: containerRef,
|
|
1541
1543
|
// className={`plot-container ${plotId}`}
|
|
1542
1544
|
style: Object.assign({}, containerStyles), children: jsx(Suspense, { fallback: jsx(Loading, {}), children: jsxs("div", { style: {
|
|
@@ -1666,19 +1668,6 @@ const PairedComparisonsBoxPlot = (props) => {
|
|
|
1666
1668
|
},
|
|
1667
1669
|
};
|
|
1668
1670
|
});
|
|
1669
|
-
groups.map((group, groupIndex) => ({
|
|
1670
|
-
type: "line",
|
|
1671
|
-
yref: "y",
|
|
1672
|
-
xref: "paper",
|
|
1673
|
-
x0: -0.01,
|
|
1674
|
-
x1: -0.01,
|
|
1675
|
-
y0: groupIndex - 0.4, // Align with the center of the group
|
|
1676
|
-
y1: groupIndex + 0.4,
|
|
1677
|
-
line: {
|
|
1678
|
-
color: group.color || "orange",
|
|
1679
|
-
width: 7,
|
|
1680
|
-
},
|
|
1681
|
-
}));
|
|
1682
1671
|
const differenceAnnotations = [];
|
|
1683
1672
|
const differenceBetweenMediansLines = [];
|
|
1684
1673
|
// Find reasonable max and min for the x axis.
|
|
@@ -1818,11 +1807,7 @@ const PairedComparisonsBoxPlot = (props) => {
|
|
|
1818
1807
|
t: 5,
|
|
1819
1808
|
r: 50,
|
|
1820
1809
|
},
|
|
1821
|
-
shapes: [
|
|
1822
|
-
...separatorShapes,
|
|
1823
|
-
...differenceBetweenMediansLines,
|
|
1824
|
-
// ...groupAnnotations,
|
|
1825
|
-
],
|
|
1810
|
+
shapes: [...separatorShapes, ...differenceBetweenMediansLines],
|
|
1826
1811
|
annotations: differenceAnnotations,
|
|
1827
1812
|
};
|
|
1828
1813
|
const legendNode = (jsxs(Box$1, { sx: { display: "flex", justifyContent: "flex-end", mb: 2 }, children: [jsx(LegendBoxPlotItem, { label: pairLabels[0], color: "#626280", boxStyle: "shaded" }), jsx(LegendBoxPlotItem, { label: pairLabels[1], color: "#626280", boxStyle: "outlined" })] }));
|
|
@@ -1830,6 +1815,222 @@ const PairedComparisonsBoxPlot = (props) => {
|
|
|
1830
1815
|
return (jsxs("div", { style: Object.assign({}, containerStyles), children: [showLegend && legendNode, jsx(BoxPlot, { data: boxPlotData, width: width, height: height, title: title, xAxisTitle: xAxisTitle, yAxisTitle: yAxisTitle, extraLayoutConfig: extraLayoutConfig, containerStyleOverrides: containerStyleOverrides, plotId: `${plotId}-boxplot` })] }));
|
|
1831
1816
|
};
|
|
1832
1817
|
|
|
1818
|
+
// This component shows a set of main boxplots as one collection, then allowes for additional
|
|
1819
|
+
// boxes to be shown in a subplot above the main boxplots for comparison.
|
|
1820
|
+
const SplitBoxPlot = (props) => {
|
|
1821
|
+
const { groups, additionalGroups = [], width = 600, height = 400, title = "", xAxisTitle, yAxisTitle, secondXAxisTitle, secondYAxisTitle, containerStyleOverrides, plotId = "paired-comparisons-boxplot", xAnnotations = [], } = props;
|
|
1822
|
+
// Transform the grouped data into an array for BoxPlot
|
|
1823
|
+
const boxPlotData = [
|
|
1824
|
+
...groups.flatMap((group, groupIndex) => {
|
|
1825
|
+
const groupYPosition = groupIndex; // Position the group on the y-axis based on its index
|
|
1826
|
+
const groupColor = group.color || "orange";
|
|
1827
|
+
return Object.assign(Object.assign({}, group), { color: group.color || groupColor, width: 0.28, fill: "none", y: groupYPosition, yaxis: "y" });
|
|
1828
|
+
}),
|
|
1829
|
+
...additionalGroups.map((group, groupIndex) => {
|
|
1830
|
+
return Object.assign(Object.assign({}, group), { color: group.color || "blue", y: groupIndex, xaxis: "x2", yaxis: "y2", fill: "auto", width: 0.28 });
|
|
1831
|
+
}),
|
|
1832
|
+
];
|
|
1833
|
+
// We have to construct nice ticks. We can position them with the group indices.
|
|
1834
|
+
const tickvals = groups.map((_, index) => index);
|
|
1835
|
+
const ticktext = groups.map((group) => group.label);
|
|
1836
|
+
// Draw some gray rectangles to live behind the boxes to help separate the groups
|
|
1837
|
+
const separatorShapes = groups.map((group, groupIndex) => {
|
|
1838
|
+
return {
|
|
1839
|
+
type: "rect",
|
|
1840
|
+
x0: 0,
|
|
1841
|
+
x1: 1,
|
|
1842
|
+
xref: "paper",
|
|
1843
|
+
y0: groupIndex - 0.5,
|
|
1844
|
+
y1: groupIndex + 0.5,
|
|
1845
|
+
yref: "y",
|
|
1846
|
+
fillcolor: groupIndex % 2 === 0 ? "#ffffff" : "#e7e5e5",
|
|
1847
|
+
opacity: 0.2,
|
|
1848
|
+
layer: "below",
|
|
1849
|
+
line: {
|
|
1850
|
+
width: 0,
|
|
1851
|
+
},
|
|
1852
|
+
};
|
|
1853
|
+
});
|
|
1854
|
+
// Leaving these for now because we're planning to use them in a tooltip or on hover.
|
|
1855
|
+
const differenceAnnotations = [];
|
|
1856
|
+
// Find reasonable max and min for the x axis.
|
|
1857
|
+
let globalMin = Infinity;
|
|
1858
|
+
let globalMax = -Infinity;
|
|
1859
|
+
// The following is only for updating difference annotations.
|
|
1860
|
+
groups.forEach((group, groupIndex) => {
|
|
1861
|
+
// Update global min and max
|
|
1862
|
+
if (isBoxPlotDataSummary(group.values)) {
|
|
1863
|
+
globalMin = Math.min(globalMin, group.values.lowerWhisker);
|
|
1864
|
+
globalMax = Math.max(globalMax, group.values.upperWhisker);
|
|
1865
|
+
}
|
|
1866
|
+
else if (Array.isArray(group.values)) {
|
|
1867
|
+
const boxMin = Math.min(...group.values);
|
|
1868
|
+
const boxMax = Math.max(...group.values);
|
|
1869
|
+
globalMin = Math.min(globalMin, boxMin);
|
|
1870
|
+
globalMax = Math.max(globalMax, boxMax);
|
|
1871
|
+
}
|
|
1872
|
+
if (additionalGroups.length === 0 ||
|
|
1873
|
+
group.label !== additionalGroups[0].label) {
|
|
1874
|
+
return; // Don't show an annotation if we don't have data for both boxes
|
|
1875
|
+
}
|
|
1876
|
+
const medianA = isBoxPlotDataSummary(group.values)
|
|
1877
|
+
? group.values.median
|
|
1878
|
+
: computeMedian(group.values);
|
|
1879
|
+
const medianB = isBoxPlotDataSummary(group.values)
|
|
1880
|
+
? group.values.median
|
|
1881
|
+
: computeMedian(group.values);
|
|
1882
|
+
const q1A = isBoxPlotDataSummary(group.values)
|
|
1883
|
+
? group.values.q1
|
|
1884
|
+
: computeQuartile(group.values, 1);
|
|
1885
|
+
const q3A = isBoxPlotDataSummary(group.values)
|
|
1886
|
+
? group.values.q3
|
|
1887
|
+
: computeQuartile(group.values, 3);
|
|
1888
|
+
const q1B = isBoxPlotDataSummary(group.values)
|
|
1889
|
+
? group.values.q1
|
|
1890
|
+
: computeQuartile(group.values, 1);
|
|
1891
|
+
const q3B = isBoxPlotDataSummary(group.values)
|
|
1892
|
+
? group.values.q3
|
|
1893
|
+
: computeQuartile(group.values, 3);
|
|
1894
|
+
const differenceBetweenMedians = medianB - medianA;
|
|
1895
|
+
// If we have the quartiles for the first box, we can determine if the second box's median is inside or outside the box
|
|
1896
|
+
const annotationColor = isBoxPlotDataSummary(group.values)
|
|
1897
|
+
? q3B < q1A || q1B > q3A
|
|
1898
|
+
? "red"
|
|
1899
|
+
: medianB < q1A || medianB > q3A
|
|
1900
|
+
? "orange"
|
|
1901
|
+
: "gray"
|
|
1902
|
+
: "gray";
|
|
1903
|
+
if (annotationColor === "gray") {
|
|
1904
|
+
return; // Don't show an annotation if the medians are close enough that they overlap in the boxes
|
|
1905
|
+
}
|
|
1906
|
+
differenceAnnotations.push({
|
|
1907
|
+
yref: "y",
|
|
1908
|
+
xref: "x",
|
|
1909
|
+
x: medianB > medianA ? medianB : medianA, // Position the annotation at the larger median
|
|
1910
|
+
y: groupIndex, // Align with the center of the group
|
|
1911
|
+
text: ` ${differenceBetweenMedians.toFixed(1)}${props.unit ? ` ${props.unit}` : ""} `, // Show the difference between the medians with optional unit
|
|
1912
|
+
showarrow: false,
|
|
1913
|
+
xanchor: "left",
|
|
1914
|
+
align: "left",
|
|
1915
|
+
font: {
|
|
1916
|
+
size: 16,
|
|
1917
|
+
color: annotationColor,
|
|
1918
|
+
style: "italic",
|
|
1919
|
+
weight: "bold",
|
|
1920
|
+
},
|
|
1921
|
+
});
|
|
1922
|
+
});
|
|
1923
|
+
const totalGroups = groups.length + additionalGroups.length;
|
|
1924
|
+
const yAxisSplitProportion = additionalGroups.length > 0 ? groups.length / totalGroups : 1;
|
|
1925
|
+
const extraLayoutConfig = Object.assign(Object.assign({ yaxis: {
|
|
1926
|
+
type: "linear", // Use linear axis for numeric positioning of boxes
|
|
1927
|
+
tickmode: "array",
|
|
1928
|
+
tickvals,
|
|
1929
|
+
ticktext,
|
|
1930
|
+
automargin: true,
|
|
1931
|
+
range: [-0.5, groups.length - 0.5], // Add some padding to the y-axis range to accommodate the boxes
|
|
1932
|
+
domain: additionalGroups ? [0, yAxisSplitProportion - 0.01] : [0, 1], // Ensure primary y-axis takes full height
|
|
1933
|
+
tickcolor: "#ffffff",
|
|
1934
|
+
ticklen: 0,
|
|
1935
|
+
showgrid: false,
|
|
1936
|
+
//@ts-ignore
|
|
1937
|
+
ticklabelstandoff: 7,
|
|
1938
|
+
ticklabelposition: "outside left",
|
|
1939
|
+
title: {
|
|
1940
|
+
text: yAxisTitle || "", // Set default to empty string to prevent Plotly from adding its own default title
|
|
1941
|
+
standoff: 45,
|
|
1942
|
+
},
|
|
1943
|
+
}, xaxis: {
|
|
1944
|
+
anchor: "y", // Anchor to primary y-axis
|
|
1945
|
+
}, margin: {
|
|
1946
|
+
t: title ? 50 : 40,
|
|
1947
|
+
r: 50,
|
|
1948
|
+
}, shapes: [
|
|
1949
|
+
...separatorShapes,
|
|
1950
|
+
// ...differenceBetweenMediansLines,
|
|
1951
|
+
...xAnnotations.map((annotation) => ({
|
|
1952
|
+
type: "line",
|
|
1953
|
+
x0: annotation.x,
|
|
1954
|
+
x1: annotation.x,
|
|
1955
|
+
y0: 0,
|
|
1956
|
+
y1: 1.05,
|
|
1957
|
+
xref: "x",
|
|
1958
|
+
yref: "paper",
|
|
1959
|
+
line: {
|
|
1960
|
+
color: annotation.color || "rgba(255, 0, 0, 0.7)",
|
|
1961
|
+
width: 2,
|
|
1962
|
+
dash: "dash",
|
|
1963
|
+
},
|
|
1964
|
+
})),
|
|
1965
|
+
], annotations: [
|
|
1966
|
+
// ...differenceAnnotations,
|
|
1967
|
+
...xAnnotations.map((annotation) => ({
|
|
1968
|
+
x: annotation.x,
|
|
1969
|
+
y: 1.06,
|
|
1970
|
+
xref: "x",
|
|
1971
|
+
yref: "paper",
|
|
1972
|
+
text: annotation.text || "",
|
|
1973
|
+
showarrow: false,
|
|
1974
|
+
yanchor: "bottom",
|
|
1975
|
+
font: {
|
|
1976
|
+
color: annotation.color || "rgba(255, 0, 0, 0.7)",
|
|
1977
|
+
size: 12,
|
|
1978
|
+
},
|
|
1979
|
+
})),
|
|
1980
|
+
] }, (additionalGroups && {
|
|
1981
|
+
xaxis2: {
|
|
1982
|
+
title: {
|
|
1983
|
+
text: secondXAxisTitle || "", // Set default to empty string to prevent Plotly from adding its own default title
|
|
1984
|
+
},
|
|
1985
|
+
side: "top",
|
|
1986
|
+
anchor: "y2", // Anchor to secondary y-axis
|
|
1987
|
+
showgrid: true,
|
|
1988
|
+
zeroline: false,
|
|
1989
|
+
showline: true,
|
|
1990
|
+
showticklabels: true, // Show x-axis tick labels for histogram
|
|
1991
|
+
gridcolor: "#efefef",
|
|
1992
|
+
gridwidth: 0.2,
|
|
1993
|
+
zerolinecolor: "#969696",
|
|
1994
|
+
zerolinewidth: 1,
|
|
1995
|
+
linecolor: "#bababa",
|
|
1996
|
+
linewidth: 1,
|
|
1997
|
+
fixedrange: true,
|
|
1998
|
+
automargin: true,
|
|
1999
|
+
matches: "x", // Match the range of xaxis to keep them synchronized
|
|
2000
|
+
},
|
|
2001
|
+
})), (additionalGroups && {
|
|
2002
|
+
yaxis2: {
|
|
2003
|
+
domain: [yAxisSplitProportion, 1], // Adjust domain for secondary y-axis (histogram)
|
|
2004
|
+
title: {
|
|
2005
|
+
text: secondYAxisTitle || "", // Set default to empty string to prevent Plotly from adding its own default title
|
|
2006
|
+
standoff: 45, // Add space between title and axis
|
|
2007
|
+
},
|
|
2008
|
+
range: [-0.5, additionalGroups.length - 0.5], // Add some padding to the y-axis range to accommodate the boxes
|
|
2009
|
+
automargin: true, // Required for standoff to work properly
|
|
2010
|
+
showgrid: true,
|
|
2011
|
+
zeroline: false,
|
|
2012
|
+
ticklabelposition: "outside left",
|
|
2013
|
+
showline: true,
|
|
2014
|
+
mirror: "ticks",
|
|
2015
|
+
gridcolor: "#efefef",
|
|
2016
|
+
gridwidth: 0.2,
|
|
2017
|
+
zerolinecolor: "#969696",
|
|
2018
|
+
zerolinewidth: 1,
|
|
2019
|
+
linecolor: "#bababa",
|
|
2020
|
+
linewidth: 1,
|
|
2021
|
+
fixedrange: true, // Disable zooming
|
|
2022
|
+
ticktext: additionalGroups.map((group) => group.label), // Use additional group labels for secondary y-axis ticks
|
|
2023
|
+
tickvals: additionalGroups.map((_, index) => index), // Position ticks based on the number of additional groups
|
|
2024
|
+
tickanchor: "end",
|
|
2025
|
+
ticklabelstandoff: 7,
|
|
2026
|
+
},
|
|
2027
|
+
}));
|
|
2028
|
+
console.log("extraLayoutConfig:", extraLayoutConfig);
|
|
2029
|
+
const containerStyles = Object.assign({ width: width, height: height, position: "relative", display: "flex", flexDirection: "column", gap: 0 }, containerStyleOverrides);
|
|
2030
|
+
console.log(boxPlotData);
|
|
2031
|
+
return (jsx("div", { style: Object.assign({}, containerStyles), children: jsx(BoxPlot, { data: boxPlotData, width: width, height: height, title: title, xAxisTitle: xAxisTitle, yAxisTitle: yAxisTitle, extraLayoutConfig: extraLayoutConfig, containerStyleOverrides: containerStyleOverrides, plotId: `${plotId}-boxplot` }) }));
|
|
2032
|
+
};
|
|
2033
|
+
|
|
1833
2034
|
const Plot$2 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
|
|
1834
2035
|
const SummaryComparisonPlot = (props) => {
|
|
1835
2036
|
const { groups, height = 250, title = "", xAxisTitle, yAxisTitle, containerStyleOverrides, plotId = "summary-comparison-plot", tooltipPosition = "right", startXAxisAtZero = true, unit = "", } = props;
|
|
@@ -100248,5 +100449,5 @@ var reactPlotlyWrapper$1 = /*#__PURE__*/Object.freeze({
|
|
|
100248
100449
|
default: reactPlotlyWrapper
|
|
100249
100450
|
});
|
|
100250
100451
|
|
|
100251
|
-
export { BarPlot, BoxPlot, HistogramPlot, LegendColorItem, LegendLineItem, LineWithHistogram, PairedComparisonsBoxPlot, RadialHistogramPlot, StatsDonut, SummaryComparisonPlot, SummaryComparisonPlotLegend, isDateArray, isNumberArray };
|
|
100452
|
+
export { BarPlot, BoxPlot, HistogramPlot, LegendColorItem, LegendLineItem, LineWithHistogram, PairedComparisonsBoxPlot, RadialHistogramPlot, SplitBoxPlot, StatsDonut, SummaryComparisonPlot, SummaryComparisonPlotLegend, isDateArray, isNumberArray };
|
|
100252
100453
|
//# sourceMappingURL=index.esm.js.map
|