forked from nicolazzie/SNPConvert
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMain.py
More file actions
614 lines (557 loc) · 39.2 KB
/
Copy pathMain.py
File metadata and controls
614 lines (557 loc) · 39.2 KB
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
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
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
'''
Created on Nov 6, 2015
@author: Ezequiel Luis Nicolazzi
'''
import os,sys,time
from PyQt4 import QtGui
from SNPConvert_gui import Ui_SNPConvert
class Main(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_SNPConvert()
self.ui.setupUi(self)
###############################################
###### TAB1 - Illumina MATRIX Format ##########
###############################################
#Setting log screen as readonly
self.ui.Log_wdg_tab1.setReadOnly(True)
#Setting action of buttons opening to Open file
self.inputFinRepFile_tab1='';self.inputSnpMapFile_tab1=''
self.ui.FinRep_tab1.clicked.connect(self.FinRep_open_tab1)
self.ui.SnpMap_tab1.clicked.connect(self.SnpMap_open_tab1)
#Setting linedit read (breed code)
self.BrdCode_tab1='UNKN_BRD'
self.ui.BrdCode_tab1.setPlaceholderText(self.BrdCode_tab1) #default (example) value
self.ui.BrdCode_tab1.cursorPositionChanged.connect(self.BrdCodeChange_tab1)
#Setting linedit read (output name)
self.OutName_tab1='Output_name'
self.ui.OutName_tab1.setPlaceholderText(self.OutName_tab1) #default (example) name
self.ui.OutName_tab1.cursorPositionChanged.connect(self.OutNameChange_tab1)
#Setting action of buttons RUN
self.ui.ConvertFile_tab1.clicked.connect(self.ConvertFile_run_tab1)
###############################################
###### TAB2 - Illumina ROW Format ##########
###############################################
#Setting log screen as readonly
self.ui.Log_wdg_tab2.setReadOnly(True)
#Setting action of buttons opening to Open file
self.inputFinRepFile_tab2='';self.inputSnpMap_tab2=''
self.ui.FinRep_tab2.clicked.connect(self.FinRep_open_tab2)
self.ui.SnpMap_tab2.clicked.connect(self.SnpMap_open_tab2)
#Setting the combo box
self.Chosen_allele='Forward'
self.items = ('Forward','Top','AB')
self.ui.AlleleCod_box_tab2.addItems(self.items)
self.ui.AlleleCod_box_tab2.currentIndexChanged.connect(self.OutAlleleCoding_tab2)
#Setting linedit read (breed code)
self.BrdCode_tab2='UNKN_BRD'
self.ui.BrdCode_tab2.setPlaceholderText(self.BrdCode_tab2) #default (example) value
self.ui.BrdCode_tab2.cursorPositionChanged.connect(self.BrdCodeChange_tab2)
#Setting linedit read (output name)
self.OutName_tab2='Output_name'
self.ui.OutName_tab2.setPlaceholderText(self.OutName_tab2) #default (example) name
self.ui.OutName_tab2.cursorPositionChanged.connect(self.OutNameChange_tab2)
#Setting action of buttons RUN
self.ui.ConvertFile_tab2.clicked.connect(self.ConvertFile_run_tab2)
################################
###### TAB3 - iConvert #########
################################
#Setting log screen as readonly
self.ui.Log_wdg_tab3.setReadOnly(True)
#Setting action of buttons opening to Open file
self.inputPedFile_tab3='';self.inputMapFile_tab3='';self.inputSnpChimpFile_tab3=''
self.ui.PedFile_tab3.clicked.connect(self.PedFile_open_tab3)
self.ui.MapFile_tab3.clicked.connect(self.MapFile_open_tab3)
self.ui.SnpChimp_tab3.clicked.connect(self.SnpChimp_open_tab3)
#Setting the combo box
self.items1=['ILMN_Forward','ILMN_Top','ILMN_AB','AFFY_Forward','AFFY_AB','NO_allele_conv']
self.INChosen_allele='ILMN_Forward'
self.ui.AlleleCodIn_box_tab3.addItems(self.items1)
self.ui.AlleleCodIn_box_tab3.currentIndexChanged.connect(self.InAlleleCoding_tab3)
self.items2=['ILMN_Top','ILMN_Forward','ILMN_AB','AFFY_Forward','AFFY_AB','NO_allele_conv']
self.OUTChosen_allele='ILMN_Top'
self.ui.AlleleCodOut_box_tab3.addItems(self.items2)
self.ui.AlleleCodOut_box_tab3.currentIndexChanged.connect(self.OutAlleleCoding_tab3)
#Setting radio buttons
self.UpdateMap = True
self.ui.YesRadioButton_tab3.clicked.connect(self.YesRadio_clicked)
self.ui.NoRadioButton_tab3.clicked.connect(self.NoRadio_clicked)
#Setting linedit read (output name)
self.OutName_tab3='Output_name'
self.ui.OutName_tab3.setPlaceholderText(self.OutName_tab3) #default (example) name
self.ui.OutName_tab3.cursorPositionChanged.connect(self.OutNameChange_tab3)
#Setting action of buttons RUN
self.ui.ConvertFile_tab3.clicked.connect(self.ConvertFile_run_tab3)
#####################################################################################################################
###### DEFS FOR TAB1 - Illumina MATRIX Format
#####################################################################################################################
def FinRep_open_tab1(self):
self.inputFinRepFile_tab1 = QtGui.QFileDialog.getOpenFileName(self,"Open FinalReport file", "Open FinalReport file", self.tr("TXT Files (*.txt);;CSV Files (*.csv);;All Files (*)"))
if self.inputFinRepFile_tab1.isEmpty():self.ui.Log_wdg_tab1.append("<font color=red><b>WARNING:</b>FinalReport file not selected!</font>")
else:self.ui.Log_wdg_tab1.append("FinalReport file selected <font color=green><b>OK</b></font>")
def SnpMap_open_tab1(self):
self.inputSnpMapFile_tab1 = QtGui.QFileDialog.getOpenFileName(self,"Open SNP map file", "Open SNP map file", self.tr("TXT Files (*.txt);;CSV Files (*.csv);;All Files (*)"))
if self.inputSnpMapFile_tab1.isEmpty():self.ui.Log_wdg_tab1.append("<font color=red><b>WARNING:</b>SNP map file not selected!</font>")
else:self.ui.Log_wdg_tab1.append("SNP map file selected <font color=green><b>OK</b></font>")
def BrdCodeChange_tab1(self):
self.BrdCode_tab1 = self.ui.BrdCode_tab1.text()
def OutNameChange_tab1(self):
self.OutName_tab1 = self.ui.OutName_tab1.text()
def ConvertFile_run_tab1(self):
### Check that parameters are not void and replace values when possible (if not return an error message!)
self.ui.Log_wdg_tab1.clear()
if not self.inputFinRepFile_tab1 or self.inputFinRepFile_tab1.isEmpty():return self.ui.Log_wdg_tab1.append("<font color=red><b>ERROR: Please select a FinalReport file!</b></font>")
if not self.inputSnpMapFile_tab1 or self.inputSnpMapFile_tab1.isEmpty():return self.ui.Log_wdg_tab1.append("<font color=red><b>ERROR: Please select a SNP map file!</b></font>")
if not self.BrdCode_tab1:self.BrdCode_tab1 = 'UNKN_BRD'
if not self.OutName_tab1:self.OutName_tab1 = 'Output_name'
### Print the options used
self.ui.Log_wdg_tab1.append("<b><i>PEDDA MATRIX software - ILMN Matrix --> PLINK</i></b>")
self.ui.Log_wdg_tab1.append("<i>Conversion and GUI coded by E.L.Nicolazzi (PTP)</i><br>")
self.ui.Log_wdg_tab1.append("<b>PARAMETERS USED</i></b>")
self.ui.Log_wdg_tab1.append(time.strftime("- Analysis run on %d/%m/%y at %H:%M:%S", time.localtime()))
self.ui.Log_wdg_tab1.append(" - FinalReport: <b>%s</b>" % self.inputFinRepFile_tab1)
self.ui.Log_wdg_tab1.append(" - SNP map: <b>%s</b>" % self.inputSnpMapFile_tab1)
self.ui.Log_wdg_tab1.append(" - Pop code: <b>%s</b>" % self.BrdCode_tab1)
self.ui.Log_wdg_tab1.append(" - Output name: <b>%s</b><br>" % self.OutName_tab1)
### Start processing the Map file first
self.ui.Log_wdg_tab1.append("<b>A) Processing SnpMap file</b>")
conv={};chroms={}
for a in open(self.inputSnpMapFile_tab1):
if 'chromosome' in a.lower():continue #.lower is used to avoid problems with capital letters
# Automatic identifier of the separator (at least 4 fields should be enough)
try:
if len(a.strip().split(','))>5:
line=a.strip().split(',')[:5]
nn,snpid,cro,pos,rest=line
elif len(a.strip().split('\t'))>5:
line=a.strip().split('\t')[:5]
nn,snpid,cro,pos,rest=line
else:
line=a.strip().split()[:5]
nn,snpid,cro,pos,rest=line
except:return self.ui.Log_wdg_tab1.append("<font color=red><b>ERROR: Bad/Corrupt SNP map file! Please check the file and make sure it is an (original) Illumina SnpMap file!</b></font>")
conv[snpid]=(cro,pos) #This dictionary keeps map info related to the SNP name
chroms[cro]=0 #This dictionary is useful only to inform the user
self.ui.Log_wdg_tab1.append("====> Total number of SNPs processed: <b>%s</b>" % len(conv))
self.ui.Log_wdg_tab1.append("====> Total number of chromosomes found: <b>%s</b>" % len(chroms))
self.ui.Log_wdg_tab1.append("====> List of chromosomes found: <b>%s</b><br>" % ','.join(sorted(chroms.keys())))
#Adapt slashes to unix/mac or Windows
direct_name=os.path.abspath(os.path.dirname(str(self.inputFinRepFile_tab1)))
if '/' in direct_name:slsh='/'
else:slsh='\\'
#Rebuild the (full) path of the original inputFinRepFile_tab1 - for compatibility with Windows
#Open the output files
outnam = direct_name+slsh+self.OutName_tab1
outgen = open(outnam+'.ped','w')
outmap = open(outnam+'.map','w')
### Start processing the genotypes
self.ui.Log_wdg_tab1.append("<b>B) Processing FinalReport file</b>")
readfrom=False
genos=[];anims=[];n=0
for en,a in enumerate(open(self.inputFinRepFile_tab1)):
if '[data]' in a.lower(): #.lower is used to avoid problems with capital letters
readfrom=True
continue
if not readfrom:continue # Skip the header of the illumina file
n+=1 # Start counting SNPs
# Automatic identifier of the file separator
if len(a.strip().split(','))>=2:
line=a.strip().split(',');sep=','
elif len(a.strip().split('\t'))>=2:
line=a.strip().split('\t');sep='\t'
else:
line=a.strip().split();sep=' '
# This is the first line containing the IDs of individuals
if n==1:
for x in range(len(line)):
if x==0:continue
anims.append(line[x]) # keep trace of individual IDs
genos.append([])
genos.append([]) # create a list n.individuals long x2
continue # Jump to the genotypes
#If it is a , or \t separator, do it easy, otherise
snp,geno=a.strip().split(sep,1)
geno.strip()
#Write the Map file as soon as it is read (to maintain the order)
try:outmap.write('%s %s 0 %s\n' % (conv[snp][0],snp,conv[snp][1]))
except:return self.ui.Log_wdg_tab1.append("<font color=red><b>ERROR: Map file writing failed at row"+str(en+1)+". Missing snp in Map?? </b></font>")
#read the genotype line as long string separated by...
if sep==',' or sep=='\t':geno=geno.replace('-','0').strip().split(sep)
else:geno=geno.replace('-','0').strip().split()
# Keep genos in memory (in order)
for x in range(len(geno)):genos[x].append(geno[x])
#check if reading of the file went ok.
if not n:return self.ui.Log_wdg_tab1.append("<font color=red><b>ERROR: Bad/Corrupt FinalReport file! Please check the file!</b></font>")
self.ui.Log_wdg_tab1.append("====> Total number of INDIVIDUALS processed: <b>%s</b><br>" % len(geno))
self.ui.Log_wdg_tab1.append("<b>C) Writing output files</b>")
for x in range(len(anims)):
gty=[genos[x][i][0]+' '+genos[x][i][1] for i in range(len(genos[x]))]
outgen.write('%s %s 0 0 0 0 %s\n' % (self.BrdCode_tab1,anims[x],' '.join(gty)))
self.ui.Log_wdg_tab1.append("<br><br>====> Bazinga! <font color=green><b><i>Conversion run OK!</i></b></font>")
self.ui.Log_wdg_tab1.append("<font color=blue>PED file available: <b>"+outnam+'.ped</b></font>')
self.ui.Log_wdg_tab1.append("<font color=blue>MAP file available: <b>"+outnam+'.map</b></font>')
#####################################################################################################################
###### DEFS FOR TAB2 - Illumina ROW Format
#####################################################################################################################
def FinRep_open_tab2(self):
self.inputFinRepFile_tab2 = QtGui.QFileDialog.getOpenFileName(self,"Open FinalReport file", "Open FinalReport file", self.tr("TXT Files (*.txt);;CSV Files (*.csv);;All Files (*)"))
if self.inputFinRepFile_tab2.isEmpty():self.ui.Log_wdg_tab2.append("<font color=red><b>WARNING:</b>FinalReport file not selected!</font>")
else:self.ui.Log_wdg_tab2.append("FinalReport file selected <font color=green><b>OK</b></font>")
def SnpMap_open_tab2(self):
self.inputSnpMapFile_tab2 = QtGui.QFileDialog.getOpenFileName(self,"Open SNP map file", "Open SNP map file", self.tr("TXT Files (*.txt);;CSV Files (*.csv);;All Files (*)"))
if self.inputSnpMapFile_tab2.isEmpty():self.ui.Log_wdg_tab2.append("<font color=red><b>WARNING:</b>SNP map file not selected!</font>")
else:self.ui.Log_wdg_tab2.append("SNP map file selected <font color=green><b>OK</b></font>")
def OutAlleleCoding_tab2(self,indice):
self.Chosen_allele = self.items[indice]
def BrdCodeChange_tab2(self):
self.BrdCode_tab2 = self.ui.BrdCode_tab2.text()
def OutNameChange_tab2(self):
self.OutName_tab2 = self.ui.OutName_tab2.text()
def ConvertFile_run_tab2(self):
### Check that parameters are not void and replace values when possible (if not return an error message!)
self.ui.Log_wdg_tab2.clear()
if not self.inputFinRepFile_tab2 or self.inputFinRepFile_tab2.isEmpty():return self.ui.Log_wdg_tab2.append("<font color=red><b>ERROR: Please select a FinalReport file!</b></font>")
if not self.inputSnpMapFile_tab2 or self.inputSnpMapFile_tab2.isEmpty():return self.ui.Log_wdg_tab2.append("<font color=red><b>ERROR: Please select a SNP map file!</b></font>")
if not self.BrdCode_tab2:self.BrdCode_tab2 = 'UNKN_BRD'
if not self.OutName_tab2:self.OutName_tab2 = 'Output_name'
### Print the options used
self.ui.Log_wdg_tab2.append("<b><i>Pedda ROW software - ILMN ROW --> PLINK</i></b>")
self.ui.Log_wdg_tab2.append("<i>Conversion and GUI coded by E.L.Nicolazzi (PTP)</i><br>")
self.ui.Log_wdg_tab2.append("<b>PARAMETERS USED</i></b>")
self.ui.Log_wdg_tab2.append(time.strftime("- Analysis run on %d/%m/%y at %H:%M:%S", time.localtime()))
self.ui.Log_wdg_tab2.append(" - FinalReport: <b>%s</b>" % self.inputFinRepFile_tab2)
self.ui.Log_wdg_tab2.append(" - SNP map: <b>%s</b>" % self.inputSnpMapFile_tab2)
self.ui.Log_wdg_tab2.append(" - Output allele coding: <b>%s</b>" % self.Chosen_allele)
self.ui.Log_wdg_tab2.append(" - Pop code: <b>%s</b>" % self.BrdCode_tab2)
self.ui.Log_wdg_tab2.append(" - Output name: <b>%s</b><br>" % self.OutName_tab2)
self.ui.Log_wdg_tab2.append("<b>A) Processing SNP map file file</b>")
conv={};chroms={}
for a in open(self.inputSnpMapFile_tab2):
if 'chromosome' in a.lower():continue #.lower is used to avoid problems with capital letters
# Automatic identifier of the separator (at least 4 fields should be enough)
try:
if len(a.strip().split(','))>5:
line=a.strip().split(',')[:5]
nn,snpid,cro,pos,rest=line
elif len(a.strip().split('\t'))>5:
line=a.strip().split('\t')[:5]
nn,snpid,cro,pos,rest=line
else:
line=a.strip().split()[:5]
nn,snpid,cro,pos,rest=line
except:return self.ui.Log_wdg_tab2.append("<font color=red><b>ERROR: Bad/Corrupt SNP map file! Please check the file and make sure it is an (original) Illumina SnpMap file!</b></font>")
conv[snpid]=(cro,pos) #This dictionary keeps map info related to the SNP name
chroms[cro]=0 #This dictionary is useful only to inform the user
self.ui.Log_wdg_tab2.append("====> Total number of SNPs processed: <b>%s</b>" % len(conv))
self.ui.Log_wdg_tab2.append("====> Total number of chromosomes found: <b>%s</b>" % len(chroms))
self.ui.Log_wdg_tab2.append("====> List of chromosomes found: <b>%s</b><br>" % ','.join(sorted(chroms.keys())))
### Start processing the Map file first
self.ui.Log_wdg_tab2.append("<b>B) Processing FinRep file</b>")
direct_name=os.path.abspath(os.path.dirname(str(self.inputFinRepFile_tab2)))
if '/' in direct_name:slsh='/'
else:slsh='\\'
#Rebuild the (full) path of the original inputFinRepFile_tab1 - for compatibility with Windows
#Open the output files
outnam = direct_name+slsh+self.OutName_tab2
outgen = open(outnam+'.ped','w')
outmap = open(outnam+'.map','w')
readfrom=False
SNPname=[]
c=0;anim=-1;snp=0
for en,a in enumerate(open(self.inputFinRepFile_tab2)):
if 'allele1' in a.lower():
readfrom=True
if len(a.strip().split(','))>=3:
sep=',';
line=a.lower().strip().split(sep)
elif len(a.strip().split('\t'))>=3:
sep='\t'
line=a.lower().strip().split(sep)
else:
sep=''
line=a.lower().strip().split()
### Seraching for 'SNP name' header
if not 'snp name' in line and not 'snpname' in line:
return self.ui.Log_wdg_tab2.append("<font color=red><b>ERROR:</b> The header does not contain the required information: 'SNP name' or 'SNPname' (not case sensitive)<\font><br>")
else:
try:
SNPid_pos = line.index('snp name')
except:
SNPid_pos = line.index('snpname')
### Searching for 'Sample ID' header
if not 'sample id' in line and not 'sampleid' in line:
return self.ui.Log_wdg_tab2.append("<font color=red><b>ERROR:</b> The header does not contain the required information: 'Sample ID' or 'SampleId' (not case sensitive)<\font><br>")
else:
try:
INDid_pos = line.index('sample id')
except:
INDid_pos = line.index('sampleid')
### Searching for the required allele coding format.
if not 'allele1 - '+self.Chosen_allele.lower() in line:
self.ui.Log_wdg_tab2.append("<font color=red><b>ERROR:</b> The header does not contain the chosen allele coding: Allele1 - "+self.Chosen_allele+'<\font><br>')
self.ui.Log_wdg_tab2.append("<font color=red> Header read:'<b><i>"+a+"'</i></b></font><br>")
self.ui.Log_wdg_tab2.append("<font color=red> Possible causes: </font>")
self.ui.Log_wdg_tab2.append("<font color=red> - The requested allele format is not present in your header <i>[SOLUTION: choose an allele format that is available]</i></font>")
self.ui.Log_wdg_tab2.append("<font color=red> - The header in the Illumina header has changed (no 'Allele1 - "+self.Chosen_allele+"' field is present - not case sensitive) <i>[SOLUTION: Contact ezequiel.nicolazzi@ptp.it, or if you know how to code in python, change the if string related to this]</i></font>")
self.ui.Log_wdg_tab2.append("<font color=red> - The FinalReport file separator is not right <i>[SOLUTION: Contact ezequiel.nicolazzi@ptp.it, or if you know how to code in python, change the if string related to this]</i></font>")
self.ui.Log_wdg_tab2.append("<font color=red> - The FinalReport file is in Matrix format <i>[SOLUTION: Use the 'Illumina MATRIX format' tag instead]</i></font>")
self.ui.Log_wdg_tab2.append("<font color=red> - Other <i>[SOLUTION: Contact ezequiel.nicolazzi@ptp.it ... send me the header (e.g. first 10 rows) of the FinalReport file you're using]</i></font>")
return
else:
alle_pos1=line.index('allele1 - '+self.Chosen_allele.lower())
alle_pos2=alle_pos1+1
continue
if not readfrom:continue
if sep:line=a.strip().split(sep)
else: line=a.strip().split()
if alle_pos2 > len(line):
return self.ui.Log_wdg_tab2.append("<font color=red><b>ERROR:</b> Probable error in the separator.. Position of Allele2 found in "+str(alle_pos2)+", but length of line in row "+str(en)+" is "+str(len(line))+" !!</font><br>"+\
"<font color=red>Please check your choice of separator is correct and that you don't have strange fields in you FinalReport file!</font>")
snp_name=line[SNPid_pos]
if not conv.has_key(snp_name):
return self.ui.Log_wdg_tab2.append("<font color=red><b>ERROR:</b> SNP name: "+snp_name+" in FinalReport file not found in SNP map file!</font><br>")
id_sample=line[INDid_pos]
alle1=line[alle_pos1]
alle2=line[alle_pos2]
if alle1=='-':alle1='0'
if alle2=='-':alle2='0'
c+=1
if c==1:
snp=-1
geno=[]
name=[]
name.append(id_sample)
if id_sample in name:
geno.append(alle1+' '+alle2)
snp+=1
if len(name)==1:
outmap.write('%s %s 0 %s\n' % (conv[snp_name][0],snp_name,conv[snp_name][1]))
SNPname.append(snp_name)
else:
if snp_name!=SNPname[snp]:
return self.ui.Log_wdg_tab2.append("The order of the SNPs in the different individuals is consistent. Check "+snp_name+" and "+SNPname[snp])
else:
snp=0
anim+=1
outgen.write('%s %s 0 0 0 -9 %s\n' % (self.BrdCode_tab2,name[anim],' '.join(geno)))
self.ui.Log_wdg_tab2.append('Finshed processing individual: '+name[anim]+' - Total SNPs: '+str(len(geno)))
geno=[]
geno.append(alle1+' '+alle2)
name.append(id_sample)
outgen.write('%s %s 0 0 0 -9 %s\n' % (self.BrdCode_tab2,id_sample,' '.join(geno)))
anim+=2
self.ui.Log_wdg_tab2.append('Finshed processing individual: '+id_sample+' - Total SNPs: '+str(len(geno)))
self.ui.Log_wdg_tab2.append('<br><b>====> Total number of INDIVIDUALS processed: '+str(anim)+'</b><br><br>')
self.ui.Log_wdg_tab2.append("====> Bazinga! <font color=green><b><i>Conversion run OK!</i></b></font>")
self.ui.Log_wdg_tab2.append("<font color=blue>PED file available: <b>"+outnam+'.ped</b></font>')
self.ui.Log_wdg_tab2.append("<font color=blue>MAP file available: <b>"+outnam+'.map</b></font>')
def PedFile_open_tab3(self):
self.inputPedFile_tab3 = QtGui.QFileDialog.getOpenFileName(self,"Open PED file", "Open PED file", self.tr("PED files (*.ped);;All files (*)"))
if self.inputPedFile_tab3.isEmpty():self.ui.Log_wdg_tab3.append("<font color=red><b>WARNING:</b>PED file not selected!</font>")
else:self.ui.Log_wdg_tab3.append("PED file selected <font color=green><b>OK</b></font>")
def MapFile_open_tab3(self):
self.inputMapFile_tab3 = QtGui.QFileDialog.getOpenFileName(self,"Open MAP file", "Open MAP file", self.tr("MAP files (*.map);;All files (*)"))
if self.inputMapFile_tab3.isEmpty():self.ui.Log_wdg_tab3.append("<font color=red><b>WARNING:</b>MAP file not selected!</font>")
else:self.ui.Log_wdg_tab3.append("MAP file selected <font color=green><b>OK</b></font>")
def SnpChimp_open_tab3(self):
self.inputSnpChimpFile_tab3 = QtGui.QFileDialog.getOpenFileName(self,"Open SNPchimp_v3 file", "Open SNPchimp_v3 file", self.tr("CSV files (*.csv);; TSV files (*.tsv);; All Files (*)"))
if self.inputSnpChimpFile_tab3.isEmpty():self.ui.Log_wdg_tab3.append("<font color=red><b>WARNING:</b>SNPchimp_v3 file not selected!</font>")
else:self.ui.Log_wdg_tab3.append("SNPchimp_v3 file selected <font color=green><b>OK</b></font>")
def InAlleleCoding_tab3(self,indice):
self.INChosen_allele = self.items1[indice]
def YesRadio_clicked(self):
self.UpdateMap = True
def NoRadio_clicked(self):
self.UpdateMap = False
def OutAlleleCoding_tab3(self,indice):
self.OUTChosen_allele = self.items2[indice]
def OutNameChange_tab3(self):
self.OutName_tab3 = self.ui.OutName_tab3.text()
def ConvertFile_run_tab3(self):
#Clear screen
self.ui.Log_wdg_tab3.clear()
#Only update map info (do not ask for ped file
if self.INChosen_allele == 'NO_allele_conv' and self.OUTChosen_allele == 'NO_allele_conv': onlymap=True
else:onlymap=False
if not onlymap:
if not self.inputPedFile_tab3 or self.inputPedFile_tab3.isEmpty():return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: Please select a PED file!</b></font>")
if not self.inputMapFile_tab3 or self.inputMapFile_tab3.isEmpty():return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: Please select a MAP file!</b></font>")
if not self.inputSnpChimpFile_tab3 or self.inputSnpChimpFile_tab3.isEmpty():return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: Please select a SNPchimp_v3 file! (visit http://bioinformatics.tecnoparco.org/SNPchimp/)</b></font>")
if not str(self.inputSnpChimpFile_tab3[-3:]).lower() in ['csv','tsv']:return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: Original SNPchimp v.3 files are csv (comma separated) or tsv (tab separated).</b></font>")
if not self.OutName_tab3:self.OutName_tab3 = 'Output_name'
if not onlymap:
if self.INChosen_allele==self.OUTChosen_allele:return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: Input and Output allele coding MUST be different!</b></font>")
if self.INChosen_allele[:4]=='ILMN' and self.OUTChosen_allele[:4]!='ILMN':return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: Input and Output allele coding MUST be compatible (ILMN-ILMN, AFFY-AFFY or NO-NO)!</b></font>")
if self.INChosen_allele[:4]=='AFFY' and self.OUTChosen_allele[:4]!='AFFY':return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: Input and Output allele coding MUST be compatible (ILMN-ILMN, AFFY-AFFY or NO-NO)!</b></font>")
if self.INChosen_allele[:2]=='NO' and self.OUTChosen_allele[:2]!='NO':return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: Input and Output allele coding MUST be compatible (ILMN-ILMN, AFFY-AFFY or NO-NO)!</b></font>")
if os.path.basename(str(self.inputPedFile_tab3)) == self.OutName_tab3+'.ped':
return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: Bad output name - Must be different from PED filename!</b></font>")
if os.path.basename(str(self.inputMapFile_tab3)) == self.OutName_tab3+'.map':
return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: Bad output name - Must be different from MAP filename!</b></font>")
### Print the options used
answer={True:'Yes',False:'No'}
self.ui.Log_wdg_tab3.append("<b><i>iConvert software - Allele conversion and map update <i></b>")
self.ui.Log_wdg_tab3.append("<i>Software and GUI coded by E.L.Nicolazzi (PTP)</i><br>")
self.ui.Log_wdg_tab3.append("<b>PARAMETERS USED</i></b>")
self.ui.Log_wdg_tab3.append(time.strftime("- Analysis run on %d/%m/%y at %H:%M:%S", time.localtime()))
self.ui.Log_wdg_tab3.append(" - PLINK fmt - Ped file: <b>%s</b>" % self.inputPedFile_tab3)
self.ui.Log_wdg_tab3.append(" - PLINK fmt - Map file: <b>%s</b>" % self.inputMapFile_tab3)
self.ui.Log_wdg_tab3.append(" - SNPchimp v.3 file : <b>%s</b>" % self.inputSnpChimpFile_tab3)
self.ui.Log_wdg_tab3.append(" - Input allele coding : <b>%s</b><br>" % self.INChosen_allele)
self.ui.Log_wdg_tab3.append(" - Map update requested: <b>%s</b><br>" % answer[self.UpdateMap])
self.ui.Log_wdg_tab3.append(" - Output allele coding: <b>%s</b>" % self.OUTChosen_allele)
self.ui.Log_wdg_tab3.append(" - Output name : <b>%s</b><br>" % self.OutName_tab3)
self.ui.Log_wdg_tab3.append("<br><b>A) Processing SNPchimp v.3 file file</b><br>")
othinfo = []
chips = {};SNPdata = {}
formats = [0,0]
SEP = ','
if self.UpdateMap: mapupd = {}
readfrom=False
for en,line in enumerate(open(self.inputSnpChimpFile_tab3)):
if str(self.inputSnpChimpFile_tab3[-3:]).lower()=='csv':SEP=','
elif str(self.inputSnpChimpFile_tab3[-3:]).lower()=='tsv':SEP='\t'
if 'chip_name' in line.lower():
readfrom=True
hlin=line.lower().strip().split(SEP)
####### FILE header and stuff checks
#Check SEPARATOR is right
if len(hlin)<3:
return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: SNPchimp v.3 separator is not comma or tab, as assumed by the extension name - Download original SNPchimp v.3 files and try again</b></font>")
# Check if user choices are coherent with the infor in SNPchimp v.3
if self.INChosen_allele=='ILMN_Top' or self.OUTChosen_allele=='ILMN_Top':
try:
head_finder=hlin.index('alleles_a_b_top')
if self.INChosen_allele=='ILMN_Top':formats[0]=head_finder
else:formats[1]=head_finder
except ValueError:return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: SNPchimp v.3 file does not contain the required Illumina TOP allele coding</b></font>")
if self.INChosen_allele=='ILMN_Forward' or self.OUTChosen_allele=='ILMN_Forward':
try:
head_finder=hlin.index('alleles_a_b_forward')
if self.INChosen_allele=='ILMN_Forward':formats[0]=head_finder
else:formats[1]=head_finder
except ValueError:return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: SNPchimp v.3 file does not contain the required Illumina FORWARD allele coding</b></font>")
if self.INChosen_allele=='AFFY_Forward' or self.OUTChosen_allele=='AFFY_Forward':
try:
head_finder=hlin.index('alleles_a_b_affymetrix')
if self.INChosen_allele=='AFFY_Forward':formats[0]=head_finder
else:formats[1]=head_finder
except ValueError:return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: SNPchimp v.3 file does not contain the required Affymetrix FORWARD allele coding</b></font>")
# Check accessory information is there, otherwise stop
try:
othinfo.append(hlin.index('chip_name'))
othinfo.append(hlin.index('chromosome'))
othinfo.append(hlin.index('position'))
othinfo.append(hlin.index('snp_name'))
continue
except ValueError:
return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR:'chip name', 'chromosome', 'position' and 'SNP_name' header variables not found. These are always present in SNPchiMp downloaded files!</b></font>")
if not readfrom:continue
lista = line.strip().split(SEP)
# Keep only useful info (skip other not wanted chips)
if not chips.has_key(lista[othinfo[0]]): chips[lista[othinfo[0]]] = 0
if 'AFFY' in self.INChosen_allele:
if not 'Bov_AffyHD' in lista[0]:continue
if 'ILMN' in self.INChosen_allele:
if 'Bov_AffyHD' in lista[0]:continue
# Keep SNPchimp map information only if required by the user.
if self.UpdateMap: mapupd[lista[othinfo[3]]] = [lista[othinfo[1]],lista[othinfo[2]]]
# If INPUT allele format is AB, use AB as IN and custom allele format as OUT
if self.INChosen_allele == 'ILMN_AB' or self.INChosen_allele == 'AFFY_AB':
SNPdata[lista[othinfo[3]]] = {'A':lista[formats[1]][0], 'B':lista[formats[1]][2]}
# If OUTPUT allele format is AB, use AB as OUT and custom allele in format as IN
elif self.OUTChosen_allele == 'ILMN_AB' or self.OUTChosen_allele == 'AFFY_AB':
SNPdata[lista[othinfo[3]]] = {lista[formats[0]][0]:'A', lista[formats[0]][2]:'B'}
else:
SNPdata[lista[othinfo[3]]] = {lista[formats[0]][0]:lista[formats[1]][0],
lista[formats[0]][2]:lista[formats[1]][2]}
if en == 0:return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: SNPchimp file is empty!</b></font>")
if len(SNPdata) == 0:return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: Problem reading SNPchimp file! Probable causes: file is empty, conversion info is not available or header is incorrect</b></font>")
if len(chips)>1:self.ui.Log_wdg_tab3.append("<font color=orange><b>WARNING: More than 1 chip in SNPchimp file. This might result in incorrect allele conversion!</b></font>")
self.ui.Log_wdg_tab3.append("- Rows read in SNP chimp file :<b>%s</b>" % str(en+1))
self.ui.Log_wdg_tab3.append("- SNPs with allele conversion information available :<b>%s</b>" % str(len(SNPdata)))
###### Process Map file
self.ui.Log_wdg_tab3.append("<br><b>B) Processing PLINK MAP file and converting map information (if required)</b><br>")
maporder = [];convert = []
tab = False
# Create new file if new remapping is needed
if self.UpdateMap:
#Adapt slashes to unix/mac or Windows
direct_name=os.path.abspath(os.path.dirname(str(self.inputMapFile_tab3)))
if '/' in direct_name:slsh='/'
else:slsh='\\'
#Rebuild the (full) path of the original inputMapFile_tab3 - for compatibility with Windows
outnammap = direct_name+slsh+self.OutName_tab3+'.map'
remap=open(outnammap,'w')
enmap=0
for enm,line in enumerate(open(self.inputMapFile_tab3)):
#Check for tab or blank space spearator (only these 2 are accepted here)
if enm == 0: test = line.strip().split('\t')
if len(test) == 4: # Warning: this only accepts "normal" MAP files (no --map3 or other options are allowed)
linea = line.strip().split('\t')
tab = True
else:
linea = line.strip().split()
if len(linea) == 0: continue # Skips void lines
enmap+=1
snp = linea[1]
maporder.append(snp)
if not SNPdata.has_key(linea[1]):return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: SNP: "+ snp +" not found in SNPchimp file. Probable causes: you downloaded the wrong SNPchip or modified SNP names </b></font>")
else:convert.append(SNPdata[linea[1]])
if self.UpdateMap:remap.write('%s %s %s %s\n' % (mapupd[snp][0],snp,linea[2],mapupd[snp][1]))
if enmap==0:return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: MAP file is empty!</b></font>")
self.ui.Log_wdg_tab3.append("- Rows read in PLINK MAP file (number of SNPs) : <b>%s</b>" % str(enmap))
if self.UpdateMap: self.ui.Log_wdg_tab3.append("<font color=blue>- MAP file updated: <b>%s</b></font>" % outnammap)
# Process PED file (if required)
if not onlymap:
self.ui.Log_wdg_tab3.append("<br><b>C) Processing PLINK PED file and converting allele format</b><br>")
tab = False
#Get full path of PED file
#Adapt slashes to unix/mac or Windows
direct_name=os.path.abspath(os.path.dirname(str(self.inputMapFile_tab3)))
if '/' in direct_name:slsh='/'
else:slsh='\\'
#Rebuild the (full) path of the original inputMapFile_tab3 - for compatibility with Windows
outnamped = direct_name+slsh+self.OutName_tab3+'.ped'
reped=open(outnamped,'w')
#Read ped file
enped=0
for enped,line in enumerate(open(self.inputPedFile_tab3)):
if enped == 0:
test = line.strip().split('\t')
if len(test) == (6 + enmap * 2): tab = True
if tab: pedline = line.strip().split('\t')
else: pedline = line.strip().split()
if (enmap*2+6) != len(pedline):
return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: Number of SNPs in PLINK MAP (" +str(enmap)+") should result in "+str(enmap*2+6)+" columns PLINK PED but found "+str(len(pedline))+"<br>Check your .ped and .map files. Usually the problem is in the first line(s)...</font><br>")
genotypes = [pedline[6 + x] + pedline[7 + x] for x in range(0, (enmap*2)-1, 2)]
seq = -1
genout = []
nfailed = 0
name_snps=[]
for alle in genotypes:
seq += 1
alle1 = alle[0];alle2 = alle[1]
if alle1 == '0' or alle1 =='?':
genout.append('0');genout.append('0')
continue
try:
genout.append(convert[seq][alle1])
genout.append(convert[seq][alle2])
except KeyError:
nfailed += 1
name_snps.append(maporder[seq])
if nfailed:
return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: Individual " + pedline[1] + " failed conversion for " + str(nfailed) + \
" SNPs [ "+', '.join(name_snps)+"]. Probably their INPUT allele coding format is not as specified</font>")
reped.write('%s %s\n' % (' '.join(pedline[:6]),' '.join(genout)))
if enped==0:return self.ui.Log_wdg_tab3.append("<font color=red><b>ERROR: PED file is empty!</b></font>")
self.ui.Log_wdg_tab3.append("- Rows read in PLINK PED file (number of animals) : <b>%s</b>" % str(enped + 1))
self.ui.Log_wdg_tab3.append("<font color=blue>- PED file updated: <b>%s</b></font><br><br>" % outnamped)
self.ui.Log_wdg_tab3.append('<br><font color=green><b> PROGRAM ENDS OK! </b></font>')
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Main()
window.show()
window.raise_()
sys.exit(app.exec_())