Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
F
formula student elo
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
GET racing FOSS
formula student elo
Commits
94007bb7
Commit
94007bb7
authored
1 month ago
by
Marius Heidenreich
Browse files
Options
Downloads
Patches
Plain Diff
claude bessere plots
parent
dc658c30
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
main.py
+73
-53
73 additions, 53 deletions
main.py
with
73 additions
and
53 deletions
main.py
+
73
−
53
View file @
94007bb7
...
@@ -19,6 +19,7 @@ class EloSystem:
...
@@ -19,6 +19,7 @@ class EloSystem:
self
.
output_directory
=
output_directory
self
.
output_directory
=
output_directory
self
.
teams
=
{}
# Dictionary to store team Elo ratings
self
.
teams
=
{}
# Dictionary to store team Elo ratings
self
.
match_history
=
[]
# List to store all match results for history tracking
self
.
match_history
=
[]
# List to store all match results for history tracking
self
.
competition_history
=
{}
# Dictionary to store competition results
self
.
initialize_output_directory
()
self
.
initialize_output_directory
()
def
initialize_output_directory
(
self
):
def
initialize_output_directory
(
self
):
...
@@ -63,7 +64,7 @@ class EloSystem:
...
@@ -63,7 +64,7 @@ class EloSystem:
'
competitions
'
:
0
,
'
competitions
'
:
0
,
'
history
'
:
[]
'
history
'
:
[]
}
}
teams_in_competition
.
append
(
team_name
)
teams_in_competition
.
append
(
(
rank
,
team_name
)
)
except
StopIteration
:
except
StopIteration
:
# File is empty
# File is empty
continue
continue
...
@@ -79,6 +80,7 @@ class EloSystem:
...
@@ -79,6 +80,7 @@ class EloSystem:
team_name
=
row
[
1
].
strip
()
if
len
(
row
)
>
1
else
row
[
0
].
strip
()
team_name
=
row
[
1
].
strip
()
if
len
(
row
)
>
1
else
row
[
0
].
strip
()
else
:
else
:
# If no explicit ranking, use row index
# If no explicit ranking, use row index
rank
=
len
(
teams_in_competition
)
+
1
team_name
=
row
[
0
].
strip
()
team_name
=
row
[
0
].
strip
()
# Initialize the team if not seen before
# Initialize the team if not seen before
...
@@ -88,20 +90,36 @@ class EloSystem:
...
@@ -88,20 +90,36 @@ class EloSystem:
'
competitions
'
:
0
,
'
competitions
'
:
0
,
'
history
'
:
[]
'
history
'
:
[]
}
}
teams_in_competition
.
append
(
team_name
)
teams_in_competition
.
append
((
rank
,
team_name
))
# Sort teams by rank
teams_in_competition
.
sort
(
key
=
lambda
x
:
x
[
0
])
# Store competition results
date
=
datetime
.
now
().
strftime
(
"
%Y-%m-%d
"
)
# Use current date as competition date
self
.
competition_history
[
competition_name
]
=
{
'
date
'
:
date
,
'
results
'
:
teams_in_competition
}
# Extract just team names in order
team_names
=
[
team
[
1
]
for
team
in
teams_in_competition
]
# Update competition count for each team
# Update competition count for each team
for
team_name
in
team
s_in_competition
:
for
team_name
in
team
_names
:
self
.
teams
[
team_name
][
'
competitions
'
]
+=
1
self
.
teams
[
team_name
][
'
competitions
'
]
+=
1
# Update Elo ratings based on this competition's results
# Update Elo ratings based on this competition's results
self
.
update_elo_for_competition
(
team
s_in_competition
,
competition_name
)
self
.
update_elo_for_competition
(
team
_names
,
competition_name
)
def
update_elo_for_competition
(
self
,
teams_in_order
,
competition_name
):
def
update_elo_for_competition
(
self
,
teams_in_order
,
competition_name
):
"""
Update Elo ratings for all teams in a competition
"""
"""
Update Elo ratings for all teams in a competition
"""
# For each team, compare with teams ranked below them
# For each team, compare with teams ranked below them
date
=
datetime
.
now
().
strftime
(
"
%Y-%m-%d
"
)
# Use current date as competition date
date
=
datetime
.
now
().
strftime
(
"
%Y-%m-%d
"
)
# Use current date as competition date
# Store pre-competition Elo ratings
pre_competition_elo
=
{
team
:
self
.
teams
[
team
][
'
elo
'
]
for
team
in
teams_in_order
}
for
i
in
range
(
len
(
teams_in_order
)):
for
i
in
range
(
len
(
teams_in_order
)):
for
j
in
range
(
i
+
1
,
len
(
teams_in_order
)):
for
j
in
range
(
i
+
1
,
len
(
teams_in_order
)):
team_a
=
teams_in_order
[
i
]
team_a
=
teams_in_order
[
i
]
...
@@ -110,6 +128,21 @@ class EloSystem:
...
@@ -110,6 +128,21 @@ class EloSystem:
# Team A is ranked higher than Team B
# Team A is ranked higher than Team B
self
.
update_elo_pair
(
team_a
,
team_b
,
1
,
0
,
competition_name
,
date
)
self
.
update_elo_pair
(
team_a
,
team_b
,
1
,
0
,
competition_name
,
date
)
# Store post-competition Elo ratings and changes
for
team
in
teams_in_order
:
elo_change
=
self
.
teams
[
team
][
'
elo
'
]
-
pre_competition_elo
[
team
]
rank
=
teams_in_order
.
index
(
team
)
+
1
# Store in team history
self
.
teams
[
team
][
'
history
'
].
append
({
'
date
'
:
date
,
'
competition
'
:
competition_name
,
'
rank
'
:
rank
,
'
previous_elo
'
:
pre_competition_elo
[
team
],
'
new_elo
'
:
self
.
teams
[
team
][
'
elo
'
],
'
elo_change
'
:
elo_change
})
def
update_elo_pair
(
self
,
team_a
,
team_b
,
score_a
,
score_b
,
competition_name
,
date
):
def
update_elo_pair
(
self
,
team_a
,
team_b
,
score_a
,
score_b
,
competition_name
,
date
):
"""
Update Elo ratings for a pair of teams based on their match outcome
"""
"""
Update Elo ratings for a pair of teams based on their match outcome
"""
# Get current Elo ratings
# Get current Elo ratings
...
@@ -139,13 +172,10 @@ class EloSystem:
...
@@ -139,13 +172,10 @@ class EloSystem:
}
}
self
.
match_history
.
append
(
match_info
)
self
.
match_history
.
append
(
match_info
)
# Update team Elo ratings
and history
# Update team Elo ratings
self
.
teams
[
team_a
][
'
elo
'
]
=
new_rating_a
self
.
teams
[
team_a
][
'
elo
'
]
=
new_rating_a
self
.
teams
[
team_b
][
'
elo
'
]
=
new_rating_b
self
.
teams
[
team_b
][
'
elo
'
]
=
new_rating_b
self
.
teams
[
team_a
][
'
history
'
].
append
({
'
date
'
:
date
,
'
elo
'
:
new_rating_a
,
'
competition
'
:
competition_name
})
self
.
teams
[
team_b
][
'
history
'
].
append
({
'
date
'
:
date
,
'
elo
'
:
new_rating_b
,
'
competition
'
:
competition_name
})
def
expected_score
(
self
,
rating_a
,
rating_b
):
def
expected_score
(
self
,
rating_a
,
rating_b
):
"""
Calculate the expected score for a player with rating_a against a player with rating_b
"""
"""
Calculate the expected score for a player with rating_a against a player with rating_b
"""
return
1
/
(
1
+
math
.
pow
(
10
,
(
rating_b
-
rating_a
)
/
400
))
return
1
/
(
1
+
math
.
pow
(
10
,
(
rating_b
-
rating_a
)
/
400
))
...
@@ -227,7 +257,7 @@ class EloSystem:
...
@@ -227,7 +257,7 @@ class EloSystem:
</head>
</head>
<body>
<body>
<h1>Team Elo Rankings</h1>
<h1>Team Elo Rankings</h1>
<p>Rankings based on
{
len
(
self
.
match_history
)
}
matches from
{
self
.
count_
competition
s
(
)
}
competitions.</p>
<p>Rankings based on
{
len
(
self
.
competition
_history
)
}
competitions.</p>
<div class=
"
chart-container
"
>
<div class=
"
chart-container
"
>
<img src=
"
images/top_teams_elo.png
"
alt=
"
Top Teams Elo Chart
"
>
<img src=
"
images/top_teams_elo.png
"
alt=
"
Top Teams Elo Chart
"
>
...
@@ -277,9 +307,8 @@ class EloSystem:
...
@@ -277,9 +307,8 @@ class EloSystem:
if
team_data
[
'
history
'
]:
if
team_data
[
'
history
'
]:
self
.
create_team_elo_history_chart
(
team_name
)
self
.
create_team_elo_history_chart
(
team_name
)
# Get matches involving this team
# Filter history for this team's competitions
team_matches
=
[
match
for
match
in
self
.
match_history
team_competitions
=
sorted
(
team_data
[
'
history
'
],
key
=
lambda
x
:
x
[
'
date
'
],
reverse
=
True
)
if
match
[
'
team_a
'
]
==
team_name
or
match
[
'
team_b
'
]
==
team_name
]
html_content
=
f
"""
<!DOCTYPE html>
html_content
=
f
"""
<!DOCTYPE html>
<html lang=
"
en
"
>
<html lang=
"
en
"
>
...
@@ -360,6 +389,12 @@ class EloSystem:
...
@@ -360,6 +389,12 @@ class EloSystem:
font-size: 14px;
font-size: 14px;
color: #666;
color: #666;
}}
}}
.positive {{
color: green;
}}
.negative {{
color: red;
}}
</style>
</style>
</head>
</head>
<body>
<body>
...
@@ -377,21 +412,19 @@ class EloSystem:
...
@@ -377,21 +412,19 @@ class EloSystem:
</div>
</div>
"""
"""
# Calculate win rate if applicable
# Calculate average ranking if available
if
team_matches
:
if
team_competitions
:
wins
=
sum
(
1
for
match
in
team_matches
avg_rank
=
sum
(
comp
[
'
rank
'
]
for
comp
in
team_competitions
)
/
len
(
team_competitions
)
if
(
match
[
'
team_a
'
]
==
team_name
and
match
[
'
score_a
'
]
>
match
[
'
score_b
'
])
best_rank
=
min
(
comp
[
'
rank
'
]
for
comp
in
team_competitions
)
or
(
match
[
'
team_b
'
]
==
team_name
and
match
[
'
score_b
'
]
>
match
[
'
score_a
'
]))
win_rate
=
(
wins
/
len
(
team_matches
))
*
100
html_content
+=
f
"""
html_content
+=
f
"""
<div class=
"
stat-box
"
>
<div class=
"
stat-box
"
>
<div class=
"
stat-value
"
>
{
len
(
team_matches
)
}
</div>
<div class=
"
stat-value
"
>
{
avg_rank
:
.
1
f
}
</div>
<div class=
"
stat-label
"
>
Total Matches
</div>
<div class=
"
stat-label
"
>
Avg. Placement
</div>
</div>
</div>
<div class=
"
stat-box
"
>
<div class=
"
stat-box
"
>
<div class=
"
stat-value
"
>
{
win_rate
:
.
1
f
}
%
</div>
<div class=
"
stat-value
"
>
{
best_rank
}
</div>
<div class=
"
stat-label
"
>
Win Rate
</div>
<div class=
"
stat-label
"
>
Best Placement
</div>
</div>
</div>
"""
"""
...
@@ -402,40 +435,31 @@ class EloSystem:
...
@@ -402,40 +435,31 @@ class EloSystem:
<img src=
"
images/{}_history.png
"
alt=
"
{} Elo History
"
>
<img src=
"
images/{}_history.png
"
alt=
"
{} Elo History
"
>
</div>
</div>
<h2>
Match
History</h2>
<h2>
Competition
History</h2>
<table>
<table>
<tr>
<tr>
<th>Date</th>
<th>Date</th>
<th>Competition</th>
<th>Competition</th>
<th>Opponent</th>
<th>Placement</th>
<th>Result</th>
<th>Elo Before</th>
<th>Elo After</th>
<th>Elo Change</th>
<th>Elo Change</th>
</tr>
</tr>
"""
.
format
(
self
.
clean_filename
(
team_name
),
team_name
)
"""
.
format
(
self
.
clean_filename
(
team_name
),
team_name
)
# Add rows for each match
# Add rows for each competition
for
match
in
sorted
(
team_matches
,
key
=
lambda
x
:
x
[
'
date
'
],
reverse
=
True
):
for
comp
in
team_competitions
:
if
match
[
'
team_a
'
]
==
team_name
:
elo_change
=
comp
[
'
elo_change
'
]
opponent
=
match
[
'
team_b
'
]
change_class
=
"
positive
"
if
elo_change
>
0
else
"
negative
"
if
elo_change
<
0
else
""
old_elo
=
match
[
'
old_elo_a
'
]
new_elo
=
match
[
'
new_elo_a
'
]
result
=
"
Win
"
if
match
[
'
score_a
'
]
>
match
[
'
score_b
'
]
else
"
Loss
"
else
:
opponent
=
match
[
'
team_a
'
]
old_elo
=
match
[
'
old_elo_b
'
]
new_elo
=
match
[
'
new_elo_b
'
]
result
=
"
Win
"
if
match
[
'
score_b
'
]
>
match
[
'
score_a
'
]
else
"
Loss
"
elo_change
=
new_elo
-
old_elo
change_color
=
"
green
"
if
elo_change
>
0
else
"
red
"
html_content
+=
f
"""
html_content
+=
f
"""
<tr>
<tr>
<td>
{
match
[
'
date
'
]
}
</td>
<td>
{
comp
[
'
date
'
]
}
</td>
<td>
{
match
[
'
competition
'
]
}
</td>
<td>
{
comp
[
'
competition
'
]
}
</td>
<td><a href=
"
team_
{
self
.
clean_filename
(
opponent
)
}
.html
"
>
{
opponent
}
</a></td>
<td>
{
comp
[
'
rank
'
]
}
</td>
<td>
{
result
}
</td>
<td>
{
int
(
comp
[
'
previous_elo
'
])
}
</td>
<td style=
"
color:
{
change_color
}
"
>
{
'
+
'
if
elo_change
>
0
else
''
}{
elo_change
:
.
1
f
}
</td>
<td>
{
int
(
comp
[
'
new_elo
'
])
}
</td>
<td class=
"
{
change_class
}
"
>
{
'
+
'
if
elo_change
>
0
else
''
}{
elo_change
:
.
1
f
}
</td>
</tr>
"""
</tr>
"""
html_content
+=
"""
html_content
+=
"""
...
@@ -515,15 +539,15 @@ class EloSystem:
...
@@ -515,15 +539,15 @@ class EloSystem:
team_history
.
sort
(
key
=
lambda
x
:
x
[
'
date
'
])
team_history
.
sort
(
key
=
lambda
x
:
x
[
'
date
'
])
dates
=
[
entry
[
'
date
'
]
for
entry
in
team_history
]
dates
=
[
entry
[
'
date
'
]
for
entry
in
team_history
]
elo_values
=
[
entry
[
'
elo
'
]
for
entry
in
team_history
]
elo_values
=
[
entry
[
'
new_
elo
'
]
for
entry
in
team_history
]
# Create line chart
# Create line chart
plt
.
plot
(
range
(
len
(
dates
)),
elo_values
,
marker
=
'
o
'
,
linestyle
=
'
-
'
,
color
=
'
blue
'
)
plt
.
plot
(
range
(
len
(
dates
)),
elo_values
,
marker
=
'
o
'
,
linestyle
=
'
-
'
,
color
=
'
blue
'
)
# Add competition labels
# Add competition labels
for
i
,
entry
in
enumerate
(
team_history
):
for
i
,
entry
in
enumerate
(
team_history
):
plt
.
annotate
(
entry
[
'
competition
'
],
plt
.
annotate
(
entry
[
'
competition
'
]
+
f
"
(#
{
entry
[
'
rank
'
]
}
)
"
,
(
i
,
entry
[
'
elo
'
]),
(
i
,
entry
[
'
new_
elo
'
]),
textcoords
=
"
offset points
"
,
textcoords
=
"
offset points
"
,
xytext
=
(
0
,
10
),
xytext
=
(
0
,
10
),
ha
=
'
center
'
,
ha
=
'
center
'
,
...
@@ -541,10 +565,6 @@ class EloSystem:
...
@@ -541,10 +565,6 @@ class EloSystem:
dpi
=
100
)
dpi
=
100
)
plt
.
close
()
plt
.
close
()
def
count_competitions
(
self
):
"""
Count the number of unique competitions
"""
return
len
(
set
(
match
[
'
competition
'
]
for
match
in
self
.
match_history
))
def
clean_filename
(
self
,
filename
):
def
clean_filename
(
self
,
filename
):
"""
Clean a string to make it suitable for use as a filename
"""
"""
Clean a string to make it suitable for use as a filename
"""
# Replace spaces and special characters
# Replace spaces and special characters
...
@@ -556,7 +576,7 @@ def main():
...
@@ -556,7 +576,7 @@ def main():
import
argparse
import
argparse
parser
=
argparse
.
ArgumentParser
(
description
=
'
Process competition results and generate Elo rankings website
'
)
parser
=
argparse
.
ArgumentParser
(
description
=
'
Process competition results and generate Elo rankings website
'
)
parser
.
add_argument
(
'
--input
'
,
default
=
'
data
'
,
help
=
'
Directory containing CSV result files
'
)
parser
.
add_argument
(
'
--input
'
,
default
=
'
csv_files
'
,
help
=
'
Directory containing CSV result files
'
)
parser
.
add_argument
(
'
--output
'
,
default
=
'
elo_website
'
,
help
=
'
Output directory for the website
'
)
parser
.
add_argument
(
'
--output
'
,
default
=
'
elo_website
'
,
help
=
'
Output directory for the website
'
)
args
=
parser
.
parse_args
()
args
=
parser
.
parse_args
()
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment