1import pandas as pd
2import numpy as np
3import plotly.graph_objects as go
4
5# Generate mock data
6duration = 600
7timestamps = np.arange(0, duration, 1)
8n_samples = len(timestamps)
9
10def generate_clustered_data(n_samples, n_hydrophones=16):
11 data = np.zeros((n_samples, n_hydrophones))
12 for t in range(n_samples):
13 center = np.random.randint(0, 16)
14 neighbors = [(center//4 + dx, center%4 + dy) for dx, dy in [(-1,0), (1,0), (0,-1), (0,1)]
15 if 0 <= center//4 + dx < 4 and 0 <= center%4 + dy < 4]
16 n_activations = np.random.randint(2, 4)
17 activations = [center] + [neighbors[i][0]*4 + neighbors[i][1]
18 for i in np.random.choice(len(neighbors),
19 min(n_activations-1, len(neighbors)), replace=False)]
20 data[t, activations] = 1
21 return data
22
23hydrophone_data = generate_clustered_data(n_samples)
24df = pd.DataFrame(hydrophone_data, columns=[f'H{i+1}' for i in range(16)])
25df['Timestamp'] = timestamps
26
27frames = []
28for t in timestamps:
29 data_at_t = df[df['Timestamp'] == t].iloc[0, :-1].values.reshape(4, 4)
30 frames.append(
31 go.Frame(
32 data=[go.Heatmap(
33 z=data_at_t,
34 colorscale=[[0, 'lightgrey'], [1, 'red']],
35 showscale=False,
36 text=[[f'H{i*4+j+1}' for j in range(4)] for i in range(4)],
37 texttemplate="%{text}",
38 textfont={"size":10},
39 xgap=3, # Add gap between cells
40 ygap=3, # Add gap between cells
41 hoverongaps=False
42 )],
43 name=str(t)
44 )
45 )
46
47fig = go.Figure(
48 data=[go.Heatmap(
49 z=df.iloc[0, :-1].values.reshape(4, 4),
50 colorscale=[[0, 'lightgrey'], [1, 'red']],
51 showscale=False,
52 text=[[f'H{i*4+j+1}' for j in range(4)] for i in range(4)],
53 texttemplate="%{text}",
54 textfont={"size":10},
55 xgap=3, # Add gap between cells
56 ygap=3, # Add gap between cells
57 hoverongaps=False
58 )],
59 frames=frames
60)
61
62fig.update_layout(
63 title="4x4 Hydrophone Grid Animation",
64 updatemenus=[{
65 'type': 'buttons',
66 'showactive': False,
67 'buttons': [
68 {'label': 'Play',
69 'method': 'animate',
70 'args': [None, {'frame': {'duration': 1000, 'redraw': True},
71 'fromcurrent': True}]},
72 {'label': 'Pause',
73 'method': 'animate',
74 'args': [[None], {'frame': {'duration': 0, 'redraw': False},
75 'mode': 'immediate',
76 'transition': {'duration': 0}}]}
77 ]
78 }],
79 sliders=[{
80 'currentvalue': {"prefix": "Time: ", "suffix": " seconds"},
81 'steps': [{'args': [[str(t)],
82 {'frame': {'duration': 0, 'redraw': True},
83 'mode': 'immediate'}],
84 'label': str(t),
85 'method': 'animate'} for t in timestamps]
86 }]
87)
88
89# Add grid styling
90fig.update_layout(
91 width=600,
92 height=600,
93 plot_bgcolor='white',
94 xaxis=dict(
95 showgrid=True,
96 gridwidth=2,
97 gridcolor='black',
98 tickmode='array',
99 ticktext=['1', '2', '3', '4'],
100 tickvals=[0, 1, 2, 3]
101 ),
102 yaxis=dict(
103 showgrid=True,
104 gridwidth=2,
105 gridcolor='black',
106 tickmode='array',
107 ticktext=['1', '2', '3', '4'],
108 tickvals=[0, 1, 2, 3],
109 scaleanchor='x' # Make cells square
110 )
111)
112
113fig.show()
Created on 2/7/2025