diff --git a/examples/data_tests/generate_astec_lineage_dist.py b/examples/data_tests/generate_astec_lineage_dist.py index f4aa04d3d9482dcfe27106394b732c1145655458..4f885ba8cb17ef8f37afd3c849b1bd97816d8d88 100644 --- a/examples/data_tests/generate_astec_lineage_dist.py +++ b/examples/data_tests/generate_astec_lineage_dist.py @@ -2,21 +2,15 @@ import os from morphonet.data import get_local_dataset,list_local_datasets from morphonet.data.dataproperty import DataProperty import xml.etree.ElementTree as ET -from morphonet.data.utils import get_object_id,indent_xml,generate_init_naming_parameters,remove_folder,generate_prop_naming_parameters,load_properties_from_xml,get_object_t_id_ch,get_node +import pickle as pkl + +from libs.lineage_distance import generate_lineage_comparison +from morphonet.data.utils import get_object_id,indent_xml,generate_init_naming_parameters,remove_folder,generate_prop_naming_parameters,load_properties_from_xml,get_object_t_id_ch,get_node,get_id_t import argparse from pathlib import Path -import edist.ted -import edist.uted as uted import time -def get_symetric_cells(property,name): - #print(name) - for cell_key in property.keys(): - sname = property[cell_key] - if sname is not None: - if sname!=name and sname[0:-1] == name[0:-1]: - #print("found symetric "+str(sname)) - return cell_key - return None + + parser = argparse.ArgumentParser() @@ -41,94 +35,17 @@ if dataset is None : for dataset in datasets_available: print(dataset) exit() +min_time = dataset.get_min_time() +output_xml = os.path.join(seg_folder_name,"output.xml") -property_object = DataProperty(dataset, "astec_lineage_distance", "", "float") -treated_cells_names = {} -print("Loading needed properties") -property_name = dataset.get_property("cell_name") -cell_lineage = dataset.get_property("cell_lineage") -if cell_lineage is None : - cell_lineage = dataset.get_property("temporal") -dict_name_by_t = {} -dict_cells_by_name = {} -dict_mincell_id_by_name = {} -if property_name is not None and cell_lineage is not None: - def local_cost_normalized(x, y): - if x is None and y is None: - return 0 - elif x is None or y is None: - return 1 - elif x not in x_nodes or y not in y_nodes: - return 1 - xl = x_life[x_nodes.index(x)] - yl = y_life[y_nodes.index(y)] - return abs(xl - yl) / (xl + yl) - - - # LINEAGE COMPARISON - # if not cell_id in existing_regions: - # existing_regions[cell_id] = {} - # Split the property into subs dict - print("Splitting names by time point") - for cell_key in property_name.values: - tc, idc, chanc = get_object_t_id_ch(cell_key) - name = property_name.values[cell_key] - if not name in dict_cells_by_name: - dict_cells_by_name[name] = [] - dict_cells_by_name[name].append(cell_key) - if not int(tc) in dict_name_by_t: - dict_name_by_t[int(tc)] = {} - dict_name_by_t[int(tc)][cell_key] = name - if not name in dict_mincell_id_by_name: - dict_mincell_id_by_name[name] = cell_key - else: - tp,idcp,chanp = get_object_t_id_ch(dict_mincell_id_by_name[name]) - if int(tc) < int(tp): - dict_mincell_id_by_name[name] = cell_key - print(dict_mincell_id_by_name) - for cell_key in property_name.get_keys(): - name = property_name.values[cell_key] - # print("Trying to compute lineage dist for cell "+str(cell_key)+" with name : "+str(name)) - if name is not None and name.endswith("*"): # ONLY GET RIGHT SIDE - print("Working on cell : "+str(name)) - # print("found cell on right side ") - # print("working time is "+str(time)) - tc, idc, chanc = get_object_t_id_ch(cell_key) - mother_key = dict_mincell_id_by_name[name] - # print("found cell at time : "+str(tc)+" with id "+str(idc)) - smo = name.replace('*','_') - if smo is not None: - # print("found symetric : "+str(smo)) - # print("smo : "+str(smo)) - # print("cell_key : "+str(cell_key)) - ts = None - ids = None - chans = None - if smo in dict_mincell_id_by_name: - for cell_f in dict_cells_by_name[smo]: - tt, idt, chant = get_object_t_id_ch(cell_f) - if ts is None or int(tt) < int(ts): - ts = tt - ids = idt - chans = chant - if mother_key is not None and not mother_key in treated_cells_names: - x_nodes, x_adj, x_life = get_node(cell_lineage, mother_key) - # print(x_nodes,x_adj,x_life) - sym_key = get_object_id(ts,ids,chans) - print(mother_key + " -> "+sym_key) - y_nodes, y_adj, y_life = get_node(cell_lineage, sym_key) - # print(y_nodes,y_adj,y_life) - d = uted.uted(x_nodes, x_adj, y_nodes, y_adj, local_cost_normalized) - # print(d) - # exit() - treated_cells_names[mother_key] = d - property_object.set_object_value(tc, idc, seg_channel, d) - property_object.set_object_value(ts, ids, seg_channel, d) - else : - property_object.set_object_value(tc, idc, seg_channel,treated_cells_names[mother_key]) - property_object.set_object_value(ts, ids, seg_channel, treated_cells_names[mother_key]) +generate_lineage_comparison(output_xml,min_time) +if os.path.exists(output_xml): + print("-> importing lineage in MorphoNet dataset") + properties = load_properties_from_xml(output_xml) step = dataset.initialize_external_step(Path(__file__).stem) - dataset.write_property_to_step(step, property_object.name + ".txt", property_object) -t2 = time.time() -print("computed in : "+str(t2-t1)+" seconds") \ No newline at end of file + for property_object in properties: + if property_object.name.startswith("float_symetric"): + dataset.write_property_to_step(step, property_object.name + ".txt", property_object) +# DELETE ALL TEMP FILES +os.system("rm -rf " + str(temp_file_path)) diff --git a/examples/data_tests/generate_lineage_names.py b/examples/data_tests/generate_lineage_names.py index 8764e92fc35acaded145852f295caab72140aa31..2807f1de93da44b5f255caf869e26b1f4e2cbad7 100644 --- a/examples/data_tests/generate_lineage_names.py +++ b/examples/data_tests/generate_lineage_names.py @@ -5,8 +5,6 @@ import xml.etree.ElementTree as ET from morphonet.data.utils import indent_xml,generate_init_naming_parameters,remove_folder,generate_prop_naming_parameters,load_properties_from_xml,get_object_t_id_ch,get_symetric_cells,get_node import argparse from pathlib import Path -import edist.ted -import edist.uted as uted def compute_atlas(): """ Returns the list of files @@ -135,59 +133,61 @@ print("Computing cell_neighbors using contact surface") property_object = DataProperty(dataset, "cell_neighbors", "", "space") if cell_contact_surface is not None: for id_cell in cell_contact_surface.get_keys(): + neigh = [] tc,idc,channelc = get_object_t_id_ch(id_cell) - for neigh in cell_contact_surface.values[id_cell]: - property_object.set_object_value(tc,idc,channelc,neigh[0]) + if len(cell_contact_surface.values[id_cell]) > 0: + for neigh in cell_contact_surface.values[id_cell].keys(): + property_object.set_object_value(tc,idc,channelc,neigh) dataset.write_property_to_step(step, property_object.name + ".txt", property_object) print("Computing lineage distance, this may be long ...") - -property_object = DataProperty(dataset, "astec_lineage_distance", "", "float") - -if property_name is not None and cell_lineage is not None: - def local_cost_normalized(x, y): - if x is None and y is None: - return 0 - elif x is None or y is None: - return 1 - elif x not in x_nodes or y not in y_nodes: - return 1 - xl = x_life[x_nodes.index(x)] - yl = y_life[y_nodes.index(y)] - return abs(xl - yl) / (xl + yl) - - - # LINEAGE COMPARISON - # if not cell_id in existing_regions: - # existing_regions[cell_id] = {} - - for cell_key in property_name.get_keys(): - name = property_name.values[cell_key] - # print("Trying to compute lineage dist for cell "+str(cell_key)+" with name : "+str(name)) - if name is not None and name.endswith("*"): # ONLY GET RIGHT SIDE - # print("found cell on right side ") - # print("working time is "+str(time)) - tc, idc, chanc = get_object_t_id_ch(cell_key) - # print("found cell at time : "+str(tc)+" with id "+str(idc)) - smo = get_symetric_cells(tc, property_name, name) - # print("found symetric : "+str(smo)) - # print("smo : "+str(smo)) - # print("cell_key : "+str(cell_key)) - if smo is not None: - ts, ids, chans = get_object_t_id_ch(smo) - x_nodes, x_adj, x_life = get_node(cell_lineage, cell_key) - # print(x_nodes,x_adj,x_life) - y_nodes, y_adj, y_life = get_node(cell_lineage, smo) - # print(y_nodes,y_adj,y_life) - d = uted.uted(x_nodes, x_adj, y_nodes, y_adj, local_cost_normalized) - # print(d) - # exit() - property_object.set_object_value(tc, idc, seg_channel, d) - property_object.set_object_value(ts, ids, seg_channel, d) - - dataset.write_property_to_step(step, property_object.name + ".txt", property_object) +if False: + property_object = DataProperty(dataset, "astec_lineage_distance", "", "float") + + if property_name is not None and cell_lineage is not None: + def local_cost_normalized(x, y): + if x is None and y is None: + return 0 + elif x is None or y is None: + return 1 + elif x not in x_nodes or y not in y_nodes: + return 1 + xl = x_life[x_nodes.index(x)] + yl = y_life[y_nodes.index(y)] + return abs(xl - yl) / (xl + yl) + + + # LINEAGE COMPARISON + # if not cell_id in existing_regions: + # existing_regions[cell_id] = {} + + for cell_key in property_name.get_keys(): + name = property_name.values[cell_key] + # print("Trying to compute lineage dist for cell "+str(cell_key)+" with name : "+str(name)) + if name is not None and name.endswith("*"): # ONLY GET RIGHT SIDE + # print("found cell on right side ") + # print("working time is "+str(time)) + tc, idc, chanc = get_object_t_id_ch(cell_key) + # print("found cell at time : "+str(tc)+" with id "+str(idc)) + smo = get_symetric_cells(tc, property_name, name) + # print("found symetric : "+str(smo)) + # print("smo : "+str(smo)) + # print("cell_key : "+str(cell_key)) + if smo is not None: + ts, ids, chans = get_object_t_id_ch(smo) + x_nodes, x_adj, x_life = get_node(cell_lineage, cell_key) + # print(x_nodes,x_adj,x_life) + y_nodes, y_adj, y_life = get_node(cell_lineage, smo) + # print(y_nodes,y_adj,y_life) + d = uted.uted(x_nodes, x_adj, y_nodes, y_adj, local_cost_normalized) + # print(d) + # exit() + property_object.set_object_value(tc, idc, seg_channel, d) + property_object.set_object_value(ts, ids, seg_channel, d) + + dataset.write_property_to_step(step, property_object.name + ".txt", property_object) # DELETE ALL TEMP FILES -os.system("rm -rf " + str(temp_file_path)) \ No newline at end of file +#os.system("rm -rf " + str(temp_file_path)) \ No newline at end of file diff --git a/examples/data_tests/libs/lineage_distance.py b/examples/data_tests/libs/lineage_distance.py new file mode 100644 index 0000000000000000000000000000000000000000..eb0b59dfdc9b6523611cde6021e601568c7d9311 --- /dev/null +++ b/examples/data_tests/libs/lineage_distance.py @@ -0,0 +1,1268 @@ +import numpy as np +import matplotlib.pyplot as plt +from scipy.cluster import hierarchy +import pickle as pkl +import re +import os +import xml.etree.ElementTree as ET +from morphonet.tools import get_longid,get_id_t + +def indent_xml(elem, level=0): + """ Recursively auto indent an xml object to save it to file + + :param elem: XML object to be indented + :type elem: xml.etree.ElementTree.Element + :param level: Level of indentation (Default value = 0) + :type level: int + :return: Indented XML object + :rtype: xml.etree.ElementTree.Element + + """ + i = "\n" + level*" " + j = "\n" + (level-1)*" " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for subelem in elem: + indent_xml(subelem, level+1) + if not elem.tail or not elem.tail.strip(): + elem.tail = j + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = j + return elem + +def list_ids_with_name(list_names,cell_name): + """Using a dict of cell key to name coming, retrieve the list of cell keys with the name + + :param list_names: List of names + :type list_names: dict + :param cell_name: The cell name to find + :type cell_name: str + :return: List of cell keys with the name + :rtype: list + + """ + list_ids = [] + for cell_key in list_names: + if cell_name == list_names[cell_key]: + list_ids.append(cell_key) + return list_ids +def find_lowest_t_with_cell(list_names, cell_name): + """Using a dict of cell key to name coming, retrieve the cell with the lowest time point + + :param list_names: List of names + :type list_names: dict + :param cell_name: The cell name to find + :type cell_name: str + :return: Cell time point, cell id of the cell + :rtype: tuple + + """ + current_found_time = None + current_found_id = None + for cell_key in list_names: + if cell_name == list_names[cell_key]: + tc, idc = get_id_t(cell_key) + if current_found_time is None or current_found_time > int(tc): + current_found_time = int(tc) + current_found_id = int(idc) + return current_found_time,current_found_id + + + +# NEW DEFINITION OF LOCAL COST FUNCTIONS FOR TREE-EDIT DISTANCES +# Definition of a LOCAL COST function. +# this function computes the elementary distance between to any vertices of two trees +def new_local_cost(v1=None,v2=None): + """Compute distance between two vertices + + :param v1: (Default value = None) + :param v2: (Default value = None) + + + """ + if isinstance(v1,tuple)==True: + if v2 == None: + cost = 0 + v = list(v1) + for i in range(len(v)): + cost+=v[i] + return cost + else: + d=len(v1) + return sum( [abs(v1[i]-v2[i]) for i in range(0,d)]) + else: + if v2==None: + return v1 + else: + return abs(v1-v2) + +# A first function to defined an attribute called 'label_for_distance' with a constant value +# on each node of a given tree (to make simple tests) +def give_label_dist_constant(tree,value): + """ + + :param tree: + :param value: + + """ + # associate a constant attribute value with all the vertices of the tree + # Parameters + # ---------- + # tree: treex tree + # value: number vector + tree.add_attribute_to_id('label_for_distance',value) + for child in tree.my_children: + give_label_dist_constant(child,value) + +# Define a more general function to attach a 'label_for_distance' to a tree +# with more general values (by copying the value of another given attribute in 'label_for_distance') +# and so that the edit-distance can then be used with this tree. + +def give_label_dist_attribute(tree,name): + """ + + :param tree: + :param name: + + """ + # associate a constant attribute value with all the vertices of the tree + # Parameters + # ---------- + # tree: treex tree + # value: number vector + value = tree.get_attribute(name) + tree.add_attribute_to_id('label_for_distance',value) + for child in tree.my_children: + give_label_dist_attribute(child,name) + +def time_stamp(cell_id): + """ Retrieve the time stamp from a cell key + + :param cell_id: Cell key + :type cell_id: int + :return: Time stamp + :rtype: int + + """ + return int(cell_id // 10000) + +def cell_lifespan_list(c,cell_lineage): + """ + + :param c: + :param cell_lineage: + + """ + cell_list = [c] + if c in cell_lineage: + child = cell_lineage[c] + while len(child) == 1: + cell_list.append(child[0]) + if child[0] in cell_lineage: + child = cell_lineage[child[0]] + else: + child = [] + return cell_list + +def life_span(cell_id,cell_lineage): + """ + + :param cell_id: + :param cell_lineage: + + """ + lst = cell_lifespan_list(cell_id,cell_lineage) + return time_stamp(lst[-1])-time_stamp(lst[0])+1 + +# Two functions to create (Treex) trees from lineage data + +# definition of a function to build a tree from astec cell lineages +# Note: this accesses the astec lineage as a global variable, as well as the translation dict from tree to astec +def add_child_tree(t,astec_child,cell_lineage,astec2tree,maxtime=None): + """ + + :param t: + :param astec_child: + :param cell_lineage: + :param astec2tree: + :param maxtime: (Default value = None) + + """ + import treex as tx + if maxtime == None: + if astec_child == None : + return False + else: + if astec_child == None or time_stamp(astec_child) > maxtime: + return False + ct = tx.Tree() + ct.add_attribute_to_id('astec_id',astec_child) + astec2tree[astec_child] = ct.my_id + t.add_subtree(ct) + if astec_child in cell_lineage: # astec_child is not terminal + for c in cell_lineage[astec_child]: + #print(c) + add_child_tree(ct, c,cell_lineage,astec2tree,maxtime) # recursive call on children of astec_child + return True + +def daughters(c,cell_lineage): + """ + + :param c: + :param cell_lineage: + + """ + if c in cell_lineage: + child = cell_lineage[c] # + if len(child) == 0: + return None + elif len(child) == 1: + return daughters(child[0],cell_lineage) + else: + return child + else: + return None + +def mother(c,cell_lineage): + """ + + :param c: + :param cell_lineage: + + """ + mother_dic = {} + for c in cell_lineage: + child = daughters(c,cell_lineage) + if child is not None: + mother_dic[child[0]] = c + mother_dic[child[1]] = c + if c in mother_dic: + return mother_dic[c] + else: + return None + +def has_sister(c,cell_lineage): + """ + + :param c: + :param cell_lineage: + + """ + m = mother(c) + if m is not None: + if m in cell_lineage: + child = cell_lineage[m] + if len(child) == 1: + return has_sister(m,cell_lineage) + else: + return True + else: + return False + else: + return False + +def sister(c,cell_lineage): + """ + + :param c: + :param cell_lineage: + + """ + m = mother(c) + if m is not None: + if m in cell_lineage: + child = cell_lineage[m] + if len(child) == 1: + return has_sister(m,cell_lineage) + else: # len == 2 + return child[0] if child[0] != c else child[1] + else: + return None + else: + return None + +# Create the lineage tree of the cell given in argument +def create_lineage_tree(astec_id,cell_lineage,maxtime=None): + """ + + :param astec_id: + :param cell_lineage: + :param maxtime: (Default value = None) + + """ + import treex as tx + t = tx.Tree() # create tree (in treex, nodes are actually trees) + astec2tree = {} + # setting root data + t.add_attribute_to_id('astec_id',astec_id) + # store mapping between vertices + astec2tree[astec_id] = t.my_id + + if astec_id in cell_lineage: + for c in cell_lineage[astec_id]: + #print(c) + add_child_tree(t,c,cell_lineage,astec2tree,maxtime) + return t + +def create_compressed_lineage_tree(astec_id,cell_lineage,cellnames=None,stop_at_division=False): + """ + + :param astec_id: + :param cell_lineage: + :param cellnames: (Default value = None) + + """ + # print(astec_id) + import treex as tx + astec2tree = {} + lifespan = life_span(astec_id,cell_lineage) + t = tx.Tree() # create tree (in treex, nodes are actually trees) + # setting root data + if cellnames is not None and astec_id in cellnames: + t.add_attribute_to_id('astec_id', cellnames[astec_id]) + else: + t.add_attribute_to_id('astec_id', astec_id) + t.add_attribute_to_id('lifespan', lifespan) + # store mapping between vertices + keytree = astec_id + if cellnames is not None and astec_id in cellnames: + keytree = cellnames[astec_id] + astec2tree[keytree] = t.my_id + if not stop_at_division: + dlist = daughters(astec_id,cell_lineage) # find the daughters (cell ids just after a division) + if dlist != None and dlist != []: + try: + assert len(dlist) == 2 + for c in dlist: + if c in cell_lineage: + tchild = create_compressed_lineage_tree(c,cell_lineage,cellnames) + if tchild != None: + t.add_subtree(tchild) + except : + print("skipping") + return t + +def apply_compressed_compare(lineage_prop1,lineage_prop2,cell_key,cell_key2,stop_at_division=False): + """ + + :param lineage_prop1: + :param lineage_prop2: + :param cell_key: + :param cell_key2: + + """ + from treex.analysis.edit_distance.zhang_labeled_trees import zhang_edit_distance + tree1 = create_compressed_lineage_tree(cell_key, lineage_prop1,stop_at_division=stop_at_division) + tree2 = create_compressed_lineage_tree(cell_key2, lineage_prop2,stop_at_division=stop_at_division) + give_label_dist_attribute(tree1, 'lifespan') + give_label_dist_attribute(tree2, 'lifespan') + + # Compute the zhang edit-distance between them (then making use of the information 'lifespan' on the nodes) + d = zhang_edit_distance(tree1, tree2, "lifespan", new_local_cost) + return d + +def apply_compare(lineage_prop1,lineage_prop2,cell_key,cell_key2,stop_at_divison=False): + """ + + :param lineage_prop1: + :param lineage_prop2: + :param cell_key: + :param cell_key2: + + """ + from treex.analysis.edit_distance.zhang_labeled_trees import zhang_edit_distance + tree1 = create_lineage_tree(cell_key, lineage_prop1) + tree2 = create_lineage_tree(cell_key2, lineage_prop2) + give_label_dist_constant(tree1, (1)) + give_label_dist_constant(tree2, (1)) + d = zhang_edit_distance(tree1, tree2, "label_for_distance", new_local_cost) + return d + +def read_lineage(lineage): + """ + + :param lineage: + + """ + import os + converted = False + lineagepkl = None + if lineage.endswith(".xml"): + lineagepkl = lineage.replace(".xml",".pkl") + os.system("conda run -n astec astec_embryoproperties -i "+lineage+" -o "+lineagepkl) + converted = True + lineage = lineagepkl + f = open(lineage, 'rb') + astec_output1 = pkl.load(f) # astec_output is the set of all dictionary returned by ASTEC (itself a dic) + f.close() + if converted and lineagepkl is not None: + if os.path.isfile(lineagepkl): + os.system("rm "+lineagepkl) + return astec_output1 +def get_id_t(idl): + """Return the cell t,id + + :param idl: + :type idl: int + + + """ + t = int(int(idl) / (10 ** 4)) + cell_id = int(idl) - int(t) * 10 ** 4 + return t, cell_id +def load_cell_names(lineage): + """ + + :param lineage: + + """ + astec_output = read_lineage(lineage) + if not 'cell_name' in astec_output: + print("Name information is missing") + exit() + return astec_output['cell_name'] +def name_to_id(output_astec,namecell): + """ + + :param output_astec: + :param namecell: + + """ + if not 'cell_name' in output_astec: + print("Name information is missing") + exit() + + cell_names1 = output_astec['cell_name'] + cell_key_1 ="" + for keyc in cell_names1: + if cell_names1[keyc] == namecell: + tc, idc = get_id_t(keyc) + previoust = None + if cell_key_1 != "": + previoust, previousid = get_id_t(cell_key_1) + if previoust is None or previoust > tc: + cell_key_1 = keyc + + return cell_key_1 + + +def compare_cells_by_name(lineage_path_1,lineage_path_2,cell_names_pairs,stop_at_division=False): + """ + + :param lineage_path_1: + :param lineage_path_2: + :param cell_name1: + :param cell_name2: + + """ + distances_by_names = {} + tested_names = [] + astec_output1 = read_lineage( + lineage_path_1) # astec_output is the set of all dictionary returned by ASTEC (itself a dic) + astec_output2 = read_lineage( + lineage_path_2) # astec_output is the set of all dictionary returned by ASTEC (itself a dic) + cell_lineage1 = astec_output1['cell_lineage'] + cell_lineage2 = astec_output2['cell_lineage'] + for cell_name1, cell_name2 in cell_names_pairs: + if not cell_name1 in tested_names: + cell_key_1 = name_to_id(astec_output1,cell_name1) + cell_key_2 = name_to_id(astec_output2,cell_name2) + if cell_key_1 != "" and cell_key_2 != "": + cell_key_1_int = int(cell_key_1) + cell_key_2_int = int(cell_key_2) + distance_found = apply_compressed_compare(cell_lineage1, cell_lineage2, cell_key_1_int,cell_key_2_int,stop_at_division=stop_at_division) + distances_by_names[cell_name1] = distance_found + distances_by_names[cell_name2] = distance_found + tested_names.append(cell_name1) + tested_names.append(cell_name2) + return distances_by_names + +def compare_cell_by_name(lineage_path_1,lineage_path_2,cell_name1,cell_name2): + """ + + :param lineage_path_1: + :param lineage_path_2: + :param cell_name1: + :param cell_name2: + + """ + astec_output1 = read_lineage( + lineage_path_1) # astec_output is the set of all dictionary returned by ASTEC (itself a dic) + astec_output2 = read_lineage( + lineage_path_2) # astec_output is the set of all dictionary returned by ASTEC (itself a dic) + cell_lineage1 = astec_output1['cell_lineage'] + cell_lineage2 = astec_output2['cell_lineage'] + cell_key_1 = name_to_id(astec_output1,cell_name1) + cell_key_2 = name_to_id(astec_output2,cell_name2) + + return apply_compressed_compare(cell_lineage1, cell_lineage2, cell_key_1,cell_key_2) + +def compare_cell_by_key(lineage_path_1,lineage_path_2,cell_key_1,cell_key_2): + """ + + :param lineage_path_1: + :param lineage_path_2: + :param cell_key_1: + :param cell_key_2: + + """ + astec_output1 = read_lineage(lineage_path_1) # astec_output is the set of all dictionary returned by ASTEC (itself a dic) + astec_output2 = read_lineage(lineage_path_2) # astec_output is the set of all dictionary returned by ASTEC (itself a dic) + cell_lineage1 = astec_output1['cell_lineage'] + cell_lineage2 = astec_output2['cell_lineage'] + + return apply_compare(cell_lineage1,cell_lineage2,cell_key_1,cell_key_2) + +def print_tree(lineage,cell_key,compressed=False,name=None): + """ + + :param lineage: + :param cell_key: + :param compressed: (Default value = False) + :param name: (Default value = None) + + """ + from treex.visualization.matplotlib_plot import view_tree + astec_output = read_lineage(lineage) + cell_lineage = astec_output['cell_lineage'] + treeforcell = None + if compressed: + treeforcell=create_compressed_lineage_tree(cell_key, cell_lineage) + else : + treeforcell=create_lineage_tree(cell_key, cell_lineage) + fig = view_tree(treeforcell) + displayname = cell_key + if name is not None: + displayname = name + prefixfig = "dendogram_plots/" + if compressed: + prefixfig=prefixfig+"compressed_tree_" + else: + prefixfig = prefixfig + "tree_" + fig.savefig(prefixfig+lineage.split('.')[0].replace('/','_')+"_"+displayname.replace(".","")+".png") + +def print_tree_by_name(lineage,cell_name,compressed=False): + """ + + :param lineage: + :param cell_name: + :param compressed: (Default value = False) + + """ + astec_output = read_lineage(lineage) + cell_key_1 = name_to_id(astec_output,cell_name) + print_tree(lineage,cell_key_1,compressed,cell_name) + + + +# Transform distance matrix into a condensed matrix (used by linkage function below) +# (= vector array made from upper triangular part of the distance matrix) +# i<j<m, where m is the number of original observations +# m * i + j - ((i + 2) * (i + 1)) // 2. +def condensed_matrix(dist_mat): + """ + + :param dist_mat: + + """ + m = np.shape(dist_mat)[0] + cm = np.zeros(m*(m-1)//2) + for j in range(m): + for i in range(j): + cm[m * i + j - ((i + 2) * (i + 1)) // 2] = dist_mat[j][i] + return cm +def count_cells(cell_list,remove_time=[]): + """ + + :param cell_list: + :param remove_time: (Default value = []) + + """ + cell_count_by_time = {} + for cell_key in cell_list: + cell_obj_t = time_stamp(cell_key) + if not cell_obj_t in remove_time: + if not cell_obj_t in cell_count_by_time: + cell_count_by_time[cell_obj_t] = 0 + cell_count_by_time[cell_obj_t] += 1 + return cell_count_by_time + +def get_cell_generation(cell_names,cellkey): + """ + + :param cell_names: + :param cellkey: + + """ + if cell_names is None or len(cell_names)==0: + return None + if not cellkey in cell_names: + return None + findgenin = cell_names[cellkey].split(".")[0] + return int(re.findall(r'\d+', findgenin)[-1]) + + +def compute_dendogram(lineage_trees): + """ + + :param lineage_trees: + + """ + from treex.analysis.edit_distance.zhang_labeled_trees import zhang_edit_distance + nbcell=len(lineage_trees) + dist_array = np.zeros((nbcell, nbcell)) + for i in range(nbcell): + for j in range(i, nbcell): + dist_array[i][j] = zhang_edit_distance(lineage_trees[i], lineage_trees[j], "lifespan", new_local_cost) + dist_array[j][i] = dist_array[i][j] + return hierarchy.linkage(condensed_matrix(dist_array), method='average', optimal_ordering=True) + +def Get_Cell_Names(path_lineage, info="cell_name"): + """ Extract the cell names information from a property file as a dict + + :param path_lineage: Path to the property file + :type path_lineage: str + :param info: Name of cell names property (Default value = "cell_name") + :type info: str + :return: Dict of cell names (key = cell key , value = cell name) + :rtype: dict + """ + cell_values={} + source = open(path_lineage) + tree = ET.parse(source) + tree = tree.getroot() + lineage_elem = tree.find(info) + if lineage_elem is not None: + for cell in lineage_elem.findall('cell'): + cell_id = cell.get('cell-id') + cell_child = cell.text + if cell_child is not None and cell_child != "None": + new_val = str(cell_child.replace("'", "").replace(" ", "")) + cell_values[cell_id] = new_val + # print(str(result)) + return cell_values + return None + +def compute_cluster_for_names(lineage_list,namelist,lineage_names): + """ + + :param lineage_list: + :param namelist: + :param lineage_names: + + """ + mapping_index={} + i=0 + lineage_trees = [] + lin=0 + for lineage in lineage_list: + cells_to_compute = [] + workedids = [] + astec_output = read_lineage(lineage) # astec_output is the set of all dictionary returned by ASTEC (itself a dic) + cell_lineage = astec_output['cell_lineage'] + cellnames = None + if 'cell_name' in astec_output: + cellnames = astec_output['cell_name'] + if cellnames is None: + print("No names found in : "+lineage) + filteredlineage = list(filter(lambda x: (cellnames is not None and x in cellnames and cellnames[x] in namelist), cell_lineage)) + for cell in filteredlineage: + if cellnames[cell] in namelist: + mother_cell = get_direct_mother(cell, cell_lineage,cellnames) + if mother_cell is not None and not mother_cell in workedids: + if mother_cell in cellnames and cellnames[mother_cell] in namelist: + print("Added cell "+str(mother_cell)+" with name "+str(cellnames[mother_cell])) + cells_to_compute.append(mother_cell) + workedids.append(mother_cell) + # Compute the lineage trees: + for cellc in cells_to_compute: + print("cell "+str(cellc)+" found name : "+str(cellnames[cellc])+" at index "+str(i)) + mapping_index[i]=cellnames[cellc]+" "+lineage_names[lin] + t = create_compressed_lineage_tree(cellc,cell_lineage,cellnames) + give_label_dist_attribute(t, 'lifespan') + lineage_trees.append(t) + i+=1 + lin+=1 + Z = compute_dendogram(lineage_trees) + return mapping_index, lineage_trees, Z + +def get_next_mother(cell,cell_lineage): + """ + + :param cell: + :param cell_lineage: + + """ + for celltest in cell_lineage: + for cellval in cell_lineage[celltest]: + if cellval==cell: + return celltest + return None + +def get_daughters(cell,cell_lineage): + """ + + :param cell: + :param cell_lineage: + + """ + d = [] + for celltest in cell_lineage: + if celltest==cell: + d=cell_lineage[celltest] + return d + +def get_direct_mother(cell,cell_lineage,cellnames=None): + """ + + :param cell: + :param cell_lineage: + :param cellnames: (Default value = None) + + """ + if not cell in cell_lineage: + return None + directmother = get_next_mother(cell,cell_lineage) + if directmother is None: + return cell + daughters = get_daughters(directmother,cell_lineage) + directmothertwice = get_next_mother(directmother, cell_lineage) + if directmothertwice is None or len(daughters) > 1: + return directmother + return get_direct_mother(directmother,cell_lineage) + +def compute_cluster_for_generation(lineage_list,generation_list,lineage_names): + """ + + :param lineage_list: + :param generation_list: + :param lineage_names: + + """ + mapping_index={} + i=0 + lineage_trees = [] + lin=0 + for lineage in lineage_list: + workedids = [] + cells_to_compute = [] + astec_output = read_lineage(lineage) # astec_output is the set of all dictionary returned by ASTEC (itself a dic) + cell_lineage = astec_output['cell_lineage'] + cellnames = None + if 'cell_name' in astec_output: + cellnames = astec_output['cell_name'] + if cellnames is None: + print("No names found in : "+lineage) + filtered_lineage=list(filter(lambda x: (x in cellnames and int(get_cell_generation(cellnames,x)) in generation_list), cell_lineage)) + for cell in filtered_lineage: + mother_cell = get_direct_mother(cell,cell_lineage,cellnames) + if mother_cell is not None and not mother_cell in workedids: + cells_to_compute.append(mother_cell) + workedids.append(mother_cell) + # Compute the lineage trees: + for cellc in cells_to_compute: + mapping_index[i] = cellnames[cellc] + " " + lineage_names[lin] + t = create_compressed_lineage_tree(cellc,cell_lineage,cellnames) + give_label_dist_attribute(t, 'lifespan') + lineage_trees.append(t) + i+=1 + lin += 1 + Z = compute_dendogram(lineage_trees) + return mapping_index, lineage_trees, Z + + +def compute_cluster_for_stage(lineage_list,cell_count,lineage_names): + """ + + :param lineage_list: + :param cell_count: + :param lineage_names: + + """ + + mapping_index={} + i=0 + lineage_trees = [] + lin=0 + for lineage in lineage_list: + workedids = [] + cells_to_compute = [] + astec_output = read_lineage(lineage) # astec_output is the set of all dictionary returned by ASTEC (itself a dic) + cell_lineage = astec_output['cell_lineage'] + cell_count_time=dict(sorted(count_cells(cell_lineage).items())) + cellnames = None + if 'cell_name' in astec_output: + cellnames = astec_output['cell_name'] + timepoint = None + for time in cell_count_time: + if timepoint is None and cell_count_time[time] == cell_count: + timepoint=int(time) + if timepoint is None: + for time in cell_count_time: + if timepoint is None and cell_count_time[time] >= cell_count: + timepoint = int(time) + filteredlineage = list(filter(lambda x: (int(time_stamp(x))==timepoint), cell_lineage)) + for cell in filteredlineage: + mother_cell = get_direct_mother(cell,cell_lineage,cellnames) + if mother_cell is not None and not mother_cell in workedids: + cells_to_compute.append(mother_cell) + workedids.append(mother_cell) + # Compute the lineage trees: + for cellc in cells_to_compute: + mapping_index[i] = cellnames[cellc] + " " + lineage_names[lin] + t = create_compressed_lineage_tree(cellc,cell_lineage,cellnames) + give_label_dist_attribute(t, 'lifespan') + lineage_trees.append(t) + i+=1 + lin += 1 + Z = compute_dendogram(lineage_trees) + return mapping_index, lineage_trees, Z + +def compute_cluster_for_time(lineage_list,timepoint_list,lineage_names): + """ + + :param lineage_list: + :param timepoint_list: + :param lineage_names: + + """ + cells_to_compute = [] + mapping_index={} + i=0 + lineage_trees = [] + lin=0 + for lineage in lineage_list: + workedids = [] + astec_output = read_lineage(lineage) # astec_output is the set of all dictionary returned by ASTEC (itself a dic) + cell_lineage = astec_output['cell_lineage'] + cellnames = None + if 'cell_name' in astec_output: + cellnames = astec_output['cell_name'] + filteredlineage = list(filter(lambda x: (time_stamp(x) is not None and int(time_stamp(x)) in timepoint_list), cell_lineage)) + for cell in filteredlineage: + if cell is not None and not cell in workedids: + cells_to_compute.append(cell) + workedids.append(cell) + # Compute the lineage trees: + for cellc in cells_to_compute: + cname = str(cellc) + if cellc in cellnames: + cname=cellnames[cellc] + mapping_index[i] = cname + " " + lineage_names[lin] + t = create_compressed_lineage_tree(cellc,cell_lineage,cellnames) + give_label_dist_attribute(t, 'lifespan') + lineage_trees.append(t) + i+=1 + lin += 1 + Z = compute_dendogram(lineage_trees) + return mapping_index, lineage_trees, Z +def compute_cluster_for_time_single_lineage(lineage,timepoint): + """ + + :param lineage: + :param timepoint: + + """ + cells_to_compute = [] + mapping_index={} + astec_output = read_lineage(lineage) # astec_output is the set of all dictionary returned by ASTEC (itself a dic) + cell_lineage = astec_output['cell_lineage'] + cellnames = None + if 'cell_name' in astec_output: + cellnames = astec_output['cell_name'] + for cell in cell_lineage: + timecell = time_stamp(cell) + if timecell==timepoint: + cells_to_compute.append(cell) + nbcell = len(cells_to_compute) + lineage_trees = [] + # Compute the lineage trees: + for i in range(nbcell): + cname=cells_to_compute[i] + if cellnames is not None and cname in cellnames: + cname = cellnames[cname] + mapping_index[i]=cname + t = create_compressed_lineage_tree(cells_to_compute[i],cell_lineage,cellnames) + give_label_dist_attribute(t, 'lifespan') + lineage_trees.append(t) + Z = compute_dendogram(lineage_trees) + return mapping_index,lineage_trees,Z +def plot_cluster(dendogram,filename,axismapping=None,title=None,figxsize=None,figysize=None): + """ + + :param dendogram: + :param filename: + :param axismapping: (Default value = None) + :param title: (Default value = None) + :param figxsize: (Default value = None) + :param figysize: (Default value = None) + + """ + dist_threshold = 350 # this is used to define the grain of the classes (see horizontal dashed line on the figure) + + # Function linkage performs the hirarchical clustering based on a condensed version of the distance matrix + # print(Z) + + # - Then the cell hierarchy is computed as a dendrogram data-structure + # It contains the labels of the points, their color code and more (see scipy doc) + + fig = plt.figure() + curr_axis = fig.gca() # current viewport (called axis) in the fig window (fig) + + # prepare the plot of the dendrogram and select the level of the classes see: 'color_threshold' + dn = hierarchy.dendrogram(dendogram, color_threshold=dist_threshold, ax=curr_axis, leaf_font_size=14) + + curr_axis.axhline(y=dist_threshold, linestyle='--', linewidth=1) + curr_axis.set_title(title if title is not None else 'Hierarchical Clustering Dendrogram', size=24) + curr_axis.set_xlabel("cell lineage", size=18) + curr_axis.set_ylabel("edit-distance", size=18) + labels = [item.get_text() for item in curr_axis.get_xticklabels()] + for i in range(0,len(labels)): + previouslabs = labels[i] + if int(previouslabs) in axismapping: + print("Working on id : "+str(previouslabs)+" with label "+ str(axismapping[int(previouslabs)])) + writtenname = str(axismapping[int(previouslabs)]) + if "." in writtenname: + namesplitted=writtenname.split('.') + labels[i]=namesplitted[0]+"."+namesplitted[1].lstrip("0").replace("_","-") + else : + labels[i] = writtenname + curr_axis.set_xticklabels(labels) + plt.xticks(fontsize=8) + fig.set_size_inches(figxsize if figxsize is not None else 40,figysize if figysize is not None else 15) # size of the plot + #fig.set_size_inches(figxsize if figxsize is not None else 40, figysize if figysize is not None else 15) # size of the plot + plt.savefig(filename) + plt.clf() +def compute_classes_dendogram(dendogram,lineage_trees): + """ + + :param dendogram: + :param lineage_trees: + + """ + # - Finally Extract the classes from the dendrogram: + dist_threshold = 350 + fig = plt.figure() + curr_axis = fig.gca() + classes = hierarchy.fcluster(dendogram, t=dist_threshold, criterion='distance') + dn = hierarchy.dendrogram(dendogram, color_threshold=dist_threshold, ax=curr_axis, leaf_font_size=14) + + # and print the detailed results corresponding to the above figure + classlist = {} + for i in range(0,len(classes)): + k = dn['leaves'][i] + if not k in classlist: + classlist[k] = [] + cell_id = lineage_trees[k].get_attribute('astec_id') + classlist[k].append(cell_id) + for k in classlist: + print("> Cells in class "+str(k)) + for cell in classlist[k]: + print(' -> ', cell) + + +def AddNodeToXML(xml_file, value_dict, node_name,node_subname,identifier_text='value',tag_type=""): + """ Save a given dict to a property file, specifying the property name and keys + + :param xml_file: Path to the property file + :type xml_file: str + :param value_dict: Dict to be saved (key being cell keys , value being value) + :type value_dict: dict + :param node_name: Name of the property + :type node_name: str + :param node_subname: Name of the cell element (should be "cell") + :type node_subname: str + :param tag_type: If set, add a tag to the created property name, to specify morphonet_type + :type tag_type: str + :param identifier_text: Text of the value for cell (Default value = 'value') + :type identifier_text: str + + """ + print(str(xml_file)) + if not os.path.isfile(xml_file) or os.stat(xml_file).st_size == 0: + print("XML file not found , create it with path : "+str(xml_file)) + f = open(xml_file,"w+") + f.close() + root = ET.Element('root') + tree = ET.ElementTree(root) + tree.write(xml_file) + source = open(xml_file) + tree = ET.parse(source) + tree = tree.getroot() + selec_node = node_name + name_selec_elem = tree.find(selec_node) + if name_selec_elem is None: + name_selec_elem = ET.SubElement(tree, selec_node) + if tag_type != "": + name_selec_elem.set("mn_type", tag_type) + for cell in name_selec_elem.findall(node_subname): + if cell.get(identifier_text) in value_dict.keys() or cell.get( + identifier_text) in value_dict.keys() and value_dict is not None and value_dict[cell.get(identifier_text)] is not None: + cell.text =str(value_dict[cell.get(identifier_text)]) + value_dict.pop(cell.get(identifier_text), None) + for cell in value_dict: + new_cell = ET.SubElement(name_selec_elem, node_subname) + if cell != "": + new_cell.set(identifier_text, str(cell)) + new_cell.text = str(value_dict[cell]) + indent_xml(tree) + source.close() + mydata = ET.tostring(tree, encoding='utf8', method='xml').decode("utf8") + myfile = open(xml_file, "w+") + myfile.write(mydata) + myfile.close() + + +def GetCellWholeLifetime(cell): + """ For a given cell, return the list of all cells in the cell lineage tree (past and future) + + :param cell: Cell to retrieve the lineage tree + :type cell: Cell + :return: List of all cells + :rtype: list + + """ + list_cells = [cell,] + daughters = GetAllCellLifeFutur(cell) + mothers = GetCellAllLifePast(cell) + for dau in daughters: + if not dau in list_cells: + list_cells.append(dau) + for mot in mothers: + if not mot in list_cells: + list_cells.append(mot) + #print("cell : "+str(cell)) + #print("Cells found : "+str(len(list_cells))) + return list_cells + + +def GetAllCellLifeFutur(cell): + """ For a given cell, return the list of future cells in the lineage tree + + :param cell: Cell to retrieve the lineage tree + :type cell: Cell + :return: List of all cells + :rtype: list + + """ + list_cells = [] + if cell is None or cell.daughters is None or len(cell.daughters) < 1: + #print("Futur : cell is : "+str(cell)) + #print("Futur : cell has no daughters") + return list_cells + for daughter in cell.daughters: + cells = GetAllCellLifeFutur(daughter) + for cell_child in cells: + list_cells.append(cell_child) + + #print("number of daughters found : "+str(len(cells))) + if list_cells is not None: + list_cells.append(cell) + return list_cells + +def GetCellAllLifePast(cell): + """ For a given cell, return the list of past cells in the lineage tree + + :param cell: Cell to retrieve the lineage tree + :type cell: Cell + :return: List of all cells + :rtype: list + + """ + list_cells = [] + #if cell is not None and cell.mothers is not None: + # print(cell.mothers) + if cell is None or cell.mothers is None or len(cell.mothers) == 0 or cell.mothers[0].daughters is None: + #print("Past : cell is : "+str(cell)) + #print("Past : cell has no mothers") + return list_cells + for mother in cell.mothers: + cells =GetCellAllLifePast(mother) + for cell_child in cells: + list_cells.append(cell_child) + #print("number of mothers found : "+str(len(cells))) + if list_cells is not None: + list_cells.append(cell) + return list_cells + +class Cell: + """ Class representing the cells in memory. A cell has a time point (int) , and id (int) , a list of cells being + their direct mothers (should only contain one) , and a list of cells being their direct daughters (can be 0 to many) + """ + id = -1 + t = -1 + mothers = [] + daughters = [] + + def __init__(self,id_cell,time_cell): + self.id = id_cell + self.t = time_cell + self.mothers = [] + self.daughters = [] + + def add_mother(self,cell): + """ Add a mother to the list of mothers , and add this cell to the mother's daughters list + + :param cell: The cell that will be current cell mother + :type cell: Cell + + """ + #print("mother len before : " + str(len(self.mothers))) + #print("add mother for cell : "+str(self.t)+","+str(self.id)+" for mother : "+str(cell.t)+","+str(cell.id)) + if self.mothers is None: + self.mothers = [] + if not cell in self.mothers: + self.mothers.append(cell) + #print("mother len after : " + str(len(self.mothers))) + cell.add_daughter(self) + + + def add_daughter(self,cell): + """ Add the given cell to current cell daughters. This function should not be called directly , + instead use "add_mother" on the parameter cell. + + :param cell: Cell to add as a daughter + :type cell: Cell + + """ + #print("daughters len before : " + str(len(self.daughters))) + #print("add daughter for cell : " + str(self.t) + "," + str(self.id) + " for mother : " + str(cell.t) + "," + str(cell.id)) + if self.daughters is None: + self.daughters = [] + if not cell in self.daughters: + self.daughters.append(cell) + + +def LoadCellList(path_lineage,lineage_property="cell_lineage"): + """ Load the content of "cell_lineage" property from the properties file , as a dict of key being cell keys, value the cell object + + :param path_lineage: Path to the property file + :type path_lineage: str + :param lineage_property: Name of the lineage property + :type lineage_property: str + :return: Dict of key being cell keys, value the cell object + :rtype: dict + + """ + try: + source = open(path_lineage) + except: + return None + tree = ET.parse(source) + cell_list_g = {} + tree = tree.getroot() + lineage_elem = tree.find(lineage_property) + if lineage_elem is not None: + for cell in lineage_elem.findall('cell'): + cell_t,cell_id = get_id_t(cell.get('cell-id').replace("'","").replace('[','').replace(']','').replace(" ","")) + cell_key = str(get_longid(cell_t,cell_id)) + #cell_key = str(cell_t)+","+str(cell_id) + if not cell_key in cell_list_g: + cell_object = Cell(int(cell_id),int(cell_t)) + cell_list_g[cell_key] = cell_object + cell_childs = cell.text.split(',') + for cell_child_in_list in cell_childs: + cell_child_t,cell_child_id = get_id_t(cell_child_in_list.replace("'","").replace('[','').replace(']','').replace(" ","")) + cell_child_key = str(get_longid(cell_child_t,cell_child_id)) + if not cell_child_key in cell_list_g: + cell_child_object = Cell(int(cell_child_id), int(cell_child_t)) + cell_list_g[cell_child_key] = cell_child_object + + if not cell_list_g[cell_child_key] in cell_list_g[cell_key].daughters: + cell_list_g[cell_child_key].add_mother(cell_list_g[cell_key]) + return cell_list_g + return None + + +def generate_lineage_comparison(lineage_file,begin_time_point,info_name_1="float_symetric_cells_branch_length_distance",info_name_2="float_symetric_cells_lineage_tree_distance",info_name_3="float_symetric_cells_lineage_subtree_distance"): + """ + Using a named lineage , generates properties representing the distance between symetric cells in lineage + The generated properties : + - name1 : the distance between symetric cells branch length + - name2 : the distance between symetric cells complete lineage tree + - name3 : the distance between symetric cells sub lineage tree + + :param dendogram: + :param lineage_trees: + """ + if not os.path.isfile(lineage_file): + raise Exception("The lineage file does not exist !") + names = Get_Cell_Names(lineage_file) + + # Build names lists + names_tuples = [] #List of tuples that will compute a distance + tested_names = [] #buffer to prevent multiples testing + name_to_tid = {} #Used to retrieve ids for information generation + for idc in names: + current_name = names[idc] + mint, minid = find_lowest_t_with_cell(names, current_name) #Find first cell of branch + name_to_tid[current_name] = str(mint) + "," + str(minid) + other_name = None + if "_" in current_name: + other_name = current_name.replace("_", "*") + elif "*" in current_name: + other_name = current_name.replace("*", "_") + if other_name is not None and not current_name in tested_names and not other_name in tested_names and current_name != "" and other_name != "": + mint, minid = find_lowest_t_with_cell(names, other_name) # Find the first cell of symetric branch + name_to_tid[other_name] = str(mint) + "," + str(minid) + names_tuples.append((current_name, other_name)) # Add to compute distance + names_tuples.append((other_name, current_name)) + tested_names.append(current_name) # prevent adding it again + tested_names.append(other_name) + + # build branch to branch length + distance_by_names = compare_cells_by_name(lineage_file, lineage_file, names_tuples, stop_at_division=True) #build distance + info_distance = {} + distance_by_names = dict(sorted(distance_by_names.items(), key=lambda key_val: key_val[1])) + for name in distance_by_names: # loop is here to fill the whole property (add the proeprty to each snapshot) + idscells = list_ids_with_name(names, name) + for cell in idscells: + if cell not in info_distance: + info_distance[cell] = distance_by_names[name] + AddNodeToXML(lineage_file, info_distance, info_name_1, "cell", identifier_text="cell-id") #Save in property file + + + all_cells = LoadCellList(lineage_file) #compute lineage for both next info + distance_by_names = compare_cells_by_name(lineage_file, lineage_file, names_tuples, stop_at_division=False) #compute whole distance + info_distance = {} + distance_by_names = dict(sorted(distance_by_names.items(), key=lambda key_val: key_val[1])) + + #Build subtree distances + for name in distance_by_names: + idscells = list_ids_with_name(names, name) + begin_t = 100000 + begin_key = None + for cell in idscells: + tc, idcell = get_id_t(cell) + if tc < begin_t: + begin_t = tc + begin_key = cell + if begin_key is not None: + idscells = GetAllCellLifeFutur(all_cells[begin_key]) + if not all_cells[begin_key] in idscells: + idscells.append(all_cells[begin_key]) + for cellc in idscells: + longcellid = get_longid(cellc.t, cellc.id) + if longcellid not in info_distance: + info_distance[longcellid] = distance_by_names[name] + AddNodeToXML(lineage_file, info_distance, info_name_3, "cell", identifier_text="cell-id") + # Build whole tree distance + info_distance = {} + for name in distance_by_names: + idscells = list_ids_with_name(names, name) + t_begin_key = None + for cell in idscells: + tc, idcell = get_id_t(cell) + if tc == begin_time_point: + t_begin_key = cell + if t_begin_key is not None: + idscells = GetCellWholeLifetime(all_cells[t_begin_key]) + for cellc in idscells: + longcellid = get_longid(cellc.t, cellc.id) + if longcellid not in info_distance: + info_distance[longcellid] = distance_by_names[name] + AddNodeToXML(lineage_file, info_distance, info_name_2, "cell", identifier_text="cell-id") \ No newline at end of file diff --git a/morphonet/tools.py b/morphonet/tools.py index 8eadf30da3699f539b51089418de8a81b6172b2c..65ef202079a4aaf633c10a17f9527f1a415d37df 100644 --- a/morphonet/tools.py +++ b/morphonet/tools.py @@ -1558,7 +1558,7 @@ def get_property_type(property_name): return "float" if property_name.lower().find("area") >= 0: return "float" - if property_name.lower().startswith("fate_map"): + if property_name.lower().find("fate_map") >= 0 : return "label" if property_name.lower().find("fate") >= 0: return "string"