Published
Edited
Sep 27, 2022
Insert cell
Insert cell
Insert cell
gene_list = network.row_nodes.map(x => x.name)
Insert cell
available_cats = Object.keys(cat_colors)
Insert cell
meta_data_cols = Object.keys(tiles_geojson.features[0].properties).concat(available_cats).concat(gene_list)
Insert cell
// selected_row_index = {
// var selected_row_index
// if (heatmap_row === null){
// selected_row_index = 0
// } else {
// selected_row_index = network.row_node_names.indexOf(heatmap_row)
// }
// return selected_row_index
// }
Insert cell
dendro_selected_genes
Insert cell
norm_dendro_genes = false
Insert cell
tile_list = network_tile.row_nodes.map(x => x.name)
Insert cell
tmp = Array(network_tile.mat[0].length).fill(0)
Insert cell
dimension_data = {

var dimension_data

if (select_meta === 'dendro_select_rows'){
var mat = network_tile.mat

// initialize
dimension_data = Array(mat[0].length).fill(0)
mat.map((row_data, row_index) => {
var row_name = tile_list[row_index]
if (dendro_selected_genes.includes(row_name)){
// each row will have total expression of 1 to better compare
// genes with variable expression
var row_total
if (norm_dendro_genes === true){
row_total = d3.sum(row_data)
} else {

row_total = 1
}
// console.log('row_total', row_total)
row_data.map((col_data, col_index) => {
// sum matrix element into vector
dimension_data[col_index] = dimension_data[col_index] + col_data/row_total
})
}
})
} else {

// console.log('clicking a row')
// console.log('*************************')
var select_row_index
if (heatmap_row === null){
select_row_index = 0
} else {
// // no longer have this property
// select_row_index = network_tile.row_node_names.indexOf(heatmap_row)

// use list of tiles
select_row_index = tile_list.indexOf(heatmap_row)
}

// console.log(heatmap_row)
dimension_data = network_tile.mat[select_row_index]
}
return dimension_data
}
Insert cell
network.mat
Insert cell
[0, 1].forEach(x => network.mat[x])
Insert cell
mutable inst_zoom = zoom
Insert cell
deckgl_dim = 800
Insert cell
deckgl_width = 800
Insert cell
deckgl_height = 800
Insert cell
// [0].length
Insert cell
// network.mat[0].length
Insert cell
// row_summed = {
// var mat = network.mat

// // initialize
// var row_summed = Array(mat[0].length).fill(0)
// mat.map((row_data, row_index) => {

// var row_name = gene_list[row_index]

// console.log(row_name)

// if (dendro_selected_genes.includes(row_name))
// row_data.map((col_data, col_index) => {
// // sum matrix element into vector
// row_summed[col_index] = row_summed[col_index] + col_data
// })
// })
// return row_summed
// }
Insert cell
// initialize view_bounds
mutable view_bounds = {
var height = deckgl_height
var width = deckgl_width

var ini_zoom = Math.pow(2, zoom)

var target_x = center_x
var target_y = center_y
var min_x = target_x - width/(2 * ini_zoom)
var max_x = target_x + width/(2 * ini_zoom)

var min_y = target_y - height/(2 * ini_zoom)
var max_y = target_y + height/(2 * ini_zoom)

var ini_bounds = [min_x, max_x, min_y, max_y]
return ini_bounds
}
Insert cell
function something(viewState){
var height = viewState.height
var width = viewState.width

// log2 zoom for python
mutable inst_zoom = viewState.zoom
var zoom = Math.pow(2, viewState.zoom)

var target_x = viewState.target[0]
var target_y = viewState.target[1]
var min_x = target_x - width/(2*zoom)
var max_x = target_x + width/(2*zoom)

var min_y = target_y - height/(2*zoom)
var max_y = target_y + height/(2*zoom)

mutable view_bounds = [min_x, max_x, min_y, max_y]

// mutable visible_transcripts = true
// if (zoom > detail_zoom_thresh){
// } else {
// mutable view_bounds = null
// // mutable visible_transcripts = false
// // mutable fovs = []
// }
}
Insert cell
debounce_time = 250
Insert cell
debounced_something = debounce(something, debounce_time);
Insert cell
function debounce(func, wait, immediate) {
// 'private' variable for instance
// The returned function will be able to reference this due to closure.
// Each call to the returned function will share this common timer.
var timeout;

// Calling debounce returns a new anonymous function
return function() {
// reference the context and args for the setTimeout function
var context = this,
args = arguments;

// Should the function be called now? If immediate is true
// and not already in a timeout then the answer is: Yes
var callNow = immediate && !timeout;

// This is the basic debounce behaviour where you can call this
// function several times, but it will only execute once
// [before or after imposing a delay].
// Each time the returned function is called, the timer starts over.
clearTimeout(timeout);

// Set the new timeout
timeout = setTimeout(function() {

// Inside the timeout function, clear the timeout variable
// which will let the next execution run when in 'immediate' mode
timeout = null;

// Check if the function already ran with the immediate flag
if (!immediate) {
// Call the original function with apply
// apply lets you define the 'this' object as well as the arguments
// (both captured before setTimeout)
func.apply(context, args);
}
}, wait);

// Immediate mode and no wait timer? Execute the function..
if (callNow) func.apply(context, args);
}
}

