Skip to content

Commit f82bc49

Browse files
committed
Add python script for statistics
1 parent 3d6ed0e commit f82bc49

1 file changed

Lines changed: 222 additions & 0 deletions

File tree

scripts/volleyball_plotly.py

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
import os
2+
import pandas as pd
3+
import plotly.graph_objects as go
4+
from plotly.subplots import make_subplots
5+
import math
6+
import numpy as np
7+
8+
# Function to read all CSV files in the current directory
9+
def read_csv_files():
10+
csv_files = [file for file in os.listdir('.') if file.endswith('.csv')]
11+
data = {file: pd.read_csv(file) for file in csv_files}
12+
for file in data:
13+
df = data[file]
14+
df['Step'] = df.index # Add step column for easy plotting
15+
latest_file = max(csv_files, key=os.path.getmtime)
16+
return data, data[latest_file]
17+
18+
# Function to plot a metric
19+
def plot_metric(fig, data, metric_name, row, col, ylabel=None, transform=None):
20+
for file, df in data.items():
21+
y = df[metric_name][1:] if transform is None else transform(df)
22+
fig.add_trace(
23+
go.Scatter(x=df['Step'][1:], y=y, name=metric_name, mode='lines'),
24+
row=row, col=col
25+
)
26+
fig.update_xaxes(title_text='Step', row=row, col=col)
27+
fig.update_yaxes(title_text=ylabel if ylabel else metric_name, row=row, col=col)
28+
fig.update_layout(title_text=metric_name, showlegend=True)
29+
30+
# Read CSV files
31+
data, last_df = read_csv_files()
32+
33+
# Define metrics to plot
34+
metrics = [
35+
("Count episodes", None),
36+
("Old episodes, %", lambda df: df["Count episodes with old"][1:] / df["Count episodes"][1:] * 100),
37+
("Old ticks, %", lambda df: df["Count ticks with old"][1:] / (df["Count ticks with current"][1:] + df["Count ticks with old"][1:]) * 100),
38+
("Average freeze time", None),
39+
("Average ball hits", lambda df: df["Cumulative ball hits"][1:] / (df["Count episodes"][1:] / 2)),
40+
("Average ball absolute velocity", None),
41+
("Average ball velocity(x)", None),
42+
("Average first bot reward", None),
43+
("Average second bot reward", None),
44+
("Average bots reward difference", lambda df: df["Average second bot reward"][1:] - df["Average first bot reward"][1:]),
45+
("Highest reward per tick", None),
46+
("Average current-old score difference", lambda df: (df["Current bot cumulative score"][1:] - df["Old bot cumulative score"][1:]) / (df["Count episodes"][1:] / 2)),
47+
("Actor loss", None),
48+
("Critic loss", None),
49+
("Critic Mean Absolute Error", None),
50+
("Critic Correlation Coefficient", None),
51+
("Training loss", None),
52+
("Policy loss", lambda df: df["Critic loss"][1:] * 0.5 - df["Actor loss"][1:]),
53+
("Entropy loss", lambda df: df["Entropy"][1:] * df["Entropy coefficient"][1:]),
54+
(["Entropy", "Minimal Entropy", "Maximal Entropy"], None),
55+
(["Angle entropy", "Minimal Angle entropy", "Maximal Angle entropy"], None),
56+
(["Hook entropy", "Minimal Hook entropy", "Maximal Hook entropy"], None),
57+
(["Hammer entropy", "Minimal Hammer entropy", "Maximal Hammer entropy"], None),
58+
(["Direction entropy", "Minimal Direction entropy", "Maximal Direction entropy"], None),
59+
("Entropy coefficient", None),
60+
("Actor grad norm", None),
61+
("Critic grad norm", None),
62+
("Actor weight norm", None),
63+
("Critic weight norm", None),
64+
("Actor activation mean", None),
65+
("Actor activation std", None),
66+
("Learning rate", None)
67+
]
68+
69+
# Number of time-related plots
70+
num_add_plots = 5 # Time to process and Decide profiling
71+
72+
# Total number of plots
73+
total_plots = len(metrics) + num_add_plots
74+
75+
# Calculate nrows and ncols dynamically
76+
ncols = 3 # Fixed number of columns
77+
nrows = math.ceil(total_plots / ncols) # Calculate rows based on total plots and columns
78+
79+
# Create subplots
80+
fig = make_subplots(rows=nrows, cols=ncols, subplot_titles=[metric[0] if not isinstance(metric[0], list) else metric[0][0] for metric in metrics] +
81+
['Average First/Second Bot Scores', 'Average bots score', 'Ticks per second', 'Time to process', 'Decide profiling'])
82+
83+
# Plot metrics
84+
for i, (metric_name, transform) in enumerate(metrics):
85+
row = (i // ncols) + 1
86+
col = (i % ncols) + 1
87+
try:
88+
if not isinstance(metric_name, list):
89+
plot_metric(fig, data, metric_name, row, col, transform=transform)
90+
else:
91+
for metric in metric_name:
92+
plot_metric(fig, data, metric, row, col, transform=transform)
93+
except Exception as e:
94+
print("Unable to plot metric:", metric_name, "error:", e)
95+
96+
# Plot bot scores (calculated dynamically)
97+
row = (len(metrics) // ncols) + 1
98+
col = (len(metrics) % ncols) + 1
99+
for file, df in data.items():
100+
fig.add_trace(
101+
go.Scatter(x=df['Step'], y=df['First bot cumulative score'] / (df['Count episodes'] / 2),
102+
name=f'First bot {file[:-4]}', mode='lines'),
103+
row=row, col=col
104+
)
105+
fig.add_trace(
106+
go.Scatter(x=df['Step'], y=df['Second bot cumulative score'] / (df['Count episodes'] / 2),
107+
name=f'Second bot {file[:-4]}', mode='lines'),
108+
row=row, col=col
109+
)
110+
fig.update_xaxes(title_text='Step', row=row, col=col)
111+
fig.update_yaxes(title_text='Average Score', row=row, col=col)
112+
fig.update_layout(title_text='Average Bot Scores', showlegend=True)
113+
114+
def ema_tb(y, smoothing=0.9):
115+
y = np.asarray(y, dtype=float)
116+
alpha = 1 - smoothing
117+
s = np.zeros_like(y)
118+
s[0] = y[0]
119+
for i in range(1, len(y)):
120+
s[i] = alpha * y[i] + (1 - alpha) * s[i-1]
121+
return s
122+
123+
# Plot bot average score
124+
row = ((len(metrics) + 1) // ncols) + 1
125+
col = ((len(metrics) + 1) % ncols) + 1
126+
for file, df in data.items():
127+
first_bot_avg_score = df['First bot cumulative score'] / (df['Count episodes'] / 2)
128+
second_bot_avg_score = df['Second bot cumulative score'] / (df['Count episodes'] / 2)
129+
fig.add_trace(
130+
go.Scatter(x=df['Step'], y=(first_bot_avg_score + second_bot_avg_score) / 2,
131+
name=f'Average score {file[:-4]}', mode='lines'),
132+
row=row, col=col
133+
)
134+
fig.add_trace(
135+
go.Scatter(x=df['Step'], y=ema_tb((first_bot_avg_score + second_bot_avg_score) / 2),
136+
name=f'Average score smoothed {file[:-4]}', mode='lines'),
137+
row=row, col=col
138+
)
139+
fig.update_xaxes(title_text='Step', row=row, col=col)
140+
fig.update_yaxes(title_text='Average Score', row=row, col=col)
141+
fig.update_layout(title_text='Average Bots Score', showlegend=True)
142+
143+
# Plot Ticks per second
144+
row = ((len(metrics) + 2) // ncols) + 1
145+
col = ((len(metrics) + 2) % ncols) + 1
146+
for file, df in data.items():
147+
fig.add_trace(
148+
go.Scatter(x=df['Step'], y=df['TPS'], name=f'Real {file[:-4]}', mode='lines'),
149+
row=row, col=col
150+
)
151+
fig.add_trace(
152+
go.Scatter(x=df['Step'], y=1000 / (df['Time to decide'] + df['Time to tick'] + df['Time rest']),
153+
name=f'Expected {file[:-4]}', mode='lines'),
154+
row=row, col=col
155+
)
156+
fig.update_xaxes(title_text='Step', row=row, col=col)
157+
fig.update_yaxes(title_text='TPS', row=row, col=col)
158+
fig.update_layout(title_text='Ticks per second', showlegend=True)
159+
160+
# Plot time-related metrics
161+
row = ((len(metrics) + 3) // ncols) + 1
162+
col = ((len(metrics) + 3) % ncols) + 1
163+
fig.add_trace(
164+
go.Scatter(x=last_df['Step'], y=last_df['Time to decide'], name='Time to decide', mode='lines'),
165+
row=row, col=col
166+
)
167+
fig.add_trace(
168+
go.Scatter(x=last_df['Step'], y=last_df['Time to tick'], name='Time to tick', mode='lines', line=dict(color='green')),
169+
row=row, col=col
170+
)
171+
fig.add_trace(
172+
go.Scatter(x=last_df['Step'], y=last_df['Time rest'], name='Time rest', mode='lines', line=dict(color='yellow')),
173+
row=row, col=col
174+
)
175+
fig.update_xaxes(title_text='Step', row=row, col=col)
176+
fig.update_yaxes(title_text='Time taken, ms', row=row, col=col)
177+
fig.update_layout(title_text='Time to process', showlegend=True)
178+
179+
# Plot Decide profiling
180+
row = ((len(metrics) + 4) // ncols) + 1
181+
col = ((len(metrics) + 4) % ncols) + 1
182+
fig.add_trace(
183+
go.Scatter(x=last_df['Step'], y=last_df['Time pre forward'], name='Time pre forward', mode='lines'),
184+
row=row, col=col
185+
)
186+
fig.add_trace(
187+
go.Scatter(x=last_df['Step'], y=last_df['Time forward'], name='Time forward', mode='lines', line=dict(color='green')),
188+
row=row, col=col
189+
)
190+
fig.add_trace(
191+
go.Scatter(x=last_df['Step'], y=last_df['Time normal'], name='Time normal', mode='lines', line=dict(color='yellow')),
192+
row=row, col=col
193+
)
194+
fig.add_trace(
195+
go.Scatter(x=last_df['Step'], y=last_df['Time to cpu'], name='Time to cpu', mode='lines', line=dict(color='black')),
196+
row=row, col=col
197+
)
198+
fig.add_trace(
199+
go.Scatter(x=last_df['Step'], y=last_df['Time process last'], name='Time process last', mode='lines', line=dict(color='orange')),
200+
row=row, col=col
201+
)
202+
fig.update_xaxes(title_text='Step', row=row, col=col)
203+
fig.update_yaxes(title_text='Time taken, ms', row=row, col=col)
204+
fig.update_layout(title_text='Decide profiling', showlegend=True)
205+
206+
# Hide unused subplots
207+
for i in range(total_plots, nrows * ncols):
208+
fig.update_xaxes(visible=False, row=(i // ncols) + 1, col=(i % ncols) + 1)
209+
fig.update_yaxes(visible=False, row=(i // ncols) + 1, col=(i % ncols) + 1)
210+
211+
# Adjust layout for full screen
212+
fig.update_layout(
213+
autosize=True, # Automatically resize to fit the screen
214+
margin=dict(l=0, r=0, t=40, b=0), # Remove margins
215+
height=300 * nrows, # Let Plotly handle height
216+
width=None, # Let Plotly handle width
217+
title_text="Training Metrics",
218+
showlegend=True
219+
)
220+
221+
# Save and show plot
222+
fig.write_html("data.html", auto_open=True)

0 commit comments

Comments
 (0)