Skip to content

Commit bd5debe

Browse files
authored
Merge pull request #3 from agitter/ccalia-contacts
Add Paesani lab contact analysis
2 parents 54244ce + 32c98b4 commit bd5debe

22 files changed

Lines changed: 9819 additions & 0 deletions

File tree

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
import os, csv
2+
import numpy as np
3+
import matplotlib.pyplot as plt
4+
5+
6+
#################### Parse csv file
7+
8+
with open('../extended_data/all_submissions.csv', 'r') as csvfile:
9+
csv_lines = [line for line in csvfile]
10+
11+
csv_lines = csv_lines[1:]
12+
13+
csv_lines = csv.reader(csv_lines, quotechar='"', delimiter=',', quoting=csv.QUOTE_ALL, skipinitialspace=True)
14+
csv_lines = [l for l in csv_lines]
15+
16+
17+
#From target_residue_list in all_submissions.csv
18+
def contacts_TF_list_from_csv_list(clist):
19+
true_false_list = [True if (i + 1) in clist else False for i in range(621)] #True/False for all res in EGFR. True if it's in contact with binder design
20+
return np.array(true_false_list)
21+
22+
23+
class Binder_csv:
24+
def __init__(self, csvline):
25+
self.csvline = csvline
26+
self.id = csvline[0]
27+
self.round = int(csvline[1])
28+
self.selected = csvline[3]
29+
if self.selected == 'No':
30+
self.binding = 'Not tested'
31+
else:
32+
self.binding = csvline[12]
33+
if len(csvline[52]) == 2:
34+
self.target_res_list = []
35+
else:
36+
self.target_res_list = [int(i) for i in csvline[52][1:-1].split(', ')]
37+
self.target_contacts = contacts_TF_list_from_csv_list(self.target_res_list)
38+
self.method = csvline[7]
39+
40+
41+
def contact_tf_matrix_to_chimera_file(tfmatrix, attrfile):
42+
num_designs = len(tfmatrix)
43+
tfmatrix = tfmatrix.sum(axis = 0)
44+
full_contacts_sum_norm = tfmatrix / num_designs
45+
header_lines = ['#\n', '# Binder contact frequency to map onto EGFR\n', '#\n', '# From Adaptyv Bio Protein Design Competition (all_submissions.csv)\n', '#\n', '# Use this file to assign the attribute in Chimera with the\n', '# Define Attribute tool or the command defattr.\n', '#\n', 'attribute: contactfreq\n', 'match mode: 1-to-1\n', 'recipient: residues\n']
46+
data_lines = [f' :{i + 1} {full_contacts_sum_norm[i]}\n' for i in range(len(full_contacts_sum_norm))]
47+
with open(attrfile, 'w') as outfile:
48+
for line in header_lines:
49+
outfile.write(line)
50+
for line in data_lines:
51+
outfile.write(line)
52+
53+
54+
binders = [Binder_csv(line) for line in csv_lines]
55+
56+
57+
58+
all_submissions_contact_tf_matrix = np.array([b.target_contacts for b in binders])
59+
60+
round1_submissions_contact_tf_matrix = np.array([b.target_contacts for b in binders if b.round == 1])
61+
62+
round2_submissions_contact_tf_matrix = np.array([b.target_contacts for b in binders if b.round == 2])
63+
64+
successful_binders_contact_tf_matrix = np.array([b.target_contacts for b in binders if b.binding in ['Strong', 'Medium', 'Weak']])
65+
66+
nonbinders_contact_tf_matrix = np.array([b.target_contacts for b in binders if b.binding == 'None'])
67+
68+
69+
print(f'All: {len(all_submissions_contact_tf_matrix)}')
70+
print(f'Round 1: {len(round1_submissions_contact_tf_matrix)}')
71+
print(f'Round 2: {len(round2_submissions_contact_tf_matrix)}')
72+
print(f'Binders: {len(successful_binders_contact_tf_matrix)}')
73+
print(f'Non-binders: {len(nonbinders_contact_tf_matrix)}')
74+
75+
76+
contact_tf_matrix_to_chimera_file(all_submissions_contact_tf_matrix, 'contact_freq_from_csv_all_submissions.txt')
77+
78+
contact_tf_matrix_to_chimera_file(round1_submissions_contact_tf_matrix, 'contact_freq_from_csv_round1_submissions.txt')
79+
80+
contact_tf_matrix_to_chimera_file(round2_submissions_contact_tf_matrix, 'contact_freq_from_csv_round2_submissions.txt')
81+
82+
contact_tf_matrix_to_chimera_file(successful_binders_contact_tf_matrix, 'contact_freq_from_csv_successful_binders_both_rounds.txt')
83+
84+
contact_tf_matrix_to_chimera_file(nonbinders_contact_tf_matrix, 'contact_freq_from_csv_nonbinders_both_rounds.txt')
85+
86+
87+
#################### Split by design method:
88+
89+
successful_binders_OPTDIV_contact_tf_matrix = np.array([b.target_contacts for b in binders if ((b.binding in ['Strong', 'Medium', 'Weak']) and (b.method in ['Optimized binder', 'Diversified binder']))])
90+
91+
nonbinders_OPTDIV_contact_tf_matrix = np.array([b.target_contacts for b in binders if ((b.binding == 'None') and (b.method in ['Optimized binder', 'Diversified binder']))])
92+
93+
94+
successful_binders_DENOVOHAL_contact_tf_matrix = np.array([b.target_contacts for b in binders if ((b.binding in ['Strong', 'Medium', 'Weak']) and (b.method in ['De novo', 'Hallucination']))])
95+
96+
nonbinders_DENOVOHAL_contact_tf_matrix = np.array([b.target_contacts for b in binders if ((b.binding == 'None') and (b.method in ['De novo', 'Hallucination']))])
97+
98+
99+
successful_binders_OTHER_contact_tf_matrix = np.array([b.target_contacts for b in binders if ((b.binding in ['Strong', 'Medium', 'Weak']) and (b.method not in ['Optimized binder', 'Diversified binder', 'De novo', 'Hallucination']))])
100+
101+
nonbinders_OTHER_contact_tf_matrix = np.array([b.target_contacts for b in binders if ((b.binding == 'None') and (b.method not in ['Optimized binder', 'Diversified binder', 'De novo', 'Hallucination']))])
102+
103+
104+
Unknown_contact_tf_matrix = np.array([b.target_contacts for b in binders if (b.binding == 'Unknown')])
105+
106+
#Split by method, just designs where binding success/failure is known (i.e. they were expressed successfully):
107+
all_expressed_OPTDIV_contact_tf_matrix = np.array([b.target_contacts for b in binders if ((b.method in ['Optimized binder', 'Diversified binder']) and (b.binding in ['Strong', 'Medium', 'Weak', 'None']))])
108+
109+
all_expressed_DENOVOHAL_contact_tf_matrix = np.array([b.target_contacts for b in binders if ((b.method in ['De novo', 'Hallucination']) and (b.binding in ['Strong', 'Medium', 'Weak', 'None']))])
110+
111+
112+
print(f'All expressed OPTDIV: {len(all_expressed_OPTDIV_contact_tf_matrix)}')
113+
print(f'All expressed DENOVOHAL: {len(all_expressed_DENOVOHAL_contact_tf_matrix)}')
114+
print(f'OPTDIV binders: {len(successful_binders_OPTDIV_contact_tf_matrix)}')
115+
print(f'OPTDIV non-binders: {len(nonbinders_OPTDIV_contact_tf_matrix)}')
116+
print(f'DENOVOHAL binders: {len(successful_binders_DENOVOHAL_contact_tf_matrix)}')
117+
print(f'DENOVOHAL non-binders: {len(nonbinders_DENOVOHAL_contact_tf_matrix)}')
118+
print(f'OTHER binders: {len(successful_binders_OTHER_contact_tf_matrix)}')
119+
print(f'OTHER non-binders: {len(nonbinders_OTHER_contact_tf_matrix)}')
120+
print(f'Unknown binding: {len(Unknown_contact_tf_matrix)}')
121+
122+
123+
contact_tf_matrix_to_chimera_file(successful_binders_OPTDIV_contact_tf_matrix, 'contact_freq_from_csv_successful_binders_OPTIMIZED_DIVERSIFIED.txt')
124+
125+
contact_tf_matrix_to_chimera_file(nonbinders_OPTDIV_contact_tf_matrix, 'contact_freq_from_csv_nonbinders_OPTIMIZED_DIVERSIFIED.txt')
126+
127+
contact_tf_matrix_to_chimera_file(successful_binders_DENOVOHAL_contact_tf_matrix, 'contact_freq_from_csv_successful_binders_DENOVO_HALLUCINATION.txt')
128+
129+
contact_tf_matrix_to_chimera_file(nonbinders_DENOVOHAL_contact_tf_matrix, 'contact_freq_from_csv_nonbinders_DENOVO_HALLUCINATION.txt')
130+
131+
contact_tf_matrix_to_chimera_file(all_expressed_OPTDIV_contact_tf_matrix, 'contact_freq_from_csv_all_expressed_OPTIMIZED_DIVERSIFIED.txt')
132+
133+
contact_tf_matrix_to_chimera_file(all_expressed_DENOVOHAL_contact_tf_matrix, 'contact_freq_from_csv_all_expressed_DENOVO_HALLUCINATION.txt')
134+
135+
136+
137+
#################### Contact freq difference (binders - non-binders):
138+
139+
140+
def parse_Chimera_file(chimerafile):
141+
with open(chimerafile, 'r') as cfile:
142+
datalines = [line for line in cfile]
143+
datalines = datalines[11:]
144+
res = [float(line.split()[0].strip(':')) for line in datalines]
145+
cf = [float(line.split()[1].strip()) for line in datalines]
146+
return res, cf
147+
148+
149+
def write_diff_file(chimerafile1, chimerafile2, output_chimera_attribute_file):
150+
reslist, cf1 = parse_Chimera_file(chimerafile1)
151+
_, cf2 = parse_Chimera_file(chimerafile2)
152+
difflist = [(cf1[i] - cf2[i]) for i in range(len(cf1))]
153+
header_lines = ['#\n', '# Binder contact frequency difference to map onto EGFR\n', '#\n', '# From Adaptyv Bio Protein Design Competition (all_submissions.csv)\n', '#\n', '# Use this file to assign the attribute in Chimera with the\n', '# Define Attribute tool or the command defattr.\n', '#\n', 'attribute: contactfreq\n', 'match mode: 1-to-1\n', 'recipient: residues\n']
154+
data_lines = [f' :{int(reslist[i])} {difflist[i]}\n' for i in range(len(difflist))]
155+
with open(output_chimera_attribute_file, 'w') as outfile:
156+
for line in header_lines:
157+
outfile.write(line)
158+
for line in data_lines:
159+
outfile.write(line)
160+
161+
162+
write_diff_file('contact_freq_from_csv_successful_binders_both_rounds.txt', 'contact_freq_from_csv_nonbinders_both_rounds.txt', 'contact_freq_diff_round1and2_binders_minus_nonbinders_from_csv.txt')
163+
164+
write_diff_file('contact_freq_from_csv_successful_binders_OPTIMIZED_DIVERSIFIED.txt', 'contact_freq_from_csv_nonbinders_OPTIMIZED_DIVERSIFIED.txt', 'contact_freq_diff_from_csv_OPTIMIZED_DIVERSIFIED_binders_minus_nonbinders.txt')
165+
166+
write_diff_file('contact_freq_from_csv_successful_binders_DENOVO_HALLUCINATION.txt', 'contact_freq_from_csv_nonbinders_DENOVO_HALLUCINATION.txt', 'contact_freq_diff_from_csv_DENOVO_HALLUCINATION_binders_minus_nonbinders.txt')
167+
168+
#To see which contacts are enriched in optimized/diversified designs relative to de novo/hallucinated designs, among those where binding success/failure is known so we can use this to interpret the binders - non-binders plot
169+
write_diff_file('contact_freq_from_csv_all_expressed_OPTIMIZED_DIVERSIFIED.txt', 'contact_freq_from_csv_all_expressed_DENOVO_HALLUCINATION.txt', 'contact_freq_diff_from_csv_all_expressed_OPTDIV_minus_DENOVOHAL.txt')
170+
171+
172+
res, cf_diff_csv = parse_Chimera_file('contact_freq_diff_round1and2_binders_minus_nonbinders_from_csv.txt')
173+
174+
_, cf_diff_csv_optdiv = parse_Chimera_file('contact_freq_diff_from_csv_OPTIMIZED_DIVERSIFIED_binders_minus_nonbinders.txt')
175+
176+
_, cf_diff_csv_denovohal = parse_Chimera_file('contact_freq_diff_from_csv_DENOVO_HALLUCINATION_binders_minus_nonbinders.txt')
177+
178+
_, cf_diff_csv_optdiv_vs_denovohal = parse_Chimera_file('contact_freq_diff_from_csv_all_expressed_OPTDIV_minus_DENOVOHAL.txt')
179+
180+
181+
fig, ax = plt.subplots(figsize=(9,3))
182+
ax.scatter(res, cf_diff_csv_denovohal, marker = 'o', s = 5.0, color = 'black')
183+
ax.axvspan(0.5, 165.5, facecolor='#ebebeb', edgecolor = '#ffffff00', zorder = -10)
184+
ax.axvspan(310.5, 480.5, facecolor='#ebebeb', edgecolor = '#ffffff00', zorder = -10)
185+
ax.set_title('Contact Frequency Difference (Binders - Non-Binders), De Novo/Hallucination', fontweight='bold')
186+
ax.set_xlabel('Residue')
187+
ax.set_ylabel('Difference')
188+
#ax.set_ylim(l, u)
189+
plt.subplots_adjust(bottom=0.15)
190+
plt.savefig('contact_freq_diff_plot_round1and2_DENOVO_HALLUCINATION_binders_minus_nonbinders_from_csv_no_epitope.png', format='png', dpi=600)
191+
192+
l, u = plt.ylim()
193+
194+
195+
fig, ax = plt.subplots(figsize=(9,3))
196+
ax.scatter(res, cf_diff_csv_optdiv, marker = 'o', s = 5.0, color = 'black')
197+
ax.axvspan(0.5, 165.5, facecolor='#ebebeb', edgecolor = '#ffffff00', zorder = -10)
198+
ax.axvspan(310.5, 480.5, facecolor='#ebebeb', edgecolor = '#ffffff00', zorder = -10)
199+
ax.set_title('Contact Frequency Difference (Binders - Non-Binders), Optimized/Diversified', fontweight='bold')
200+
ax.set_xlabel('Residue')
201+
ax.set_ylabel('Difference')
202+
ax.set_ylim(l, u)
203+
plt.subplots_adjust(bottom=0.15)
204+
plt.savefig('contact_freq_diff_plot_round1and2_OPTIMIZED_DIVERSIFIED_binders_minus_nonbinders_from_csv_no_epitope.png', format='png', dpi=600)
205+
206+
207+
fig, ax = plt.subplots(figsize=(9,3))
208+
ax.scatter(res, cf_diff_csv, marker = 'o', s = 5.0, color = 'black')
209+
ax.axvspan(0.5, 165.5, facecolor='#ebebeb', edgecolor = '#ffffff00', zorder = -10)
210+
ax.axvspan(310.5, 480.5, facecolor='#ebebeb', edgecolor = '#ffffff00', zorder = -10)
211+
ax.set_title('Contact Frequency Difference (Binders - Non-Binders), All Methods', fontweight='bold')
212+
ax.set_xlabel('Residue')
213+
ax.set_ylabel('Difference')
214+
ax.set_ylim(l, u)
215+
plt.subplots_adjust(bottom=0.15)
216+
plt.savefig('contact_freq_diff_plot_round1and2_binders_minus_nonbinders_from_csv_no_epitope_ylim_matching_method_comparisons.png', format='png', dpi=600)
217+
218+
219+
fig, ax = plt.subplots(figsize=(9,3))
220+
ax.scatter(res, cf_diff_csv_optdiv_vs_denovohal, marker = 'o', s = 5.0, color = 'black')
221+
ax.axvspan(0.5, 165.5, facecolor='#ebebeb', edgecolor = '#ffffff00', zorder = -10)
222+
ax.axvspan(310.5, 480.5, facecolor='#ebebeb', edgecolor = '#ffffff00', zorder = -10)
223+
ax.set_title('Contact Frequency Difference (Optimized/Diversified - De Novo/Hallucination)', fontweight='bold')
224+
ax.set_xlabel('Residue')
225+
ax.set_ylabel('Difference')
226+
ax.set_ylim(l, u)
227+
plt.subplots_adjust(bottom=0.15)
228+
plt.savefig('contact_freq_diff_plot_round1and2_OPTDIV_minus_DENOVOHAL_all_expressed_from_csv_no_epitope.png', format='png', dpi=600)
229+
230+
231+
232+
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import os, csv
2+
import numpy as np
3+
import matplotlib.pyplot as plt
4+
from matplotlib.colors import LinearSegmentedColormap
5+
6+
7+
#################### Parse csv file
8+
9+
with open('../../extended_data/all_submissions.csv', 'r') as csvfile:
10+
csv_lines = [line for line in csvfile]
11+
12+
csv_lines = csv_lines[1:]
13+
14+
csv_lines = csv.reader(csv_lines, quotechar='"', delimiter=',', quoting=csv.QUOTE_ALL, skipinitialspace=True)
15+
csv_lines = [l for l in csv_lines]
16+
17+
18+
#From target_residue_list in all_submissions.csv
19+
def contacts_TF_list_from_csv_list(clist):
20+
true_false_list = [True if (i + 1) in clist else False for i in range(621)] #True/False for all res in EGFR. True if it's in contact with binder design
21+
return np.array(true_false_list)
22+
23+
24+
class Binder_csv:
25+
def __init__(self, csvline):
26+
self.csvline = csvline
27+
self.id = csvline[0]
28+
self.round = int(csvline[1])
29+
self.selected = csvline[3]
30+
if self.selected == 'No':
31+
self.binding = 'Not tested'
32+
else:
33+
self.binding = csvline[12]
34+
if len(csvline[52]) == 2:
35+
self.target_res_list = []
36+
else:
37+
self.target_res_list = [int(i) for i in csvline[52][1:-1].split(', ')]
38+
self.target_contacts = contacts_TF_list_from_csv_list(self.target_res_list)
39+
self.method = csvline[7]
40+
41+
42+
def contact_tf_matrix_to_chimera_file(tfmatrix, attrfile):
43+
num_designs = len(tfmatrix)
44+
tfmatrix = tfmatrix.sum(axis = 0)
45+
full_contacts_sum_norm = tfmatrix / num_designs
46+
header_lines = ['#\n', '# Binder contact frequency to map onto EGFR\n', '#\n', '# From Adaptyv Bio Protein Design Competition (all_submissions.csv)\n', '#\n', '# Use this file to assign the attribute in Chimera with the\n', '# Define Attribute tool or the command defattr.\n', '#\n', 'attribute: contactfreq\n', 'match mode: 1-to-1\n', 'recipient: residues\n']
47+
data_lines = [f' :{i + 1} {full_contacts_sum_norm[i]}\n' for i in range(len(full_contacts_sum_norm))]
48+
with open(attrfile, 'w') as outfile:
49+
for line in header_lines:
50+
outfile.write(line)
51+
for line in data_lines:
52+
outfile.write(line)
53+
54+
55+
binders = [Binder_csv(line) for line in csv_lines]
56+
57+
58+
successful_binders_contact_tf_matrix = np.array([b.target_contacts for b in binders if b.binding in ['Strong', 'Medium', 'Weak']])
59+
60+
nonbinders_contact_tf_matrix = np.array([b.target_contacts for b in binders if b.binding == 'None'])
61+
62+
63+
64+
print(f'Binders: {len(successful_binders_contact_tf_matrix)}')
65+
print(f'Non-binders: {len(nonbinders_contact_tf_matrix)}')
66+
67+
68+
#num structures with both target res i and j contacted out of total num structures in group
69+
def pairwise_cf(i, j, tfmatrix):
70+
tfmatrix_ij_vector = [(row[i] and row[j]) for row in tfmatrix]
71+
return np.sum(tfmatrix_ij_vector) / len(tfmatrix_ij_vector)
72+
73+
74+
def contact_tf_matrix_to_pairwise_contactfreq_matrix(tfmatrix):
75+
pair_cf_matrix = [[pairwise_cf(i, j, tfmatrix) for j in range(len(tfmatrix[0]))] for i in range(len(tfmatrix[0]))]
76+
return np.array(pair_cf_matrix)
77+
78+
79+
successful_binders_contact_pairwise_matrix = contact_tf_matrix_to_pairwise_contactfreq_matrix(successful_binders_contact_tf_matrix)
80+
81+
nonbinders_contact_pairwise_matrix = contact_tf_matrix_to_pairwise_contactfreq_matrix(nonbinders_contact_tf_matrix)
82+
83+
84+
pairwise_contact_freq_diff = successful_binders_contact_pairwise_matrix - nonbinders_contact_pairwise_matrix
85+
86+
87+
#colormap:
88+
89+
mycmap = LinearSegmentedColormap.from_list('mycmap', (
90+
(0.0, (1, 0.7879051733, 0.63241797799)),
91+
(0.272928598702, (1, 1, 1)),
92+
(1.0, (0.0207754808187, 0.397776920703, 0.73658860434))))
93+
94+
95+
plt.figure(figsize = (9, 9))
96+
plt.imshow(pairwise_contact_freq_diff, cmap = mycmap)
97+
plt.title('Pairwise Contact Frequency Difference (Binders - Non-Binders)\n All Methods', fontweight='bold')
98+
plt.xlabel('Residue i')
99+
plt.ylabel('Residue j')
100+
plt.xticks([99, 199, 299, 399, 499, 599], ['100', '200', '300', '400', '500', '600'])
101+
plt.yticks([99, 199, 299, 399, 499, 599], ['100', '200', '300', '400', '500', '600'])
102+
plt.colorbar()
103+
104+
plt.savefig('pairwise_contact_freq_diff_plot_round1and2_binders_minus_nonbinders_from_csv.png', format='png', dpi=600)
105+
106+
107+
372 KB
Loading

0 commit comments

Comments
 (0)