scRNA-seq_analysis

This commit is contained in:
veghp 2019-07-08 12:22:01 +01:00
commit 82cc2d191e
188 changed files with 146184 additions and 0 deletions

93
tools/AGA/AGA_from_Seurat.py Executable file
View file

@ -0,0 +1,93 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Aug 1 15:18:12 2018
@author: doru
"""
import sys
args = sys.argv
CWD = args[1]
#split_cat = args[2]
from os import chdir
chdir(CWD)
import matplotlib; matplotlib.use('Agg');
import scanpy.api as sc;
import pandas as pd
from scipy.sparse import csr_matrix
import numpy as np
sc.settings.verbosity = 3
scObj = sc.read("./material/raw_data.mtx", cache = False).T
# load gene names
scObj.var_names = pd.read_csv("./material/genenames.csv").iloc[:, 1]
# load cell names
scObj.obs_names = pd.read_csv("./material/cellnames.csv").iloc[:, 1]
# filter out genes present in less than 3 cells
sc.pp.filter_genes(scObj, min_cells=3)
# log-normalize the data
scObj.raw = sc.pp.log1p(scObj, copy=True)
sc.pp.normalize_per_cell(scObj, counts_per_cell_after=1e4)
# variable genes
filter_result = sc.pp.filter_genes_dispersion(
scObj.X, min_mean=0.0125, max_mean=3, min_disp=0.5)
# subset data on variable genes
scObj = scObj[:, filter_result.gene_subset]
# not sure?
sc.pp.log1p(scObj)
# scale the data
sc.pp.scale(scObj, max_value=10)
# run pca
sc.tl.pca(scObj)
# compunte neighborhood graph
sc.pp.neighbors(scObj, n_neighbors = 15, n_pcs = 20, knn = True, random_state = 10, method = "gauss")
# add cell labels
cell_labels = pd.read_csv("./material/cell_labels.csv", index_col = 0)
scObj.obs["cell_labels"] = cell_labels
# run aga
sc.tl.paga(scObj, groups = "cell_labels")
# save the scipy paga graph to disk for comparison to the plot generated by ggplot - for trouble shooting
sc.pl.paga(scObj, save = "_ugly_scanpy_plot.pdf", show = True, edge_width_scale = .4, solid_edges = "connectivities", layout="fa")
#sc.pl.paga(scObj, save = "ugly_scanpy_plot.pdf", show = False, edge_width_scale = .4, solid_edges = "connectivities",
# layout = "fa")
#sc.pl.paga(scObj, save = "_ugly_scanpy_plot.pdf", show = True, edge_width_scale = .4, solid_edges = "connectivities", layout = "fa")
#sc.pl.paga(scObj, save = "{split_cat}_ugly_scanpy_plot.pdf".format(split_cat=split_cat), show = True, edge_width_scale = .4, solid_edges = "connectivities", layout = "fa")
#sc.pl.paga(scObj, save = "{split_cat}_ugly_scanpy_plot.pdf".format(split_cat=split_cat),
# show = True, edge_width_scale = .4, solid_edges = "connectivities",
# layout = "fa") # layout = "fa"
# prepare the output and save it to disk
cell_cats = list(scObj.obs["cell_labels"].cat.categories)
population_size = cell_labels["Labels"].value_counts()
population_size = population_size[cell_cats].values
connectivities = np.array(csr_matrix.todense(scObj.uns["paga"]["connectivities"]), dtype = "float64")
connectivities[connectivities < .05] = 0.0
#connectivities[connectivities < .1] = 0.0
connectivities = pd.DataFrame(connectivities, columns = cell_cats, index = cell_cats)
connectivities.to_csv("./AGA_folder/connectivities.csv", index = True, header = True)
coordinates = pd.DataFrame(scObj.uns["paga"]["pos"], columns = ["X", "Y"], index = cell_cats)
coordinates["Size"] = pd.Series(population_size, coordinates.index)
coordinates.to_csv("AGA_folder/coordinates.csv", index = True, header = True)

354
tools/bunddle_utils.R Executable file
View file

@ -0,0 +1,354 @@
tool_addr = "../../tools"
python.addr = "python3.6"
# a function to compute force-directed graph; return coordinates as a data frame
# arguments:
# pca.df: pca data as a data frame
# snn : a nearest-neighbor graph as a sparse data matrix
runFDG = function(pca.df, snn, iterations = 600, tool_addr, python.addr){
current.wd = getwd()
setwd(file.path(tool_addr, "force_abstract_graph_2D"))
# generate unique name for pca data file
pca.data.fname = paste(sample(LETTERS, 20, TRUE), collapse = "")
pca.data.fname = paste(pca.data.fname, ".csv", sep = "")
# generate unique name for snn file
snn.fname = paste(sample(LETTERS, 20, TRUE), collapse = "")
snn.fname = paste(snn.fname, ".smm", sep = "")
# generate unique name for fdg coordinates
fdg.coordinates.fname = paste(sample(LETTERS, 20, TRUE), collapse = "")
fdg.coordinates.fname = paste(fdg.coordinates.fname, ".csv", sep = "")
write.csv(pca.df, pca.data.fname)
writeMM(obj=snn, file=snn.fname)
command = gsub(pattern="ITER", replacement=as.character(iterations), paste(python.addr, "./make_fdg.py ITER", sep = " "))
command = paste(command, paste(c(pca.data.fname, snn.fname, fdg.coordinates.fname), collapse = " "), sep = " ")
system(command, wait = T)
fdg_coordinates = read.csv(fdg.coordinates.fname, header = FALSE)
colnames(fdg_coordinates) = c("X", "Y")
rownames(fdg_coordinates) = rownames(pca.df)
file.remove(c(pca.data.fname, snn.fname, fdg.coordinates.fname))
setwd(current.wd)
return(fdg_coordinates)
}
# a function to perform UMAP on a seurat object using PCA coordinates
RunUMAP = function(pca.df, tool_addr, python.addr){
current.wd = getwd()
setwd(file.path(tool_addr, "umap"))
print("writting pca data to disk...")
# generate unique name for pca data file
pca.data.fname = paste(sample(LETTERS, 20, TRUE), collapse = "")
pca.data.fname = paste(pca.data.fname, ".csv", sep = "")
# generate unique name for umap coordinates data file
umap.coordinates.fname = paste(sample(LETTERS, 20, TRUE), collapse = "")
umap.coordinates.fname = paste(umap.coordinates.fname, ".csv", sep = "")
write.csv(pca.df, pca.data.fname)
print("perfoming UMAP")
command = paste(python.addr, "umap_compute.py", sep = " ")
command = paste(command, pca.data.fname, sep = ' ')
command = paste(command, umap.coordinates.fname, ' ')
system(command, wait = T)
print("Reading results...")
umap.coordinates = read.csv(umap.coordinates.fname, stringsAsFactors = F)
file.remove(c(pca.data.fname, umap.coordinates.fname))
setwd(current.wd)
umap.coordinates = umap.coordinates[, c("UMAPx", "UMAPy")]
return(umap.coordinates)
}
# a function that load a SVM models and make predictions like cell types or doublet.singlets
Apply_Classifier_On_Seurat_Object = function(seurat.obj, classifier.fname, tool_addr, python.addr){
current.wd = getwd()
setwd(file.path(tool_addr, 'predict_by_classifier'))
predictor.addr = file.path("../../resources", classifier.fname, sep = "")
print(predictor.addr)
if(!dir.exists(predictor.addr)){
print("classifier does not exists")
available.classifiers = list.dirs("../../resources", full.names=F)
available.classifiers = available.classifiers[grepl("classifier_", available.classifiers)]
print(paste("Available doublets identifiers: ", paste(available.classifiers, collapse = ", "), sep = ""))
setwd(current.wd)
return(NULL)
}
tryCatch({
OK = FALSE
print("reading feature genes ...")
feature.genes = readRDS(file.path(predictor.addr, "feature_genes.RDS"))
features.present = feature.genes[feature.genes %in% rownames(seurat.obj@data)]
features.not.present = feature.genes[!(feature.genes %in% rownames(seurat.obj@data))]
expr.data = as.data.frame(t(as.matrix(seurat.obj@data[features.present, ])))
if (length(features.not.present) > 0){
zeros.data = as.data.frame(t(as.matrix(seurat.obj@data[1:length(features.not.present),])))
zeros.data[] = 0
colnames(zeros.data) = features.not.present
expr.data=cbind(expr.data, zeros.data)
expr.data = expr.data[, feature.genes]
}
print("Writting data to disk ... ")
data.fname = paste(sample(LETTERS, 20, TRUE), collapse = "")
data.fname = paste(data.fname, ".csv", sep = '')
write.csv(expr.data, data.fname)
print(sprintf("Dims are: %s", dim(expr.data)))
print("Copying pickle file ... ")
model.fname = paste(sample(LETTERS, 20, T), collapse = '')
model.fname = paste(model.fname, '.pickle', sep = "")
file.copy(from=file.path(predictor.addr, "model.pickle"), to=model.fname)
pca.fname = paste(sample(LETTERS, 20, T), collapse = '')
pca.fname = paste(pca.fname, '.pickle', sep = "")
file.copy(from=file.path(predictor.addr, "pca.pickle"), to=pca.fname)
print("Running classifier in Python ... ")
predictions.fname = paste(sample(LETTERS, 20, TRUE), collapse = "")
predictions.fname = paste(predictions.fname, ".csv", sep = "")
command = paste(python.addr, "predict.py", sep = " ")
command = paste(command, paste(c(model.fname, data.fname, predictions.fname, pca.fname), collapse = " "), sep = " ")
system(command, wait = T)
predictions = read.csv(predictions.fname)
OK = TRUE
},
warning = function(warning_conditions){print("")},
error = function(error_condition){
print("Errors occured. Cleaning up and then returning NULL ...")
file.remove(c(model.fname, data.fname, predictions.fname, pca.fname))
setwd(current.wd)
})
if(OK){
if(length(features.not.present) > 0){
print('Everything went well except the fact that some of the features genes were not present in the data. These are:')
print(paste(features.not.present, collapse = ", "))
}else{
print("Everything went smooth. Cleaning up and returning the predictions ... ")
}
file.remove(c(model.fname, data.fname, predictions.fname, pca.fname))
setwd(current.wd)
return(predictions$X0)
}
return(NULL)
}
# a function that takes 3D coordinates (e.g. from diffusion map) and generates an
# interactive html page
make_3D_interactive_page = function(data_frame_3D, tool_addr, python.addr, save.to){
data.frame.fname = paste(sample(LETTERS, 20, T), collapse = '')
data.frame.fname = paste(data.frame.fname, ".csv", sep = "")
save.to = file.path(getwd(), save.to)
command = gsub(pattern="save.to", replacement=save.to, x=paste(python.addr, './html_WebGL_3D_viewer.py save.to', sep = " "))
command = paste(command, data.frame.fname, sep = " ")
current.wd = getwd()
setwd(file.path(tool_addr, "interactive_3D_viewer"))
write.csv(data_frame_3D, data.frame.fname, row.names = F)
system(command, wait=T)
file.remove(data.frame.fname)
setwd(current.wd)
}
# a function that takes 2D coordinates and generates an interactive html page
make_2D_interactive_page = function(data_frame_2D, tool_addr, python.addr, save.to="./"){
data.frame.fname = paste(sample(LETTERS, 20, T), collapse = '')
data.frame.fname = paste(data.frame.fname, ".csv", sep = "")
save.to = file.path(getwd(), save.to)
command = gsub(pattern="save.to", replacement=save.to, x=paste(python.addr, './html_WebGL_2D_viewer.py save.to', sep = " "))
command = paste(command, data.frame.fname, sep = " ")
current.wd = getwd()
setwd(file.path(tool_addr, "interactive_2D_viewer"))
write.csv(data_frame_2D, data.frame.fname, row.names = F)
system(command, wait=T)
file.remove(data.frame.fname)
setwd(current.wd)
}
# a function that create interactic html pages to explore gene expression in data parsed by different categories
create_gene_expression_viewer_apps = function(seurat.obj, dim.type = 'umap', save.to, tool_addr, python.addr, categories.colours=NA){
categories = c("cell.labels", "fetal.ids", "sort.ids", "lanes", "stages", "gender", "doublets")
if(is.na(categories.colours)){
categories.colours = rep(NA, length(categories))
}
categories.data = as.data.frame(seurat.obj@meta.data[names(seurat.obj@ident), categories])
for(j in 1:length(categories)){
category = categories[j]
category.colour.scheme = categories.colours[j]
if (!is.na(category.colour.scheme)){
category.colour.scheme = read.csv(category.colour.scheme)
category.colour.scheme = mapvalues(x=categories.data[, category], from=as.vector(unique(category.colour.scheme$CellTypes)), to=as.vector(unique(category.colour.scheme$Colours)))
}else{
category.colour.scheme = sample(colorRampPalette(brewer.pal(12, "Paired"))(length(as.vector(unique(categories.data[, category])))))
category.colour.scheme = mapvalues(x=categories.data[, category], from=as.vector(unique(categories.data[, category])), to=category.colour.scheme)
}
category = paste(category, "colours", sep = "_")
categories.data[, category] = category.colour.scheme
}
eval(parse(text = sprintf("dim.data = seurat.obj@dr$%s@cell.embeddings[names(seurat.obj@ident), 1:2]", dim.type)))
n.categories = length(categories)
genes.by.file = round( 20 * 26149846 / ncol(seurat.obj@data))
approx.no.of.files = round(nrow(seurat.obj@data) / genes.by.file)
gene.names = sort(rownames(seurat.obj@data))
gene.names = sort(seurat.obj@var.genes)
gene.splits = split(gene.names, sort(1:length(gene.names) %% approx.no.of.files))
curdir = getwd()
unlink(x=save.to, recursive=T)
dir.create(save.to)
setwd(file.path(tool_addr, 'gene_expression_viewer_apps'))
folder_name = paste(sample(LETTERS, 20, T), collapse = "")
dir.create(folder_name)
for (l in 1:length(gene.splits)){
gene.split = unlist(gene.splits[[l]])
expression.data = as.data.frame(as.matrix(t(seurat.obj@data[gene.split, names(seurat.obj@ident)])))
expression.data = cbind(dim.data, categories.data, expression.data)
first_gene = gene.split[1]
last_gene = gene.split[length(gene.split)]
print(sprintf("Creating app for: %s", paste(c(first_gene, "to", last_gene), collapse = "_")))
expression.data.fname = paste(c(first_gene, "to", last_gene), collapse = "_")
expression.data.fname = paste(expression.data.fname, ".csv", sep = "")
expression.data.fname = file.path(folder_name, expression.data.fname)
save_to = paste(c(first_gene, "to", last_gene), collapse = "_")
save_to = paste(save_to, ".html", sep = "")
save_to = file.path(file.path(curdir, save.to), save_to)
command = sprintf("%s gene_expression_viewer_apps.py %s %s %s", python.addr, save_to, expression.data.fname, n.categories)
write.csv(expression.data, expression.data.fname, row.names = F)
system(command, wait = T)
}
unlink(x=folder_name, recursive=T, force=T)
setwd(curdir)
}
# a plotting function for indexed legend
plot.indexed.legend = function(label.vector, color.vector, ncols = 2, left.limit = 3.4, symbol.size = 8, text.size = 10, padH = 1, padV = 1, padRight = 0){
if (length(label.vector) != length(color.vector)){
stop("number of labels is different from number colors\nAdvice: learn to count!")
}
if (length(ncol) > length(label.vector)){
stop("You cannot have more columns than labels\nSolution: Learn to count")
}
indices.vector = 1:length(label.vector)
label.no = length(label.vector)
nrows = ceiling(label.no / ncols)
legend.frame = data.frame(X = rep(0, label.no), Y = rep(0, label.no), CS = color.vector, Txt = label.vector)
legend.frame$X = rep(1:ncols, each=nrows)[1:nrow(legend.frame)]
legend.frame$Y = rep(nrows:1, times = ncols)[1:nrow(legend.frame)]
Xrange = range(legend.frame$X)
Yrange = range(legend.frame$Y)
plot.obj = ggplot(data = legend.frame, aes(x = X, y = Y))
plot.obj = plot.obj + geom_point(size = symbol.size, colour = color.vector)
plot.obj = plot.obj + scale_x_continuous(limits = c(Xrange[1] - padRight, Xrange[2] + padH))
plot.obj = plot.obj + scale_y_continuous(limits = c(Yrange[1] - padV, Yrange[2] + padV))
plot.obj = plot.obj + theme_void()
plot.obj = plot.obj + annotate("text", x=legend.frame$X, y = legend.frame$Y, label = indices.vector, size = text.size)
plot.obj = plot.obj + annotate("text", x=legend.frame$X+.1, y = legend.frame$Y, label=legend.frame$Txt, hjust = 0, size = text.size)
return(plot.obj)
}
# plotting function for dimensionaly-reduced data to label population by a round indexed label
dr.plot = function(point.labels, dr1, dr2, dr1.name, dr2.name, no.legend = F, plt.lb.sz = 5, txt.lb.size = 3, pt.size = .2, random_state = 2, use.cols = NULL, use.labels = NULL, limits = NULL, annotate.plot = T, index.map = NA){
if(!is.na(overlay.data)){
df.dr = data.frame("Cell Labels" = point.labels, DR1 = dr1, DR2 = dr2, overlay.data=factor(df$overlay.data))
}
else{
df.dr = data.frame("Cell Labels" = point.labels, DR1 = dr1, DR2 = dr2)
}
if(is.null(use.labels)){
p.labels = sort(unique(as.vector(point.labels)))
}
else{
p.labels = use.labels
}
df.dr$Cell.Labels = factor(df.dr$Cell.Labels, levels=p.labels)
p.labels.medians = aggregate(df.dr[, 2:3], list(df.dr$Cell.Labels), median)
df.dr$Cell.Labels = mapvalues(x = df.dr$Cell.Labels, from = p.labels, to = paste(1:length(p.labels), p.labels, sep = " "))
if(is.null(use.cols)){
set.seed(random_state)
plt.colours = sample(colorRampPalette(brewer.pal(12, "Paired"))(length(p.labels)))
}else{
plt.colours = use.cols
}
if(is.na(index.map)){
index.map = 1:length(p.labels)
}
if(!is.na(overlay.data)){
plot.obj = ggplot(data = df.dr, aes(x = DR1, y = DR2, color = Cell.Labels, shape = factor(overlay.data, levels=overlay.data.ordered)))
print("levels=overlay.data.ordered")
print(overlay.data.ordered)
}
else{
plot.obj = ggplot(data = df.dr, aes(x = DR1, y = DR2, color = Cell.Labels))
}
# this line should give different sizes for different values in overlay.data metadata column
if(!is.na(overlay.data)){
plot.obj = plot.obj + geom_point(size=pt.size)
plot.obj = plot.obj + geom_point(data = subset(df.dr, overlay.data == overlay.data.ordered[2]))
print("overlay.data.ordered[2]")
print(overlay.data.ordered[2])
}
else{
plot.obj = plot.obj + geom_point(size=pt.size)
}
plot.obj = plot.obj + scale_color_manual(values=plt.colours)
if(annotate.plot){
if(!is.na(overlay.data)){
plot.obj = plot.obj + geom_point(data=p.labels.medians,aes(x = DR1, y = DR2), colour = "gray", size = plt.lb.sz, fill = plt.colours, alpha = .5, pch = 21, shape=factor(df.dr$overlay.data, levels=overlay.data.ordered))
}
else{
plot.obj = plot.obj + geom_point(data=p.labels.medians,aes(x = DR1, y = DR2), colour = "gray", size = plt.lb.sz, fill = plt.colours, alpha = .5, pch = 21)
}
plot.obj = plot.obj + annotate("text", x=p.labels.medians$DR1, y = p.labels.medians$DR2, label = index.map, size = txt.lb.size)
}
if (no.legend){
plot.obj = plot.obj + theme(legend.position="none")
}else{
plot.obj = plot.obj + guides(color = guide_legend(override.aes = list(size=5)))
}
plot.obj = plot.obj + xlab(dr1.name) + ylab(dr2.name)
if(!is.null(limits)){
X0 = limits[1]; X1 = limits[2]; Y0 = limits[3]; Y1 = limits[4];
plot.obj = plot.obj + scale_x_continuous(limits = c(X0, X1))
plot.obj = plot.obj + scale_y_continuous(limits = c(Y0, Y1))
}
return(plot.obj)
}
# plotting function for dimensionaly-reduced data to label population by a round indexed label
dr.plot.numerical = function(point.labels, dr1, dr2, dr1.name, dr2.name, no.legend = F, plt.lb.sz = 5, txt.lb.size = 3, pt.size = .2, random_state = 2, use.cols = NULL, use.labels = NULL, limits = NULL, annotate.plot = T, index.map = NA){
df.dr = data.frame("Cell Labels" = point.labels, DR1 = dr1, DR2 = dr2)
if(is.null(use.labels)){
p.labels = sort(unique(as.vector(point.labels)))
}
else{
p.labels = use.labels
}
df.dr$Cell.Labels = factor(df.dr$Cell.Labels, levels=p.labels)
p.labels.medians = aggregate(df.dr[, 2:3], list(df.dr$Cell.Labels), median)
#df.dr$Cell.Labels = mapvalues(x = df.dr$Cell.Labels, from = p.labels, to = paste(1:length(p.labels), p.labels, sep = " "))
if(is.null(use.cols)){
set.seed(random_state)
plt.colours = sample(colorRampPalette(brewer.pal(12, "Paired"))(length(p.labels)))
}else{
plt.colours = use.cols
}
if(is.na(index.map)){
#index.map = 1:length(p.labels)
index.map = p.labels
}
plot.obj = ggplot(data = df.dr, aes(x = DR1, y = DR2, color = Cell.Labels))
plot.obj = plot.obj + geom_point(size = pt.size)
plot.obj = plot.obj + scale_color_manual(values=plt.colours)
if(annotate.plot){
plot.obj = plot.obj + geom_point(data=p.labels.medians,aes(x = DR1, y = DR2), colour = "gray", size = plt.lb.sz, fill = plt.colours, alpha = .5, pch = 21)
plot.obj = plot.obj + annotate("text", x=p.labels.medians$DR1, y = p.labels.medians$DR2, label = index.map, size = txt.lb.size)
}
if (no.legend){
plot.obj = plot.obj + theme(legend.position="none")
}else{
plot.obj = plot.obj + guides(color = guide_legend(override.aes = list(size=5)))
}
plot.obj = plot.obj + xlab(dr1.name) + ylab(dr2.name)
if(!is.null(limits)){
X0 = limits[1]; X1 = limits[2]; Y0 = limits[3]; Y1 = limits[4];
plot.obj = plot.obj + scale_x_continuous(limits = c(X0, X1))
plot.obj = plot.obj + scale_y_continuous(limits = c(Y0, Y1))
}
return(plot.obj)
}

261
tools/color_management.html Executable file
View file

@ -0,0 +1,261 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Color Picker</title>
<meta name="description" content="interactive page for color picker">
<meta name="author" content="Dorin-Mirel Popescu">
<style>
#div_cats{
overflow-y: scroll;
height: 400px;
}
#instructionBtn {
margin-bottom: 1em;
font-size: 1em;
background-color: #00bfff;
border: none;
color: white;
padding: 10px 10px;
text-align: center;
text-decoration: none;
cursor: pointer;
}
#end_div {
margin-bottom: 3em;
}
</style>
</head>
<body>
<h2>Colors Management</h2>
<input id = 'instructionBtn' type = 'button' value = 'Show description and instructions' onclick = 'showHideInstructions(value)' />
<div id = 'instructionBoard'>
<div><b>Author:</b> Dorin-Mirel Popescu</div>
<div><b>Description: </b>This is a tool used for generating color mappings. The output can be saved as a csv file that maps colours to categories.</div>
<div>
<b>Instructions:</b>
<ul>
<li>Enter a number in the text area right to "Number of categories:"</li>
<li>Press the button "Make". This will generate coloured dots on the plot at right. For each dot there is a corresponding control panel in the column at left</li>
<li>Each dot and its menu are linked by name. At generation names have the format "Name indes" (e.g. "Name 0", "Name 1")</li>
<li>Names can be edited. Any change will update the colour area</li>
<li>The colour of each category can be change by the 3 sliders in each colour menu</li>
<li>The sliders are "H" (for hue), "S" (for saturation) and L ('light')</li>
<li>The colour key can be saved to a csv file by pressing the button "Save color key as csv file"</li>
</ul>
</div>
<div id = 'end_div'><hr/></div>
</div>
<table border='1'>
<tr>
<td>
Number of categories: <input type = 'text' size = '2' id='noOfCats'> <input type ='button' value = 'Make' id='makeButton' onclick='makeCategories()'>
</td>
<td>
<input type='button' value='Save color key as csv file' disabled id='saveAsBttn' onclick='saveColorKey()'>
</td>
</tr>
<tr>
<td style="text-align: left;vertical-align: top;">
<div id='div_cats'></div>
</td>
<td>
<div>
<label for='white_bg_input'>White background</label><input type='radio' id='white_bg_input' name='bg_color_input' value='white' checked onchange="setBG(value)">
<label for='black_bg_input'>Black background</label><input type='radio' id='black_bg_input' name='bg_color_input' value='black' onchange="setBG(value)"></br>
<canvas width='640' height='400' id='canvas'></canvas>
</div>
</td>
</tr>
</table>
<script type="text/javascript">
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
makeButton = document.getElementById('makeButton'),
noOfCats = document.getElementById('noOfCats'),
div_cats = document.getElementById('div_cats'),
saveAsBttn = document.getElementById('saveAsBttn'),
instructionBoard = document.getElementById('instructionBoard'),
categories = [],
categories_name = [],
categories_coordinates = [],
white_bg = true;
context.textAlign = 'center'
context.textBaseline = 'middle'
instructionBoard.style.display = 'none'
// function to show/hide description and instructions
function showHideInstructions(val){
if (val == 'Show description and instructions'){
instructionBtn.value = 'Hide description and instructions'
instructionBoard.style.display = 'block'
}else{
instructionBtn.value = 'Show description and instructions'
instructionBoard.style.display = 'none'
}
}
function saveColorKey(){
var num_items = categories.length,
content = "";
for(i=0;i<num_items;i++){
var hsl = categories[i]
h = hsl[0],
s = hsl[1],
l = hsl[2],
rgb = hslToHex(h, s, l),
category_name = categories_name[i]
content = content + category_name + "," + rgb + "\n"
}
uriContent = "data:application/octet-stream," + encodeURIComponent(content)
var fname = prompt("Please enter file name (must end with .csv otherwise ... best of luck!");
saveAs(uriContent, fname)
}
function saveAs(uri, filename) {
var link = document.createElement('a');
if (typeof link.download === 'string') {
document.body.appendChild(link); // Firefox requires the link to be in the body
link.download = filename;
link.href = uri;
link.click();
document.body.removeChild(link); // remove the link when done
} else {
location.replace(uri);
}
}
function hslToHex(h, s, l) {
h /= 360;
s /= 100;
l /= 100;
let r, g, b;
if (s === 0) {
r = g = b = l; // achromatic
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
const toHex = x => {
const hex = Math.round(x * 255).toString(16);
return hex.length === 1 ? '0' + hex : hex;
};
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}
function setBG(value){
white_bg = false
console.log(white_bg)
if (value == 'white'){
white_bg = true
}
paintAll()
}
function paintAll(){
context.fillStyle = '#000000';
if(white_bg){
context.fillStyle = '#FFFFFF';
}
context.fillRect(0, 0, canvas.width, canvas.height)
var num_items = categories.length
for(i=0;i<num_items;i++){
var flagCanvas = div_cats.children[i].children[1].children[0]
flagContext = flagCanvas.getContext('2d');
hsl = categories[i],
h = hsl[0],
s = hsl[1],
l = hsl[2],
rgb = hslToHex(h, s, l),
x = categories_coordinates[i][0],
y = categories_coordinates[i][1],
category_name = categories_name[i]
flagContext.fillStyle = rgb
flagContext.fillRect(0, 0, flagCanvas.width, flagCanvas.height)
context.fillStyle = rgb
context.beginPath()
context.arc(x, y, 25, 0, 2*Math.PI, false)
context.fill()
context.closePath()
context.fillStyle = '#FFFFFF';
if(white_bg){
context.fillStyle = '#000000';
}
context.fillText(category_name, x, y - 35)
}
}
function updateHSL(){
var num_items = categories.length
for(i=0;i<num_items;i++){
var inputs = div_cats.children[i].children[0],
name_input = inputs.children[0],
h_input = inputs.children[2],
s_input = inputs.children[4],
l_input = inputs.children[6],
h = h_input.value,
s = s_input.value,
l = l_input.value;
categories[i] = [h, s, l]
categories_name[i] = name_input.value
}
paintAll()
}
function makeCategories(){
var numOfCategories = parseInt(noOfCats.value)
if(isNaN(numOfCategories)){
div_cats.innerHTML = '<h3>please introduce an integer</h3>'
saveAsBttn.disabled = true
}else if (numOfCategories < 41){
categories_name = []
categories = []
saveAsBttn.disabled = false
div_cats.innerHTML = ""
for (i=0;i<numOfCategories;i++){
var h = parseInt(Math.random() * 359),
s = parseInt(Math.random() * 100),
l = parseInt(Math.random() * 100);
categories[i] = [h, s, l]
name_i = 'Name ' + i
categories_name[i] = name_i
y = 40 + Math.floor(i / 8) * 80
x = 40 + (i % 8) * 80
categories_coordinates[i] = [x,y]
var innerDiv = document.createElement('div')
leftDiv = document.createElement('div'),
rightDiv = document.createElement('div');
div_cats.appendChild(innerDiv)
innerDiv.appendChild(leftDiv)
innerDiv.appendChild(rightDiv)
leftContent = '<input type="text" onchange = "updateHSL()" size = 35 value ="' + name_i + '"></br>'
leftContent = leftContent + 'H <input type="range" onchange="updateHSL()" min="0" max="360" value="' + h + '"></br>'
leftContent = leftContent + 'S <input type="range" onchange="updateHSL()" min="0" max="100" value="' + s + '"></br>'
leftContent = leftContent + 'L <input type="range" onchange="updateHSL()" min="0" max="100" value="' + l + '">'
leftDiv.innerHTML = leftContent
rightContent = '<canvas width="80" height="80"></canvas>'
rightDiv.innerHTML = rightContent
leftDiv.style.float = "left"
rightDiv.style.float = "right"
}
paintAll()
}else{
div_cats.innerHTML = '<h3>40 maximum categories permisible</h3>'
saveAsBttn.disabled = true
}
}
</script>
</body>
</html>

View file

@ -0,0 +1,33 @@
import sys
args = sys.argv
n_iterations = int(args[1])
pca_data_fname = args[2]
snn_fname = args[3]
fdg_coordinates_fname = args[4]
from fa2 import ForceAtlas2
import pandas as pd
from scipy.io import mmread
import numpy as np
# load pca, SNN and label colours data
# the first 2 PC form PCA are used as initial conditions
# SNN is used for building the force directed graph
pca_data = pd.read_csv(pca_data_fname, index_col = 0)
snn = mmread(snn_fname)
# set initialposition as the first 2 PCs
positions = pca_data.values[:, 0:2]
# initialize force directed graph class instance
forceatlas2 = ForceAtlas2(outboundAttractionDistribution=False, linLogMode=False,
adjustSizes=False, edgeWeightInfluence=1.0,
jitterTolerance=1.0, barnesHutTheta = .8,
barnesHutOptimize=True, multiThreaded=False,
scalingRatio=2.0, strongGravityMode=True, gravity=1, verbose=True)
# run force directed graph; for each iterations generates the coordinates use din each frame
coord = forceatlas2.forceatlas2(G = snn, pos = positions, iterations = n_iterations)
coord = np.array(coord)
np.savetxt( fname = fdg_coordinates_fname, X = coord, delimiter = ",")

View file

@ -0,0 +1,629 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Sep 7 11:42:51 2018
@author: doru
"""
import sys
args = sys.argv
save_to = args[1]
expression_data_fname = args[2]
no_of_categories = int(args[3])
template_str = """
<!doctype html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<title>3D viewer</title>
<meta name='description' content='The HTML5 Herald'>
<meta name='author' content='Dorin-Mirel Popescu'>
</head>
<body>
<table>
<tr>
<td align='left'>
<form>
<fieldset>
<legend><b>Visualisation options</b></legend>
<label for = 'particleSizeBar'>Particle size: </label>
<input type='range' name = 'particleSizeBar' min = 1 max = 14 step=0.1 oninput='setParticleSize(value)' value = 2 /><br />
<label for = 'alphaInput'>Transparency: </label>
<input type='range' name = 'alphaInput' min = 0 max = 1000 oninput='setAlpha(value)' value = 1000 /><br />
<label for = 'canvasSizeInput'>Canvas size: </label>
<input type='range' name = 'canvasSizeInput' min = 200 max = 2000 oninput='setCanvasSize(value)' value = 500 /><br />
<label for = 'bgInput'>Dark background: </label>
<input type='radio' name = 'bgInput' oninput='setBackground(value)' value = 'dark' />
<label for = 'bgInput'>White background: </label>
<input type='radio' name = 'bgInput' oninput='setBackground(value)' value = 'white' checked />
<br />
</fieldset>
</form>
</td>
<td style='vertical-align: top' rowspan='2'>
<form>
<fieldset>
<legend><b>Colour by:</b></legend>
<table>
<tr>
<td>
<label for='colourType'><input type='radio' name='colourType' onchange='setColourBy(value)' value='gene_expression' />Gene expression: </label>
</td>
<td>
<label for='geneSelector'><select name='geneSelector' id='geneSelector' onchange='selectFeature()'>gene_options_here</select></label>
</td>
</tr>
<tr>
<td colspan = '2' align='center'>
<canvas id='canvasColorScale' width = 200 height=40></canvas>
</td>
</tr>
<tr>
<td>
<label for='colourType'><input type='radio' name='colourType' checked onchange='setColourBy(value)' value='category' />Category:</label>
</td>
<td>
<label for='categorySelector'><select name='categorySelector' id='categorySelector' onchange = 'setCategory()'>category_options_here</select></label>
</td>
</tr>
</table>
</fieldset>
</form>
<br />
<div>
<fieldset>
<legend><b>Cell types:</b></legend>
<label for='toggleRadio'><input type='checkbox' name = 'toggleRadio' id='toggleRadio' onchange='toggleAllTypes()' checked />Show all:</label>
<form id = 'typesControlPanel'>
</form>
</fieldset>
</div>
</td>
</tr>
<tr>
<td style='vertical-align: text-top' >
<canvas id='canvas' width=600 height=600></canvas>
</td>
</tr>
</table>
<script id='vertex-shader' type='x-shader/x-fragment'>
attribute vec4 a_Position;
attribute vec3 a_Color;
uniform float u_basePointSize;
uniform float u_Alpha;
uniform int u_PaintFeatureScale;
varying vec4 v_Color;
void main() {
gl_Position = a_Position;
gl_PointSize = u_basePointSize;
if (u_PaintFeatureScale == 0){
v_Color = vec4(a_Color, u_Alpha);
}
else{
float r = 0.0;
float g = 0.0;
float b = 0.0;
r = max(0.0, 2.0 * a_Color.r - 1.0);
b = max(0.0, 2.0 * (1.0 - a_Color.r) - 1.0);
g = 1.0 - 2.0 * abs(a_Color.r - 0.5);
v_Color = vec4(r, g, b, u_Alpha);
}
}
</script>
<script id ='fragment-shader' type='x-shader/x-fragment'>
precision mediump float;
varying vec4 v_Color;
void main() {
float r = 0.0;
vec2 cxy = 2.0 * gl_PointCoord - 1.0;
r = dot(cxy, cxy);
if (r > 1.0){
discard;
}
gl_FragColor = v_Color;
}
</script>
<script type = 'text/javascript'>
var Matrix4 = function(opt_src) {
var i, s, d;
if (opt_src && typeof opt_src === 'object' && opt_src.hasOwnProperty('elements')) {
s = opt_src.elements;
d = new Float32Array(16);
for (i = 0; i < 16; ++i) {
d[i] = s[i];
}
this.elements = d;
} else {
this.elements = new Float32Array([1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]);
}
};
Matrix4.prototype.setTranslate = function(x, y, z) {
var e = this.elements;
e[0] = 1; e[4] = 0; e[8] = 0; e[12] = x;
e[1] = 0; e[5] = 1; e[9] = 0; e[13] = y;
e[2] = 0; e[6] = 0; e[10] = 1; e[14] = z;
e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1;
return this;
};
Matrix4.prototype.setLookAt = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
var e, fx, fy, fz, rlf, sx, sy, sz, rls, ux, uy, uz;
fx = centerX - eyeX;
fy = centerY - eyeY;
fz = centerZ - eyeZ;
// Normalize f.
rlf = 1 / Math.sqrt(fx*fx + fy*fy + fz*fz);
fx *= rlf;
fy *= rlf;
fz *= rlf;
// Calculate cross product of f and up.
sx = fy * upZ - fz * upY;
sy = fz * upX - fx * upZ;
sz = fx * upY - fy * upX;
// Normalize s.
rls = 1 / Math.sqrt(sx*sx + sy*sy + sz*sz);
sx *= rls;
sy *= rls;
sz *= rls;
// Calculate cross product of s and f.
ux = sy * fz - sz * fy;
uy = sz * fx - sx * fz;
uz = sx * fy - sy * fx;
// Set to this.
e = this.elements;
e[0] = sx;
e[1] = ux;
e[2] = -fx;
e[3] = 0;
e[4] = sy;
e[5] = uy;
e[6] = -fy;
e[7] = 0;
e[8] = sz;
e[9] = uz;
e[10] = -fz;
e[11] = 0;
e[12] = 0;
e[13] = 0;
e[14] = 0;
e[15] = 1;
// Translate.
return this.translate(-eyeX, -eyeY, -eyeZ);
};
Matrix4.prototype.translate = function(x, y, z) {
var e = this.elements;
e[12] += e[0] * x + e[4] * y + e[8] * z;
e[13] += e[1] * x + e[5] * y + e[9] * z;
e[14] += e[2] * x + e[6] * y + e[10] * z;
e[15] += e[3] * x + e[7] * y + e[11] * z;
return this;
};
Matrix4.prototype.setPerspective = function(fovy, aspect, near, far) {
var e, rd, s, ct;
if (near === far || aspect === 0) {
throw 'null frustum';
}
if (near <= 0) {
throw 'near <= 0';
}
if (far <= 0) {
throw 'far <= 0';
}
fovy = Math.PI * fovy / 180 / 2;
s = Math.sin(fovy);
if (s === 0) {
throw 'null frustum';
}
rd = 1 / (far - near);
ct = Math.cos(fovy) / s;
e = this.elements;
e[0] = ct / aspect;
e[1] = 0;
e[2] = 0;
e[3] = 0;
e[4] = 0;
e[5] = ct;
e[6] = 0;
e[7] = 0;
e[8] = 0;
e[9] = 0;
e[10] = -(far + near) * rd;
e[11] = -1;
e[12] = 0;
e[13] = 0;
e[14] = -2 * near * far * rd;
e[15] = 0;
return this;
};
</script>
<script type='text/javascript'>
function buildCategoryRadioButtons(){
category_type = categorySelector.options[categorySelector.selectedIndex].value;
current_indices = indices_all;
// create radio commands from categories
typesControlPanel.innerHTML = "";
radio_commands_HTML = "";
for(name in categories_indices[category_type]){
f_index = categories_indices[category_type][name][0]
cols = categories_colours[category_type].slice(3 * f_index, 3 * f_index + 3)
col_label = "#";
for(k=0;k<cols.length;k++){col_hex = Math.round(255 * cols[k]).toString(16).padStart(2, '0'); col_label = col_label + col_hex}
radio_command = "<div style='background-color:" + col_label + "'>";
radio_command = radio_command + "<input style='float:left' type='checkbox' id='" + name;
radio_command = radio_command + "' checked onchange='toggleCategoryAction()' /><label style='float:left' for='" + name + "'";
radio_command = radio_command + ">" + name + ": </label><br/></div>"
radio_commands_HTML = radio_commands_HTML + radio_command
}
typesControlPanel.innerHTML = radio_commands_HTML;
}
function toggleCategoryAction(){
updateBuffer()
draw()
}
function setCategory(){
buildCategoryRadioButtons()
updateBuffer()
draw()
}
function setColourBy(value){
colour_by = value;
if (colour_by =='category'){
PaintFeatureScale = 0;
}else{
PaintFeatureScale = 1;
}
gl_context.uniform1i(u_PaintFeatureScale, PaintFeatureScale)
updateBuffer()
draw()
}
function toggleAllTypes(){
controlRadios = typesControlPanel.elements
for(i=0;i<controlRadios.length;i++){
controlRadios[i].checked = toggleRadio.checked
}
updateBuffer()
draw()
}
function selectFeature(){
feature = geneSelector.value
updateBuffer()
draw()
console.log('selected features')
}
function draw(){
if(bg_color == "white"){
gl_context.clearColor(1, 1, 1, 1)
}else{
gl_context.clearColor(0, 0, 0, 1)
}
gl_context.clear(gl_context.COLOR_BUFFER_BIT);
gl_context.bufferData(gl_context.ARRAY_BUFFER, buffer_data_array, gl_context.STATIC_DRAW)
gl_context.drawArrays(gl_context.POINTS, 0, n)
}
function updateBuffer(){
var buffer_data = [];
// first update indices to be used - for this read the category control panel radio buttons
controlRadios = typesControlPanel.elements
current_indices = []
for(i=0;i<controlRadios.length;i++){
if(controlRadios[i].checked){
radio_type = controlRadios[i].id
current_indices = current_indices.concat(categories_indices[category_type][radio_type])
}
}
// now just populate the buffer_data
if(colour_by == 'gene_expression'){
current_indices.forEach(function(index, i){
buffer_data.push(coordinates_data[2 * index])
buffer_data.push(coordinates_data[2 * index + 1])
buffer_data.push(gene_expression[feature][index])
buffer_data.push(gene_expression[feature][index])
buffer_data.push(gene_expression[feature][index])
})
}else{
current_indices.forEach(function(index, i){
buffer_data.push(coordinates_data[2 * index])
buffer_data.push(coordinates_data[2 * index + 1])
buffer_data.push(categories_colours[category_type][3 * index])
buffer_data.push(categories_colours[category_type][3 * index + 1])
buffer_data.push(categories_colours[category_type][3 * index + 2])
})
}
buffer_data_array = new Float32Array(buffer_data)
n = buffer_data_array.length / 5
}
function setParticleSize(value){
particleSize = parseInt(value)
gl_context.uniform1f(u_basePointSize, particleSize)
updateBuffer()
draw()
}
function setAlpha(value){
alphaValue = parseInt(value) / 1000
gl_context.uniform1f(u_Alpha, alphaValue)
updateBuffer()
draw()
}
function setCanvasSize(value){
value = parseInt(value)
canvas.width = value
canvas.height = value
gl_context = getContext(canvas)
gl_context = initContext(gl_context)
gl_context.viewport(0, 0, canvas.width, canvas.height)
updateBuffer()
draw()
}
function setBackground(value){
bg_color = value;
draw()
}
function shadersFromScriptElement(gl, ID, type){
shaderScript = document.getElementById(ID)
var str = ''
var k = shaderScript.firstChild;
while(k){
if (k.nodeType == 3){
str += k.textContent;
}
k = k.nextSibling
}
var shader = gl.createShader(type)
gl.shaderSource(shader, str)
gl.compileShader(shader)
return shader
}
function getContext(canvasWidget){
var names = ['webgl', 'experimental-webgl', 'webkit-3d', 'moz-webgl'];
for(var i=0; i<names.length; i++){
try{
var gl = canvasWidget.getContext(names[i])
}catch(e){}
if(gl){i=names.length}
}
var vshader = shadersFromScriptElement(gl, 'vertex-shader', gl.VERTEX_SHADER),
fshader = shadersFromScriptElement(gl, 'fragment-shader', gl.FRAGMENT_SHADER)
program = gl.createProgram();
gl.attachShader(program, vshader)
gl.attachShader(program, fshader)
gl.linkProgram(program)
gl.useProgram(program)
gl.program = program
return gl
}
function initContext(gl){
n = buffer_data_array.length / 5
var vertexColourBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColourBuffer)
var FSIZE = buffer_data_array.BYTES_PER_ELEMENT;
var a_Position = gl.getAttribLocation(gl.program, 'a_Position')
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0)
gl.enableVertexAttribArray(a_Position)
var a_Color = gl.getAttribLocation(gl.program, 'a_Color')
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, 2 * FSIZE)
gl.enableVertexAttribArray(a_Color)
u_basePointSize = gl.getUniformLocation(gl.program, 'u_basePointSize')
gl.uniform1f(u_basePointSize, particleSize)
u_Alpha = gl.getUniformLocation(gl.program, "u_Alpha")
gl.uniform1f(u_Alpha, alphaValue)
u_PaintFeatureScale = gl.getUniformLocation(gl.program, 'u_PaintFeatureScale')
gl.uniform1i(u_PaintFeatureScale, PaintFeatureScale)
gl.clearColor(1, 1, 1, 1);
if(bg_color == "dark"){
gl.clearColor(0, 0, 0, 1)
}
gl.disable(gl.DEPTH_TEST)
gl.enable(gl.BLEND)
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
gl.clear(gl.COLOR_BUFFER_BIT);
return gl
}
var categorySelector = document.getElementById('categorySelector'),
geneSelector = document.getElementById('geneSelector'),
typesControlPanel = document.getElementById('typesControlPanel'),
toggleRadio = document.getElementById('toggleRadio')
var canvas = document.getElementById('canvas'),
particleSize = 5,
alphaValue = 1.0,
bg_color = "white",
n = 0,
particleSize = 2,
PaintFeatureScale = 0;
coordinates_data = [coordinates_data_here]
gene_expression = []; gene_expression_colour_coded;
categories_colours = []
categories_colours_data_here
categories_indices = []
categories_indices_data_here
// initialize flags
// when toggling between gene expression and category, do not slice data i.e. do not recompute index data
// when choosing a category always re-initiate index data
var colour_by = 'category', // the other options is can be 'category'
category_types = [],
category_type = '',
features = [],
feature = '';
// set category
for(name in categories_colours){category_types.push(name)}
category_type = category_types[0]
// set feature
for(name in gene_expression){features.push(name)}
feature = features[0];
// create global data holders
var indices_all = [],
current_indices = [],
current_colours = [],
buffer_data_array = [];
for(j=0;j<categories_colours[category_type].length/3;j++){indices_all.push(j)}
// build the categories buttons for the first time
buildCategoryRadioButtons()
updateBuffer()
// create the renderer
var gl_context = getContext(canvas);
gl_context = initContext(gl_context)
// now draw
draw()
// draw the scale
var canvasColorScale = document.getElementById('canvasColorScale'),
canvas_ctx = canvasColorScale.getContext('2d'),
scale_gradient = canvas_ctx.createLinearGradient(0, 0, 200, 0);
scale_gradient.addColorStop(0, 'blue');
scale_gradient.addColorStop(0.5, 'green');
scale_gradient.addColorStop(1, 'red');
canvas_ctx.fillStyle = scale_gradient;
canvas_ctx.fillRect(0, 20, canvasColorScale.width, canvasColorScale.height)
canvas_ctx.fillStyle = 'black'
canvas_ctx.fillText('0', 10, 10)
canvas_ctx.fillText('8', 190, 10)
</script>
</body>
</html>
"""
import pandas as pd
import numpy as np
data = pd.read_csv(expression_data_fname, index_col = None)
# convert Colours to r, g, b values, then to floats < 1.0
def hexdec_to_1floats(hexdec):
return np.array([int(hexdec[1:][i:(i+2)], 16) for i in (0, 2, 4)]) / 255.0
gene_names = [gene_name for gene_name in data.columns[(2 + 2 * no_of_categories):]]
raw_expression = data.values[:, (2 + 2 * no_of_categories):]
gene_options = []
gene_expression_colour_coded = []
max_expression = round(raw_expression.max())
raw_expression /= max_expression
for index, gene_name in enumerate(gene_names):
gene_expression = raw_expression[:, index]
gene_expression = [str(value)[:min(4, len(str(value)))] for value in gene_expression]
gene_expression = ",".join(gene_expression)
gene_expression_colour_coded.append("gene_expression['{gn}'] = [{ge}]".format(gn = gene_name, ge = gene_expression))
gene_options.append("<option value='{gn}'>{gn}</option>".format(gn = gene_name))
gene_options = "".join(gene_options)
gene_expression_colour_coded = ";".join(gene_expression_colour_coded)
# make coordinates data string
coordinates = data.values[:, 0:2].astype('float32')
# next few steps are compressing the data into a stadard cube centered at (0,0,0) and L = 200
Xrange = np.percentile(coordinates[:, 0], q = [1, 98]) * 1.2
Yrange = np.percentile(coordinates[:, 1], q = [1, 98]) * 1.2
center = np.array((np.mean(Xrange), np.mean(Yrange)))
coordinates = coordinates - np.tile(center, (coordinates.shape[0], 1))
ratio = max(np.abs(np.percentile(coordinates[:, 0], q = [1, 98]) * 1.2))
ratio = max(ratio, max(np.abs(np.percentile(coordinates[:, 1], q = [1, 98]) * 1.2)))
ratio = 1.0 / ratio
coordinates = coordinates * ratio
coordinates = ",".join([str(value)[:min(6, len(str(value)))] for value in coordinates.ravel()])
categories = [str(value).replace(".", " ") for value in data.columns[2:(2 + no_of_categories)]]
categories_options = ["<option value='{cat}'>{cat}</option>".format(cat=cat) for cat in categories]
categories_options = "".join(categories_options)
categories_colours = []
categories_indices = []
for cat_index in range(no_of_categories):
category_name = data.columns[2 + cat_index]
category_name = category_name.replace(".", " ")
category_colours = [hexdec_to_1floats(colour) for colour in data.values[:, 2 + cat_index + no_of_categories]]
category_colours = [",".join([str(value)[:min(4, len(str(value)))] for value in colour]) for colour in category_colours]
category_colours = ",".join(category_colours)
categories_colours.append("categories_colours['{cn}'] = [{cc}]".format(cn = category_name, cc = category_colours))
types = [value for value in np.unique(data.values[:, 2 + cat_index])]
cat_indices = []
categories_indices.append("categories_indices['{cn}'] = []".format(cn = category_name))
for t_name in types:
indices = data.values[:, 2 + cat_index] == t_name
indices = np.where(indices)[0]
indices = ",".join([str(value) for value in indices])
cat_indices.append("categories_indices['{cn}']['{tn}'] = [{ind}]".format(cn = category_name, tn = t_name, ind = indices))
cat_indices = "\n".join(cat_indices)
categories_indices.append(cat_indices)
categories_indices = "\n".join(categories_indices)
categories_colours = "\n".join(categories_colours)
template_str = template_str.replace('gene_options_here', gene_options)
template_str = template_str.replace('gene_expression_colour_coded', gene_expression_colour_coded)
template_str = template_str.replace('coordinates_data_here', coordinates)
template_str = template_str.replace('category_options_here', categories_options)
template_str = template_str.replace('categories_colours_data_here', categories_colours)
template_str = template_str.replace('categories_indices_data_here', categories_indices)
with open(save_to, 'w') as result:
result.write(template_str)

