Dmtlant commited on
Commit
30998ff
·
verified ·
1 Parent(s): f1e60b9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -48
app.py CHANGED
@@ -4,16 +4,21 @@ import matplotlib.pyplot as plt
4
  import random
5
  from scipy.stats import entropy as scipy_entropy
6
  import time
7
- from matplotlib.animation import FuncAnimation
8
 
9
  # --- НАСТРОЙКИ ---
 
10
  seqlen = 60
11
  steps = 120
 
12
  min_run, max_run = 1, 2
 
13
  ANGLE_MAP = {'A': 60.0, 'C': 180.0, 'G': -60.0, 'T': -180.0, 'N': 0.0}
 
14
  bases = ['A', 'C', 'G', 'T']
15
 
16
- # Функции остаются такими же
 
 
17
  def find_local_min_runs(profile, min_run=1, max_run=2):
18
  result = []
19
  N = len(profile)
@@ -28,8 +33,10 @@ def find_local_min_runs(profile, min_run=1, max_run=2):
28
  i += run_length
29
  return result
30
 
 
31
  def bio_mutate(seq):
32
  r = random.random()
 
33
  if r < 0.70:
34
  idx = random.randint(0, len(seq)-1)
35
  orig = seq[idx]
@@ -41,17 +48,20 @@ def bio_mutate(seq):
41
  else:
42
  newbase = random.choice([b for b in bases if b != orig])
43
  seq = seq[:idx] + newbase + seq[idx+1:]
 
44
  elif r < 0.80:
45
  idx = random.randint(0, len(seq)-1)
46
  ins = ''.join(random.choices(bases, k=random.randint(1, 3)))
47
  seq = seq[:idx] + ins + seq[idx:]
48
  if len(seq) > seqlen:
49
  seq = seq[:seqlen]
 
50
  elif r < 0.90:
51
  if len(seq) > 4:
52
  idx = random.randint(0, len(seq)-2)
53
  dell = random.randint(1, min(3, len(seq)-idx))
54
  seq = seq[:idx] + seq[idx+dell:]
 
55
  else:
56
  if len(seq) > 10:
57
  start = random.randint(0, len(seq)-6)
@@ -59,87 +69,94 @@ def bio_mutate(seq):
59
  subseq = seq[start:end]
60
  subseq = subseq[::-1]
61
  seq = seq[:start] + subseq + seq[end:]
 
62
  while len(seq) < seqlen:
63
  seq += random.choice(bases)
 
64
  if len(seq) > seqlen:
65
  seq = seq[:seqlen]
66
  return seq
67
 
 
68
  def compute_autocorr(profile):
69
  profile = profile - np.mean(profile)
70
  result = np.correlate(profile, profile, mode='full')
