forked from sokrypton/ColabFold
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdata_engine.py
More file actions
238 lines (198 loc) · 8.03 KB
/
Copy pathdata_engine.py
File metadata and controls
238 lines (198 loc) · 8.03 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
"""
Luke Cirne
Data Engine for ColabFold Wrapper
Functions for graphing and data analysis
Ma Lab
"""
import matplotlib.pyplot as plt
from matplotlib import ticker
import pandas as pd
import numpy as np
import random
import os
from collections import defaultdict
def compute_E(distances, R_0=51):
for i in range(len(distances)):
distances[i] = float(1 / (1 + (distances[i] / R_0)**6))
return distances
def graph_output_accuracy(efficiencies: dict, bins=0.025, graph_name=None, N=None) -> str:
# Collect and convert distances
effs = np.array([float(d) for d in efficiencies.values()])
total_strucs = len(effs)
if not N:
N = total_strucs
# If bins is a float, treat it as bin width and generate edges
if isinstance(bins, float) or isinstance(bins, int):
min_d = effs.min()
max_d = effs.max()
bin_edges = np.arange(min_d, max_d + bins, bins)
bin_edges = np.arange(0, 1 + bins, bins)
else:
# If bins is an array (from build_distribution), use it directly
bin_edges = bins
# Debug
#print(effs.min(), effs.max())
#print(bin_edges[:5], bin_edges[-5:])
# Plot
plt.figure(figsize=(8, 5))
plt.hist(effs, bins=bin_edges, edgecolor="black", color="skyblue", label=f"Structures per Efficiency\ntotal structures: {total_strucs}")
plt.title("CF Output Structures Separated by FRET Efficiency")
plt.xlabel("FRET Efficiency")
plt.ylabel("Frequency")
plt.legend(title=f"N: {N}")
plt.xticks(0, 1, 0.05)
plt.tight_layout()
# Gaussian curve (same x-range as histogram)
y_exp = 0.291 # mean
sigma = 0.083 # stdev
x = np.linspace(bin_edges.min(), bin_edges.max(), 500)
gaussian = (1 / (sigma * np.sqrt(2 * np.pi))) * np.exp(-0.5 * ((x - y_exp) / sigma) ** 2)
# Scale to match histogram frequency
gaussian_scaled = gaussian * len(effs) * (bin_edges[1] - bin_edges[0])
plt.plot(x, gaussian_scaled, color="red", linewidth=2, label="Ideal Gaussian")
# Save
plot_name = "iteration_distances_hist"
if graph_name:
plot_name = graph_name
plt.savefig(f"{plot_name}.png")
return plot_name
def graph_output_accuracy_bar(efficiencies: dict, bins=0.0083, graph_name=None, N=None) -> str:
"""
Plots a bar chart where each bar corresponds to a histogram bin.
X values are bin centers, and Y values are counts of efficiencies in each bin.
"""
# Convert dictionary values to numpy array
filenames = np.array(list(efficiencies.keys()))
effs = np.array([float(d) for d in efficiencies.values()])
total_strucs = len(effs)
if not N:
N = total_strucs
# Determine bin edges and centers
if isinstance(bins, float) or isinstance(bins, int):
min_d = effs.min()
max_d = effs.max()
bin_edges = np.arange(min_d, max_d + bins, bins)
bin_centers = bin_edges[:-1] + bins / 2
else:
# If bins provided as array
bin_edges = bins
bin_centers = bin_edges[:-1] + (bin_edges[1] - bin_edges[0]) / 2
print("===== GRAPH DEBUG =====")
print("Number of efficiencies:", len(effs))
print("Efficiencies:")
print(effs)
print("Bin edges:")
print(bin_edges)
# Count unique and duplicated files in each bin separately
dupe_mask = np.char.find(np.char.lower(filenames.astype(str)), "dupe") >= 0
unique_counts, _ = np.histogram(effs[~dupe_mask], bins=bin_edges)
dupe_counts, _ = np.histogram(effs[dupe_mask], bins=bin_edges)
print("Unique histogram counts:")
print(unique_counts)
print("Duplicate histogram counts:")
print(dupe_counts)
print("=======================")
# --- Plot ---
plt.figure(figsize=(8, 5))
plt.bar(bin_centers, unique_counts, width=(bin_edges[1] - bin_edges[0]) * 0.9,
color="mediumseagreen", edgecolor="black", label="Unique PDB files")
plt.bar(bin_centers, dupe_counts, width=(bin_edges[1] - bin_edges[0]) * 0.9,
bottom=unique_counts, color="coral", edgecolor="black", label="Duplicate PDB files")
plt.title("CF Output Structures Separated by FRET Efficiency")
plt.xlabel("FRET Efficiency")
plt.ylabel("Frequency")
plt.legend(title=f"N: {N}")
# Set x-axis ticks
#xticks = np.arange(bin_edges.min(), bin_edges.max() + 0.1, 0.1)
xticks = np.arange(0, 1+0.1, 0.1)
plt.xticks(xticks)
plt.tight_layout()
# --- Save ---
plot_name = "iteration_distances_bar"
if graph_name:
plot_name = graph_name
plt.savefig(f"{plot_name}.png")
return plot_name
def build_distribution(
file_eff_dict: dict,
mean: float,
std: float,
bin_width: float = 0.0083,
seed: int = None,
n: int = None
) -> dict:
"""
Selects file-efficiency pairs such that their histogram best follows a Gaussian distribution defined by the provided mean and standard deviation.
Parameters:
file_eff_dict : dict
Dictionary of {filename: efficiency}, where efficiency is a float.
mean : float
Mean value for the target Gaussian distribution.
std : float
Standard deviation for the target Gaussian distribution.
bin_width : float, optional
Width of histogram bins. Default is 5.
seed : int, optional
Random seed for reproducibility. Default is 42.
Returns
dict: Dictionary of {filename: efficiency} containing the selected
file-efficiency pairs adjusted to match the Gaussian distribution.
"""
# Set seeds for reproducibility
if seed is not None:
np.random.seed(seed)
random.seed(seed)
# Extract efficiencies
efficiencies = np.array(list(file_eff_dict.values()))
filenames = np.array(list(file_eff_dict.keys()))
# N represents the number of structures that will be returned
N = n if n else len(efficiencies)
# Define bin edges across observed range
min_val, max_val = efficiencies.min(), efficiencies.max()
#print(f"min_val: {min_val} max_val: {max_val}")
#bins = np.arange(min_val, max_val + bin_width, bin_width)
bins = np.arange(0, 1+bin_width, bin_width)
# Bin assignments for each efficiency
bin_indices = np.digitize(efficiencies, bins) - 1
print(f"bin_indices: {bin_indices}")
# Compute bin centers
bin_centers = bins[:-1] + bin_width / 2
# --- Compute Gaussian-based target counts ---
gauss_probs = np.exp(-0.5 * ((bin_centers - mean) / std) ** 2)
gauss_probs /= gauss_probs.sum() # normalize
target_counts = np.round(gauss_probs * N).astype(int)
# Group files by bin
bin_to_files = defaultdict(list)
for fname, eff, bidx in zip(filenames, efficiencies, bin_indices):
bin_to_files[bidx].append((fname, eff))
# Collect selected filename-efficiency pairs
selected = {}
dupe_counter = defaultdict(int)
mod_count = 0
for bidx, desired_count in enumerate(target_counts):
#print(f"desired_count: {desired_count}")
available_files = bin_to_files.get(bidx, [])
if desired_count == 0 or len(available_files) == 0:
continue
if len(available_files) >= desired_count:
# Too many files, sample down
chosen = random.sample(available_files, desired_count)
mod_count += len(available_files) - desired_count
else:
# Too few files, duplicate as needed
multiplier = -(-desired_count // len(available_files)) # ceiling division
extended = available_files * multiplier
chosen = random.sample(extended, desired_count)
mod_count += desired_count - len(available_files)
# Add chosen pairs with dupe suffixes if needed
for fname, eff in chosen:
if fname in selected:
dupe_counter[fname] += 1
name, ext = os.path.splitext(fname)
new_fname = f"{name}_dupe{dupe_counter[fname]}{ext}"
selected[new_fname] = eff
else:
selected[fname] = eff
#print(len(chosen))
#print(len(selected))
return selected, bins, bin_centers, mod_count