View file

@ -0,0 +1,318 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Aug 7 13:45:37 2018
@author: Dorin-Mirel Popescu
"""
import sys
args = sys.argv
save_to = args[1]
data_frame_fname = args[2]
template = """
<!doctype html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<title>3D viewer</title>
<meta name='description' content='The HTML5 Herald'>
<meta name='author' content='Dorin-Mirel Popescu'>
</head>
<body>
<table>
<tr>
<th align='left'>
<label for = 'particleSizeBar'>Particle size: </label>
<input type='range' name = 'particleSizeBar' min = 0 max = 50 onchange='setParticleSize(value)' value = 5 /><br />
<label for = 'alphaInput'>Transparency: </label>
<input type='range' name = 'alphaInput' min = 0 max = 1000 onchange='setAlpha(value)' value = 1000 /><br />
<label for = 'canvasSizeInput'>Canvas size: </label>
<input type='range' name = 'canvasSizeInput' min = 200 max = 2000 onchange='setCanvasSize(value)' value = 500 /><br />
<label for = 'bgInput'>Dark background: </label>
<input type='radio' name = 'bgInput' onchange='setBackground(value)' value = 'dark' />
<label for = 'bgInput'>White background: </label>
<input type='radio' name = 'bgInput' onchange='setBackground(value)' value = 'white' checked />
<br />
</th>
</tr>
<tr>
<td>
<canvas id='canvas' width=600 height=600></canvas>
</td>
<td style='vertical-align: top'>
<label>Categories: </label><br />
<label for='toggleRadio'>Show all:</label>
<input type='checkbox' name = 'toggleRadio' id='toggleRadio' onchange='toggleShowTypes()' checked /><br />
<form id = 'ControlPanel'>
radiocommands
</form>
</td>
</tr>
</table>
<script id='vertex-shader' type='x-shader/x-fragment'>
attribute vec4 a_Position;
attribute vec3 a_Color;
uniform float u_basePointSize;
uniform float u_Alpha;
varying vec4 v_Color;
void main() {
gl_Position = a_Position;
gl_PointSize = u_basePointSize;
v_Color = vec4(a_Color, u_Alpha);
}
</script>
<script id ='fragment-shader' type='x-shader/x-fragment'>
precision mediump float;
varying vec4 v_Color;
void main() {
float r = 0.0;
vec2 cxy = 2.0 * gl_PointCoord - 1.0;
r = dot(cxy, cxy);
if (r > 1.0){
discard;
}
gl_FragColor = v_Color;
}
</script>
<script type='text/javascript'>
function selectData(){
controlPanel = document.getElementById('ControlPanel')
controlRadios = controlPanel.elements
values = []
for(i=0;i<controlRadios.length;i++){
if(controlRadios[i].checked){
values = values.concat(index_table[controlRadios[i].id])
}
}
new_indices = []
for (i=0;i<values.length;i++){
v = values[i]
new_indices.push(5*v)
new_indices.push(5*v+1)
new_indices.push(5*v+2)
new_indices.push(5*v+3)
new_indices.push(5*v+4)
}
current_data_buffer = []
new_indices.forEach(function(val, i){current_data_buffer.push(data_buffer[val])})
current_data_buffer = new Float32Array(current_data_buffer)
gl_context.bufferData(gl_context.ARRAY_BUFFER, current_data_buffer, gl_context.STATIC_DRAW); // load data to buffer
n = current_data_buffer.length / 5
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
gl_context.drawArrays(gl_context.POINTS, 0, n)
}
function toggleShowTypes(){
toggleRadio = document.getElementById('toggleRadio')
controlPanel = document.getElementById('ControlPanel')
controlRadios = controlPanel.elements
for(i=0;i<controlRadios.length;i++){
controlRadios[i].checked = toggleRadio.checked
}
selectData()
}
function setParticleSize(value){
particleSize = parseInt(value)
gl_context.uniform1f(u_basePointSize, particleSize)
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
gl_context.drawArrays(gl_context.POINTS, 0, n)
}
function setAlpha(value){
alphaValue = parseInt(value) / 1000
gl_context.uniform1f(u_Alpha, alphaValue)
gl_context.clear(gl_context.COLOR_BUFFER_BIT);
gl_context.drawArrays(gl_context.POINTS, 0, n)
}
function setCanvasSize(value){
value = parseInt(value)
canvas.width = value
canvas.height = value
gl_context = getContext(canvas)
gl_context = initContext(gl_context)
gl_context.viewport(0, 0, canvas.width, canvas.height)
if(bg_color == "white"){
gl_context.clearColor(1, 1, 1, 1)
}else{
gl_context.clearColor(0, 0, 0, 1)
}
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
gl_context.drawArrays(gl_context.POINTS, 0, n)
}
function setBackground(value){
if(value == "dark"){
gl_context.clearColor(0, 0, 0, 1)
bg_color = "dark"
}else{
gl_context.clearColor(1, 1, 1, 1)
bg_color = "white"
}
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
gl_context.drawArrays(gl_context.POINTS, 0, n)
}
function shadersFromScriptElement(gl, ID, type){
shaderScript = document.getElementById(ID)
var str = ''
var k = shaderScript.firstChild;
while(k){
if (k.nodeType == 3){
str += k.textContent;
}
k = k.nextSibling
}
var shader = gl.createShader(type)
gl.shaderSource(shader, str)
gl.compileShader(shader)
return shader
}
function getContext(canvasWidget){
var names = ['webgl', 'experimental-webgl', 'webkit-3d', 'moz-webgl'];
for(var i=0; i<names.length; i++){
try{
var gl = canvasWidget.getContext(names[i])
}catch(e){}
if(gl){i=names.length}
}
var vshader = shadersFromScriptElement(gl, 'vertex-shader', gl.VERTEX_SHADER),
fshader = shadersFromScriptElement(gl, 'fragment-shader', gl.FRAGMENT_SHADER)
program = gl.createProgram();
gl.attachShader(program, vshader)
gl.attachShader(program, fshader)
gl.linkProgram(program)
gl.useProgram(program)
gl.program = program
return gl
}
function initContext(gl){
n = current_data_buffer.length / 5
var vertexColourBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColourBuffer)
gl.bufferData(gl.ARRAY_BUFFER, current_data_buffer, gl.STATIC_DRAW)
var FSIZE = data_buffer.BYTES_PER_ELEMENT;
var a_Position = gl.getAttribLocation(gl.program, 'a_Position')
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0)
gl.enableVertexAttribArray(a_Position)
var a_Color = gl.getAttribLocation(gl.program, 'a_Color')
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, 2 * FSIZE)
gl.enableVertexAttribArray(a_Color)
u_basePointSize = gl.getUniformLocation(gl.program, 'u_basePointSize')
gl.uniform1f(u_basePointSize, particleSize)
u_Alpha = gl.getUniformLocation(gl.program, "u_Alpha")
gl.uniform1f(u_Alpha, alphaValue)
gl.clearColor(1, 1, 1, 1); // add ternary conditional
if(bg_color == "dark"){
gl.clearColor(0, 0, 0, 1)
}
gl.disable(gl.DEPTH_TEST)
gl.enable(gl.BLEND)
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
//gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
gl.clear(gl.COLOR_BUFFER_BIT);
return gl
}
var canvas = document.getElementById('canvas'),
particleSize = 5,
alphaValue = 1.0,
bg_color = "white"
data_buffer = new Float32Array([
datahere
])
index_table = []
indiceshere
current_data_buffer = data_buffer
gl_context = getContext(canvas)
gl_context = initContext(gl_context)
gl_context.drawArrays(gl_context.POINTS, 0, n)
</script>
</body>
</html>
"""
import pandas as pd
import numpy as np
data = pd.read_csv(data_frame_fname, index_col = None)
# convert Colours to r, g, b values, then to floats < 1.0
def hexdec_to_1floats(hexdec):
return np.array([int(hexdec[1:][i:(i+2)], 16) for i in (0, 2, 4)]) / 255.0
# map Labels to colours
labels = sorted(list(data.Labels.unique()))
index_table = []
radio_commands = []
for index, label in enumerate(labels):
indices = data.Labels == label
indices = indices.values
indices = np.where(indices)
indices = ','.join([str(i) for i in indices[0]])
indices = "[{indices}]".format(indices = indices)
index_table.append("index_table['{label}'] = {indices}".format(label = label, indices = indices))
colour = data.Colours[data.Labels == label].values[0]
radio_command = "<div style='background-color:{colour}'><input style='float:left' type='checkbox' id='{label}' checked onchange='selectData()' /><label style='float:left' for='{label}'>{label}: </label><br /></div>".format(colour = colour, label = label)
radio_commands.append(radio_command)
index_table = ';\n '.join(index_table)
radio_commands = '\n '.join(radio_commands)
# make data string
coordinates = data.values[:, 0:2].astype('float32')
# next few steps are compressing the data into a stadard cube centered at (0,0,0) and L = 200
Xrange = np.percentile(coordinates[:, 0], q = [1, 98]) * 1.2
Yrange = np.percentile(coordinates[:, 1], q = [1, 98]) * 1.2
center = np.array((np.mean(Xrange), np.mean(Yrange)))
coordinates = coordinates - np.tile(center, (coordinates.shape[0], 1))
ratio = max(np.abs(np.percentile(coordinates[:, 0], q = [1, 98]) * 1.2))
ratio = max(ratio, max(np.abs(np.percentile(coordinates[:, 1], q = [1, 98]) * 1.2)))
ratio = 1.0 / ratio
coordinates = coordinates * ratio
# next few steps the buffer data is created as string
colours = data.Colours.values
buffer_data = []
for index in range(coordinates.shape[0]):
coordinate = [str(i) for i in coordinates[index, :]]
colour = [str(i) for i in hexdec_to_1floats(colours[index]).astype('float32')]
vertex_data = coordinate + colour
buffer_data.append(",".join(vertex_data))
buffer_data = ",".join(buffer_data)
template_str = template.replace('datahere', buffer_data)
template_str = template_str.replace('indiceshere', index_table)
template_str = template_str.replace('radiocommands', radio_commands)
with open(save_to, 'w') as result:
result.write(template_str)

View file

@ -0,0 +1,589 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Aug 7 13:45:37 2018
@author: Dorin-Mirel Popescu
"""
import sys
args = sys.argv
save_to = args[1]
data_frame_fname = args[2]
template = """
<!doctype html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<title>3D viewer</title>
<meta name='description' content='The HTML5 Herald'>
<meta name='author' content='Dorin-Mirel Popescu'>
</head>
<body>
<table>
<tr>
<th align='left'>
<label for = 'particleSizeBar'>Particle size: </label>
<input type='range' name = 'particleSizeBar' min = 10 max = 300 onchange='setParticleSize(value)' value = 150 /><br />
<label for = 'alphaInput'>Transparency: </label>
<input type='range' name = 'alphaInput' min = 0 max = 1000 onchange='setAlpha(value)' value = 1000 /><br />
<label for = 'canvasSizeInput'>Canvas size: </label>
<input type='range' name = 'canvasSizeInput' min = 200 max = 2000 onchange='setCanvasSize(value)' value = 500 /><br />
<label for = "zoom">Zoom: </label>
<input type='range' name = 'zoom' min = 100 max = 1000 onchange='setZoom(value)' value = 400 /><br />
<label for = 'bgInput'>Dark background: </label>
<input type='radio' name = 'bgInput' onchange='setBackground(value)' value = 'dark' />
<label for = 'bgInput'>White background: </label>
<input type='radio' name = 'bgInput' onchange='setBackground(value)' value = 'white' checked />
<br />
</th>
</tr>
<tr>
<td style='vertical-align: text-top' >
<canvas id='canvas' width=600 height=600></canvas>
</td>
<td style='vertical-align: top'>
<label>Categories: </label><br />
<label for='toggleRadio'>Show all:</label>
<input type='checkbox' name = 'toggleRadio' id='toggleRadio' onchange='toggleShowTypes()' checked /><br />
<form id = 'ControlPanel'>
radiocommands
</form>
</td>
</tr>
</table>
<script id='vertex-shader' type='x-shader/x-fragment'>
attribute vec4 a_Position;
attribute vec3 a_Color;
uniform mat4 u_ModelMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjMatrix;
uniform float u_basePointSize;
uniform float u_Alpha;
varying vec4 v_Color;
void main() {
vec4 cubePos = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;
float currentWidth = 0.0;
//currentWidth = u_basePointSize;
currentWidth = 3.0 + (u_basePointSize - 3.0) * (1.0 - cubePos.z / cubePos.w) / 2.0;
gl_Position = cubePos;
gl_PointSize = currentWidth;
v_Color = vec4(a_Color, u_Alpha);
}
</script>
<script id ='fragment-shader' type='x-shader/x-fragment'>
precision mediump float;
varying vec4 v_Color;
void main() {
float r = 0.0;
vec2 cxy = 2.0 * gl_PointCoord - 1.0;
r = dot(cxy, cxy);
if (r > 1.0){
discard;
}
vec2 D = vec2(0.0, 0.0), centers = vec2(.65, .35);
float light = 0.0;
light = length(centers - gl_PointCoord);
light = .1 + .9 * (pow(50.0, -light));
gl_FragColor = v_Color * light + (1.0 - light) * vec4(0.0, 0.0, 0.0, 1.0);
}
</script>
<script type = 'text/javascript'>
var Matrix4 = function(opt_src) {
var i, s, d;
if (opt_src && typeof opt_src === 'object' && opt_src.hasOwnProperty('elements')) {
s = opt_src.elements;
d = new Float32Array(16);
for (i = 0; i < 16; ++i) {
d[i] = s[i];
}
this.elements = d;
} else {
this.elements = new Float32Array([1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]);
}
};
Matrix4.prototype.setTranslate = function(x, y, z) {
var e = this.elements;
e[0] = 1; e[4] = 0; e[8] = 0; e[12] = x;
e[1] = 0; e[5] = 1; e[9] = 0; e[13] = y;
e[2] = 0; e[6] = 0; e[10] = 1; e[14] = z;
e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1;
return this;
};
Matrix4.prototype.setLookAt = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
var e, fx, fy, fz, rlf, sx, sy, sz, rls, ux, uy, uz;
fx = centerX - eyeX;
fy = centerY - eyeY;
fz = centerZ - eyeZ;
// Normalize f.
rlf = 1 / Math.sqrt(fx*fx + fy*fy + fz*fz);
fx *= rlf;
fy *= rlf;
fz *= rlf;
// Calculate cross product of f and up.
sx = fy * upZ - fz * upY;
sy = fz * upX - fx * upZ;
sz = fx * upY - fy * upX;
// Normalize s.
rls = 1 / Math.sqrt(sx*sx + sy*sy + sz*sz);
sx *= rls;
sy *= rls;
sz *= rls;
// Calculate cross product of s and f.
ux = sy * fz - sz * fy;
uy = sz * fx - sx * fz;
uz = sx * fy - sy * fx;
// Set to this.
e = this.elements;
e[0] = sx;
e[1] = ux;
e[2] = -fx;
e[3] = 0;
e[4] = sy;
e[5] = uy;
e[6] = -fy;
e[7] = 0;
e[8] = sz;
e[9] = uz;
e[10] = -fz;
e[11] = 0;
e[12] = 0;
e[13] = 0;
e[14] = 0;
e[15] = 1;
// Translate.
return this.translate(-eyeX, -eyeY, -eyeZ);
};
Matrix4.prototype.translate = function(x, y, z) {
var e = this.elements;
e[12] += e[0] * x + e[4] * y + e[8] * z;
e[13] += e[1] * x + e[5] * y + e[9] * z;
e[14] += e[2] * x + e[6] * y + e[10] * z;
e[15] += e[3] * x + e[7] * y + e[11] * z;
return this;
};
Matrix4.prototype.setPerspective = function(fovy, aspect, near, far) {
var e, rd, s, ct;
if (near === far || aspect === 0) {
throw 'null frustum';
}
if (near <= 0) {
throw 'near <= 0';
}
if (far <= 0) {
throw 'far <= 0';
}
fovy = Math.PI * fovy / 180 / 2;
s = Math.sin(fovy);
if (s === 0) {
throw 'null frustum';
}
rd = 1 / (far - near);
ct = Math.cos(fovy) / s;
e = this.elements;
e[0] = ct / aspect;
e[1] = 0;
e[2] = 0;
e[3] = 0;
e[4] = 0;
e[5] = ct;
e[6] = 0;
e[7] = 0;
e[8] = 0;
e[9] = 0;
e[10] = -(far + near) * rd;
e[11] = -1;
e[12] = 0;
e[13] = 0;
e[14] = -2 * near * far * rd;
e[15] = 0;
return this;
};
</script>
<script type='text/javascript'>
function selectData(){
controlPanel = document.getElementById('ControlPanel')
controlRadios = controlPanel.elements
values = []
for(i=0;i<controlRadios.length;i++){
if(controlRadios[i].checked){
values = values.concat(index_table[controlRadios[i].id])
}
}
new_indices = []
for (i=0;i<values.length;i++){
v = values[i]
new_indices.push(6*v)
new_indices.push(6*v+1)
new_indices.push(6*v+2)
new_indices.push(6*v+3)
new_indices.push(6*v+4)
new_indices.push(6*v+5)
}
current_data_buffer = []
new_indices.forEach(function(val, i){current_data_buffer.push(data_buffer[val])})
current_data_buffer = new Float32Array(current_data_buffer)
gl_context.bufferData(gl_context.ARRAY_BUFFER, current_data_buffer, gl_context.STATIC_DRAW); // load data to buffer
n = current_data_buffer.length / 6
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
gl_context.drawArrays(gl_context.POINTS, 0, n)
}
function toggleShowTypes(){
toggleRadio = document.getElementById('toggleRadio')
controlPanel = document.getElementById('ControlPanel')
controlRadios = controlPanel.elements
for(i=0;i<controlRadios.length;i++){
controlRadios[i].checked = toggleRadio.checked
}
selectData()
}
function setParticleSize(value){
particleSize = parseInt(value)
gl_context.uniform1f(u_basePointSize, particleSize)
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
gl_context.drawArrays(gl_context.POINTS, 0, n)
}
function setAlpha(value){
alphaValue = parseInt(value) / 1000
gl_context.uniform1f(u_Alpha, alphaValue)
gl_context.clear(gl_context.COLOR_BUFFER_BIT);
gl_context.drawArrays(gl_context.POINTS, 0, n)
}
function setCanvasSize(value){
value = parseInt(value)
canvas.width = value
canvas.height = value
gl_context = getContext(canvas)
gl_context = initContext(gl_context)
gl_context.viewport(0, 0, canvas.width, canvas.height)
if(bg_color == "white"){
gl_context.clearColor(1, 1, 1, 1)
}else{
gl_context.clearColor(0, 0, 0, 1)
}
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
gl_context.drawArrays(gl_context.POINTS, 0, n)
}
function setZoom(value){
eyeVN = parseInt(value)
farField = eyeVN + 100;
rotateData(0, 0)
}
function setBackground(value){
if(value == "dark"){
gl_context.clearColor(0, 0, 0, 1)
bg_color = "dark"
}else{
gl_context.clearColor(1, 1, 1, 1)
bg_color = "white"
}
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
gl_context.drawArrays(gl_context.POINTS, 0, n)
}
function shadersFromScriptElement(gl, ID, type){
shaderScript = document.getElementById(ID)
var str = ''
var k = shaderScript.firstChild;
while(k){
if (k.nodeType == 3){
str += k.textContent;
}
k = k.nextSibling
}
var shader = gl.createShader(type)
gl.shaderSource(shader, str)
gl.compileShader(shader)
return shader
}
function getContext(canvasWidget){
var names = ['webgl', 'experimental-webgl', 'webkit-3d', 'moz-webgl'];
for(var i=0; i<names.length; i++){
try{
var gl = canvasWidget.getContext(names[i])
}catch(e){}
if(gl){i=names.length}
}
var vshader = shadersFromScriptElement(gl, 'vertex-shader', gl.VERTEX_SHADER),
fshader = shadersFromScriptElement(gl, 'fragment-shader', gl.FRAGMENT_SHADER)
program = gl.createProgram();
gl.attachShader(program, vshader)
gl.attachShader(program, fshader)
gl.linkProgram(program)
gl.useProgram(program)
gl.program = program
return gl
}
function initContext(gl){
n = current_data_buffer.length / 6
var vertexColourBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColourBuffer)
gl.bufferData(gl.ARRAY_BUFFER, current_data_buffer, gl.STATIC_DRAW)
var FSIZE = data_buffer.BYTES_PER_ELEMENT;
var a_Position = gl.getAttribLocation(gl.program, 'a_Position')
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0)
gl.enableVertexAttribArray(a_Position)
var a_Color = gl.getAttribLocation(gl.program, 'a_Color')
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, 3 * FSIZE)
gl.enableVertexAttribArray(a_Color)
u_basePointSize = gl.getUniformLocation(gl.program, 'u_basePointSize')
gl.uniform1f(u_basePointSize, particleSize)
u_Alpha = gl.getUniformLocation(gl.program, "u_Alpha")
gl.uniform1f(u_Alpha, alphaValue)
u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');
u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
modelMatrix = new Matrix4(); // The model matrix
viewMatrix = new Matrix4(); // The view matrix
projMatrix = new Matrix4(); // The projection matrix
modelMatrix.setTranslate(0, 0, 0); //
viewMatrix.setLookAt(eyeX, eyeY, eyeZ, 0, 0, 0, upX, upY, upZ); // eyeX, eyeY, eyeZ, camX, camY, camZ, upX, upY, upY
projMatrix.setPerspective(30, canvas.width/canvas.height, 100, farField); // fov, ratio, near, far
// Pass the model, view, and projection matrix to the uniform variable respectively
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);
gl.clearColor(1, 1, 1, 1); // add ternary conditional
gl.enable(gl.DEPTH_TEST)
gl.enable(gl.BLEND)
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
//gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
gl.clear(gl.COLOR_BUFFER_BIT);
return gl
}
var canvas = document.getElementById('canvas'),
particleSize = 150,
alphaValue = 1.0,
bg_color = "dark",
eyeX = 0.0,
eyeY = 0.0,
eyeZ = 400.0,
upX = 0.0,
upY = 1.0,
upZ = 0.0,
eyeVN = 400.0,
farField = 500.0,
previousX = null,
previousY = null,
currentX = null,
currentY = null;
data_buffer = new Float32Array([
datahere
])
index_table = []
indiceshere
current_data_buffer = data_buffer
gl_context = getContext(canvas)
gl_context = initContext(gl_context)
gl_context.drawArrays(gl_context.POINTS, 0, n)
function negCrossProduct(vecA, vecB){
crossproduct = [ - vecA[1] * vecB[2] + vecA[2] * vecB[1],
- vecA[2] * vecB[0] + vecA[0] * vecB[2],
- vecA[0] * vecB[1] + vecA[1] * vecB[0]
]
return(crossproduct)
}
function vectNorm(vector){
return(Math.sqrt((vector[0] * vector[0]) + (vector[1] * vector[1]) + (vector[2] * vector[2])))
}
function rotateData(hAngle, vAngle){
// change vector for very small angles is approximately the cross product of the eye vector and up vector
change = negCrossProduct([eyeX, eyeY, eyeZ], [upX, upY, upZ])
// normalize the change vector
normChange = vectNorm(change)
// scale the change vector by the horizontal angle
change = [hAngle * change[0]/normChange, hAngle * change[1]/normChange, hAngle * change[2]/normChange]
// update the eye vector by adding the change vector
eyeX = eyeX - change[0]
eyeY = eyeY - change[1]
eyeZ = eyeZ - change[2]
// renormalize the eye vector, otherwise it will increase with each change (due to approx error)
normEye = vectNorm([eyeX, eyeY, eyeZ])
eyeX = eyeVN * eyeX / normEye
eyeY = eyeVN * eyeY / normEye
eyeZ = eyeVN * eyeZ / normEye
// get the (eye, up) plane normal
planeInvNormal = negCrossProduct([eyeX, eyeY, eyeZ], [upX, upY, upZ])
// in the case of vertical angle, the up vector is already the change vector
normChange = vectNorm([upX, upY, upZ])
change = [vAngle * upX / normChange, vAngle * upY / normChange, vAngle * upZ / normChange]
// update the eye vector by adding the change vector
eyeX = eyeX + change[0]
eyeY = eyeY + change[1]
eyeZ = eyeZ + change[2]
// renormalize the eye vector, other it will increase with each change (due to approx error)
normEye = Math.sqrt((eyeX * eyeX)+(eyeY * eyeY)+(eyeZ * eyeZ))
eyeX = eyeVN * eyeX / normEye
eyeY = eyeVN * eyeY / normEye
eyeZ = eyeVN * eyeZ / normEye
// but the up vector needs changing as well
newUp = negCrossProduct([eyeX, eyeY, eyeZ], planeInvNormal)
newUpNormal = vectNorm(newUp)
upX = -newUp[0] / newUpNormal
upY = -newUp[1] / newUpNormal
upZ = -newUp[2] / newUpNormal
gl_context.clear(gl_context.COLOR_BUFFER_BIT);
viewMatrix.setLookAt(eyeX, eyeY, eyeZ, 0, 0, 0, upX, upY, upZ);
projMatrix.setPerspective(30, canvas.width/canvas.height, 100, farField);
gl_context.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
gl_context.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);
gl_context.drawArrays(gl_context.POINTS, 0, n);
}
function startRotating(ev){
previousX = ev.clientX
previousY = ev.clientY
canvas.addEventListener('mousemove', rotateEvent)
canvas.addEventListener('mouseup', stopRotation)
canvas.addEventListener('mouseout', stopRotation)
}
function stopRotation(ev){
canvas.removeEventListener('mousemove', rotateEvent)
canvas.removeEventListener('mouseup', stopRotation)
canvas.removeEventListener('mouseout', stopRotation)
}
function rotateEvent(ev){
currentX = ev.clientX
currentY = ev.clientY
var dX = currentX - previousX,
dY = currentY - previousY;
rotateData(2.0 * dX, 2.0 * dY)
previousX = currentX;
previousY = currentY;
}
canvas.addEventListener('mousedown', startRotating)
</script>
</body>
</html>
"""
import pandas as pd
import numpy as np
data = pd.read_csv(data_frame_fname, index_col = None)
# convert Colours to r, g, b values, then to floats < 1.0
def hexdec_to_1floats(hexdec):
return np.array([int(hexdec[1:][i:(i+2)], 16) for i in (0, 2, 4)]) / 255.0
# map Labels to colours
labels = sorted(list(data.Labels.unique()))
index_table = []
radio_commands = []
for index, label in enumerate(labels):
indices = data.Labels == label
indices = indices.values
indices = np.where(indices)
indices = ','.join([str(i) for i in indices[0]])
indices = "[{indices}]".format(indices = indices)
index_table.append("index_table['{label}'] = {indices}".format(label = label, indices = indices))
colour = data.Colours[data.Labels == label].values[0]
radio_command = "<div style='background-color:{colour}'><input style='float:left' type='checkbox' id='{label}' checked onchange='selectData()' /><label style='float:left' for='{label}'>{label}: </label><br /></div>".format(colour = colour, label = label)
radio_commands.append(radio_command)
index_table = ';\n '.join(index_table)
radio_commands = '\n '.join(radio_commands)
# make data string
coordinates = data.values[:, 0:3].astype('float32')
# next few steps are compressing the data into a stadard cube centered at (0,0,0) and L = 200
Xrange = np.percentile(coordinates[:, 0], q = [1, 99]) * 1.2
Yrange = np.percentile(coordinates[:, 1], q = [1, 99]) * 1.2
Zrange = np.percentile(coordinates[:, 2], q = [1, 99]) * 1.2
center = np.tile(np.array([np.mean(Xrange), np.mean(Yrange), np.mean(Zrange)]),
(coordinates.shape[0], 1))
coordinates = coordinates - center
Xrange = Xrange[1] - Xrange[0]
Yrange = Yrange[1] - Yrange[0]
Zrange = Zrange[1] - Zrange[0]
maxRange = max((Xrange, Yrange, Zrange))
ratio = 180.0 / maxRange
coordinates = coordinates * ratio
# next few steps the buffer data is created as string
colours = data.values[:, 4]
buffer_data = []
for index in range(coordinates.shape[0]):
coordinate = [str(i) for i in coordinates[index, :]]
colour = [str(i) for i in hexdec_to_1floats(colours[index]).astype('float32')]
vertex_data = coordinate + colour
buffer_data.append(",".join(vertex_data))
buffer_data = ",".join(buffer_data)
template_str = template.replace('datahere', buffer_data)
template_str = template_str.replace('indiceshere', index_table)
template_str = template_str.replace('radiocommands', radio_commands)
with open(save_to, 'w') as result:
result.write(template_str)