71
  result = result[result.size // 2:]
72
- norm = np.max(result) if np.max(result)!=0 else 1
73
  return result[:10]/norm
74
 
 
75
  def compute_entropy(profile):
76
  vals, counts = np.unique(profile, return_counts=True)
77
  p = counts / counts.sum()
78
  return scipy_entropy(p, base=2)
79
 
80
- # --- Streamlit интерфейс ---
 
81
  st.title("🧬 Эволюция ДНК-подобной последовательности")
82
  st.markdown("Модель визуализирует мутации и анализирует структуру последовательности во времени.")
83
 
84
- # Кнопка запуска симуляции
85
  if st.button("▶️ Запустить симуляцию"):
 
86
  seq = ''.join(random.choices(bases, k=seqlen))
 
87
  stat_bist_counts = []
88
  stat_entropy = []
89
  stat_autocorr = []
90
 
 
91
  plot_placeholder = st.empty()
92
 
93
- # Создание фигуры и осей
94
- fig, axs = plt.subplots(3, 1, figsize=(10, 8))
95
- plt.subplots_adjust(hspace=0.45)
96
-
97
- # Начальная инициализация для графиков
98
- line1, = axs[0].plot([], [], color='royalblue', label="Торсионный угол")
99
- runs_patch = axs[0].axvspan(0, 0, color="red", alpha=0.3)
100
- line2, = axs[1].plot([], [], '-o', color='crimson', markersize=4)
101
- bar = axs[2].bar([], [], color='teal', alpha=0.7)
102
-
103
- axs[0].set_ylim(-200, 200)
104
- axs[0].set_xlabel("Позиция")
105
- axs[0].set_ylabel("Торсионный угол (град.)")
106
- axs[0].set_title(f"Шаг 0: {seq}")
107
- axs[0].legend()
108
-
109
- axs[1].set_xlabel("Шаг")
110
- axs[1].set_ylabel("Число машин")
111
- axs[1].set_ylim(0, 10)
112
- axs[1].set_title("Динамика: число 'биомашин'")
113
-
114
- axs[2].set_xlabel("Лаг")
115
- axs[2].set_ylabel("Автокорреляция")
116
- axs[2].set_title("Автокорреляция углового профиля")
117
-
118
- def update(frame):
119
- nonlocal seq
120
- if frame != 0:
121
  seq = bio_mutate(seq)
 
122
  torsion_profile = np.array([ANGLE_MAP.get(nt, 0.0) for nt in seq])
 
123
  runs = find_local_min_runs(torsion_profile, min_run, max_run)
 
124
  stat_bist_counts.append(len(runs))
125
  ent = compute_entropy(torsion_profile)
126
  stat_entropy.append(ent)
127
  acorr = compute_autocorr(torsion_profile)
128
 
129
- # Обновление графиков
130
- line1.set_data(range(len(torsion_profile)), torsion_profile)
131
- for start, end, val in runs:
132
- runs_patch.set_xy([(start, -200), (end, -200), (end, 200), (start, 200)])
133
- line2.set_data(range(len(stat_bist_counts)), stat_bist_counts)
134
- for i, b in enumerate(acorr[:6]):
135
- bar[i].set_height(b)
136
-
137
- axs[0].set_title(f"Шаг {frame}: {seq}\nЧисло машин: {len(runs)}, энтропия: {ent:.2f}")
138
- axs[2].text(0.70, 0.70, f"Энтропия: {ent:.2f}", transform=axs[2].transAxes)
139
-
140
- return line1, runs_patch, line2, bar
141
 
142
- ani = FuncAnimation(fig, update, frames=range(steps), blit=True, interval=100)
 
 
143
 
144
- # Показ анимации в Streamlit
145
- plot_placeholder.pyplot(fig)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  import random
5
  from scipy.stats import entropy as scipy_entropy
6
  import time
 
7
 
8
  # --- НАСТРОЙКИ ---
9
+ # Длина последовательности и количество шагов симуляции
10
  seqlen = 60
11
  steps = 120
12
+ # Параметры минимальной и максимальной длины "бега" (сегмента с одинаковыми значениями)
13
  min_run, max_run = 1, 2
14
+ # Торсионные углы для каждого нуклеотида ДНК: A, C, G, T
15
  ANGLE_MAP = {'A': 60.0, 'C': 180.0, 'G': -60.0, 'T': -180.0, 'N': 0.0}
16
+ # Возможные нуклеотиды в последовательности
17
  bases = ['A', 'C', 'G', 'T']
18
 
19
+ # --- БИОЛОГИЧЕСКИЕ ФУНКЦИИ ---
20
+ # Функция для нахождения локальных минимумов на графике торсионного угла (профиль ДНК)
21
+ # Бег - это последовательности одинаковых значений на графике (например, серии одинаковых углов)
22
  def find_local_min_runs(profile, min_run=1, max_run=2):
23
  result = []
24
  N = len(profile)
 
33
  i += run_length
34
  return result
35
 
36
+ # Функция для мутации последовательности ДНК (включает точечные мутации, инсерции, делеции и блочные перестановки)
37
  def bio_mutate(seq):
38
  r = random.random()
39
+ # Точечная мутация (меняется один нуклеотид)
40
  if r < 0.70:
41
  idx = random.randint(0, len(seq)-1)
42
  orig = seq[idx]
 
48
  else:
49
  newbase = random.choice([b for b in bases if b != orig])
50
  seq = seq[:idx] + newbase + seq[idx+1:]
51
+ # Инсерция (вставка случайного блока нуклеотидов)
52
  elif r < 0.80:
53
  idx = random.randint(0, len(seq)-1)
54
  ins = ''.join(random.choices(bases, k=random.randint(1, 3)))
55
  seq = seq[:idx] + ins + seq[idx:]
56
  if len(seq) > seqlen:
57
  seq = seq[:seqlen]
58
+ # Делеция (удаление случайного блока из последовательности)
59
  elif r < 0.90:
60
  if len(seq) > 4:
61
  idx = random.randint(0, len(seq)-2)
62
  dell = random.randint(1, min(3, len(seq)-idx))
63
  seq = seq[:idx] + seq[idx+dell:]
64
+ # Блочная перестановка (инверсия случайного сегмента)
65
  else:
66
  if len(seq) > 10:
67
  start = random.randint(0, len(seq)-6)
 
69
  subseq = seq[start:end]
70
  subseq = subseq[::-1]
71
  seq = seq[:start] + subseq + seq[end:]
72
+ # Если последовательность короче нужной длины, добавляются случайные нуклеотиды
73
  while len(seq) < seqlen:
74
  seq += random.choice(bases)
75
+ # Ограничиваем длину последовательности
76
  if len(seq) > seqlen:
77
  seq = seq[:seqlen]
78
  return seq
79
 
80
+ # Функция для вычисления автокорреляции профиля (анализ структурных зависимостей)
81
  def compute_autocorr(profile):
82
  profile = profile - np.mean(profile)
83
  result = np.correlate(profile, profile, mode='full')
84
  result = result[result.size // 2:]
85
+ norm = np.max(result) if np.max(result) != 0 else 1
86
  return result[:10]/norm
87
 
88
+ # Функция для вычисления энтропии профиля (мера хаоса или неопределенности)
89
  def compute_entropy(profile):
90
  vals, counts = np.unique(profile, return_counts=True)
91
  p = counts / counts.sum()
92
  return scipy_entropy(p, base=2)
93
 
94
+ # --- STREAMLIT ИНТЕРФЕЙС ---
95
+ # Заголовок приложения
96
  st.title("🧬 Эволюция ДНК-подобной последовательности")
97
  st.markdown("Модель визуализирует мутации и анализирует структуру последовательности во времени.")
98
 
99
+ # Кнопка для запуска симуляции
100
  if st.button("▶️ Запустить симуляцию"):
101
+ # Инициализация случайной последовательности
102
  seq = ''.join(random.choices(bases, k=seqlen))
103
+ # Списки для хранения статистик на каждом шаге
104
  stat_bist_counts = []
105
  stat_entropy = []
106
  stat_autocorr = []
107
 
108
+ # Плейсхолдер для графика
109
  plot_placeholder = st.empty()
110
 
111
+ # Симуляция изменения последовательности
112
+ for step in range(steps):
113
+ if step != 0:
114
+ # Мутация последовательности на каждом шаге
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  seq = bio_mutate(seq)
116
+ # Профиль торсионных углов для последовательности
117
  torsion_profile = np.array([ANGLE_MAP.get(nt, 0.0) for nt in seq])
118
+ # Поиск локальных минимумов
119
  runs = find_local_min_runs(torsion_profile, min_run, max_run)
120
+ # Сохранение статистик
121
  stat_bist_counts.append(len(runs))
122
  ent = compute_entropy(torsion_profile)
123
  stat_entropy.append(ent)
124
  acorr = compute_autocorr(torsion_profile)
125
 
126
+ # Визуализация результатов
127
+ fig, axs = plt.subplots(3, 1, figsize=(10, 8))
128
+ plt.subplots_adjust(hspace=0.45)
129
+ lags_shown = 6
 
 
 
 
 
 
 
 
130
 
131
+ axs[0].cla() # Очищаем предыдущий график
132
+ axs[1].cla() # Очищаем предыдущий график
133
+ axs[2].cla() # Очищаем предыдущий график
134
 
135
+ # График торсионного угла
136
+ axs[0].plot(torsion_profile, color='royalblue', label="Торсионный угол")
137
+ for start, end, val in runs:
138
+ axs[0].axvspan(start, end, color="red", alpha=0.3)
139
+ axs[0].plot(range(start, end+1), torsion_profile[start:end+1], 'ro', markersize=5)
140
+ axs[0].set_ylim(-200, 200)
141
+ axs[0].set_xlabel("Позиция")
142
+ axs[0].set_ylabel("Торсионный угол (град.)")
143
+ axs[0].set_title(f"Шаг {step}: {seq}\nЧисло машин: {len(runs)}, энтропия: {ent:.2f}")
144
+ axs[0].legend()
145
+
146
+ # График динамики числа 'биомашин'
147
+ axs[1].plot(stat_bist_counts, '-o', color='crimson', markersize=4)
148
+ axs[1].set_xlabel("Шаг")
149
+ axs[1].set_ylabel("Число машин")
150
+ axs[1].set_ylim(0, max(10, max(stat_bist_counts)+1))
151
+ axs[1].set_title("Динамика: число 'биомашин'")
152
+
153
+ # График автокорреляции
154
+ axs[2].bar(np.arange(lags_shown), acorr[:lags_shown], color='teal', alpha=0.7)
155
+ axs[2].set_xlabel("Лаг")
156
+ axs[2].set_ylabel("Автокорреляция")
157
+ axs[2].set_title("Автокорреляция углового профиля (структурность) и энтропия")
158
+ axs[2].text(0.70,0.70, f"Энтропия: {ent:.2f}", transform=axs[2].transAxes)
159
+
160
+ # Отображаем график в Streamlit
161
+ plot_placeholder.pyplot(fig)
162
+ time.sleep(0.5)