Skip to content

Commit

Permalink
tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
MrTomRod committed Apr 27, 2022
1 parent 083130b commit 45e0268
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 43 deletions.
20 changes: 13 additions & 7 deletions scoary/create_final_overview.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
from matplotlib.figure import Figure
from matplotlib.collections import PatchCollection, QuadMesh
from matplotlib.patches import Rectangle
from matplotlib.colors import LogNorm
from matplotlib.colors import LogNorm, ListedColormap, Colormap, LinearSegmentedColormap

from .utils import MockNamespace, ROOT_DIR


def plot_dendrogram(linkage_matrix: np.ndarray, labels: [str], ax: Axes) -> {}:
# no recursion problem here. Works with up to 20'000 isolates, becomes more of a memory problem.
dendrogram_params = hierarchy.dendrogram(
linkage_matrix,
orientation='left',
Expand Down Expand Up @@ -53,12 +54,12 @@ def add_clickable_patches(patch_names, fig: Figure, ax: Axes):
fig.add_artist(pc)


def plot_qvals(qvals: pd.DataFrame, fig: Figure, ax: Axes, cmap: str = None) -> [QuadMesh]:
def plot_qvals(qvals: pd.DataFrame, fig: Figure, ax: Axes, cmaps: {str: str | Colormap}) -> [QuadMesh]:
# determine y intervals: [0, 10, 20, ...]
y = np.arange(start=0, stop=len(qvals.index) * 10 + 1, step=10)

pcms = []
for i, col in enumerate(qvals.columns):
for i, (col, cmap) in enumerate(cmaps.items()):
# determine x intervals: [0, 1] / [1, 2] / ...
x = np.array([i, i + 1])

Expand Down Expand Up @@ -161,10 +162,15 @@ def create_final_overview(overview_ds: pd.DataFrame, ns: MockNamespace, isolate_
overview_ds = overview_ds.reindex(dendrogram_params['ivl'])

# plot qvals
cols = ['min_qval', 'min_pval_empirical']
cols = [c for c in cols if c in overview_ds.columns]

pcms = plot_qvals(overview_ds[cols], fig=fig, ax=ax_colorbar, cmap='afmhot')
cmaps = {
'min_qval': 'Spectral',
'min_pval_empirical': LinearSegmentedColormap.from_list(
name='pval_emp_cbar',
colors=['#590d22', '#800f2f', '#a4133c', '#c9184a', '#ff4d6d',
'#ff758f', '#ff8fa3', '#ffb3c1', '#ffccd5', '#fff0f3'])
}
cols = [col for col in cmaps.keys() if col in overview_ds.columns]
pcms = plot_qvals(overview_ds[cols], fig=fig, ax=ax_colorbar, cmaps=cmaps)

# save plot
plt.savefig(f'{ns.outdir}/overview_plot.svg', format='svg')
Expand Down
2 changes: 1 addition & 1 deletion scoary/scoary.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def scoary(
)

# dynamically set recursion limit, should work for ~ 13'000 isolates
_recursion_limit = 100 + len(traits_df.index) ** 2
_recursion_limit = max(1000, 100 + len(traits_df.index) ** 2)
logger.warning(f'Setting recursion limit to {_recursion_limit}')
sys.setrecursionlimit(_recursion_limit)

Expand Down
21 changes: 12 additions & 9 deletions scoary/templates/config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
{
"default-hidden-cols": ["sensitivity", "specificity", "odds_ratio", "contrasting", "supporting", "opposing", "best", "worst", "pval"],
"sanitize-genes": true,
"colors": {
"g+t+": "#1f78b4",
"g+t-": "#a6cee3",
Expand All @@ -9,20 +7,25 @@
"g-t-": "#33a02c",
"g-t?": "#edffdf"
},
"table-config": {
"sanitize-genes": true,
"default-hidden-cols": ["sensitivity", "specificity", "odds_ratio", "contrasting", "supporting", "opposing", "best", "worst", "pval"],
"float-cols": ["pval", "qval", "pval_empirical", "best", "worst"]
},
"tree-config": {
"type": "Hierarchical",
"height": 400,
"leaf-nodes": {
"g+": {"shape": "hexagon", "fillColour": "#000000"},
"g-": {"shape": "diamond", "fillColour": "#e3e3e3"}
},
"color-scale": ["yellow", "red"],
"metadata-bars": {
"g+": {"colour": "#000000", "label": "present"},
"g-": {"colour": "#e3e3e3", "label": "absent"},
"t+": {"colour": "red", "label": "present"},
"t-": {"colour": "yellow", "label": "absent"},
"t?": {"colour": "#ffffff", "label": "unclear"}
"color-scale": ["yellow", "red"],
"metadata-bars": {
"g+": {"colour": "#000000", "label": "present"},
"g-": {"colour": "#e3e3e3", "label": "absent"},
"t+": {"colour": "red", "label": "present"},
"t-": {"colour": "yellow", "label": "absent"},
"t?": {"colour": "#ffffff", "label": "unclear"}
}
},
"link-config": {
Expand Down
57 changes: 33 additions & 24 deletions scoary/templates/trait.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ documentReadyPromise.then(() => document.querySelector('#trait-name').textConten
const configPromise = fetch(`config.json`)
.then(response => response.json())
.then(config => {
if (config['sanitize-genes']) {
if (config['table-config']['sanitize-genes']) {
config.geneListToArray = (d, i) => d[i].split(',').map((gene) => gene.split('|').slice(-1)[0])
} else {
config.geneListToArray = (d, i) => d[i].split(',')
Expand All @@ -119,6 +119,13 @@ const metaPromise = fetch(`traits/${trait}/meta.json`)
.then(response => response.json())


/**
* Promise for tree.nwk. Returns the dictionary object.
*/
const newickPromise = fetch('tree.nwk')
.then(response => response.text())


/**
* Show metadata.
*/
Expand Down Expand Up @@ -193,13 +200,18 @@ const tablePromise = configPromise.then(config => {
header: true, download: true, skipEmptyLines: true, delimiter: '\t', newline: '\n',
}).then(tableData => {
// Preprocess data
const tableConfig = config['table-config']
const floatCols = tableConfig['float-cols']
const renderFloat = (data, type, row) => parseFloat(data).toPrecision(3)
tableData.columns = tableData.meta.fields.map(col => {
return {'data': col, 'label': col, 'title': col}
const column = {'data': col, 'label': col, 'title': col}
if (floatCols.includes(col)) column['render'] = renderFloat
return column
})
tableData.index = tableData['data'].map(col => col['Gene'])
tableData.orderCol = tableData.meta.fields.indexOf(
tableData.meta.fields.includes('pval_empirical') ? 'pval_empirical' : 'qval')
tableData.hiddenCols = config['default-hidden-cols'].map(col => tableData.meta.fields.indexOf(col))
tableData.hiddenCols = tableConfig['default-hidden-cols'].map(col => tableData.meta.fields.indexOf(col))

return tableData
})
Expand Down Expand Up @@ -264,7 +276,7 @@ const isolateInfoPromise = Papa.execPromise('isolate_info.tsv', {

return isolateInfo
}).catch(() => {
console.log('no isolate info')
console.info('no isolate info')
return 'no isolate info'
})

Expand Down Expand Up @@ -378,18 +390,17 @@ const coverageMatrixPromise = Promise.all(
* Hide all shown popovers if a click on a non-popover is registered.
*/
let popovers = []
$(document).on("click", (event) => {
// ignore clicks on .popover or .has-popover
if (Boolean(event.target.closest('.popover, .has-popover'))) {
return
}

// hide all popovers
const hidePopovers = () => {
while (popovers.length > 0) {
const popover = popovers.pop()
bootstrap.Popover.getInstance(popover).hide()
}

}
$(document).on("click", (event) => {
// ignore clicks on .popover or .has-popover
if (!Boolean(event.target.closest('.popover, .has-popover'))) {
hidePopovers()
}
})


Expand Down Expand Up @@ -439,6 +450,7 @@ const coverageMatrixTablePromise = Promise.all(
return `${gene} x ${isolate}`
}

if (!cmValues.isNumeric) return cmValues

/**
* Make genes clickable depending on config.json.
Expand Down Expand Up @@ -609,7 +621,7 @@ Promise.all([tablePromise, coverageMatrixTablePromise, valuesPromise, configProm
Promise.all([tablePromise, metaPromise, valuesPromise, coverageMatrixPromise, configPromise])
.then(([tableData, metaData, valuesData, cmValues, config]) => {
if (!valuesData.isNumeric) {
console.log('no numeric data')
console.info('no numeric data')
return
}

Expand Down Expand Up @@ -706,6 +718,8 @@ Promise.all([coverageMatrixPromise, valuesPromise, configPromise])
}

const genesPopover = (event, currentGene) => {
if (popovers.includes(event.target)) throw 'popover already exists'

event.target.classList.add('has-popover')

new bootstrap.Popover(event.target, {
Expand All @@ -721,27 +735,22 @@ Promise.all([coverageMatrixPromise, valuesPromise, configPromise])

// append to click event
geneClickFunctions.push(genesPopover)


})


// load newick file (tree)
const newickPromise = fetch('tree.nwk')
.then(response => response.text())


const getSize = (element) => {
const getInnerWidth = (element) => {
const computedStyle = getComputedStyle(element)
return element.clientWidth - parseFloat(computedStyle.paddingLeft) - parseFloat(computedStyle.paddingRight)
}


// draw tree
const treePromise = Promise.all([documentReadyPromise, newickPromise, valuesPromise, configPromise])
.then(([_, newickTree, valuesData, config]) => {
// https://www.phylocanvas.gl/examples/metadata-blocks.html
// https://www.phylocanvas.gl/docs/methods.html
const treeContainer = document.querySelector("#tree")
let currentWidth = getSize(treeContainer.parentElement)
let currentWidth = getInnerWidth(treeContainer.parentElement)
const tree = new phylocanvas.PhylocanvasGL(
treeContainer, {
type: phylocanvas.TreeTypes[config['tree-config']['type']],
Expand All @@ -759,7 +768,7 @@ const treePromise = Promise.all([documentReadyPromise, newickPromise, valuesProm
)

const redrawWrapper = () => {
const newWidth = getSize(treeContainer.parentElement)
const newWidth = getInnerWidth(treeContainer.parentElement)
if (newWidth !== currentWidth) {
tree.resize(newWidth, config['tree-config']['height'])
currentWidth = newWidth
Expand Down Expand Up @@ -862,7 +871,7 @@ const calcOrthogeneData = (gene, coverageMatrix, valuesData) => {
const isolate = isolateData['Isolate']
const genePositive = isolateData[sanitizedGene].toString() !== '0'
const traitPositive = valuesData.isolateToTrait[isolate]
const traitValue = valuesData.isolateToValue[isolate]
const traitValue = valuesData.isNumeric ? valuesData.isolateToValue[isolate] : null

if (genePositive) {
if (traitPositive === 'true') {
Expand Down
3 changes: 2 additions & 1 deletion scoary/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ def load_info_file(

if expected_overlap_set is not None:
overlap_size = len(set.intersection(set(info_df.index), expected_overlap_set))
assert overlap_size > 0, f'The {merge_col}s in {info_file} do not match any {merge_col}s in {reference_file}'
if overlap_size == 0:
logger.warning(f'The {merge_col}s in {info_file} do not match any {merge_col}s in {reference_file}')
logger.info(f'Loaded descriptions for {overlap_size} {merge_col}s')

logger.info(f'Loaded {merge_col} descriptions. columns={info_df.columns.tolist()}')
Expand Down
10 changes: 9 additions & 1 deletion tests/init_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,15 @@
'traits-lc-meta': 'LC-meta.tsv',
'traits-gc-vol': 'GC-VOL.tsv',
'traits-gc-vol-meta': 'none-meta.tsv',
}
},
'full_ds': {
'genes': 'N0.tsv',
'gene-info': 'N0_best_names.tsv',
'isolate-info': 'isolate_info.tsv',
'traits': 'traits.tsv',
'trait-info': 'trait_info.tsv',
},

}
for tree_id in range(5):
DATA['bigger_ds'][f'genes-{tree_id}'] = f'pres_abs_tree_id_{tree_id}.csv'
Expand Down

0 comments on commit 45e0268

Please sign in to comment.