ourotools 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ourotools/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ # import top-level functions
2
+ from .__main__ import *
3
+ from ourotools.core import *
4
+
5
+ # Version of cressp package
6
+ __version__ = "0.2.0"
7
+
8
+ # import modules
9
+
10
+ __all__ = ["core"]
ourotools/__main__.py ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env python
2
+ # the date of last modification
3
+ from ourotools.core import *
4
+
5
+ import warnings
6
+
7
+ warnings.filterwarnings(action="ignore")
8
+
9
+ if __name__ == "__main__":
10
+ pass
ourotools/core/BA.py ADDED
@@ -0,0 +1,225 @@
1
+ from bitarray import bitarray
2
+ import numpy as np
3
+
4
+
5
+ def Find(ba, val=1):
6
+ """deprecated
7
+ # 2022-06-12 21:09:22
8
+ search the given value in the bitarray iteratively and return the start position of the occurrences
9
+ """
10
+ len_ba = len(ba)
11
+ l_int_pos_occurrence = []
12
+ int_pos_start = 0
13
+ while int_pos_start < len_ba:
14
+ int_pos_occurrence = ba.find(val, int_pos_start + 1)
15
+ if int_pos_occurrence < 0:
16
+ break
17
+ else:
18
+ l_int_pos_occurrence.append(int_pos_occurrence)
19
+ int_pos_start = int_pos_occurrence
20
+ return l_int_pos_occurrence
21
+
22
+
23
+ def find(ba, val=1):
24
+ """# 2022-07-03 21:41:36
25
+ generator that returns the location of 'val' from the start of the bitarray.
26
+ Use this function for memory-efficient iteration of position of active entries in a bitarray (np.int64 takes 64 times more memory than an entry in bitarray, which is 1 bit).
27
+ """
28
+ len_ba = len(ba)
29
+ if len(ba) == 0: # handle empty bitarray input
30
+ return
31
+ int_pos_start = 0
32
+ if ba[0] == val: # handle the case when the first bit is equal to 'val'
33
+ yield int_pos_start
34
+ while int_pos_start < len_ba:
35
+ int_pos_occurrence = ba.find(val, int_pos_start + 1)
36
+ if int_pos_occurrence < 0:
37
+ break
38
+ else:
39
+ yield int_pos_occurrence
40
+ int_pos_start = int_pos_occurrence
41
+
42
+
43
+ # def Find_Segment( ba, background = 1, flag_use_bitwise_operation = True ) :
44
+ # ''' # 2022-06-12 19:36:38
45
+ # find segment of a given bitarray for the given background 'background'. for example, when background = 1, find segment of 0
46
+
47
+ # 'flag_use_bitwise_operation' : use bitwise operation. useful when the length of bitarray is very long
48
+ # '''
49
+ # len_ba = len( ba )
50
+ # if flag_use_bitwise_operation :
51
+ # ''' the implementation using bitwise operation '''
52
+ # mask = ( ba ^ ba >> 1 ) # find boundary of value change
53
+ # l_int_pos = Find( mask, 1 )
54
+ # flag_start_is_a_segment = ba[ 0 ] != background
55
+ # if flag_start_is_a_segment :
56
+ # l_int_pos = [ 0 ] + l_int_pos
57
+ # if len( l_int_pos ) % 2 != 0 :
58
+ # l_int_pos += [ len_ba ]
59
+ # return np.array( l_int_pos ).reshape( ( int( len( l_int_pos ) / 2 ), 2 ) )
60
+ # else :
61
+ # ''' the implementation using simple iteration '''
62
+ # int_pos = 0
63
+ # int_seg_start = None
64
+ # l_seg = [ ]
65
+ # while int_pos < len_ba :
66
+ # if int_seg_start is None and ba[ int_pos ] != background :
67
+ # int_seg_start = int_pos
68
+ # elif int_seg_start is not None and ba[ int_pos ] == background :
69
+ # l_seg.append( [ int_seg_start, int_pos ] )
70
+ # int_seg_start = None
71
+ # int_pos += 1
72
+ # if int_seg_start is not None :
73
+ # l_seg.append( [ int_seg_start, len_ba ] )
74
+ # return l_seg
75
+
76
+
77
+ def Find_Segment(ba, background=1):
78
+ """# 2022-06-12 19:36:38
79
+ find segment of a given bitarray for the given background 'background'. for example, when background = 1, find segment of 0
80
+
81
+ # 2022-06-22 18:36:45
82
+ updated to a new implementation that does not require generating a copy of the bitarray.
83
+ """
84
+
85
+ def toggle_bit(bit):
86
+ return (bit + 1) % 2 # toggle the bit
87
+
88
+ # initialize
89
+ l_int_pos = []
90
+ state_of_interest = toggle_bit(background) # look for a non-background state
91
+ int_pos = 0
92
+
93
+ while True:
94
+ int_pos = ba.find(state_of_interest, int_pos)
95
+ if int_pos < 0:
96
+ break
97
+ l_int_pos.append(int_pos)
98
+ state_of_interest = toggle_bit(
99
+ state_of_interest
100
+ ) # toggle the state of interest
101
+ if len(l_int_pos) % 2 != 0:
102
+ l_int_pos.append(len(ba))
103
+ return np.array(l_int_pos).reshape(
104
+ (int(len(l_int_pos) / 2), 2)
105
+ ) # reshape a list of int_pos to list of segments
106
+
107
+
108
+ def Retrieve_Integer_Indices(ba, background=0):
109
+ """deprecated (slow)
110
+ # 2022-06-23 08:31:45
111
+ returns a list of integer indices of non-background bits in a given bitarray 'ba'
112
+
113
+ 'background' : a bit representing background. either 0 or 1
114
+ """
115
+ # compose a list of integer indices of active rows after applying filter
116
+ l = []
117
+ for st, en in Find_Segment(
118
+ ba, background=background
119
+ ): # retrieve active segments from bitarray filter
120
+ l.extend(range(st, en)) # retrieve integer indices of the active rows
121
+ return l
122
+
123
+
124
+ def to_array(ba):
125
+ """# 2022-06-24 23:54:12
126
+ return a boolean numpy array of the given bitarray"""
127
+ return np.frombuffer(ba.unpack(), dtype=bool)
128
+
129
+
130
+ def to_bitarray(arr_bool):
131
+ """# 2022-06-27 15:29:56
132
+ convert numpy boolean array to bitarray
133
+ """
134
+ ba = bitarray()
135
+ ba.pack(arr_bool.tobytes())
136
+ return ba
137
+
138
+
139
+ def from_integer_indices_to_bitarray(l_int_index, length):
140
+ """# 2022-07-01 11:21:12
141
+ convert list of integer indices to bitarray
142
+
143
+ 'length' : length of the output bitarray object
144
+ """
145
+ ba = bitarray(length)
146
+ ba.setall(0)
147
+ for int_index in l_int_index:
148
+ ba[int_index] = True
149
+ return ba
150
+
151
+
152
+ def to_integer_indices(ba):
153
+ """# 2022-07-01 21:38:17
154
+ retrieve integer indices of the active entries of the given bitarray
155
+
156
+ 'ba' : input bitarray object
157
+ """
158
+ return np.where(to_array(ba))[0]
159
+
160
+
161
+ def COUNTER(l_values, dict_counter=None, ignore_float=True): # 2020-07-29 23:49:51
162
+ """Count values in l_values and return a dictionary containing count values. if 'dict_counter' is given, countinue counting by using the 'dict_counter'. if 'ignore_float' is True, ignore float values, including np.nan"""
163
+ if dict_counter is None:
164
+ dict_counter = dict()
165
+ if ignore_float: # if 'ignore_float' is True, ignore float values, including np.nan
166
+ for value in l_values:
167
+ if isinstance(value, float):
168
+ continue # ignore float values
169
+ if value in dict_counter:
170
+ dict_counter[value] += 1
171
+ else:
172
+ dict_counter[value] = 1
173
+ else: # faster counting by not checking type of value
174
+ for value in l_values:
175
+ if value in dict_counter:
176
+ dict_counter[value] += 1
177
+ else:
178
+ dict_counter[value] = 1
179
+ return dict_counter
180
+
181
+
182
+ def detect_boolean_mask(ba):
183
+ """# 2022-08-10 23:29:06
184
+ detect boolean mask by looking up to 10 values
185
+ """
186
+ # extract the first row from the ndarray data
187
+ if isinstance(ba, np.ndarray) and len(ba.shape) > 1:
188
+ for i in range(len(ba.shape) - 1):
189
+ ba = ba[0]
190
+ if not hasattr(ba, "__iter__"): # 'ba' should be iterable to be a boolean mask
191
+ return False
192
+ ba = ba[:10]
193
+ # if the length of array of interest is <= 2 and only consists of 0, 1, consider it as an integer array, not boolean array
194
+ if len(ba) <= 2 and set(COUNTER(ba[:10])).issubset({0, 1}):
195
+ return False
196
+ return set(COUNTER(ba[:10])).issubset({0, 1, True, False})
197
+
198
+
199
+ def convert_mask_to_bitarray(ba):
200
+ """# 2022-07-03 00:54:46
201
+ convert boolean mask to a bitarray
202
+ """
203
+ # handle when a list type has been given (convert it to np.ndarray)
204
+ if isinstance(ba, list):
205
+ ba = np.array(ba, dtype=bool)
206
+ # handle when a numpy ndarray has been given (convert it to bitarray)
207
+ if isinstance(ba, np.ndarray):
208
+ ba = to_bitarray(ba)
209
+ assert isinstance(ba, bitarray) # check the return value is bitarray object
210
+ return ba
211
+
212
+
213
+ def convert_mask_to_array(ba):
214
+ """# 2022-07-03 00:54:46
215
+ convert boolean mask to a array
216
+ """
217
+ """ handle non-bitarray mask types """
218
+ # handle when a list type has been given (convert it to np.ndarray)
219
+ if isinstance(ba, list):
220
+ ba = np.array(ba, dtype=bool)
221
+ # handle when a numpy ndarray has been given (convert it to np.ndarray)
222
+ if isinstance(ba, bitarray):
223
+ ba = to_array(ba)
224
+ assert isinstance(ba, np.ndarray) # check the return value is np.ndarray object
225
+ return ba
ourotools/core/MAP.py ADDED
@@ -0,0 +1,38 @@
1
+ import numpy as np
2
+
3
+
4
+ # In[ ]:
5
+
6
+
7
+ dict_a2b = dict() # an empty dictionary for mapping
8
+
9
+
10
+ # In[ ]:
11
+
12
+
13
+ def Remove_version_info(ID):
14
+ return ID.split(".")[0]
15
+
16
+
17
+ # In[ ]:
18
+
19
+
20
+ def Retrive_Length(entry):
21
+ return len(entry)
22
+
23
+
24
+ class Map(object):
25
+ def __init__(self, dict_a2b):
26
+ self.dict_a2b = dict_a2b
27
+
28
+ def a2b(self, a):
29
+ if a in self.dict_a2b:
30
+ return self.dict_a2b[a]
31
+ else:
32
+ return np.nan
33
+
34
+ def a2b_if_mapping_available_else_Map_a2a(self, a):
35
+ if a in self.dict_a2b:
36
+ return self.dict_a2b[a]
37
+ else:
38
+ return a
ourotools/core/ONT.py ADDED
@@ -0,0 +1,174 @@
1
+ # load internal module
2
+ from .biobookshelf import *
3
+ from . import biobookshelf as bk
4
+ from typing import Union, List, Literal, Dict, Callable, Set, Iterable, Tuple
5
+
6
+
7
+ def Minimap2_Align(
8
+ path_file_fastq,
9
+ path_file_minimap2_index="/node210data/shared/ensembl/Mus_musculus/index/minimap2/Mus_musculus.GRCm38.dna.primary_assembly.k_14.idx",
10
+ path_folder_minimap2_output=None,
11
+ n_threads=20,
12
+ verbose=True,
13
+ drop_unaligned=False,
14
+ return_bash_shellscript=False,
15
+ n_threads_for_sort=10,
16
+ flag_use_split_prefix: bool = False,
17
+ path_file_junc_bed: Union[
18
+ None, str
19
+ ] = None, # if given, the bed file will be used for prioritizing known splice sites.
20
+ path_file_gtf: Union[
21
+ None, str
22
+ ] = None, # path to gene and exon annotation files, required if 'path_file_junc_bed' is given but the file does not exist
23
+ ):
24
+ """
25
+ # 2023-04-23 01:18:58
26
+ align given fastq file of nanopore reads using minimap2 and write an output as a bam file
27
+ 'path_file_fastq' : input fastq or fasta file (gzipped or uncompressed file is accepted)
28
+ 'path_file_minimap2_index' : minimap2 index file
29
+ 'path_folder_minimap2_output' : minimap2 output folder
30
+ 'drop_unaligned' : a flag indicating whether reads not aligned to the reference ('SAM flag == 4') are included in the output bam file
31
+ 'return_bash_shellscript' : return shellscript instead of running minimap2 using the subprocess module
32
+ 'flag_use_split_prefix' = False # for large index, split-prefix should be used
33
+ """
34
+ path_folder_fastq, name_file_fastq = path_file_fastq.rsplit("/", 1)
35
+ if (
36
+ path_folder_minimap2_output is None
37
+ ): # default output folder is a subdirectory of the folder containing the input fastq file
38
+ path_folder_minimap2_output = f"{path_folder_fastq}/minimap2/"
39
+ if (
40
+ path_folder_minimap2_output[-1] != "/"
41
+ ): # add '/' at the end of the output directory if it does not exist
42
+ path_folder_minimap2_output += "/"
43
+ os.makedirs(
44
+ path_folder_minimap2_output, exist_ok=True
45
+ ) # create folder if it does not exist
46
+
47
+ path_file_sam = (
48
+ f"{path_folder_minimap2_output}{name_file_fastq}.minimap2_aligned.sam"
49
+ )
50
+ path_file_bam = (
51
+ f"{path_folder_minimap2_output}{name_file_fastq}.minimap2_aligned.bam"
52
+ )
53
+ # if index file of the output BAM file exists, exit
54
+ if os.path.exists( f"{path_file_bam}.bai" ) :
55
+ return
56
+
57
+ l_bash_shellscript = []
58
+
59
+ """ perform minimap2 alignment """
60
+ l_arg = [
61
+ "minimap2",
62
+ "-t",
63
+ str(int(n_threads)),
64
+ "-ax",
65
+ "splice",
66
+ "-o",
67
+ path_file_sam,
68
+ ]
69
+
70
+ # for large index, split-prefix should be used
71
+ if flag_use_split_prefix:
72
+ l_arg += [f"--split-prefix={path_folder_minimap2_output}{UUID( )}"]
73
+
74
+ if path_file_junc_bed is not None:
75
+ if (
76
+ not os.path.exists(path_file_junc_bed) and path_file_gtf is not None
77
+ ): # if the bed file does not exist, create the bed file using paftools.js, packaged with the minimap2 executable
78
+ l_args_for_creating_junc_bed = ["paftools.js", "gff2bed", path_file_gtf]
79
+ if (
80
+ return_bash_shellscript
81
+ ): # perform minimap2 alignment using subprocess module
82
+ l_bash_shellscript.append(
83
+ " ".join(l_args_for_creating_junc_bed + [">", path_file_junc_bed])
84
+ )
85
+ else:
86
+ bk.OS_Run(
87
+ l_args_for_creating_junc_bed,
88
+ path_file_stdout=path_file_junc_bed,
89
+ stdout_binary=False,
90
+ )
91
+ if os.path.exists(path_file_junc_bed):
92
+ l_arg += ["--junc-bed", path_file_junc_bed]
93
+
94
+ if drop_unaligned:
95
+ l_arg += ["--sam-hit-only"]
96
+ l_arg += [path_file_minimap2_index, path_file_fastq]
97
+ if return_bash_shellscript: # perform minimap2 alignment using subprocess module
98
+ l_bash_shellscript.append(" ".join(l_arg))
99
+ else:
100
+ run = subprocess.run(l_arg, capture_output=True)
101
+ with open(
102
+ f"{path_folder_minimap2_output}{name_file_fastq}.minimap2_aligned.out", "w"
103
+ ) as file:
104
+ file.write(run.stdout.decode())
105
+ if verbose:
106
+ print("minimap2 completed")
107
+
108
+ """ sort output SAM file """
109
+ l_arg = [
110
+ "samtools",
111
+ "sort",
112
+ "-@",
113
+ str(int(min(n_threads_for_sort, 10))),
114
+ "-O",
115
+ "BAM",
116
+ "-o",
117
+ path_file_bam,
118
+ path_file_sam,
119
+ ]
120
+ if return_bash_shellscript: # perform minimap2 alignment using subprocess module
121
+ l_bash_shellscript.append(" ".join(l_arg))
122
+ l_bash_shellscript.append(" ".join(["rm", "-f", path_file_sam]))
123
+ else:
124
+ run = subprocess.run(l_arg, capture_output=False)
125
+ os.remove(path_file_sam) # remove sam file
126
+
127
+ """ index resulting BAM file """
128
+ l_arg = ["samtools", "index", path_file_bam]
129
+ if return_bash_shellscript: # perform minimap2 alignment using subprocess module
130
+ l_bash_shellscript.append(" ".join(l_arg))
131
+ else:
132
+ run = subprocess.run(l_arg, capture_output=False)
133
+ if verbose:
134
+ print("samtools bam file compressing and indexing completed")
135
+
136
+ if return_bash_shellscript: # retrun bash shell scripts
137
+ return " && ".join(l_bash_shellscript)
138
+
139
+
140
+ def Minimap2_Index(path_file_fasta, path_file_minimap2_index=None, verbose=False):
141
+ """
142
+ # 2021-03-24 00:44:51
143
+ index given fasta file for nanopore reads alignment
144
+ 'path_file_fasta' : input reference fasta file
145
+ 'path_file_minimap2_index' : minimap2 index file
146
+ """
147
+ path_folder_fastq, name_file_fasta = path_file_fasta.rsplit("/", 1)
148
+ if (
149
+ path_file_minimap2_index is None
150
+ ): # set the default directory of the minimap index
151
+ path_file_minimap2_index = (
152
+ f"{path_folder_fastq}/index/minimap2/{name_file_fasta}.ont.mmi"
153
+ )
154
+ path_folder_minimap2_index, name_file_index = path_file_minimap2_index.rsplit(
155
+ "/", 1
156
+ )
157
+ path_folder_minimap2_index += "/"
158
+ os.makedirs(
159
+ path_folder_minimap2_index, exist_ok=True
160
+ ) # create folder if it does not exist
161
+ if os.path.exists(path_file_minimap2_index): # exit if an index file already exists
162
+ return
163
+ # build minimap2 index
164
+ run = subprocess.run(
165
+ ["minimap2", "-x", "map-ont", "-d", path_file_minimap2_index, path_file_fasta],
166
+ capture_output=True,
167
+ )
168
+
169
+ with open(
170
+ f"{path_folder_minimap2_index}{name_file_index}.minimap2_index.out", "w"
171
+ ) as file:
172
+ file.write(run.stdout.decode())
173
+ if verbose:
174
+ print("minimap2 indexing completed")
ourotools/core/OT.py ADDED
@@ -0,0 +1,125 @@
1
+ from . import biobookshelf as bk
2
+
3
+ """
4
+ Implementing Ontology functions
5
+ """
6
+ import owlready2 as ol
7
+
8
+ class OntologyTerms :
9
+ def __init__( self, path_file_owl : str, name_prefix : str, name_root_term : str ) :
10
+ """
11
+ load an ontology file, given as 'path_file_owl'
12
+
13
+ name_prefix : str # prefix of the name
14
+ name_root_term : str # name of the root term
15
+ # 2024-02-29 22:50:55
16
+ """
17
+ self.path_file_owl = path_file_owl
18
+ self.name_prefix = name_prefix
19
+ self.name_root_term = name_root_term
20
+ self.onto = ol.get_ontology(f"file://{path_file_owl}").load( )
21
+ self._set_terms = set( self.onto.classes( ) )
22
+ self._root_term = self[ self.name_root_term ]
23
+ def __repr__( self ) :
24
+ return f"<{len( self._set_terms )} ontology terms stored at {self.path_file_owl}>"
25
+ def __contains__( self, term ) :
26
+ """
27
+ # 2024-03-01 21:35:55
28
+ """
29
+ return self[ term ] in self._set_terms
30
+ def __getitem__( self, ontology_id : str ) :
31
+ """
32
+ get ontology term using an ID
33
+ # 2024-02-29 22:51:49
34
+ """
35
+ # handle 'ontology_id' that is not a string
36
+ if not isinstance( ontology_id, str ) :
37
+ ontology_id
38
+ l = self.onto.search( iri = f"*{ontology_id}*")
39
+ if len( l ) == 0 :
40
+ return None
41
+ elif len( l ) == 1 :
42
+ return l[ 0 ]
43
+ else :
44
+ return l # if more then one terms are matched, return more than one elements
45
+ def __iter__( self ) :
46
+ """
47
+ return an iterater returning each class
48
+ # 2024-03-01 00:54:16
49
+ """
50
+ return self.onto.classes( )
51
+ def get_ancestor_chain( self, term ) :
52
+ """
53
+ get a chain of ancestors (excluding restriction objects), excluding self, from the most distant ancestor (owl.Thing) to the closest ancestor.
54
+ Note)
55
+ This function utilizes a recursive algorithm to explore the tree structure.
56
+ # 2024-03-01 21:45:16
57
+ """
58
+ # get the ontology term
59
+ term = self[ term ]
60
+ # initialize the 'l_ancestor'
61
+ def get_superclasses( term ) :
62
+ """
63
+ get filtered super classes of a term
64
+ # 2024-03-01 23:15:04
65
+ """
66
+ return list( e for e in term.is_a if hasattr( e, 'name' ) and e.name[ : len( self.name_prefix ) ] == self.name_prefix )
67
+ def get_ancestor_chains( term ) :
68
+ l_ancestor_chain = [ ]
69
+ l_term_super = get_superclasses( term )
70
+ # termination condition
71
+ if len( l_term_super ) == 0 :
72
+ if term == self._root_term :
73
+ return [ [ ] ]
74
+ else : # if the chain terminate with a term that is not a root term, add the root term
75
+ return [ [ self._root_term ] ]
76
+ # recursive condition
77
+ for e in l_term_super :
78
+ for l_ancestor in get_ancestor_chains( e ) :
79
+ l_ancestor_chain.append( [ e ] + l_ancestor )
80
+ return l_ancestor_chain
81
+ # reverse the order (from the most distant ancestor (the root term) to the closest ancestor)
82
+ l_ancestor_chain_reverse_order = get_ancestor_chains( term )
83
+ l_ancestor_chain = [ ] # l_ancestor_chain
84
+ for ancestor_chain_reverse_order in l_ancestor_chain_reverse_order :
85
+ l_ancestor_chain.append( ancestor_chain_reverse_order[ : : -1 ] )
86
+ return l_ancestor_chain
87
+ def get_longest_shared_ancestor_chains( self, term_1, term_2 ) -> set :
88
+ """
89
+ return the ancestor chains to the most closest shared ancestors between the term1 and term2
90
+
91
+ # 2024-02-29 22:58:13
92
+ """
93
+ # retrieve ancestor chains
94
+ l_ancestor_chain_1 = self.get_ancestor_chain( term_1 )
95
+ l_ancestor_chain_2 = self.get_ancestor_chain( term_2 )
96
+
97
+ # collect the ancestor chains to the most closest shared ancestors
98
+ set_ancestor_chain_to_most_closest_shared_ancestor = set( ) # initialize 'set_ancestor_chain_to_most_closest_shared_ancestor'
99
+ for ancestor_chain_1 in l_ancestor_chain_1 : # iterate over chain list #1
100
+ # find the chain in the chain list # 2 that contains the longest shared chain with the chain in the chain list #1
101
+ l_index_most_closest_shared_ancestor = [ ]
102
+ for ancestor_chain_2 in l_ancestor_chain_2 : # iterate over chain list #2
103
+ index_most_closest_shared_ancestor = 0 # initialize the index that indicate the location of the most closest shared ancestor between the two chains # initialize with the index of the root term
104
+ for ancestor_1, ancestor_2 in zip( ancestor_chain_1[ 1 : ], ancestor_chain_2[ 1 : ] ) : # retrieve ancester from chain #1 and chain #2 (from the most distant ancestor (excluding the root term) to the closest ancestor)
105
+ if ancestor_1 != ancestor_2 : # if the ancestors diverged between chain_1 and chain_2
106
+ break
107
+ index_most_closest_shared_ancestor += 1 # increase the pointer (take into account the current shared ancestor)
108
+ l_index_most_closest_shared_ancestor.append( index_most_closest_shared_ancestor )
109
+ index_chain_2_with_most_closest_shared_ancestor = np.argmax( l_index_most_closest_shared_ancestor ) # define 'most closest shared ancestor' as the ancestor that has the the largest number of ancestors between itself and the root term.
110
+ ancestor_chain_to_most_closest_shared_ancestor = tuple( l_ancestor_chain_2[ index_chain_2_with_most_closest_shared_ancestor ][ : l_index_most_closest_shared_ancestor[ index_chain_2_with_most_closest_shared_ancestor ] + 1 ] ) # including the most_closest_shared_ancestor in the chain
111
+ set_ancestor_chain_to_most_closest_shared_ancestor.add( ancestor_chain_to_most_closest_shared_ancestor )
112
+ return set_ancestor_chain_to_most_closest_shared_ancestor
113
+ def get_properties( self, term ) :
114
+ """
115
+ return the properties of the termk
116
+ # 2024-03-01 12:51:18
117
+ """
118
+ # get ontology terms
119
+ term = self[ term ]
120
+ # retrieve properties
121
+ l_label, l_comment, l_broadsynonym, l_exactsynonym = list( set( term.label ) ), list( set( term.comment ) ), list( set( term.hasBroadSynonym ) ), list( set( term.hasExactSynonym ) )
122
+ def _parse_property( l ) :
123
+ return l[ 0 ] if len( l ) > 0 else None
124
+ dict_property = { 'label' : _parse_property( l_label ), 'comment' : _parse_property( l_comment ), 'broad_synonym' : l_broadsynonym, 'exact_synonym' : l_exactsynonym }
125
+ return dict_property