Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added correlation plugin #522

Merged
merged 9 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 78 additions & 49 deletions packages/libs/components/src/plots/BipartiteNetwork.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,17 @@ import { partition } from 'lodash';
import { LabelPosition, Link, NodeWithLabel } from './Network';
import { Graph } from '@visx/network';
import { Text } from '@visx/text';
import { CSSProperties } from 'react';
import {
CSSProperties,
Ref,
forwardRef,
useImperativeHandle,
useRef,
} from 'react';
import { DEFAULT_CONTAINER_HEIGHT } from './PlotlyPlot';
import Spinner from '../components/Spinner';
import { ToImgopts } from 'plotly.js';
import domToImage from 'dom-to-image';

export interface BipartiteNetworkProps {
/** Bipartite network data */
Expand All @@ -26,7 +34,10 @@ export interface BipartiteNetworkProps {

// The BipartiteNetwork function draws a two-column network using visx. This component handles
// the positioning of each column, and consequently the positioning of nodes and links.
export function BipartiteNetwork(props: BipartiteNetworkProps) {
function BipartiteNetwork(
props: BipartiteNetworkProps,
ref: Ref<HTMLDivElement>
) {
const {
data,
column1Name,
Expand All @@ -45,6 +56,20 @@ export function BipartiteNetwork(props: BipartiteNetworkProps) {
const DEFAULT_COLUMN1_X = 100;
const DEFAULT_COLUMN2_X = (width ?? DEFAULT_WIDTH) - DEFAULT_COLUMN1_X;

// Use ref forwarding to enable screenshotting of the plot for thumbnail versions.
const plotRef = useRef<HTMLDivElement>(null);
useImperativeHandle<HTMLDivElement, any>(
ref,
() => ({
// The thumbnail generator makePlotThumbnailUrl expects to call a toImage function
toImage: async (imageOpts: ToImgopts) => {
if (!plotRef.current) throw new Error('Plot not ready');
return domToImage.toPng(plotRef.current, imageOpts);
},
}),
[]
);

// In order to assign coordinates to each node, we'll separate the
// nodes based on their column, then will use their order in the column
// (given by columnXNodeIDs) to finally assign the coordinates.
Expand Down Expand Up @@ -105,54 +130,58 @@ export function BipartiteNetwork(props: BipartiteNetworkProps) {
className={containerClass}
style={{ ...containerStyles, position: 'relative' }}
>
<svg
width={width ?? DEFAULT_WIDTH}
height={
Math.max(data.column1NodeIDs.length, data.column2NodeIDs.length) *
DEFAULT_NODE_VERTICAL_SPACE +
DEFAULT_TOP_PADDING
}
>
{/* Draw names of node colums if they exist */}
{column1Name && (
<Text
x={DEFAULT_COLUMN1_X}
y={DEFAULT_TOP_PADDING / 2}
textAnchor="end"
>
{column1Name}
</Text>
)}
{column2Name && (
<Text
x={DEFAULT_COLUMN2_X}
y={DEFAULT_TOP_PADDING / 2}
textAnchor="start"
>
{column2Name}
</Text>
)}
<div ref={plotRef} style={{ width: '100%', height: '100%' }}>
<svg
width={width ?? DEFAULT_WIDTH}
height={
Math.max(data.column1NodeIDs.length, data.column2NodeIDs.length) *
DEFAULT_NODE_VERTICAL_SPACE +
DEFAULT_TOP_PADDING
}
>
{/* Draw names of node colums if they exist */}
{column1Name && (
<Text
x={DEFAULT_COLUMN1_X}
y={DEFAULT_TOP_PADDING / 2}
textAnchor="end"
>
{column1Name}
</Text>
)}
{column2Name && (
<Text
x={DEFAULT_COLUMN2_X}
y={DEFAULT_TOP_PADDING / 2}
textAnchor="start"
>
{column2Name}
</Text>
)}

<Graph
graph={{
nodes: nodesByColumnWithCoordinates[0].concat(
nodesByColumnWithCoordinates[1]
),
links: linksWithCoordinates,
}}
// Using our Link component so that it uses our nice defaults and
// can better expand to handle more complex events (hover and such).
linkComponent={({ link }) => <Link link={link} />}
nodeComponent={({ node }) => {
const nodeWithLabelProps = {
node: node,
labelPosition: node.labelPosition,
};
return <NodeWithLabel {...nodeWithLabelProps} />;
}}
/>
</svg>
{showSpinner && <Spinner />}
<Graph
graph={{
nodes: nodesByColumnWithCoordinates[0].concat(
nodesByColumnWithCoordinates[1]
),
links: linksWithCoordinates,
}}
// Using our Link component so that it uses our nice defaults and
// can better expand to handle more complex events (hover and such).
linkComponent={({ link }) => <Link link={link} />}
nodeComponent={({ node }) => {
const nodeWithLabelProps = {
node: node,
labelPosition: node.labelPosition,
};
return <NodeWithLabel {...nodeWithLabelProps} />;
}}
/>
</svg>
{showSpinner && <Spinner />}
</div>
</div>
);
}

export default forwardRef(BipartiteNetwork);
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { useState, useEffect, useRef } from 'react';
import { Story, Meta } from '@storybook/react/types-6-0';
import {
NodeData,
LinkData,
BipartiteNetworkData,
} from '../../types/plots/network';
import {
BipartiteNetwork,
import BipartiteNetwork, {
BipartiteNetworkProps,
} from '../../plots/BipartiteNetwork';
import { twoColorPalette } from '../../types/plots';
import { twoColorPalette } from '../../types/plots/addOns';

export default {
title: 'Plots/Network/BipartiteNetwork',
Expand All @@ -20,18 +20,42 @@ interface TemplateProps {
column1Name?: string;
column2Name?: string;
loading?: boolean;
showThumbnail?: boolean;
}

// Template for showcasing our BipartiteNetwork component.
const Template: Story<TemplateProps> = (args) => {
// Generate a jpeg version of the network (svg).
// Mimicks the makePlotThumbnailUrl process in web-eda.
const ref = useRef<any>(null);
const [img, setImg] = useState('');
useEffect(() => {
setTimeout(() => {
ref.current
?.toImage({ format: 'jpeg', height: 400, width: 600 })
.then((src: string) => setImg(src));
}, 2000);
}, []);

const bipartiteNetworkProps: BipartiteNetworkProps = {
data: args.data,
column1Name: args.column1Name,
column2Name: args.column2Name,
showSpinner: args.loading,
width: 500,
};
return <BipartiteNetwork {...bipartiteNetworkProps} />;
return (
<>
<BipartiteNetwork ref={ref} {...bipartiteNetworkProps} />
{args.showThumbnail && (
<>
<br></br>
<h3>A snapshot of the plot will appear below after two sconds...</h3>
<img src={img} />
</>
)}
</>
);
};

/**
Expand Down Expand Up @@ -69,6 +93,15 @@ Loading.args = {
loading: true,
};

// Show thumbnail
export const Thumbnail = Template.bind({});
Thumbnail.args = {
data: genBipartiteNetwork(10, 10),
column1Name: 'Column 1',
column2Name: 'Column 2',
showThumbnail: true,
};

// Gerenate a bipartite network with a given number of nodes and random edges
function genBipartiteNetwork(
column1nNodes: number,
Expand Down
30 changes: 30 additions & 0 deletions packages/libs/eda/src/lib/core/api/DataClient/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,36 @@ export interface VolcanoPlotRequestParams {
config: {}; // Empty viz config because there are no viz input vars
}

// Bipartite network
export type BipartiteNetworkResponse = TypeOf<typeof BipartiteNetworkResponse>;

const NodeData = type({
id: string,
});

export const BipartiteNetworkResponse = type({
column1NodeIDs: array(string),
column2NodeIDs: array(string),
nodes: array(NodeData),
links: array(
type({
source: NodeData,
target: NodeData,
strokeWidth: number,
color: string,
})
),
});

export interface BipartiteNetworkRequestParams {
studyId: string;
filters: Filter[];
config: {
correlationCoefThreshold?: number;
significanceThreshold?: number;
};
}

////////////////
// Table Data //
////////////////
Expand Down
Loading