...
 
Commits (39)
......@@ -3,7 +3,7 @@
"samples" : {
"number" : 1,
"original_names" : [ "/some/file" ] ,
"original_names" : [ "/some/file_1" ] ,
"run_timestamp" : [ "2015-02-19 16:37:06" ] ,
"producer" : [ "vidjil dev 0cf35de (2015-02-17)" ] ,
"log" : [ "Some log" ],
......
......@@ -3,7 +3,7 @@
"samples" : {
"number" : 1,
"original_names" : [ "/some/file" ] ,
"original_names" : [ "/some/file_2" ] ,
"run_timestamp" : [ "2015-02-19 16:37:06" ] ,
"producer" : [ "vidjil dev 0cf35de (2015-02-17)" ] ,
"log" : [ "Some log" ],
......
This diff is collapsed.
......@@ -333,6 +333,7 @@ Builder.prototype = {
var li = document.createElement('li');
li.appendChild(div)
var listTag = document.getElementById("tagList")
listTag.appendChild(li);
},
......@@ -357,8 +358,9 @@ Builder.prototype = {
// listGermline.appendChild(li);
},
/*complete displaySelector menu with correct info about current tagname / top
* */
/**
* complete displaySelector menu with correct info about current tagname / top
**/
build_displaySelector: function () {
var self = this;
......@@ -367,7 +369,9 @@ Builder.prototype = {
// var listGermline = document.getElementById("germline_list")
//reset
listTag.removeAllChildren();
if (listTag != null){
listTag.removeAllChildren();
}
// listGermline.removeAllChildren();
//init tag list
......
This diff is collapsed.
......@@ -385,16 +385,20 @@ Graph.prototype = {
}
}else{
var list = []
for (var j = 0; j < this.m.clones.length; j++) list.push(j)
for (var j = 0; j < this.m.clones.length; j++) {
if (!this.m.clones[j].is_distrib()){
list.push(j)
}
}
this.updateElem(list)
}
return this
},
/* update a list of selected clones
*
* */
/**
* update a list of selected clones
**/
updateElem: function (list) {
if (this.mode == "stack"){
//in stack mode we can't update only a few clones whithout updating all of them
......
This diff is collapsed.
......@@ -260,7 +260,7 @@ Model_loader.prototype = {
var index = 0
for (var i = 0; i < data.clones.length; i++) {
if (data.clones[i].top <= limit) {
var clone = new Clone(data.clones[i], self, index)
var clone = new Clone(data.clones[i], self, index, "real")
self.mapID[data.clones[i].id] = index;
index++
}
......@@ -348,13 +348,15 @@ Model_loader.prototype = {
"reads": [],
"germline" : this.system_available[q],
};
new Clone(other, self, index, true);
new Clone(other, self, index, "virtual");
index++ ;
}
//remove incomplete similarity matrix (TODO: fix fuse.py)
this.similarity = undefined;
this.distributions = data.distributions
// this.init_distributions()
return this
}
......@@ -365,6 +367,124 @@ Model_loader.prototype = {
},
init_distributions: function(){
// Que faire dans cette fonction ?
// Il faut à priori prendre chaque clones et soustraire ses valeurs aux distributions concernés;
// Il faudrait donc à terme pouvoir retrouvé chaque axe d'un clone, comme fait en fuse.py
//
var repnames = Object.keys(this.distributions.repertoires)
console.log( repnames)
// Ne serait-ce pas a faire au moment de l'utilisation de la distribution ?
// Dans ce cas, on ne soustrait que les clones qui sont NON filtrés.
// ainsi, on voit les filtred dans les valeurs des distributions.
// On pourrait aussi imaginer vouloir les voir en séparés.
for (var i = 0; i < repnames.length; i++) {
var repname = repnames[i]
var sample_position = this.samples.original_names.indexOf(repname)
var rep = this.distributions.repertoires[repname]
for (var j = 0; j < rep.length; j++) {
var distrib = rep[j]
for (var c = 0; c < this.clones.length; c++) {
var reads = clone.getReads(sample_position)
// don't remove if clone is not present in this timepoint
if (reads){
var distrib = rep[j]
// pass if clones are unreal, or filtered ?
// How to do that dinamicly... and take into account the filtered ones..., but not all
// Don't show twice the same data.
var clone = this.clones[c]
var values = clone.get_distributions_values(distrib.axes)
var dvalues = distrib.values
// Decrease the size of the distribution for each clones that are not filtered.
// part1, get the position for the current clone
for (var v = 0; v < values.length; v++) {
var axe_val = values[v]
// Warning, if values is see as todo, take it into account for the moment
if (dvalues[axe_val] == undefined){
if (v != values.length-1)
dvalues[axe_val] = {}
else{
dvalues[axe_val] = [0, 0]
}
}
dvalues = dvalues[axe_val]
}
// Soustraction
dvalues[1] -= clone.getReads(sample_position)
dvalues[0] -= 1
// todo; choisir les position d'apres les keys
}
}
var distrib = rep[j]
}
}
},
/**
* Allow to get th e distributions, usable for use for graphical purpose, like genscan like graph.
* @param {[type]} axe [description]
* @param {[type]} sample_position [description]
* @return {[type]} [description]
*/
get_usable_distribution: function(axes, sample_position){
var repname = this.samples.original_names[sample_position]
console.log( repname )
var distribs_rep = this.distributions.repertoires[repname]
for (var j = 0; j < distribs_rep.length; j++) {
var distrib_raw = distribs_rep[j]
if (distrib_raw.axes == axes) { // !!! comment comparer 2 listes ?
for (var c = 0; c < this.clones.length; c++) {
var reads = clone.getReads(sample_position)
// don't remove if clone is not present in this timepoint
if (reads){
var distrib = distrib_raw
// pass if clones are unreal, or filtered ?
// How to do that dinamicly... and take into account the filtered ones..., but not all
// Don't show twice the same data.
var clone = this.clones[c]
if (clone.isVirtual() == false && clone.isActive() == true){
var values = clone.get_distributions_values(distrib.axes)
var dvalues = distrib.values
// Decrease the size of the distribution for each clones that are not filtered.
// part1, get the position for the current clone
for (var v = 0; v < values.length; v++) {
var axe_val = values[v]
// Warning, if values is see as todo, take it into account for the moment
if (dvalues[axe_val] == undefined){
if (v != values.length-1)
dvalues[axe_val] = {}
else{
dvalues[axe_val] = [0, 0]
}
}
dvalues = dvalues[axe_val]
}
// Soustraction
dvalues[1] -= clone.getReads(sample_position)
dvalues[0] -= 1
// todo; choisir les position d'apres les keys
}
}
}
return distrib_raw // values ?
}
}
// distrib don't exist
return []
// Ensuite, il faut faire des clones virtuel qui correspondront à la distribution.
// Dans un premier temps, size/lenSeq
},
getFilteredFields: function(src) {
// Hacky way of managing fields we do not want to copy without restructuring the whole JSON
var exceptions = ['id', 'log', 'producer'];
......
......@@ -37,35 +37,36 @@ function ScatterPlot(id, model, database, default_preset) {
View.call(this, model);
this.db = database;
this.view_type = "scatterplot"
this.id = id; //ID of the scatterPlot div
//size ( computed value -> resize() function)
this.resizeCoef = 1; //Multiplifying factor, application to nodes radius
this.resizeMinSize = 0.001; // Any clone with a non-null size is displayed as clones of this size
this.resizeW = 1; //scatterplot width
this.resizeH = 1; //scatterplot height
this.gridSizeW = 1; //grid width
this.gridSizeH = 1; //grid height
this.resizeCoef = 1; //Multiplifying factor, application to nodes radius
this.resizeMinSize = 0.001; // Any clone with a non-null size is displayed as clones of this size
this.resizeW = 1; //scatterplot width
this.resizeH = 1; //scatterplot height
this.gridSizeW = 1; //grid width
this.gridSizeH = 1; //grid height
//Margins (css style : top/right/bottom/left)
this.default_margin = [45,10,15,90];
this.graph_margin = [25,25,25,25];
this.margin = this.default_margin;
this.graph_margin = [25,25,25,25];
this.margin = this.default_margin;
this.max_precision = 9; //Precision max (default: 9)
this.max_precision = 9; //Precision max (default: 9)
/* EDIT DISTANCE ONLY
BEG --
*/
this.mouseZoom = 1; //Zoom (scroll wheel)
this['continue'] = false; //Boolean used for the nodes movements
this.allEdges = []; //Initial edges array
this.edgeSaved = []; //Edges array saved for the Edit Distance visualization
this.edge = []; //Edges array given to the engine
this.edgeContainer = null; //SVG element to save the container of graph distribution
this.active_move = false; //Boolean given to the nodes movements
this.reloadCharge = true; //Boolean allowing to reload the physic engine charge (reject)
this.mouseZoom = 1; //Zoom (scroll wheel)
this['continue'] = false; //Boolean used for the nodes movements
this.allEdges = []; //Initial edges array
this.edgeSaved = []; //Edges array saved for the Edit Distance visualization
this.edge = []; //Edges array given to the engine
this.edgeContainer = null; //SVG element to save the container of graph distribution
this.active_move = false; //Boolean given to the nodes movements
this.reloadCharge = true; //Boolean allowing to reload the physic engine charge (reject)
this.canSavePositions = true; //Boolean which allows to save initial positions of nodes
//Object which allows to save new position, and move all nodes according to the object's parameters
this.positionToMove = {
......@@ -309,7 +310,8 @@ ScatterPlot.prototype = {
.append("svg:circle"); //Ajout d'un élément SVG (cercle) à un node
this.node.exit()
.remove() //On efface tous les cercles non pris en compte
// this.updateNodes()
//Action concernant tous les nodes présents dans le ScatterPlot
this.plot_container.selectAll("circle")
.attr("stroke", "")
......@@ -343,6 +345,49 @@ ScatterPlot.prototype = {
},
updateNodes: function(bar){
//Initialisation of nodes
// console.error("previous")
// console.log( this.m.clones.length)
// console.log(this.nodes.length)
this.nodes = d3.range(this.m.clones.length)
.map(Object);
for (var i = 0; i < this.m.clones.length; i++) {
this.nodes[i].id = i; //L'id d'un cercle vaut le nombre de i dans la boucle
this.nodes[i].r1 = 0; // longueur du rayon1
this.nodes[i].r2 = 0; // longueur du rayon2
this.nodes[i].x = Math.random() * 500;
this.nodes[i].old_x = [0, 0, 0, 0, 0]
this.nodes[i].y = Math.random() * 250;
this.nodes[i].old_y = [0, 0, 0, 0, 0]
}
//Initialisation of the D3JS physic engine
this.force = d3.layout.force();
this.initMotor();
this.force
.nodes(this.nodes) //Nodes array initialisation
.on("tick", function(){self.tick()}); // on -> Listen updates compared to modified positions
//Création d'un element SVG pour chaque nodes (this.node.[...])
this.node = this.plot_container.selectAll("circle")
.data(this.nodes) //Ajout de données pour les cercles
this.node.enter()
.append("svg:circle"); //Ajout d'un élément SVG (cercle) à un node
this.node.exit()
.remove() //On efface tous les cercles non pris en compte
// console.log(this.nodes.length)
// this.updateClones()
if (bar){ this.initBar() }
},
/**
* Function which allows to return the number of active clones
* @return {integer}
......@@ -621,10 +666,18 @@ ScatterPlot.prototype = {
},
includeBar: function(clone) {
return ((!this.use_system_grid ||
(this.use_system_grid && this.m.germlineV.system == clone.get('germline') )) &&
clone.isActive() &&
!clone.isVirtual());
var system_grid = (!this.use_system_grid || (this.use_system_grid && this.m.germlineV.system == clone.get('germline') ))
var showVirtual;
// Set if the clone should be show on is virtual/distrib status
if (clone.isVirtual() ){
showVirtual = ( (this.m.distrib_inclusion) && clone.is_distrib() )
} else {
showVirtual = true
}
var include = (system_grid && (clone.isActive() || clone.is_distrib()) && showVirtual)
return include
},
/**
......@@ -655,7 +708,7 @@ ScatterPlot.prototype = {
var tab_length = Object.keys(this.axisX.value_mapping).length;
var width = Math.min(0.08, 0.8 / tab_length);
this.updateNodes(false)
//reset (TODO improve default position )
for (var n in this.nodes) {
this.nodes[n].bar_y = 0.5;
......@@ -1128,7 +1181,11 @@ ScatterPlot.prototype = {
}
for (var i = 0; i < this.nodes.length; i++) {
this.updateClone(i);
try {
this.updateClone(i);
} catch(err) {
console.log("cannot update this clone from graph")
}
}
this.force.start();
this.updateElemStyle();
......@@ -1250,6 +1307,7 @@ ScatterPlot.prototype = {
* */
updateElemStyle: function() {
var self = this;
test = this.node
if (this.mode == this.MODE_BAR) {
this.updateBar();
} else {
......@@ -1295,6 +1353,8 @@ ScatterPlot.prototype = {
var elem = this.select_x;
this.changeSplitMethod(elem.value, this.splitY, this.mode);
this.cancelPreset()
// this.m.change_active_distrib(elem.value, this.splitY, this.mode, this.id)
this.updateNodes(true)
this.m.update();
},
......@@ -1305,6 +1365,8 @@ ScatterPlot.prototype = {
var elem = this.select_y;
this.changeSplitMethod(this.splitX, elem.value, this.mode);
this.cancelPreset()
// this.m.change_active_distrib(this.splitY, elem.value, this.mode, this.id)
this.updateNodes(true)
this.m.update();
},
......@@ -1314,6 +1376,7 @@ ScatterPlot.prototype = {
changePreset: function(){
var elem = this.select_preset;
this.changeSplitMethod(this.preset[elem.value].x, this.preset[elem.value].y, this.preset[elem.value].mode);
// this.m.change_active_distrib(this.splitY, elem.value, this.mode, this.id)
},
updatePreset: function(){
......@@ -1714,6 +1777,8 @@ ScatterPlot.prototype = {
this.m.graph.setOtherVisibility(this.otherVisibility)
}
// this.changeSplitMethod(this.preset[elem.value].x, this.preset[elem.value].y, this.preset[elem.value].mode);
this.m.change_active_distrib(splitX, splitY, mode, this.id)
},
/**
......
......@@ -1511,6 +1511,8 @@ Sequence.prototype = Object.create(genSeq.prototype);
var stop = -1;
var clone = this.m.clone(this.id);
if (!clone.sequence) return
if (clone.hasSeg('cdr3')){
if (typeof clone.seg.cdr3.start != "undefined") {
start = this.pos[clone.seg.cdr3.start];
......
......@@ -712,4 +712,37 @@ function openAndFillNewTab (content){
html: content
}).appendTo(w.document.body);
return
}
\ No newline at end of file
}
/**
* Function that allow to make comparison between two arrays.
*/
if(Array.prototype.equals)
console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code.");
// attach the .equals method to Array's prototype to call it on any array
Array.prototype.equals = function (array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
for (var i = 0, l=this.length; i < l; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}
// Hide method from for-in loops
Object.defineProperty(Array.prototype, "equals", {enumerable: false});
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
### fuse.py - Vidjil utility to parse and regroup list of clones of different timepoints or origins
# This file is part of Vidjil <http://www.vidjil.org>,
# High-throughput Analysis of V(D)J Immune Repertoire.
# Copyright (C) 2011-2017 by Bonsai bioinformatics
# at CRIStAL (UMR CNRS 9189, Université Lille) and Inria Lille
# Contributors:
# Marc Duez <marc.duez@vidjil.org>
# Mathieu Giraud <mathieu.giraud@vidjil.org>
# The Vidjil Team <contact@vidjil.org>
#
# "Vidjil" is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# "Vidjil" is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with "Vidjil". If not, see <http://www.gnu.org/licenses/>
from __future__ import print_function
import check_python_version
import sys
import json
import argparse
import os.path
import os
import datetime
import subprocess
import tempfile
from operator import itemgetter, le
from utils import *
from defs import *
from collections import defaultdict
from pipes import quote
# ==================
from fuse import *
# ==================
class Tree(defaultdict):
""" une classe qui permet de descendre sur de nombreux niveaux (defaultdict recursif)"""
def __init__(self, value=None):
super(Tree, self).__init__(Tree)
self.value = value
# On peut reprendre le Window de fuse, mais il faudra l'augmenter d'une fct get_value
# Cette fonction permettra pour un champs donne de retrouver la valeur specifique a ce clone
class Clone(Window):
# def __init__(self, data):
# """ un objet pour manipuler les champs d'un clone """
# return
def get_values(self, value):
""" une liste pour recuperer la valeur d'un champs du clone """
try:
if value == 'reagmt':
return clone. ...
if clone == "seg5":
return clone.d["seg"]["5"]["name"]
...
except:
return ""
return
def get_reads(self, t=-1):
""" une fonction direct """
if t== -1:
return self.d["reads"]
else
return self.d["reads"][t]
### Je pense que distribution devrait pouvoir être autonome, dans passer par fuse
#
class Distribution(VidjilJson):
""" on garde la fct originale """
def __init__(self, fileIn):
'''init ListWindows with the minimum data required'''
super(VidjilJson, self).__init__(VidjilJson)
self.distributions = {}
self.load(fileIn)
def cut_at_top(self, top):
if top != 0:
self.clones = self.clones[:top]
return
def load(self, file_path, verbose=True):
'''init listWindows with data file
Detects and selects the parser according to the file extension.'''
extension = file_path.split('.')[-1]
if verbose:
print("<==", file_path, "\t", end=' ')
with open(file_path, "r") as f:
self.d(json.load(f))
### On pourrait aussi imaginer prendre directement un jlist depuis fuse
return
def init_distrib(self, list_distrib):
""" on creer la liste des distributions avec des valeurs nulles pour le moment """
distributions = []
nb_sample = self.d["samples"].d["number"]...
for distrib in list_distrib:
### ICI le defaultdict devra être de la profondeur du nombre d'axes (soit la len de distrib)
distributions.append([{"axes":distrib,"values":[Tree([0, 0])] * nb_sample}])
return
def compute_distribution(self, list_distrib):
"""
list_distrib sera une listes des combinaisons d'axes a calculer
"""
# creer une liste de distribs
# ATTENTION, on peut avoir plusieurs fichier deja fuse
for filename in self.sample...:
self.distributions[filename] = self.init_distrib(list_distrib)
# passer la liste des clones en revue
for clone in self.clones:
for distrib_pos in range(self.distribs):
distrib = list_distrib[distrib_pos]
clone_values = clone.get_values( distrib.axes )
# list de valeur, pour les axes
self.add_clone_values( filename, distrib_pos, clone_values, clone["reads"])
return
def add_clone_values(self, filename, distrib_pos, values, nb_reads):
# ici on ajoute la valeur souhaité; peut-être de manière recursive au cas ou
self.distributions[filename][distrib_pos]...
i = 0
# Je ne pense pas que cette approche marche en python
obj = self.distributions[filename][distrib_pos]["values"]
while i != len(values):
obj = obj[values[i]]
i += 1
# incremente
# Pas certain que ça marche. A voir.
obj[0] += 1
obj[1] += nb_reads
return
if __name__ =='__main__':
DESCRIPTION = 'Vidjil utility to create distributions'
#### Argument parser (argparse)
parser = argparse.ArgumentParser(description= DESCRIPTION,
epilog='''Example:
python %(prog)s out/data1.vidjil out/data2.vidjil''',
formatter_class=argparse.RawTextHelpFormatter)
group_options = parser.add_argument_group() # title='Options and parameters')
group_options.add_argument('--top', '-t', type=int, default=50, help='keep only clones in the top TOP of some point (%(default)s)')
parser.add_argument('file', nargs='+', help='''input files (.vidjil/.cnltab)''')
args = parser.parse_args()
print("### fuse.py -- %s\n" % DESCRIPTION)
LISTE_D = []
## pouvoir lire un fichier de paramètres ?
#envoyer ça par cli ?
LISTE_D.append(["reagmt", "lenCDR3"])
LISTE_D.append(["reagmt", "productive"])
LISTE_D.append(["reagmt", "seg5"])
LISTE_D.append(["reagmt", "seg4"])
LISTE_D.append(["reagmt", "seg3"])
LISTE_D.append(["seg5", "seg3"])
files = args.file
jlist = Distribution()
for path_name in files:
jlist.load(path_name, args.pipeline)
jlist.cut_at_top(args.top)
jlist.get_distrib(LISTE_D)
# Perrmettre ensuite de faire un export json ?
# Un seul esport pour tous les fichiers ? Ou bien un par fichier ?
# Les deux me semble logique.
# On peux imaginer que chaque fichier .vidjil soit accompagné d'un fichier de metadata et de distributions qui lui soit propre.
#
This diff is collapsed.
### Without distributions computing
python3 ../../fuse.py ../../../algo/tests/data/results-two-clones-1-2.vidjil ../../../algo/tests/data/results-two-clones-1-3.vidjil; cat fused.vidjil
$ Get correct file name in original_names field
1:"/some/file_1"
1:"/some/file_2"
$ should not have distribution if not asked
0:distributions
$ categories should be present one time ... ; even if no vidjil file already have it
1:categories
$ ... with empty content (2 for categories +1 for germlines)
3:{}
### With distributions computing
rm fused.vidjil
python3 ../../fuse.py -d ../../../algo/tests/data/results-two-clones-1-2.vidjil ../../../algo/tests/data/results-two-clones-1-3.vidjil; cat fused.vidjil
$ Get correct file name in original_names field and distributions
2:"/some/file_1"
2:"/some/file_2"
$ should not have distribution if not asked
1:distributions
$ categories should be present 2 times; in vidjil top level, and in distribution["categories"]
2:categories
$ Axes title should be present 62 times (2*31)
62:rearangment
\ No newline at end of file
###################################
### Part with unsegmented clones ##
###################################
# python3 ../../fuse.py -D ../../../algo/tests/data/nico_very_short.vidjil ../../../algo/tests/data/results-two-clones-1-2.vidjil; cat fused.json
python3 ../../fuse.py -D ../../../algo/tests/data/results-two-clones-1-2.vidjil ../../../algo/tests/data/results-two-clones-1-3.vidjil; cat fused.json
$ Get correct keys for distributions json content
1:"repertoires"
1:"keys"
1:"categories"
1:"filters"
$ Get correct files names
r:/some/file_[12]
# For the moment, 31 call by repertoire
$ Have correct number of entrie for seg5
62:"seg5"
$ Have correct number of entrie for germline
62:germline
############################
### Part with real clones ##
############################
rm fused.json
python3 ../../fuse.py -D ../../../algo/tests/data/results_five_segmented_clones.vidjil ../../../algo/tests/data/results-two-clones-1-2.vidjil ../../../algo/tests/data/results-two-clones-1-3.vidjil; cat fused.json
$ Get correct keys for distributions json content
1:"repertoires"
1:"keys"
1:"categories"
1:"filters"
# For the moment, 31 call by repertoire
$ Have correct number of entrie for seg5 (increase of 31 by the new repertoire)
93:"seg5"
$ Get correct files names
:sequence_file
$ Test on categories -undefined
2:undefined
$ Test on categories - "cat"
1:"cat"
$ Test on categories - disease
1:disease
$ Test on categories - a_category
1:a_category
$ EAch filename should appear 3 times (1 for repname, 1 for both category)
3:"sequence_file"
3:"/some/file_1"
3:"/some/file_2"
\ No newline at end of file