Insert cell
inst_zoom
Insert cell
view_bounds
Insert cell
scale_bar_height
Insert cell
orientation = 'portait' //'landscape'
Insert cell
function make_dashboard(){

console.log('--- make_dashboard\n--------------------')

var dashboard_html
if (orientation === 'landscape'){
dashboard_html = html`
<div id='dashboard' style="height: 1050px; border:1px;">
<div style="display: flex;">
<div>
<div style="display: flex; margin-top: 20px">
<div style="margin-left: 15px; margin-right: -20px; border:1px; text-align:right;">${viewof map_type} </div>
<div id="meta_dropdown", style="margin-right: 15px; border:1px; text-align:right;">${viewof select_meta_dropdown} </div>
<div id="select_scalebar", style="margin-right: 15px; border:1px; text-align:right;">${viewof select_scalebar_length} </div>
<div style="margin-right: 15px; border:1px; text-align:left;">${viewof cell_min_radius} </div>
</div>
<div style="margin-top: 100px; border:1px ">${container} </div>
</div>
<div style="margin-top:-10px; ">${viewof cgm}</div>
</div>
</div>
`
} else {

dashboard_html = html`
<div id='dashboard' style="height: 2000px; border:1px;">
<div>
<div>
<div style="display: flex; margin-top: 20px">
<div style="margin-left: 15px; margin-right: -20px; border:1px; text-align:right;">${viewof map_type} </div>
<div id="meta_dropdown", style="margin-right: 15px; border:1px; text-align:right;">${viewof select_meta_dropdown} </div>
<div id="select_scalebar", style="margin-right: 15px; border:1px; text-align:right;">${viewof select_scalebar_length} </div>
<div style="margin-right: 15px; border:1px; text-align:left;">${viewof cell_min_radius} </div>
</div>
<div style="margin-top: 100px; border:1px ">${container} </div>
</div>
<div style="margin-top:-10px; ">${viewof cgm}</div>
</div>
</div>
`

}

// <div style="margin-right: 15px; border:1px; text-align:left;">${viewof cell_opacity} </div>
return dashboard_html
}

Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
network_tile = obs_data['network_tile']
Insert cell
scale_bar_width = parseInt(select_scalebar_length)
Insert cell
scale_bar_buffer_x = 5 / Math.pow(2, inst_zoom)
Insert cell
scale_bar_buffer_y = 20 / Math.pow(2, inst_zoom)
Insert cell
scale_bar_height = 5
Insert cell
scale_bar_height_proc = scale_bar_height / Math.pow(2, inst_zoom)
Insert cell
scale_bar_geojson = ({
"type": "FeatureCollection",
"features":
[
{
"id": "scale_bar",
"type": "Feature",
"properties":
{
"color": "yellow",
"name": "scale_bar"
},
"geometry":
{
"type": "Polygon",
"coordinates":
[
[
[
scale_bar_buffer_x + view_bounds[0],
-scale_bar_buffer_y + view_bounds[3]
],
[
scale_bar_buffer_x + view_bounds[0] + scale_bar_width,
-scale_bar_buffer_y + view_bounds[3]
],
[
scale_bar_buffer_x + view_bounds[0] + scale_bar_width,
-scale_bar_buffer_y + view_bounds[3] + scale_bar_height_proc
],
[
scale_bar_buffer_x + view_bounds[0],
-scale_bar_buffer_y + view_bounds[3] + scale_bar_height_proc
]
]
]
}
}
]
})
Insert cell
Insert cell
cat_name = 'leiden'
Insert cell
mutable select_cat = 'all'
Insert cell
Insert cell
Insert cell
Insert cell
check_ini_cat = ini_cat
Insert cell
Insert cell
// radius_min_pixels = 5
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
cell_metadata = Object.keys(data[0]).filter(x => non_param_cols.includes(x) === false)
Insert cell
// meta_data_cols = cell_metadata.concat(all_genes)
Insert cell
// all_genes = Object.keys(gex_dict).sort()
Insert cell
mutable dendro_selected_leidens = []
Insert cell
zoom = -6
Insert cell
min_zoom = -100 // zoom - 1
Insert cell
cell_opacity = 0.9
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// viewof cell_opacity = slider({
// value: 1.00,
// min: 0.0,
// max: 1.0,
// precision: 2,
// description: "cell opacity",
// })
Insert cell
ini_min_radius = 1
Insert cell
viewof cell_min_radius = slider({
value: ini_min_radius,
min: 0.1,
max: 5.0,
precision: 2,
description: "min cell size"
})
Insert cell
Insert cell
viewof select_meta_dropdown = select({'options':meta_data_cols, value: ini_cat})
Insert cell
ini_scale_bar_length = 0
Insert cell
viewof select_scalebar_length = select({'options':[0, 0.1, 1, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000], value: ini_scale_bar_length})
Insert cell
Insert cell
Insert cell
Insert cell
select_meta
Insert cell
// cell_layer = new deck.ScatterplotLayer({
// id: 'cell_layer',
// // data: cell_data,
// data: data,
// getPosition: apply_map_morph,