View file

@ -0,0 +1,23 @@
import sys
args = sys.argv
model_fname = args[1]
data_fname = args[2]
predictions_fname = args[3]
pca_fname = args[4]
import pickle
import pandas as pd
print("loading model ...")
model = pickle.loads(open(model_fname, "rb").read())
pca = pickle.loads(open(pca_fname, "rb").read())
print("loading data....")
predictions = pd.read_csv(data_fname, sep = ",", index_col = 0).values
print("classifying cells ... ")
predictions = pca.transform(predictions);
predictions = model.predict(predictions)
print("writing results to disk...")
df = pd.DataFrame(predictions)
df.to_csv(predictions_fname)
print("Classification finished. Python will now say goodbye. Handing over to R.")

20
tools/umap/umap_compute.py Executable file
View file

@ -0,0 +1,20 @@
import sys
args = sys.argv
pca_data_fname = args[1]
umap_coordinates_fname = args[2]
import pandas as pd
import umap
print("Loading data ...")
pca = pd.read_csv(pca_data_fname, sep = ",", index_col = 0).values
print("creating UMAP ... ")
embedding = umap.UMAP(n_neighbors = 5, min_dist = .3, metric = "correlation", random_state = 17)
embedding = embedding.fit_transform(pca)
print("saving UMAP coordinates to disk ...")
df = pd.DataFrame(embedding)
df.columns = ["UMAPx", "UMAPy"]
df.to_csv(umap_coordinates_fname)