Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
loosolab
/
master_project_JLU2018
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
7
Pull requests
1
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
f742731
bin
Modules
config
RegGTFExtractor.py
bed_to_fasta.R
call_peaks.py
call_peaks.yaml
cdhit_wrapper.R
compareBed.sh
get_best_motif.py
maxScore.R
merge.R
reduce_bed.R
config
.gitignore
README.md
masterenv.yml
nextflow.config
pipeline.nf
uropa.config
Breadcrumbs
master_project_JLU2018
/
bin
/
call_peaks.py
Blame
Blame
Latest commit
History
History
351 lines (255 loc) · 15.6 KB
Breadcrumbs
master_project_JLU2018
/
bin
/
call_peaks.py
Top
File metadata and controls
Code
Blame
351 lines (255 loc) · 15.6 KB
Raw
""" call_peaks uses the uncontinuous score from a bigWig file to estimate footpints within peaks of interest. @author: Anastasiia Petrova @contact: anastasiia.petrova(at)mpi-bn.mpg.de """ import argparse #for parsing the parameters import sys #for example to exit if a problem occured import os #for example to check the existing path of a file import re #for example to split a string import time #to calculate time needed to proceed the data import logging #to write informaiton about the programm run import numpy as np #to calculate mean for example from collections import defaultdict #to create nested dictionaries import pyBigWig #to work with bigWig files import textwrap #to print the help message nice logger = logging.getLogger('call_peaks') logger.setLevel(logging.INFO) formatter = logging.Formatter("%(asctime)s : %(message)s", "%Y-%m-%d %H:%M") def parse_args(): parser = argparse.ArgumentParser(prog = '', description = textwrap.dedent(''' This script produces a file with footprints in .bed format from the file with scores in .bigWig format and a corresponding .bed file with peaks of interest. '''), epilog='That is what you need to make this script work for you. Enjoy it') required_arguments = parser.add_argument_group('required arguments') required_arguments.add_argument('--bigwig', help='a bigWig-file with scores', required=True) required_arguments.add_argument('--bed', help='provide a file with peaks in .bed format', required=True) #all other arguments are optional #parser.add_argument('--output_directory', default='output', const='output', nargs='?', help='output directory, by default ./output/') parser.add_argument('--output_file', default='call_peaks_output.bed', type=str, help='enter a name for the output file, by default ./call_peaks_output.bed') parser.add_argument('--window_length', default=200, type=int, help='enter the length for a window, by defauld 200 bp') parser.add_argument('--step', default=100, type=int, help='enter a step to move the window, by default 100 bp') parser.add_argument('--percentage', default=0, type=int, help='enter a percentage to be added to background while searching for footprints, by default 0%') parser.add_argument('--silent', action='store_true', help='while working with data write the information only into ./call_peaks_log.log') args = parser.parse_args() return args #this function is currently unused, but may be used in the future version def check_directory(directory): if not os.path.exists(directory): os.makedirs(directory) print('a new directory ' + directory + ' was created') #check if the file to remove exists and if so, delete it def remove_file(file): if os.path.isfile(file): os.remove(file) #check if the input files exist. We have no possibility to check if the format of the files is right here though def check_existing_input_files(args): #check if the bigWig file exists if not os.path.isfile(args.bigwig): print('there is no such bigWig file ' + args.bigwig + ', the exit is forced') sys.exit() #check if the bed file exists if not os.path.isfile(args.bed): print('there is no such bed file ' + args.bed + ', the exit is forced') sys.exit() #make a dictionary out of the .bed file for easy access to the information from the .bed file def make_bed_dictionary(bed_file): logger.info('reading of the bed file') try: #if i can't proceede this file like so, the input was not a .bed file! bed_dictionary = {} with open(bed_file) as read_bed_file: for bed_line in read_bed_file: bed_line_array = re.split(r'\t', bed_line.rstrip('\n')) if bed_line_array[1].isdigit() and bed_line_array[2].isdigit() and int(bed_line_array[1]) <= int(bed_line_array[2]): #in the real bedfile the second column is a start position, and the third column is an end position, so we are checking if these are integers and if the start position is smaller than the end one key = bed_line_array[0] + ":" + bed_line_array[1] + "-" + bed_line_array[2] value = [] for i in range(3, len(bed_line_array)): value.append(bed_line_array[i]) bed_dictionary[key] = value else: #this is not a bed file, force exit logger.info('please make sure the input bed file has a right format, the problem occured on the line ' + bed_line) sys.exit() read_bed_file.close() return bed_dictionary except UnicodeDecodeError: #force exit, if input is not a .bed file logger.info('please make sure that the .bed file has a right format! The exit is forced') sys.exit() #to save a footprint, find the score and max_pos for this footprint and check for overlapping with other footprints within the current peak def save_footprint(footprint_count, footprint_scores, peak_footprints, chromosom, footprint_start, footprint_end, bonus_info_from_bed): save_current_footprint = False if len(footprint_scores) > 2: #exclude small regions to not work with them #calculate the position with the max score. if there are many positions with the same score, save one from the middle first_max_pos = footprint_scores.index(max(footprint_scores)) last_max_pos = first_max_pos #assume that there is only one pos with max score #find the region with the highest score for j in range(first_max_pos, len(footprint_scores)): if footprint_scores[j] < first_max_pos: last_max_pos = j else: last_max_pos = len(footprint_scores) if first_max_pos != last_max_pos: #find a pos in the middle of these both max_pos = int((last_max_pos - first_max_pos) / 2) else: max_pos = first_max_pos #calculate the score for the current footprint as mean of all scores from the bigwig file footprint_score = np.mean(footprint_scores) #checking for existing and overlapping footprints if len(peak_footprints.keys()) == 0: # hey, this is the first footprint! save_current_footprint = True else: # there are already footprints in this peak, so look for overlaps for existing_footprint_name in peak_footprints.keys(): old_start = peak_footprints[existing_footprint_name]['start'] old_end = peak_footprints[existing_footprint_name]['end'] old_score = peak_footprints[existing_footprint_name]['score'] if footprint_start >= old_start and footprint_start <= old_end: #the start of the new footprint is between the start and end of an old footprint if footprint_end > old_end: #the new footprint is not completely inside the old one #update the information about the existing footprint footprint_score = (peak_footprints[existing_footprint_name]['score'] + footprint_score) / 2 #find the average of both scores peak_footprints[existing_footprint_name]['end'] = footprint_end peak_footprints[existing_footprint_name]['score'] = footprint_score #we can not update the max_pos as we do not have the information about scores array of the existing footprint #else: the new footprint is completely inside the old one, do nothing save_current_footprint = False break elif footprint_end >= old_start and footprint_end <= old_end: #the end of the new footprint is between the start and end of an old footprint if footprint_start < old_start: #the new footprint is not completely inside the old one #update the information about the existing footprint footprint_score = (peak_footprints[existing_footprint_name]['score'] + footprint_score) / 2 #find the average of both scores peak_footprints[existing_footprint_name]['start'] = footprint_start peak_footprints[existing_footprint_name]['score'] = footprint_score #else do nothing save_current_footprint = False break elif footprint_start <= old_start and footprint_end >= old_end: #the old footprint is exactly between the start and end positions of the new footprint #update the information about the existing footprint peak_footprints[existing_footprint_name] = {'chromosom': chromosom, 'start': footprint_start, 'end': footprint_end, 'score': footprint_score, 'len': len(footprint_scores), 'bonus': bonus_info_from_bed, 'max_pos': max_pos} save_current_footprint = False break else: #so this is a new footprint that has no overlaps with the others save_current_footprint = True if save_current_footprint == True: #make sure to save this footprint footprint_name = "footprint_" + str(footprint_count) peak_footprints[footprint_name] = peak_footprints.get(footprint_name, {}) peak_footprints[footprint_name] = {'chromosom': chromosom, 'start': footprint_start, 'end': footprint_end, 'score': footprint_score, 'len': len(footprint_scores), 'bonus': bonus_info_from_bed, 'max_pos': max_pos} footprint_count = footprint_count + 1 #else do nothing return footprint_count, peak_footprints #apply slide window searching to estimate regions that are higher than the background and can possibly be footprints def search_in_window(peak_footprints, footprint_count, chromosom, peak_start, peak_end, scores_in_peak, window_length, bed_dictionary_entry, step, percentage): peak_len = len(scores_in_peak) parts = [] parts_positions = [] #if necessary divide the peak with help of a sliding window if peak_len <= window_length: window_length = peak_len parts.append(scores_in_peak) parts_positions.append(0) else: pos = 0 while pos < (peak_len - step): if (pos + window_length) >= len(scores_in_peak): part = scores_in_peak[pos:] else: part = scores_in_peak[pos:pos + window_length] if len(part) != 1: #otherwise it makes no sense to look on the mean within this part and look for footprints parts.append(part) parts_positions.append(pos) pos = pos + step #look in each window and try to save the footprints for j in range(len(parts)): window = parts[j] #add some percent to the background to avoid the noice we don't want to have bw_peak_background = np.mean(window) #find the mean of all scores within one peak part = (percentage*bw_peak_background)/100 #x procent of the background bw_peak_background = bw_peak_background + part check_position = parts_positions[j] #the start position not within the window, but within the peak!!! footprint_start = check_position #for each footprint footprint_scores = [] #for each footprint #look on each position inside the window for i in range(len(window)): position = i + 1 #calculate the relative position for a score score = window[i] #extract one score from the list if score >= bw_peak_background: if position != (check_position + 1): #if this position is not the next with the last position we have checked #save the last footprint if check_position != 0: #if this is not the start of the first footprint within this peak footprint_count, peak_footprints = save_footprint(footprint_count, footprint_scores, peak_footprints, chromosom, footprint_start + peak_start + parts_positions[j], check_position + peak_start + parts_positions[j], bed_dictionary_entry) #start a new footprint footprint_start = position footprint_scores = [] check_position = position footprint_scores.append(score) #save the current score check_position = position footprint_count, peak_footprints = save_footprint(footprint_count, footprint_scores, peak_footprints, chromosom, footprint_start + peak_start + parts_positions[j], check_position + peak_start + parts_positions[j], bed_dictionary_entry) #save the last footprint return peak_footprints, footprint_count #use the information provided from the .bed file to look for footprints within the peaks of interest def find_peaks_from_bw(bed_dictionary, bw_file, window_length, step, percentage): logger.info('looking for footprints within peaks') try: footprint_count = 1 all_footprints = {} bw_open = pyBigWig.open(bw_file) for header in bed_dictionary: peak_footprints = {} header_splitted = re.split(r':', header) chromosom = header_splitted[0] positions = re.split(r'-', header_splitted[-1]) peak_start = int(positions[0]) peak_end = int(positions[1]) scores_in_peak = np.nan_to_num(np.array(list(bw_open.values(chromosom, peak_start, peak_end)))) #save the scores to an array peak_footprints, footprint_count = search_in_window(peak_footprints, footprint_count, chromosom, peak_start, peak_end, scores_in_peak, window_length, bed_dictionary[header], step, percentage) for footprint_name in peak_footprints.keys(): all_footprints[footprint_name] = all_footprints.get(footprint_name, {}) all_footprints[footprint_name] = peak_footprints[footprint_name] all_footprints = sorted(all_footprints.items(), key = lambda x : (x[1]['chromosom'], x[1]['start']), reverse = False) return all_footprints except RuntimeError: #if i can't work with the bigwig file like so, it was not a bigwig file! logger.info('please make sure that the .bigWig file has a right format! The exit is forced') sys.exit() #write the found footprints to the .bed file def write_to_bed_file(all_footprints, sorted_output_file_name): output_file_name = "not_sorted_" + sorted_output_file_name #save in the working directory header = ["#chr", "start", "end", "name", "score", "len", "max_pos", "bonus_info"] #a header to know what is in the columns output_file = open(output_file_name, 'w') #open a file to write logger.info("print to the output file") output_file.write('\t'.join(header) + '\n') #write the header #write each footprint line for line to the output file for footprint in all_footprints: output_file.write('\t'.join([footprint[1]['chromosom'], str(footprint[1]['start']), str(footprint[1]['end']), footprint[0], str(round(footprint[1]['score'], 6)), str(footprint[1]['len']), str(footprint[1]['max_pos']), '\t'.join(footprint[1]['bonus'])]) + '\n') output_file.close() #sort the bed file logger.info('sorting the output file') os.system("(head -n 2 " + output_file_name + " && tail -n +3 " + output_file_name + " | sort -k1,1V -k2,2n -k3,3n) > " + sorted_output_file_name) logger.info('remove the non-sorted file') remove_file(output_file_name) def main(): start = time.time() args = parse_args() check_existing_input_files(args) #check if there is an existing directory that user gave as input, otherwise create this directory from the path provided from the user #check_directory(args.output_directory) #fh = logging.FileHandler(os.path.join(args.output_directory, "call_peaks_log.log")) fh = logging.FileHandler("call_peaks_log.log") fh.setLevel(logging.INFO) fh.setFormatter(formatter) logger.addHandler(fh) ch = logging.StreamHandler() ch.setLevel(logging.INFO) ch.setFormatter(formatter) logger.addHandler(ch) #if user do not want to see the information about the status of jobs, remove the handler, that writes to the terminal if args.silent: logger.removeHandler(ch) logger.info("call_peaks.py was called using these parameters: " + str(vars(args))) bed_dictionary = make_bed_dictionary(args.bed) all_footprints = find_peaks_from_bw(bed_dictionary, args.bigwig, args.window_length, args.step, args.percentage) write_to_bed_file(all_footprints, args.output_file) logger.info("the number of peaks: " + str(len(bed_dictionary))) logger.info("the number of footprints: " + str(len(all_footprints))) logger.info("call_peaks needed %s minutes to generate the output" % (round((time.time() - start)/60, 2))) for handler in logger.handlers: handler.close() logger.removeFilter(handler) if __name__ == "__main__": main()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
You can’t perform that action at this time.