// getFillColor: d => {
// var inst_color = [0, 255, 0, 255]
// if (select_meta === 'none'){

// // default coloring
// ////////////////////////////////////////
// inst_color = [50, 50, 50, 100]

// } else if (select_meta === 'leiden') {

// if (selected_leidens.length === 0){
// // single leiden
// ///////////////////
// var rgb = d3.color(cat_colors[d[select_meta]])
// inst_color = [rgb.r, rgb.g, rgb.b, 255 * cat_opacity]
// } else {
// // multiple leidens
// /////////////////////
// if (selected_leidens.includes(d['leiden'])){
// var rgb = d3.color(cat_colors[d[select_meta]])
// inst_color = [rgb.r, rgb.g, rgb.b, 255 * cat_opacity]
// } else {
// inst_color = [50, 50, 50, 10]
// }
// }
// } else if (select_meta === 'mean-gene') {

// // mean gene expression
// ////////////////////////////////////////
// inst_color = [0, 0, 0, 255]
// } else {
// // single gene expression
// ////////////////////////////////////////
// if (all_genes.includes(select_meta)){
// if (d.name in gex_dict[select_meta]){
// var inst_expression = gex_dict[select_meta][d.name]
// var inst_opacity = opacity_scale(inst_expression)
// inst_color = [255, 0, 0, inst_opacity]
// } else {
// inst_color = [50, 50, 50, 2]
// }
// }
// }
// return inst_color
// },
// getRadius: cell_radius,
// pickable: is_pickable,
// highlightColor: d => [50, 50, 50],
// radiusMinPixels: cell_min_radius,
// opacity: cell_opacity,
// updateTriggers: {
// getFillColor: [select_meta, selected_leidens], // , mean_gene_data],
// getPosition: map_type,
// },
// transitions: transitions,
// onClick: (info, event) => {

