-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Based on report.html generated by haddock3-analyse command
- Loading branch information
1 parent
76284bb
commit 8b06ac7
Showing
4 changed files
with
258 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,5 @@ | ||
import type { Scores } from "./CaprievalReport.client"; | ||
|
||
export function BoxPlots({ scores }: { scores: Scores }) { | ||
return ( | ||
<div>Place holder for boxplots</div> | ||
) | ||
} | ||
return <div>Place holder for boxplots</div>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,87 +1,271 @@ | ||
import type { Scores } from "./CaprievalReport.client"; | ||
import type { Layout, Data } from "plotly.js"; | ||
import type { Layout, Data, LayoutAxis, AxisName } from "plotly.js"; | ||
import { useMemo } from "react"; | ||
import Plot from "react-plotly.js"; | ||
import type { Scores } from "./CaprievalReport.client"; | ||
|
||
const SUBPLOTS = { | ||
columns: ["score", "desolv", "vdw", "elec", "air"], | ||
rows: ["irmsd", "dockq", "lrmsd", "ilrmsd"].reverse(), | ||
}; | ||
|
||
const SCATTER_AXES = { | ||
x: ["score", "desolv", "vdw", "elec", "air"], | ||
y: ["irmsd", "dockq", "lrmsd", "ilrmsd"], | ||
const DOMAINS = { | ||
columns: [ | ||
[0.0, 0.152], | ||
[0.212, 0.364], | ||
[0.424, 0.576], | ||
[0.636, 0.788], | ||
[0.848, 1.0], | ||
], | ||
rows: [ | ||
[0, 0.175], | ||
[0.275, 0.45], | ||
[0.55, 0.725], | ||
[0.825, 1.0], | ||
], | ||
}; | ||
|
||
export function generateSubPlots(scores: Scores, axes = SCATTER_AXES): Data[] { | ||
const TITLE_NAMES = { | ||
"score": "HADDOCK score", | ||
"irmsd": "i-RMSD", | ||
"lrmsd": "l-RMSD", | ||
"ilrmsd": "il-RMSD", | ||
"dockq": "DOCKQ", | ||
"desolv": "Edesolv", | ||
"vdw": "Evdw", | ||
"elec": "Eelec", | ||
"air": "Eair", | ||
"fnat": "FCC", | ||
}; | ||
|
||
// Dark24 from venv/lib/python3.10/site-packages/_plotly_utils/colors/qualitative.py | ||
const CLUSTER_COLORS = [ | ||
"#2E91E5", | ||
"#E15F99", | ||
"#1CA71C", | ||
"#FB0D0D", | ||
"#DA16FF", | ||
"#222A2A", | ||
"#B68100", | ||
"#750D86", | ||
"#EB663B", | ||
"#511CFB", | ||
"#00A08B", | ||
"#FB00D1", | ||
"#FC0080", | ||
"#B2828D", | ||
"#6C7C32", | ||
"#778AAE", | ||
"#862A16", | ||
"#A777F1", | ||
"#620042", | ||
"#1616A7", | ||
"#DA60CA", | ||
"#6C4516", | ||
"#0D2A63", | ||
"#AF0038", | ||
]; | ||
|
||
const MAX_CLUSTER_TO_PLOT = 10; | ||
|
||
function generateSubPlots(scores: Scores, axes = SUBPLOTS): Data[] { | ||
const data: Data[] = []; | ||
for (const x of axes.x) { | ||
const xaxis = axes.x.indexOf(x) == 0 ? "x" : "x" + (axes.x.indexOf(x) + 1); | ||
for (const y of axes.y) { | ||
const yaxis = | ||
axes.y.indexOf(y) == 0 ? "y" : "y" + (axes.y.indexOf(y) + 1); | ||
scores.clusters.forEach((cluster) => { | ||
const structures4cluster = scores.structures.filter( | ||
(s) => s["cluster-id"] === cluster.cluster_id | ||
); | ||
const name = cluster.cluster_id === "-" ? "Unclustered" : cluster.cluster_id + "" | ||
data.push({ | ||
x: structures4cluster.map((s) => s[x]), | ||
y: structures4cluster.map((s) => s[y]), | ||
name, | ||
legendgroup: name, | ||
mode: "markers", | ||
type: "scatter", | ||
// "Model: mdscoring_17.pdb<br>Score: -13.82<br>Caprieval rank: 16", | ||
text: structures4cluster.map( | ||
(s) => | ||
`Model: ${s.model}<br>Score: ${s.score}<br>Caprieval rank: ${s.caprieval_rank}` | ||
), | ||
xaxis, | ||
yaxis, | ||
}); | ||
data.push({ | ||
error_x: { | ||
array: [cluster[x + "_std"]], | ||
type: "data", | ||
visible: true, | ||
}, | ||
error_y: { | ||
array: [cluster[y + "_std"]], | ||
type: "data", | ||
visible: true, | ||
}, | ||
x: [cluster[x]], | ||
y: [cluster[y]], | ||
marker: { color: "#2E91E5", size: 10, symbol: "square-dot" }, | ||
legendgroup: name, | ||
mode: "markers", | ||
showlegend: false, | ||
type: "scatter", | ||
xaxis, | ||
yaxis, | ||
}) | ||
}); | ||
let aIndex = 1; | ||
for (const row of axes.rows) { | ||
for (const column of axes.columns) { | ||
generateSubPlot(aIndex, scores, row, column, data); | ||
aIndex++; | ||
} | ||
} | ||
|
||
// console.log(data); | ||
|
||
return data; | ||
} | ||
|
||
function generateSubPlot( | ||
aIndex: number, | ||
scores: Scores, | ||
row: string, | ||
column: string, | ||
data: Data[] | ||
) { | ||
const xaxis = "x" + (aIndex === 1 ? "" : aIndex); | ||
const yaxis = "y" + (aIndex === 1 ? "" : aIndex); | ||
const other: Data = { | ||
xaxis, | ||
yaxis, | ||
type: "scatter", | ||
mode: "markers", | ||
marker: { color: "white", line: { color: "DarkSlateGrey", width: 2 } }, | ||
name: "Other", | ||
legendgroup: "Other", | ||
showlegend: aIndex === 1, | ||
hoverlabel: { | ||
bgcolor: "white", | ||
font: { family: "Helvetica", size: 16 }, | ||
}, | ||
text: [], | ||
x: [], | ||
y: [], | ||
}; | ||
for (const cluster of scores.clusters) { | ||
const structures4cluster = scores.structures.filter( | ||
(s) => s["cluster-id"] === cluster.cluster_id | ||
); | ||
if ( | ||
cluster.cluster_rank === "-" || | ||
Number(cluster.cluster_rank) <= MAX_CLUSTER_TO_PLOT | ||
) { | ||
const color = | ||
CLUSTER_COLORS[ | ||
cluster.cluster_rank === "-" ? 0 : Number(cluster.cluster_rank) - 1 | ||
]; | ||
const name = | ||
"Cluster " + | ||
(cluster.cluster_rank === "-" | ||
? "Unclustered" | ||
: cluster.cluster_rank + ""); | ||
data.push({ | ||
x: structures4cluster.map((s) => s[row]), | ||
y: structures4cluster.map((s) => s[column]), | ||
name, | ||
legendgroup: name, | ||
hoverlabel: { | ||
bgcolor: color, | ||
font: { family: "Helvetica", size: 16 }, | ||
}, | ||
showlegend: aIndex === 1, | ||
marker: { color }, | ||
mode: "markers", | ||
type: "scatter", | ||
// "Model: mdscoring_17.pdb<br>Score: -13.82<br>Caprieval rank: 16", | ||
text: structures4cluster.map( | ||
(s) => | ||
`Model: ${s.model}<br>Score: ${s.score}<br>Caprieval rank: ${s.caprieval_rank}` | ||
), | ||
xaxis, | ||
yaxis, | ||
}); | ||
// Error bars | ||
data.push({ | ||
error_x: { | ||
array: [cluster[row + "_std"]], | ||
type: "data", | ||
visible: true, | ||
}, | ||
error_y: { | ||
array: [cluster[column + "_std"]], | ||
type: "data", | ||
visible: true, | ||
}, | ||
x: [cluster[row]], | ||
y: [cluster[column]], | ||
marker: { color, size: 10, symbol: "square-dot" }, | ||
text: [ | ||
`Cluster ${cluster.cluster_id}<br>${column}: ${cluster[column]}<br>${row}: ${cluster[row]}`, | ||
], | ||
hoverlabel: { | ||
bgcolor: color, | ||
font: { family: "Helvetica", size: 16 }, | ||
}, | ||
hovertemplate: `<b>Cluster ${cluster.cluster_id}<br>${column}: ${cluster[column]}<br>${row}: ${cluster[row]}</b><extra></extra>`, | ||
legendgroup: name, | ||
mode: "markers", | ||
showlegend: false, | ||
type: "scatter", | ||
xaxis, | ||
yaxis, | ||
}); | ||
} else { | ||
// Fill other cluster | ||
other.x = (other.x as Array<string | number>).concat( | ||
structures4cluster.map((s) => s[row]) | ||
); | ||
other.y = (other.y as Array<string | number>).concat( | ||
structures4cluster.map((s) => s[column]) | ||
); | ||
other.text = (other.text as Array<string>).concat( | ||
structures4cluster.map( | ||
(s) => | ||
`Model: ${s.model}<br>Score: ${s.score}<br>Caprieval rank: ${s.caprieval_rank}` | ||
) | ||
); | ||
} | ||
} | ||
if (other.x!.length > 0) { | ||
data.push(other); | ||
} | ||
} | ||
|
||
function generateAxes() { | ||
const axes: Record<string, Partial<LayoutAxis & { matches: string }>> = {}; | ||
let aIndex = 1; | ||
for (const row of SUBPLOTS.rows) { | ||
for (const column of SUBPLOTS.columns) { | ||
generatePlotAxes(aIndex, column, row, axes); | ||
aIndex++; | ||
} | ||
} | ||
// console.log(JSON.stringify(axes,null,2)); | ||
return axes; | ||
} | ||
|
||
function generatePlotAxes( | ||
aIndex: number, | ||
column: string, | ||
row: string, | ||
axes: Record<string, Partial<LayoutAxis & { matches: string }>> | ||
) { | ||
const xaxisKey = "xaxis" + (aIndex === 1 ? "" : aIndex); | ||
const yaxisKey = "yaxis" + (aIndex === 1 ? "" : aIndex); | ||
const ax = ("x" + (aIndex === 1 ? "" : aIndex)) as AxisName; | ||
const ay = ("y" + (aIndex === 1 ? "" : aIndex)) as AxisName; | ||
const colindex = SUBPLOTS.columns.indexOf(column); | ||
const rowindex = SUBPLOTS.rows.indexOf(row); | ||
const mx = | ||
rowindex === 0 ? "x" : "x" + (rowindex + 1) * SUBPLOTS.columns.length; | ||
const my = colindex === 0 ? "y" : "y" + (colindex + 1); | ||
axes[xaxisKey] = { | ||
anchor: ay, | ||
domain: DOMAINS.columns[colindex], | ||
title: { | ||
text: TITLE_NAMES[row as keyof typeof TITLE_NAMES], | ||
standoff: 5, | ||
}, | ||
automargin: true, | ||
}; | ||
axes[yaxisKey] = { | ||
anchor: ax, | ||
domain: DOMAINS.rows[rowindex], | ||
title: { | ||
text: TITLE_NAMES[column as keyof typeof TITLE_NAMES], | ||
standoff: 5, | ||
}, | ||
automargin: true, | ||
}; | ||
if (mx !== ax) { | ||
axes[xaxisKey].matches = mx; | ||
} | ||
if (my !== ay) { | ||
axes[yaxisKey].matches = my; | ||
} | ||
} | ||
|
||
export function ScatterPlots({ scores }: { scores: Scores }) { | ||
const data = useMemo(() => generateSubPlots(scores), [scores]); | ||
const subplots = [ | ||
["xy", "x2y", "x3y", "x4y", "x5y"], | ||
["xy2", "x2y2", "x3y2", "x4y2", "x5y2"], | ||
["xy3", "x2y3", "x3y3", "x4y3", "x5y3"], | ||
["xy4", "x2y4", "x3y4", "x4y4", "x5y4"], | ||
] as any; | ||
const axes = useMemo(() => generateAxes(), []); | ||
const layout: Partial<Layout> = { | ||
width: 1400, | ||
height: 1750, | ||
grid: { | ||
rows: 4, | ||
columns: 5, | ||
pattern: "coupled", | ||
subplots, | ||
roworder: "bottom to top", | ||
}, | ||
height: 800, | ||
width: 1300, | ||
legend: { title: { text: "Cluster Rank" } }, | ||
...axes, | ||
}; | ||
return <Plot data={data} layout={layout} />; | ||
return ( | ||
<Plot | ||
data={data} | ||
layout={layout} | ||
config={{ | ||
responsive: true, | ||
}} | ||
/> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters