Published
Edited
Aug 12, 2021
1 fork
Insert cell
Insert cell
## Zip Data
Insert cell
number_of_genes = Object.keys(unzip_json).length
Insert cell
unzip_json = JSON.parse(decode_zipped_string(zip_string))
Insert cell
zip_string = 'eJyrVspLLdfNTq1UslIAM8sSc0pTlWoBZN4ILw=='
Insert cell
function decode_zipped_string(b64Data){
// Get some base64 encoded binary data from the server. Imagine we got this:
// Decode base64 (convert ascii to binary)
var strData = atob(b64Data);
// Convert binary string to character-number array
var charData = strData.split('').map(function(x){return x.charCodeAt(0);});
// Turn number array into byte-array
var binData = new Uint8Array(charData);
// Pako magic
var data = pako.inflate(binData);
// // Convert gunzipped byteArray back to ascii string:
// var strData = String.fromCharCode.apply(null, new Uint16Array(data));

// https://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript
var strData = new TextDecoder().decode(data);
// Output to console
// console.log(strData);
return strData
}
Insert cell
pako = require('pako/dist/pako.min.js')
Insert cell
dashboard = html`
<div id='dashboard'>
<div style="display: flex;">
<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>
<div>

<div style="display: flex;">
<div style="margin-top: 150px; flex-basis:50%; border:1px ">${container} </div>
<div style="margin-top: -25px; flex-basis:1000px">${viewof cgm}</div>
</div>

</div>
</div>
`

Insert cell
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)
cell_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
mean_gene_data = Object.keys(mean_gene_data_ini).map(x => mean_gene_data_ini[x])
Insert cell
viewof select_meta_dropdown = select({'options':['none'].concat(meta_data_cols), value: ini_cat})
Insert cell
mutable select_meta = select_meta_dropdown
Insert cell
select_meta_dropdown
Insert cell
ini_cat = inputs['ini_cat']
Insert cell
promised_selected_genes
Insert cell
container = html `<div style="height:800px; border-style:solid; border-color:#d3d3d3; border-width:1px"></div>`
Insert cell
cat_colors
Insert cell
Insert cell
## GEX Dic Zip
Insert cell
promised_data
Insert cell
transcript_data_ini = [].concat.apply([], promised_data)
Insert cell
// transcript_data_chunks = promised_data.map(x =>
// x.map(x => ({
// '': x[''],
// 'barcode_id': x.barcode_id,
// 'position': eval(x.position),
// 'color': eval(x.color),
// })))
Insert cell
select_meta
Insert cell
transcript_data = transcript_data_ini.map(x => {
var new_obj

var inst_gene = barcode_id_gene_dictionary[x.barcode_id]

// allow transcript color filtering
if (['leiden', 'none'].includes(select_meta)){
new_obj = ({
'': x[''],
'name': inst_gene,
'barcode_id': x.barcode_id,
'position': eval(x.position),
'color': eval(x.color),
'tooltip_name': inst_gene
})
} else {
var inst_color = [0, 0, 0, 15]

// check gene name
if (inst_gene === select_meta || dendro_selected_genes.includes(inst_gene)){
inst_color = eval(x.color)
}
new_obj = ({
'': x[''],
'barcode_id': x.barcode_id,
'name': inst_gene,
'position': eval(x.position),
'color': inst_color,
'tooltip_name': inst_gene
})
}
return new_obj
})
Insert cell
promised_data = Promise.all(fovs.map(fov => {
const url = 'https://raw.githubusercontent.com/cornhundred/testing_something/master/transcripts_csv_zip_round_colors/' +
String(fov) + '.csv.zip'

var unzipped_csv
if (map_type === 'Spatial'){
unzipped_csv = load_zip_csv(url)
} else {
unzipped_csv = []
}
return unzipped_csv
}))
Insert cell
cell_data = inputs['data'].map(x => {
x.tooltip_name = 'Leiden-' + x.leiden
return x
})
Insert cell
// cell_data = cell_data_ini.map(x => {
// x.name = 'Leiden-' + x.leiden
// return x
// })
Insert cell
cat_colors = inputs['cat_colors']
Insert cell
inputs = FileAttachment("observable_inputs_slice-1_replicate-2.json").json()
Insert cell
Insert cell
selected_leidens.length
Insert cell
cell_data[0]
Insert cell
cell_layer = new deck.ScatterplotLayer({
id: 'cell_layer',
data: cell_data,
getPosition: apply_map_morph,
getFillColor: d => {
var inst_color
if (select_meta === 'none'){
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 of multiple genes
var inst_opacity = mean_opacity_scale(parseInt(mean_gene_data[d['name']]['mean-gene']))
if (inst_opacity > 0){
inst_color = [255, 0, 0, inst_opacity]
} else {
inst_color = [50, 50, 50, 10]
}
}
else {
// single gene
var inst_opacity = opacity_scale(parseInt(gene_data[d['name']][select_meta]))
if (inst_opacity > 0){
inst_color = [255, 0, 0, inst_opacity]
} else {
inst_color = [50, 50, 50, 10]
}
}
return inst_color
},
getRadius: radius,
pickable: is_pickable,
highlightColor: d => [50, 50, 50],
radiusMinPixels: radius_min_pixels,
updateTriggers: {
getFillColor: [select_meta, selected_leidens, mean_gene_data],
getPosition: map_type
},
transitions: transitions,
onClick: (info, event) => {
if (mutable selected_leidens[0] !== info.object.leiden){
mutable select_meta = 'leiden'
mutable selected_leidens = [info.object.leiden]
} else {
mutable select_meta = 'leiden'
mutable selected_leidens = []
}
// if (d3.select('#meta_dropdown').select('select').node().value !== info.object.name){
// mutable select_meta = info.object.name
// d3.select('#meta_dropdown').select('select').node().value = info.object.name
// } else {
// mutable select_meta = 'leiden'
// d3.select('#meta_dropdown').select('select').node().value = 'leiden'
// }
}
})
Insert cell
radius = 2
Insert cell
// map_type = 'Spatial'
Insert cell
radius_min_pixels = 1.5
Insert cell
transitionDuration = 3000
Insert cell
transitions = ({
getPosition: {
duration:transitionDuration,
easing: d3.easeCubic
}
})
Insert cell
// have deck.gl animate
function apply_map_morph(d){
var inst_x
var inst_y
if (map_type === 'Spatial'){
inst_x = d.x
inst_y = -d.y
} else {
inst_x = umap_x_scale(d['umap-x'])
inst_y = - umap_y_scale(d['umap-y'])
}
return [inst_x, inst_y]
}
Insert cell
umap_x_scale = d3.scaleLinear()
.domain([umap_x_min, umap_x_max])
.range([x_min, x_max]);
Insert cell
umap_y_scale = d3.scaleLinear()
.domain([umap_y_min, umap_y_max])
.range([y_min, y_max]);
Insert cell
x_min = Math.min.apply(Math, cell_data.map(function(o) { return parseFloat(o['x']) }))
Insert cell
x_max = Math.max.apply(Math, cell_data.map(function(o) { return parseFloat(o['x']) }))
Insert cell
y_min = Math.min.apply(Math, cell_data.map(function(o) { return parseFloat(o['y']) }))
Insert cell
y_max = Math.max.apply(Math, cell_data.map(function(o) { return parseFloat(o['y']) }))
Insert cell
umap_x_min = Math.min.apply(Math, cell_data.map(function(o) { return parseFloat(o['umap-x']) }))
Insert cell
umap_x_max = Math.max.apply(Math, cell_data.map(function(o) { return parseFloat(o['umap-x']) }))
Insert cell
umap_y_min = Math.min.apply(Math, cell_data.map(function(o) { return parseFloat(o['umap-y']) }))
Insert cell
umap_y_max = Math.max.apply(Math, cell_data.map(function(o) { return parseFloat(o['umap-y']) }))
Insert cell
transcript_layer = new deck.ScatterplotLayer({
id: 'transcript_layer',
data: transcript_data,
pickable: is_pickable,
// getPosition: d => [d.x, - d.y],
getPosition: d => d.position,
getFillColor: d => {
var color
if (map_type === 'Spatial'){
color = d.color
} else {
color = [0, 0, 0, 0]
}
return color
},
getRadius: 0.25,
radiusMinPixels: 0.5,
// opacity: d => {
// var opacity
// if (map_type === 'Spatial'){
// opacity = 0.5
// } else {
// opacity = 0.0
// }
// return opacity
// },
pickable: true,
visible: visible_transcripts,
updateTriggers: {
getFillColor: map_type,
},
onClick: (info, event) => {
if (d3.select('#meta_dropdown').select('select').node().value !== info.object.name){
mutable select_meta = info.object.name
d3.select('#meta_dropdown').select('select').node().value = info.object.name
} else {
mutable select_meta = 'leiden'
d3.select('#meta_dropdown').select('select').node().value = 'leiden'
}
}
})
Insert cell
mutable visible_transcripts = false
Insert cell
layers = [cell_layer, polygon_layer, transcript_layer]
// layers = [polygon_layer]
Insert cell
is_pickable = true
Insert cell
deckgl.setProps({layers: layers});
Insert cell
view = new deck.OrthographicView({id: 'ortho'})
Insert cell
deckgl = {
return new deck.DeckGL({
container,
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.tooltip_name}`,
style: {
fontSize: '0.8em',
padding: '5px',
}
},
onViewStateChange: ({viewState}) => {
debounced_something(viewState)
return viewState
},
// onClick: ({info}) => {
// console.log(info)
// return info
// }
});
}
Insert cell
initial_view_state = ({
target: [2000, 7000, 0],
zoom: -3.5,
minZoom: -3.75,
maxZoom:10
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
height = 800
Insert cell
Insert cell
detail_zoom_thresh = 1.25
Insert cell
function something(viewState){
var height = viewState.height
var width = viewState.width
var zoom = Math.pow(2, viewState.zoom)
if (zoom > detail_zoom_thresh){
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 fovs = cell_data
.filter(d => d.x >= min_x)
.filter(d => d.x < max_x)
// y values are inverted
.filter(d => -d.y >= min_y)
.filter(d => -d.y < max_y)
.map(x => x.fov)
.filter((x, i, a) => a.indexOf(x) === i)

mutable visible_transcripts = true
} else {
mutable visible_transcripts = false
mutable fovs = []
}
}
Insert cell
visible_transcripts
Insert cell
debounced_something = debounce(something, 250);
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
mutable fovs = []
Insert cell
# Requirements
Insert cell
deck = require.alias({
// optional dependencies
h3: {}
})('deck.gl@latest/dist.min.js')
Insert cell
Insert cell
function load_zip_csv(url) {

const data = fetch(url)
.then(x => x.arrayBuffer())
.then(x => jszip.loadAsync(x))
.then(buf => {
const file = Object.keys(buf.files)[0];
const js_array = buf.file(file)
.async("string")
.then(x => d3.csvParse(x, d3.autoType))
return js_array
})

return data
}
Insert cell
jszip = require("jszip@3/dist/jszip.min.js")
Insert cell
Insert cell
// tmp_url = 'https://raw.githubusercontent.com/cornhundred/testing_something/master/transcripts_csv_zip_round_colors/' +
// String(100) + '.csv.zip'

Insert cell
// load_zip_csv(tmp_url)
Insert cell
// mutable tmp1 = []
Insert cell
// tmp2 = []
Insert cell
// mutable tmp1.push(tmp2)
Insert cell
polygon_layer = new deck.PolygonLayer({
id: 'polygon_layer',
data: cell_polygons,
// getFillColor: [250, 0, 0],
getLineColor: [0, 0, 0, 150],
getLineWidth: d => 0.2,
getPolygon: d => d.coordinates,
stroked: true,
filled: false,
wireframe: true,
pickable: true,
lineWidthMinPixels: 0.1,
visible: visible_transcripts,
// opacity: rescue_opacity
});
Insert cell
// mutable tmp1
Insert cell
Insert cell
data_ini = inputs['data']
Insert cell
data = data_ini
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
zip_gene_data_url = 'https://raw.githubusercontent.com/cornhundred/testing_something/master/per-gene-counts_slice-1_replicate-2_zip/' + select_meta + '.csv.zip'
Insert cell
mutable gene_data = {
var gene_data

// reset gene_data
if (['none', 'leiden', 'mean-gene'].includes(select_meta)){
gene_data = ([{}])
} else {
gene_data = load_zip_csv(zip_gene_data_url)
}
return gene_data
}
Insert cell
cat_opacity = 0.9
Insert cell
mean_opacity_scale = d3.scaleLinear()
.domain([0, mean_gene_max])
.range([0, 250])
Insert cell
opacity_scale = d3.scaleLinear()
.domain([0, 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_data
Insert cell
mean_gene_max = Math.max.apply(Math, mean_gene_data.map(function(x) { return parseInt(x['mean-gene']) })) * 0.90
Insert cell
ini_map_type = 'UMAP'
Insert cell
map_type
Insert cell
// load_zip_csv(zip_gene_data_url)
Insert cell
Insert cell
// cell_polygon_data = Promise.all([49].map(fov => {
// const url = 'https://raw.githubusercontent.com/cornhundred/testing_something/master/cell_polygons/cells_' +
// String(fov) + '.json'
// return fetch(url)
// }))
Insert cell
// response = {
// const response = await fetch(cell_polygon_url);
// // return {
// // ok: response.ok,
// // body: await response.text()
// // };
// return response.text()
// }
Insert cell
// cell_polygons = fetch(cell_polygon_url)
// .then(response => response.json())
// .then (d => d)
Insert cell
// requested_fovs = [30, 98, 99]
Insert cell
// available_fovs = [98, 99]
Insert cell
cell_poly_fovs = fovs.filter(x => available_fovs.includes(x))
Insert cell
promised_cell_polygons = Promise.all(cell_poly_fovs.map(fov => {
const url = 'https://raw.githubusercontent.com/cornhundred/testing_something/master/cell_polygons/cells_' + fov + '.json'

var cell_polygons
if (map_type === 'Spatial'){
cell_polygons = fetch(url)
.then(response => response.json())
} else {
cell_polygons = []
}

return cell_polygons
}))
Insert cell
barcode_id_gene_url = 'https://raw.githubusercontent.com/cornhundred/testing_something/master/barcode_id_gene_dictionary.json'
Insert cell
barcode_id_gene_dictionary = fetch(barcode_id_gene_url)
.then(d => d.json())
Insert cell
cell_polygons = [].concat.apply([], promised_cell_polygons)
Insert cell
await fetch(cell_polygon_url)
Insert cell
fetch(cell_polygon_url)
Insert cell
cell_polygon_url = 'https://raw.githubusercontent.com/cornhundred/testing_something/master/cell_polygons/cells_99.json'
Insert cell
await fetch(cell_polygon_url)
Insert cell
fetch('https://raw.githubusercontent.com/cornhundred/testing_something/master/cell_polygons/available_fovs_cell_polygons.csv')
.then(d => d)
Insert cell
available_fovs_ini = d3.text('https://raw.githubusercontent.com/cornhundred/testing_something/master/cell_polygons/available_fovs_cell_polygons.csv')
// .apply(x => x)
Insert cell
available_fovs = d3.csvParseRows(available_fovs_ini).map(x => parseInt(x[0]))
Insert cell
viewof map_type = {
const element = html`
<div style="display: inline-block; user-select: none;"></div>`;

d3.select(element)
.style('padding-right', '25px')
.selectAll('div')
.data(['UMAP', 'Spatial'])
.join('span')
.on('click', function(d){

// clear fovs when switching map types to prevent
// fovs from UMAP view propagating to Spatial view and
// causing many requests for FOV transcripts and cell polygons
mutable fovs = []
d3.select(element).selectAll('span')
.style('color', '#808080')
d3.select(this)
.style('color', 'blue')
element.value = d.replace(', ', '')
element.dispatchEvent(new CustomEvent("input"));
})
.style('min-width', '200px')
.style('max-width', '200px')
.style('margin-right', '10px')
.text(d => d)
.style('font-weight', '550px')
.style("font-family", "sans-serif")
.style("font-size", "16")
.style("text-anchor", "end")
.style('font-weight', 'bold')
.style('color', d => d === 'UMAP' ? 'blue': '#808080')
.attr('id', d => d)



element.value = ini_map_type
element.dispatchEvent(new CustomEvent("input"))

return element
}
Insert cell
import {select} from '@jashkenas/inputs'
Insert cell
meta_data_cols = meta_data_cols_ini.concat(gene_names)
Insert cell
non_param_cols = ['x', 'y', 'umap-x', 'umap-y', 'name', 'fov', 'tooltip_name']
Insert cell
meta_data_cols_ini = Object.keys(data[0])
.filter(x => non_param_cols.includes(x) === false)
Insert cell
gene_names = meta_gene.map(x => x['']).sort()
Insert cell
meta_gene = meta_gene_ini
.filter(x => x[''].includes('Blank') === false)
.sort(function(a, b){
return b['mean'] - a['mean'];
});
Insert cell
meta_gene_url = 'https://raw.githubusercontent.com/cornhundred/testing_something/master/meta_gene.csv'
Insert cell
meta_gene_ini = d3.csv(meta_gene_url)
Insert cell
meta_gene_ini[0]
Insert cell
d3 = require("d3@5")
Insert cell
Insert cell
CGM = require(await FileAttachment("clustergrammer-gl@1.0.22.0.packd.js").url())
Insert cell
network = FileAttachment("leiden_cgm_gene_cats.json").json()
Insert cell
heatmap_row
Insert cell
select_meta
Insert cell
viewof cgm = {

// Clustergrammer-GL uses query selectors to re-find the container, so if we
// might want multiple instances of Clustergrammer-GL on the page, they’ll
// need unique identifiers! (It’d be better if Clustergrammer-GL used
// element.querySelector instead of document.querySelector and didn’t dependf
// on these identifiers.)
const containerId = DOM.uid("container");
const container = html`<div style="height: 1025px;" id='${containerId.id}'></div>`;

// Clustergrammer-GL depends on the container having been inserted into the
// document before it’s initialized. We can do this by yielding! However,
// since we’re defining this cell as a view, we don’t want the view’s value
// to resolve until CGM has initialized. Hence we define the view’s value as
// a promise that resolves immediately after. We could simplify this if
// Clustergrammer-GL allowed you to pass in a detached container.
let resolve;
container.value = new Promise(r => resolve = r);
yield container;

// Initialize CGM.
// (We might want to use Observable’s reactive width here?)
const cgm = CGM({
network,
viz_width: 900,
viz_height: 900,
container
});

// Redefine the order parameters as getters and setters, so that we can intercept
// when these change and emit an event. That will allow other cells to listen for
// these changes are react accordingly.
Object.defineProperties(cgm.params.order.new, {
row: reactiveValue(container, "row", cgm.params.order.new.row),
col: reactiveValue(container, "col", cgm.params.order.new.col)
});

// Resolve the view’s value so that downstream cells can run.
resolve(cgm);
d3.select('.row_search_container').remove()
}
Insert cell
mutable selected_leidens = []
Insert cell
col_selection = Generators.observe(notify => {

const mousemove = () => {
if (cgm.params.tooltip.tooltip_type === 'col-dendro'){
mutable select_meta = 'leiden'
mutable dendro_selected_genes = []
mutable 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
mutable dendro_selected_genes = []
Insert cell
// promised_selected_genes
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
// cell_data
Insert cell
row_selection = Generators.observe(notify => {

const mousemove = () => {
// if (cgm.params.tooltip.tooltip_type.includes('dendro')){
if (cgm.params.tooltip.tooltip_type === 'row-dendro'){
mutable select_meta = 'mean-gene'
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
heatmap_col = Generators.observe(notify => {
const mouse_click = () => {

// clicking cluster col
//////////////////////////////
if (cgm.params.tooltip.tooltip_type === 'col-label'){
mutable select_meta = 'leiden'
mutable dendro_selected_genes = []
mutable selected_leidens = [cgm.params.int.mouseover.col.name.replace('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
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
function reactiveValue(container, name, value) {
return {
get() {
return value;
},
set(v) {
value = v;
container.dispatchEvent(new CustomEvent("input", {detail: {name, value}}));
return value;
}
};
}
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