// console.log(info.object[cat_name])
// if (mutable dendro_selected_leidens[0] !== info.object[cat_name]){
// mutable select_meta = 'leiden'
// mutable dendro_selected_leidens = [info.object[cat_name]]
// d3.select('#meta_dropdown').select('select').node().value = 'none'
// } else {
// mutable select_meta = 'leiden'
// mutable dendro_selected_leidens = []
// d3.select('#meta_dropdown').select('select').node().value = 'leiden'
// }
// }
// })
Insert cell
tiles_geojson = obs_data['tiles_geojson']
Insert cell
max_gene_value = d3.max(dimension_data)
Insert cell
gene_opacity_scale = d3.scaleLinear()
.domain([0, max_gene_value])
.range([0, 255]);
Insert cell
unselected_opacity_255 = 5
Insert cell
typeof 'pancake' === 'string'
Insert cell
tile_layer = new deck.GeoJsonLayer({
id: 'nbhd_layer',
data: tiles_geojson,
// opacity: opacity,
stroked: false,
filled: true,
getFillColor: d => {

var inst_color// = [255, 0, 0, 255]

var inst_cat = d.properties[cat_name]

// console.log(inst_cat)

if (select_meta === cat_name){


//////////////////////////
// show all categories
//////////////////////////
var rgb = d3.color(cat_colors[inst_cat])
inst_color = [rgb.r, rgb.g, rgb.b, 255 * cat_opacity]

} else if (available_cats.includes(select_meta)){
//////////////////////////
// show single category
//////////////////////////
var rgb = d3.color(cat_colors[inst_cat])

if (inst_cat === select_meta){
inst_color = [rgb.r, rgb.g, rgb.b, 255 * cat_opacity]
} else {
inst_color = [rgb.r, rgb.g, rgb.b, unselected_opacity_255]
}
}
else if (tile_list.includes(select_meta) || select_meta === 'dendro_select_rows') {

// color by single gene
//////////////////////////
// look up cell index from columns
var inst_index = network_tile.col_nodes.map(x => x.name).indexOf(d.id)
var inst_data
if (inst_index !== -1){
inst_data = dimension_data[inst_index]
} else {
inst_data = 0
}
var inst_opacity = gene_opacity_scale(Math.abs(inst_data))
if (inst_data >= 0){
inst_color = [255, 0, 0, inst_opacity]
} else {
console.log('less than zero', inst_data, inst_index)
inst_color = [0, 0, 255, inst_opacity]
}
}
else if (select_meta === 'dendro_select_cols'){

var rgb = d3.color(cat_colors[d.properties[cat_name]])

// check if tile id is in the dendro list
// if (dendro_selected_leidens.includes(d.id)){
if (dendro_selected_leidens.includes(d.properties[cat_name])){
inst_color = [rgb.r, rgb.g, rgb.b, 255 * cat_opacity]
} else {
inst_color = [rgb.r, rgb.g, rgb.b, unselected_opacity_255]
}
} else if (select_meta === 'col_select'){

var rgb = d3.color(cat_colors[d.properties[cat_name]])

// check if tile id is in the dendro list
// if (d.id === heatmap_col){
if (d.properties[cat_name] === heatmap_col){
inst_color = [rgb.r, rgb.g, rgb.b, 255 * cat_opacity]
} else {
inst_color = [rgb.r, rgb.g, rgb.b, unselected_opacity_255]
}
}

return inst_color
},

updateTriggers: {
getFillColor: [select_meta, dendro_selected_leidens, dendro_selected_genes],
},
onClick: (info, event) => {

// show single leiden
var inst_cat = info.object.properties[cat_name]
if (select_meta !== inst_cat){

// switch to single category
mutable select_meta = inst_cat

d3.select('#meta_dropdown').select('select').node().value = inst_cat
} else {

// switch to showing all categories
mutable select_meta = cat_name
d3.select('#meta_dropdown').select('select').node().value = cat_name
}
},
pickable: true
});
Insert cell
scale_bar_layer = new deck.GeoJsonLayer({
id: 'scale_bar_layer',
data: scale_bar_geojson,
// opacity: opacity,
stroked: false,
filled: true,
getFillColor: d => [0, 0, 0],
getLineColor: d => [0, 0, 0],
pickable: false
});
Insert cell
Insert cell
Insert cell
layers = [tile_layer, scale_bar_layer]
Insert cell
Insert cell
deckgl.setProps({layers: layers});
Insert cell
Insert cell
flipY = false
Insert cell
view = new deck.OrthographicView({id: 'ortho', flipY: flipY})
Insert cell
center_x = 100
Insert cell
center_y = 100
Insert cell
initial_view_state = ({
target: [center_x, center_y, 0],
zoom: zoom,
minZoom:min_zoom
})
Insert cell
deckgl = {
console.log('--- deckgl\n----------')
var deckgl = new deck.DeckGL({
container,
// width: '800px',
// height: '800px',
width: deckgl_dim + 'px',
height: deckgl_dim + 'px',
top: '175px',
views:[view],
initialViewState: initial_view_state,
// controller: true,
controller: {doubleClickZoom: false},
// getTooltip: ({object}) => {
// return object &&
// `${object['name']}`
// // `pos: ${object.x}, ${object['y']}\n`
// },
getTooltip: ({object}) => object && {
html: `${object.properties[cat_name]}`,
style: {
fontSize: '0.8em',
padding: '5px',
}
},
onViewStateChange: ({viewState}) => {
debounced_something(viewState)
return viewState
},
// onClick: ({info}) => {
// console.log(info)
// return info
// }
});

// // shift downwards
// console.log('wait to move down move down')
// console.log('is empty', d3.select('#the-container').empty())
// d3.select('#the-container div').style('top', '150px')
// d3.select('#the-container canvas').style('top', '150px')

return deckgl
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// ini_x = d3.mean(data, d => +d.x) // 0
Insert cell
// ini_y = -d3.mean(data, d => +d.y) // 0
Insert cell
gex_opacity_contrast_scale = 1
Insert cell
opacity_scale = d3.scaleLinear()
.domain([0, meta_max * gex_opacity_contrast_scale])
.range([0, 255])
Insert cell
// meta_max = {
// var meta_max
// if (select_meta in gex_dict){
// meta_max = d3.max(Object.values(gex_dict[select_meta]))
// } else if (select_meta === 'mean-gene') {

// // set up calculation of mean gene max
// meta_max = 0
// } else {
// meta_max = 0
// }
// return meta_max
// }

meta_max = d3.max(meta_values)
Insert cell
meta_values = tiles_geojson.features.map(x => x.properties[select_meta])
Insert cell
Insert cell
Insert cell
row_selection = Generators.observe(notify => {

const mousemove = () => {
if (cgm.params.tooltip.tooltip_type === 'row-dendro'){
mutable select_meta = 'dendro_select_rows'
mutable dendro_selected_genes = cgm.params.dendro.selected_clust_names
}
}
viewof cgm.addEventListener("click", mousemove);
// notify(cgm.params.dendro.selected_clust_names.map(x => x.replace('Leiden-', '')));

return () => viewof cgm.removeEventListener("mousemove", mousemove);
})
Insert cell
Generators.observe(notify => {

const mousemove = () => {
if (cgm.params.tooltip.tooltip_type === 'col-dendro'){
mutable select_meta = 'dendro_select_cols'
mutable dendro_selected_genes = []
mutable dendro_selected_leidens = cgm.params.dendro.selected_clust_names.map(x => x.replace('Leiden-', ''))
}

}
viewof cgm.addEventListener("click", mousemove);
notify(cgm.params.dendro.selected_clust_names.map(x => x.replace('Leiden-', '')));

return () => viewof cgm.removeEventListener("mousemove", mousemove);
})
Insert cell
Insert cell
heatmap_row = Generators.observe(notify => {
const mouse_click = () => {

// clicking gene row
//////////////////////////////
if (cgm.params.tooltip.tooltip_type === 'row-label'){
var inst_name = cgm.params.int.mouseover.row.name
mutable dendro_selected_genes = []
mutable select_meta = inst_name
d3.select('#meta_dropdown').select('select').node().value = inst_name
}
notify(cgm.params.int.mouseover.row.name)
}
viewof cgm.addEventListener("click", mouse_click);
notify(cgm.params.int.mouseover.row.name);

return () => viewof cgm.removeEventListener("mousemove", mouse_click);
})
Insert cell
heatmap_col = Generators.observe(notify => {
const mouse_click = () => {

// clicking cluster col
//////////////////////////////
if (cgm.params.tooltip.tooltip_type === 'col-label'){
mutable select_meta = 'col_select'
mutable dendro_selected_genes = []
mutable dendro_selected_leidens = [cgm.params.int.mouseover.col.name.replace('Leiden-', '')]

// if (mutable dendro_selected_leidens[0] !== info.object[cat_name]){
// mutable select_meta = 'leiden'
// mutable dendro_selected_leidens = [info.object[cat_name]]
// d3.select('#meta_dropdown').select('select').node().value = 'none'
// } else {
// mutable select_meta = 'leiden'
// mutable dendro_selected_leidens = []
// d3.select('#meta_dropdown').select('select').node().value = 'leiden'
// }
}
notify(cgm.params.int.mouseover.col.name)
}
viewof cgm.addEventListener("click", mouse_click);
notify(cgm.params.int.mouseover.col.name);

return () => viewof cgm.removeEventListener("mousemove", mouse_click);
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
pako = require('pako/dist/pako.min.js')
Insert cell
import {slider, select} from '@jashkenas/inputs'
Insert cell
// mean_gene_data = Object.keys(mean_gene_data_ini).map(x => mean_gene_data_ini[x])
Insert cell
// gene_data_url = 'https://raw.githubusercontent.com/cornhundred/testing_something/master/per-gene-counts_slice-1_replicate-2/' + select_meta + '.csv'
Insert cell
// mean_opacity_scale = d3.scaleLinear()
// .domain([0, mean_gene_max])
// .range([0, 250])
Insert cell
// // the maximum opacity will happen when the value reaches 90% of the maximum
// gene_max = Math.max.apply(Math, gene_data.map(function(x) { return parseInt(x[select_meta]) })) * 0.90
Insert cell
// mean_gene_max = Math.max.apply(Math, mean_gene_data.map(function(x) { return parseInt(x['mean-gene']) })) * 0.90
Insert cell
// promised_selected_genes = Promise.all(dendro_selected_genes.map(gene => {

// const url = 'https://raw.githubusercontent.com/cornhundred/testing_something/master/per-gene-counts_slice-1_replicate-2_zip/' + String(gene) + '.csv.zip'

// const unzipped_csv = load_zip_csv(url)
// return unzipped_csv
// }))
Insert cell
// mean_gene_data_ini = {
// // selected dendro genes
// ///////////////////////////////
// var mean_gene_data_ini = {}
// promised_selected_genes.forEach((x, i) => {
// if (i === 0){
// // initialize gene_data after getting promised gene data (after dendrogram click)
// data.forEach((x,i) => {
// mean_gene_data_ini[i] = ({
// '': i,
// 'mean-gene': 0
// })
// })
// }

// const gene_name = dendro_selected_genes[i]

// // var sum_gene = d3.sum(x, d => d[gene_name])
// x.forEach((cell_obj, cell_index) => {
// mean_gene_data_ini[cell_index]['mean-gene'] = mean_gene_data_ini[cell_index]['mean-gene'] + cell_obj[gene_name] // /sum_gene
// })
// })

// return mean_gene_data_ini
// }
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more