Package Bio :: Package SubsMat
[hide private]
[frames] | no frames]

Source Code for Package Bio.SubsMat

  1  # Copyright 2000-2009 by Iddo Friedberg.  All rights reserved. 
  2  # This code is part of the Biopython distribution and governed by its 
  3  # license.  Please see the LICENSE file that should have been included 
  4  # as part of this package. 
  5  # 
  6  # Iddo Friedberg idoerg@cc.huji.ac.il 
  7   
  8  """Substitution matrices, log odds matrices, and operations on them. 
  9   
 10  General: 
 11  -------- 
 12   
 13  This module provides a class and a few routines for generating 
 14  substitution matrices, similar ot BLOSUM or PAM matrices, but based on 
 15  user-provided data. 
 16  The class used for these matrices is SeqMat 
 17   
 18  Matrices are implemented as a dictionary. Each index contains a 2-tuple, 
 19  which are the two residue/nucleotide types replaced. The value differs 
 20  according to the matrix's purpose: e.g in a log-odds frequency matrix, the 
 21  value would be log(Pij/(Pi*Pj)) where: 
 22  Pij: frequency of substitution of letter (residue/nucleotide) i by j 
 23  Pi, Pj: expected frequencies of i and j, respectively. 
 24   
 25  Usage: 
 26  ------ 
 27  The following section is laid out in the order by which most people wish 
 28  to generate a log-odds matrix. Of course, interim matrices can be 
 29  generated and investigated. Most people just want a log-odds matrix, 
 30  that's all. 
 31   
 32  Generating an Accepted Replacement Matrix: 
 33  ------------------------------------------ 
 34  Initially, you should generate an accepted replacement matrix (ARM) 
 35  from your data. The values in ARM are the _counted_ number of 
 36  replacements according to your data. The data could be a set of pairs 
 37  or multiple alignments. So for instance if Alanine was replaced by 
 38  Cysteine 10 times, and Cysteine by Alanine 12 times, the corresponding 
 39  ARM entries would be: 
 40  ['A','C']: 10, 
 41  ['C','A'] 12 
 42  As order doesn't matter, user can already provide only one entry: 
 43  ['A','C']: 22 
 44  A SeqMat instance may be initialized with either a full (first 
 45  method of counting: 10, 12) or half (the latter method, 22) matrix. A 
 46  Full protein alphabet matrix would be of the size 20x20 = 400. A Half 
 47  matrix of that alphabet would be 20x20/2 + 20/2 = 210. That is because 
 48  same-letter entries don't change. (The matrix diagonal). Given an 
 49  alphabet size of N: 
 50  Full matrix size:N*N 
 51  Half matrix size: N(N+1)/2 
 52   
 53  If you provide a full matrix, the constructor will create a half-matrix 
 54  automatically. 
 55  If you provide a half-matrix, make sure of a (low, high) sorted order in 
 56  the keys: there should only be 
 57  a ('A','C') not a ('C','A'). 
 58   
 59  Internal functions: 
 60   
 61  Generating the observed frequency matrix (OFM): 
 62  ----------------------------------------------- 
 63  Use: OFM = _build_obs_freq_mat(ARM) 
 64  The OFM is generated from the ARM, only instead of replacement counts, it 
 65  contains replacement frequencies. 
 66   
 67  Generating an expected frequency matrix (EFM): 
 68  ---------------------------------------------- 
 69  Use: EFM = _build_exp_freq_mat(OFM,exp_freq_table) 
 70  exp_freq_table: should be a freqTableC instantiation. See freqTable.py for 
 71  detailed information. Briefly, the expected frequency table has the 
 72  frequencies of appearance for each member of the alphabet 
 73   
 74  Generating a substitution frequency matrix (SFM): 
 75  ------------------------------------------------- 
 76  Use: SFM = _build_subs_mat(OFM,EFM) 
 77  Accepts an OFM, EFM. Provides the division product of the corresponding 
 78  values. 
 79   
 80  Generating a log-odds matrix (LOM): 
 81  ----------------------------------- 
 82  Use: LOM=_build_log_odds_mat(SFM[,logbase=10,factor=10.0,roundit=1]) 
 83  Accepts an SFM. logbase: base of the logarithm used to generate the 
 84  log-odds values. factor: factor used to multiply the log-odds values. 
 85  roundit: default - true. Whether to round the values. 
 86  Each entry is generated by log(LOM[key])*factor 
 87  And rounded if required. 
 88   
 89  External: 
 90  --------- 
 91  In most cases, users will want to generate a log-odds matrix only, without 
 92  explicitly calling the OFM --> EFM --> SFM stages. The function 
 93  build_log_odds_matrix does that. User provides an ARM and an expected 
 94  frequency table. The function returns the log-odds matrix. 
 95   
 96  Methods for subtraction, addition and multiplication of matrices: 
 97  ----------------------------------------------------------------- 
 98  * Generation of an expected frequency table from an observed frequency 
 99    matrix. 
100  * Calculation of linear correlation coefficient between two matrices. 
101  * Calculation of relative entropy is now done using the 
102    _make_relative_entropy method and is stored in the member 
103    self.relative_entropy 
104  * Calculation of entropy is now done using the _make_entropy method and 
105    is stored in the member self.entropy. 
106  * Jensen-Shannon distance between the distributions from which the 
107    matrices are derived. This is a distance function based on the 
108    distribution's entropies. 
109  """ 
110   
111   
112  from __future__ import print_function 
113   
114  import re 
115  import sys 
116  import copy 
117  import math 
118  import warnings 
119   
120  # BioPython imports 
121  import Bio 
122  from Bio import Alphabet 
123  from Bio.SubsMat import FreqTable 
124   
125   
126  log = math.log 
127  # Matrix types 
128  NOTYPE = 0 
129  ACCREP = 1 
130  OBSFREQ = 2 
131  SUBS = 3 
132  EXPFREQ = 4 
133  LO = 5 
134  EPSILON = 0.00000000000001 
135   
136   
137 -class SeqMat(dict):
138 """A Generic sequence matrix class 139 The key is a 2-tuple containing the letter indices of the matrix. Those 140 should be sorted in the tuple (low, high). Because each matrix is dealt 141 with as a half-matrix.""" 142
143 - def _alphabet_from_matrix(self):
144 ab_dict = {} 145 s = '' 146 for i in self: 147 ab_dict[i[0]] = 1 148 ab_dict[i[1]] = 1 149 for i in sorted(ab_dict): 150 s += i 151 self.alphabet.letters = s
152
153 - def __init__(self, data=None, alphabet=None, mat_name='', build_later=0):
154 # User may supply: 155 # data: matrix itself 156 # mat_name: its name. See below. 157 # alphabet: an instance of Bio.Alphabet, or a subclass. If not 158 # supplied, constructor builds its own from that matrix. 159 # build_later: skip the matrix size assertion. User will build the 160 # matrix after creating the instance. Constructor builds a half matrix 161 # filled with zeroes. 162 163 assert isinstance(mat_name, str) 164 165 # "data" may be: 166 # 1) None --> then self.data is an empty dictionary 167 # 2) type({}) --> then self takes the items in data 168 # 3) An instance of SeqMat 169 # This whole creation-during-execution is done to avoid changing 170 # default values, the way Python does because default values are 171 # created when the function is defined, not when it is created. 172 if data: 173 try: 174 self.update(data) 175 except ValueError: 176 raise ValueError("Failed to store data in a dictionary") 177 if alphabet is None: 178 alphabet = Alphabet.Alphabet() 179 assert Alphabet.generic_alphabet.contains(alphabet) 180 self.alphabet = alphabet 181 182 # If passed alphabet is empty, use the letters in the matrix itself 183 if not self.alphabet.letters: 184 self._alphabet_from_matrix() 185 # Assert matrix size: half or full 186 if not build_later: 187 N = len(self.alphabet.letters) 188 assert len(self) == N ** 2 or len(self) == N * (N + 1) / 2 189 self.ab_list = list(self.alphabet.letters) 190 self.ab_list.sort() 191 # Names: a string like "BLOSUM62" or "PAM250" 192 self.mat_name = mat_name 193 if build_later: 194 self._init_zero() 195 else: 196 # Convert full to half 197 self._full_to_half() 198 self._correct_matrix() 199 self.sum_letters = {} 200 self.relative_entropy = 0
201
202 - def _correct_matrix(self):
203 for key in self: 204 if key[0] > key[1]: 205 self[(key[1], key[0])] = self[key] 206 del self[key]
207
208 - def _full_to_half(self):
209 """ 210 Convert a full-matrix to a half-matrix 211 """ 212 # For instance: two entries ('A','C'):13 and ('C','A'):20 will be summed 213 # into ('A','C'): 33 and the index ('C','A') will be deleted 214 # alphabet.letters:('A','A') and ('C','C') will remain the same. 215 216 N = len(self.alphabet.letters) 217 # Do nothing if this is already a half-matrix 218 if len(self) == N * (N + 1) / 2: 219 return 220 for i in self.ab_list: 221 for j in self.ab_list[:self.ab_list.index(i) + 1]: 222 if i != j: 223 self[j, i] = self[j, i] + self[i, j] 224 del self[i, j]
225
226 - def _init_zero(self):
227 for i in self.ab_list: 228 for j in self.ab_list[:self.ab_list.index(i) + 1]: 229 self[j, i] = 0.
230
231 - def make_entropy(self):
232 self.entropy = 0 233 for i in self: 234 if self[i] > EPSILON: 235 self.entropy += self[i] * log(self[i]) / log(2) 236 self.entropy = -self.entropy
237
238 - def sum(self):
239 result = {} 240 for letter in self.alphabet.letters: 241 result[letter] = 0.0 242 for pair, value in self.items(): 243 i1, i2 = pair 244 if i1 == i2: 245 result[i1] += value 246 else: 247 result[i1] += value / 2 248 result[i2] += value / 2 249 return result
250
251 - def print_full_mat(self, f=None, format="%4d", topformat="%4s", 252 alphabet=None, factor=1, non_sym=None):
253 f = f or sys.stdout 254 # create a temporary dictionary, which holds the full matrix for 255 # printing 256 assert non_sym is None or isinstance(non_sym, float) or \ 257 isinstance(non_sym, int) 258 full_mat = copy.copy(self) 259 for i in self: 260 if i[0] != i[1]: 261 full_mat[(i[1], i[0])] = full_mat[i] 262 if not alphabet: 263 alphabet = self.ab_list 264 topline = '' 265 for i in alphabet: 266 topline = topline + topformat % i 267 topline = topline + '\n' 268 f.write(topline) 269 for i in alphabet: 270 outline = i 271 for j in alphabet: 272 if alphabet.index(j) > alphabet.index(i) and non_sym is not None: 273 val = non_sym 274 else: 275 val = full_mat[i, j] 276 val *= factor 277 if val <= -999: 278 cur_str = ' ND' 279 else: 280 cur_str = format % val 281 282 outline = outline + cur_str 283 outline = outline + '\n' 284 f.write(outline)
285
286 - def print_mat(self, f=None, format="%4d", bottomformat="%4s", 287 alphabet=None, factor=1):
288 """Print a nice half-matrix. f=sys.stdout to see on the screen 289 User may pass own alphabet, which should contain all letters in the 290 alphabet of the matrix, but may be in a different order. This 291 order will be the order of the letters on the axes""" 292 293 f = f or sys.stdout 294 if not alphabet: 295 alphabet = self.ab_list 296 bottomline = '' 297 for i in alphabet: 298 bottomline = bottomline + bottomformat % i 299 bottomline = bottomline + '\n' 300 for i in alphabet: 301 outline = i 302 for j in alphabet[:alphabet.index(i) + 1]: 303 try: 304 val = self[j, i] 305 except KeyError: 306 val = self[i, j] 307 val *= factor 308 if val == -999: 309 cur_str = ' ND' 310 else: 311 cur_str = format % val 312 313 outline = outline + cur_str 314 outline = outline + '\n' 315 f.write(outline) 316 f.write(bottomline)
317
318 - def __str__(self):
319 """Print a nice half-matrix.""" 320 output = "" 321 alphabet = self.ab_list 322 n = len(alphabet) 323 for i in range(n): 324 c1 = alphabet[i] 325 output += c1 326 for j in range(i + 1): 327 c2 = alphabet[j] 328 try: 329 val = self[c2, c1] 330 except KeyError: 331 val = self[c1, c2] 332 if val == -999: 333 output += ' ND' 334 else: 335 output += "%4d" % val 336 output += '\n' 337 output += '%4s' * n % tuple(alphabet) + "\n" 338 return output
339
340 - def __sub__(self, other):
341 """ returns a number which is the subtraction product of the two matrices""" 342 mat_diff = 0 343 for i in self: 344 mat_diff += (self[i] - other[i]) 345 return mat_diff
346
347 - def __mul__(self, other):
348 """ returns a matrix for which each entry is the multiplication product of the 349 two matrices passed""" 350 new_mat = copy.copy(self) 351 for i in self: 352 new_mat[i] *= other[i] 353 return new_mat
354
355 - def __add__(self, other):
356 new_mat = copy.copy(self) 357 for i in self: 358 new_mat[i] += other[i] 359 return new_mat
360 361
362 -class AcceptedReplacementsMatrix(SeqMat):
363 """Accepted replacements matrix"""
364 365
366 -class ObservedFrequencyMatrix(SeqMat):
367 """Observed frequency matrix"""
368 369
370 -class ExpectedFrequencyMatrix(SeqMat):
371 """Expected frequency matrix"""
372 373
374 -class SubstitutionMatrix(SeqMat):
375 """Substitution matrix""" 376
377 - def calculate_relative_entropy(self, obs_freq_mat):
378 """Calculate and return the relative entropy with respect to an 379 observed frequency matrix""" 380 relative_entropy = 0. 381 for key, value in self.items(): 382 if value > EPSILON: 383 relative_entropy += obs_freq_mat[key] * log(value) 384 relative_entropy /= log(2) 385 return relative_entropy
386 387
388 -class LogOddsMatrix(SeqMat):
389 """Log odds matrix""" 390
391 - def calculate_relative_entropy(self, obs_freq_mat):
392 """Calculate and return the relative entropy with respect to an 393 observed frequency matrix""" 394 relative_entropy = 0. 395 for key, value in self.items(): 396 relative_entropy += obs_freq_mat[key] * value / log(2) 397 return relative_entropy
398 399
400 -def _build_obs_freq_mat(acc_rep_mat):
401 """ 402 build_obs_freq_mat(acc_rep_mat): 403 Build the observed frequency matrix, from an accepted replacements matrix 404 The acc_rep_mat matrix should be generated by the user. 405 """ 406 # Note: acc_rep_mat should already be a half_matrix!! 407 total = float(sum(acc_rep_mat.values())) 408 obs_freq_mat = ObservedFrequencyMatrix(alphabet=acc_rep_mat.alphabet, 409 build_later=1) 410 for i in acc_rep_mat: 411 obs_freq_mat[i] = acc_rep_mat[i] / total 412 return obs_freq_mat
413 414
415 -def _exp_freq_table_from_obs_freq(obs_freq_mat):
416 exp_freq_table = {} 417 for i in obs_freq_mat.alphabet.letters: 418 exp_freq_table[i] = 0. 419 for i in obs_freq_mat: 420 if i[0] == i[1]: 421 exp_freq_table[i[0]] += obs_freq_mat[i] 422 else: 423 exp_freq_table[i[0]] += obs_freq_mat[i] / 2. 424 exp_freq_table[i[1]] += obs_freq_mat[i] / 2. 425 return FreqTable.FreqTable(exp_freq_table, FreqTable.FREQ)
426 427
428 -def _build_exp_freq_mat(exp_freq_table):
429 """Build an expected frequency matrix 430 exp_freq_table: should be a FreqTable instance 431 """ 432 exp_freq_mat = ExpectedFrequencyMatrix(alphabet=exp_freq_table.alphabet, 433 build_later=1) 434 for i in exp_freq_mat: 435 if i[0] == i[1]: 436 exp_freq_mat[i] = exp_freq_table[i[0]] ** 2 437 else: 438 exp_freq_mat[i] = 2.0 * exp_freq_table[i[0]] * exp_freq_table[i[1]] 439 return exp_freq_mat
440 441 442 # 443 # Build the substitution matrix 444 #
445 -def _build_subs_mat(obs_freq_mat, exp_freq_mat):
446 """ Build the substitution matrix """ 447 if obs_freq_mat.ab_list != exp_freq_mat.ab_list: 448 raise ValueError("Alphabet mismatch in passed matrices") 449 subs_mat = SubstitutionMatrix(obs_freq_mat) 450 for i in obs_freq_mat: 451 subs_mat[i] = obs_freq_mat[i] / exp_freq_mat[i] 452 return subs_mat
453 454 455 # 456 # Build a log-odds matrix 457 #
458 -def _build_log_odds_mat(subs_mat, logbase=2, factor=10.0, round_digit=0, keep_nd=0):
459 """_build_log_odds_mat(subs_mat,logbase=10,factor=10.0,round_digit=1): 460 Build a log-odds matrix 461 logbase=2: base of logarithm used to build (default 2) 462 factor=10.: a factor by which each matrix entry is multiplied 463 round_digit: roundoff place after decimal point 464 keep_nd: if true, keeps the -999 value for non-determined values (for which there 465 are no substitutions in the frequency substitutions matrix). If false, plants the 466 minimum log-odds value of the matrix in entries containing -999 467 """ 468 lo_mat = LogOddsMatrix(subs_mat) 469 for key, value in subs_mat.items(): 470 if value < EPSILON: 471 lo_mat[key] = -999 472 else: 473 lo_mat[key] = round(factor * log(value) / log(logbase), round_digit) 474 mat_min = min(lo_mat.values()) 475 if not keep_nd: 476 for i in lo_mat: 477 if lo_mat[i] <= -999: 478 lo_mat[i] = mat_min 479 return lo_mat
480 481 482 # 483 # External function. User provides an accepted replacement matrix, and, 484 # optionally the following: expected frequency table, log base, mult. factor, 485 # and rounding factor. Generates a log-odds matrix, calling internal SubsMat 486 # functions. 487 #
488 -def make_log_odds_matrix(acc_rep_mat, exp_freq_table=None, logbase=2, 489 factor=1., round_digit=9, keep_nd=0):
490 obs_freq_mat = _build_obs_freq_mat(acc_rep_mat) 491 if not exp_freq_table: 492 exp_freq_table = _exp_freq_table_from_obs_freq(obs_freq_mat) 493 exp_freq_mat = _build_exp_freq_mat(exp_freq_table) 494 subs_mat = _build_subs_mat(obs_freq_mat, exp_freq_mat) 495 lo_mat = _build_log_odds_mat(subs_mat, logbase, factor, round_digit, keep_nd) 496 return lo_mat
497 498
499 -def observed_frequency_to_substitution_matrix(obs_freq_mat):
500 exp_freq_table = _exp_freq_table_from_obs_freq(obs_freq_mat) 501 exp_freq_mat = _build_exp_freq_mat(exp_freq_table) 502 subs_mat = _build_subs_mat(obs_freq_mat, exp_freq_mat) 503 return subs_mat
504 505
506 -def read_text_matrix(data_file):
507 matrix = {} 508 tmp = data_file.read().split("\n") 509 table = [] 510 for i in tmp: 511 table.append(i.split()) 512 # remove records beginning with ``#'' 513 for rec in table[:]: 514 if (rec.count('#') > 0): 515 table.remove(rec) 516 517 # remove null lists 518 while (table.count([]) > 0): 519 table.remove([]) 520 # build a dictionary 521 alphabet = table[0] 522 j = 0 523 for rec in table[1:]: 524 # print(j) 525 row = alphabet[j] 526 # row = rec[0] 527 if re.compile('[A-z\*]').match(rec[0]): 528 first_col = 1 529 else: 530 first_col = 0 531 i = 0 532 for field in rec[first_col:]: 533 col = alphabet[i] 534 matrix[(row, col)] = float(field) 535 i += 1 536 j += 1 537 # delete entries with an asterisk 538 for i in matrix: 539 if '*' in i: 540 del(matrix[i]) 541 ret_mat = SeqMat(matrix) 542 return ret_mat
543 544 diagNO = 1 545 diagONLY = 2 546 diagALL = 3 547 548
549 -def two_mat_relative_entropy(mat_1, mat_2, logbase=2, diag=diagALL):
550 rel_ent = 0. 551 key_list_1 = sorted(mat_1) 552 key_list_2 = sorted(mat_2) 553 key_list = [] 554 sum_ent_1 = 0. 555 sum_ent_2 = 0. 556 for i in key_list_1: 557 if i in key_list_2: 558 key_list.append(i) 559 if len(key_list_1) != len(key_list_2): 560 sys.stderr.write("Warning: first matrix has more entries than the second\n") 561 if key_list_1 != key_list_2: 562 sys.stderr.write("Warning: indices not the same between matrices\n") 563 for key in key_list: 564 if diag == diagNO and key[0] == key[1]: 565 continue 566 if diag == diagONLY and key[0] != key[1]: 567 continue 568 if mat_1[key] > EPSILON and mat_2[key] > EPSILON: 569 sum_ent_1 += mat_1[key] 570 sum_ent_2 += mat_2[key] 571 572 for key in key_list: 573 if diag == diagNO and key[0] == key[1]: 574 continue 575 if diag == diagONLY and key[0] != key[1]: 576 continue 577 if mat_1[key] > EPSILON and mat_2[key] > EPSILON: 578 val_1 = mat_1[key] / sum_ent_1 579 val_2 = mat_2[key] / sum_ent_2 580 # rel_ent += mat_1[key] * log(mat_1[key]/mat_2[key])/log(logbase) 581 rel_ent += val_1 * log(val_1 / val_2) / log(logbase) 582 return rel_ent
583 584 585 # Gives the linear correlation coefficient between two matrices
586 -def two_mat_correlation(mat_1, mat_2):
587 try: 588 import numpy 589 except ImportError: 590 raise ImportError("Please install Numerical Python (numpy) if you want to use this function") 591 values = [] 592 assert mat_1.ab_list == mat_2.ab_list 593 for ab_pair in mat_1: 594 try: 595 values.append((mat_1[ab_pair], mat_2[ab_pair])) 596 except KeyError: 597 raise ValueError("%s is not a common key" % ab_pair) 598 correlation_matrix = numpy.corrcoef(values, rowvar=0) 599 correlation = correlation_matrix[0, 1] 600 return correlation
601 602 603 # Jensen-Shannon Distance 604 # Need to input observed frequency matrices
605 -def two_mat_DJS(mat_1, mat_2, pi_1=0.5, pi_2=0.5):
606 assert mat_1.ab_list == mat_2.ab_list 607 assert pi_1 > 0 and pi_2 > 0 and pi_1 < 1 and pi_2 < 1 608 assert not (pi_1 + pi_2 - 1.0 > EPSILON) 609 sum_mat = SeqMat(build_later=1) 610 sum_mat.ab_list = mat_1.ab_list 611 for i in mat_1: 612 sum_mat[i] = pi_1 * mat_1[i] + pi_2 * mat_2[i] 613 sum_mat.make_entropy() 614 mat_1.make_entropy() 615 mat_2.make_entropy() 616 # print(mat_1.entropy, mat_2.entropy) 617 dJS = sum_mat.entropy - pi_1 * mat_1.entropy - pi_2 * mat_2.entropy 618 return dJS
619 620 """ 621 This isn't working yet. Boo hoo! 622 def two_mat_print(mat_1, mat_2, f=None, alphabet=None, factor_1=1, factor_2=1, 623 format="%4d", bottomformat="%4s", topformat="%4s", 624 topindent=7*" ", bottomindent=1*" "): 625 f = f or sys.stdout 626 if not alphabet: 627 assert mat_1.ab_list == mat_2.ab_list 628 alphabet = mat_1.ab_list 629 len_alphabet = len(alphabet) 630 print_mat = {} 631 topline = topindent 632 bottomline = bottomindent 633 for i in alphabet: 634 bottomline += bottomformat % i 635 topline += topformat % alphabet[len_alphabet-alphabet.index(i)-1] 636 topline += '\n' 637 bottomline += '\n' 638 f.write(topline) 639 for i in alphabet: 640 for j in alphabet: 641 print_mat[i, j] = -999 642 diag_1 = {} 643 diag_2 = {} 644 for i in alphabet: 645 for j in alphabet[:alphabet.index(i)+1]: 646 if i == j: 647 diag_1[i] = mat_1[(i, i)] 648 diag_2[i] = mat_2[(alphabet[len_alphabet-alphabet.index(i)-1], 649 alphabet[len_alphabet-alphabet.index(i)-1])] 650 else: 651 if i > j: 652 key = (j, i) 653 else: 654 key = (i, j) 655 mat_2_key = [alphabet[len_alphabet-alphabet.index(key[0])-1], 656 alphabet[len_alphabet-alphabet.index(key[1])-1]] 657 # print(mat_2_key) 658 mat_2_key.sort() 659 mat_2_key = tuple(mat_2_key) 660 # print("%s||%s" % (key, mat_2_key) 661 print_mat[key] = mat_2[mat_2_key] 662 print_mat[(key[1], key[0])] = mat_1[key] 663 for i in alphabet: 664 outline = i 665 for j in alphabet: 666 if i == j: 667 if diag_1[i] == -999: 668 val_1 = ' ND' 669 else: 670 val_1 = format % (diag_1[i]*factor_1) 671 if diag_2[i] == -999: 672 val_2 = ' ND' 673 else: 674 val_2 = format % (diag_2[i]*factor_2) 675 cur_str = val_1 + " " + val_2 676 else: 677 if print_mat[(i, j)] == -999: 678 val = ' ND' 679 elif alphabet.index(i) > alphabet.index(j): 680 val = format % (print_mat[(i, j)]*factor_1) 681 else: 682 val = format % (print_mat[(i, j)]*factor_2) 683 cur_str = val 684 outline += cur_str 685 outline += bottomformat % (alphabet[len_alphabet-alphabet.index(i)-1] + 686 '\n') 687 f.write(outline) 688 f.write(bottomline) 689 """ 690