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
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
GET racing FOSS
formula student elo
Commits
39ade655
Commit
39ade655
authored
3 months ago
by
maeries
Browse files
Options
Downloads
Patches
Plain Diff
gradient charts and separated mixed results
parent
75bb4b8b
No related branches found
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
main.py
+362
-51
362 additions, 51 deletions
main.py
with
362 additions
and
51 deletions
main.py
+
362
−
51
View file @
39ade655
...
...
@@ -6,6 +6,8 @@ from operator import truediv
import
pandas
as
pd
import
matplotlib.pyplot
as
plt
import
matplotlib.patches
as
patches
from
matplotlib.colors
import
LinearSegmentedColormap
from
pathlib
import
Path
import
numpy
as
np
from
datetime
import
datetime
...
...
@@ -27,6 +29,20 @@ def clean_filename(filename):
# Replace spaces and special characters
return
''
.
join
(
c
if
c
.
isalnum
()
else
'
_
'
for
c
in
filename
).
lower
()
def
hex_to_normalized_rgb
(
hex_color
):
# Remove the '#' symbol if present
hex_color
=
hex_color
.
lstrip
(
'
#
'
)
# Convert the hex values to integers
red
=
int
(
hex_color
[
0
:
2
],
16
)
green
=
int
(
hex_color
[
2
:
4
],
16
)
blue
=
int
(
hex_color
[
4
:
6
],
16
)
# Normalize the values by dividing by 255
normalized_rgb
=
(
red
/
255
,
green
/
255
,
blue
/
255
)
return
normalized_rgb
class
EloSystem
:
def
__init__
(
self
,
csv_directory
,
output_directory
,
competition_type
):
...
...
@@ -39,6 +55,16 @@ class EloSystem:
self
.
competition_history
=
{}
# Dictionary to store competition results
self
.
initialize_output_directory
()
self
.
competition_meta_data
=
self
.
load_competition_meta_data
()
if
self
.
competition_type
==
"
electric
"
:
self
.
BORDER_COLOR
=
"
#ffff1e
"
self
.
LINK_COLOR
=
"
#ffc400
"
self
.
LINK_COLOR_HOVER
=
"
#cc9d00
"
elif
self
.
competition_type
==
"
combustion
"
:
self
.
BORDER_COLOR
=
"
#ff8c00
"
self
.
LINK_COLOR
=
"
#ff4500
"
self
.
LINK_COLOR_HOVER
=
"
#cc3700
"
else
:
raise
def
load_competition_meta_data
(
self
):
if
self
.
competition_type
==
"
combustion
"
:
...
...
@@ -62,6 +88,9 @@ class EloSystem:
date_string
=
self
.
competition_meta_data
[
self
.
competition_meta_data
[
"
id
"
]
==
int
(
id
)][
'
date
'
].
values
[
0
]
return
datetime
.
strptime
(
date_string
,
"
%Y-%m-%d
"
)
def
get_competition_type
(
self
,
id
):
return
self
.
competition_meta_data
[
self
.
competition_meta_data
[
"
id
"
]
==
int
(
id
)][
'
kind
'
].
values
[
0
].
strip
()
def
load_and_process_csv_files
(
self
):
"""
Load all CSV files and process them to calculate Elo ratings
"""
csv_files
=
self
.
competition_meta_data
...
...
@@ -95,7 +124,9 @@ class EloSystem:
rank
=
row
[
3
].
strip
()
team_name
=
row
[
0
].
strip
()
kind
=
row
[
2
].
strip
()
if
kind
.
lower
()
==
self
.
competition_type
:
# Initialize the team if not seen before
if
team_name
not
in
self
.
teams
:
self
.
teams
[
team_name
]
=
{
...
...
@@ -131,7 +162,6 @@ class EloSystem:
def
update_elo_for_competition
(
self
,
teams_in_order
,
competition_id
):
"""
Update Elo ratings for all teams in a competition
"""
# HIER ÄNDERN, UM EV UND CV AUSEINANDERZUSORTIEREN
# For each team, compare with teams ranked below them
date
=
self
.
get_competition_date
(
competition_id
)
# Use current date as competition date
...
...
@@ -152,6 +182,7 @@ class EloSystem:
'
date
'
:
date
.
strftime
(
"
%Y-%m-%d
"
),
'
competition
'
:
self
.
get_competition_name
(
competition_id
),
'
rank
'
:
rank
,
'
participants
'
:
len
(
teams_in_order
),
'
previous_elo
'
:
self
.
teams
[
team
][
'
elo
'
],
'
new_elo
'
:
self
.
teams
[
team
][
'
elo
'
]
+
self
.
teams
[
team
][
'
delta_elo
'
],
'
elo_change
'
:
self
.
teams
[
team
][
'
delta_elo
'
]
...
...
@@ -228,57 +259,191 @@ class EloSystem:
<title>Formula Student
{
self
.
competition_type
}
Elo Rankings</title>
<style>
body {{
font-family: Arial, sans-serif;
line-height: 1.6;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
min-height: 100vh;
margin: 0;
font-family:
'
Helvetica Neue
'
, sans-serif;
background: linear-gradient(135deg, #ebebeb, #c9c9c9);
animation: gradientAnimation 10s ease infinite;
background-size: 200% 200%;
padding: 20px;
}}
@keyframes gradientAnimation {{
0%, 100% {{
background-position: 0% 50%;
}}
50% {{
background-position: 100% 50%;
}}
}}
.content-container {{
max-width: 1200px;
margin: 0 auto;
width: 100%;
animation: fadeInUp 0.5s ease-out forwards;
}}
h1, h2 {{
h1 {{
font-size: 40px;
text-transform: uppercase;
text-align: center;
color: #333;
animation: fadeInUp 0.5s ease-out forwards;
opacity: 0;
}}
h2 {{
text-align: center;
color: #555;
animation: fadeInUp 0.5s ease-out forwards;
opacity: 0;
margin-top: 30px;
}}
p {{
text-align: center;
color: #777;
animation: fadeInUp 0.5s ease-out forwards;
opacity: 0;
}}
table {{
border-collapse: collapse;
width: 100%;
margin-top: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
border-radius: 5px;
overflow: hidden;
animation: fadeInUp 0.5s ease-out forwards;
opacity: 0;
}}
th, td {{
border: 1px solid #ddd;
padding: 8px;
padding: 12px 15px;
text-align: left;
}}
th {{
background-color: #f2f2f2;
color: #333;
font-weight: bold;
text-transform: uppercase;
border-bottom: 2px solid
{
self
.
BORDER_COLOR
}
;
}}
tr {{
background-color: white;
transition: background-color 0.3s ease;
}}
tr:nth-child(even) {{
background-color: #f9f9f9;
}}
tr:hover {{
background-color: #f5f5f5;
}}
.chart-container {{
margin
-top
: 30px;
margin: 30px
0
;
display: flex;
justify-content: center;
width: 100%;
animation: fadeInUp 0.5s ease-out forwards;
opacity: 0;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
border-radius: 5px;
overflow: hidden;
background-color: white;
padding: 20px;
box-sizing: border-box;
}}
.chart-container img {{
max-width: 100%;
height: auto;
}}
a {{
color:
#0066cc
;
color:
{
self
.
LINK_COLOR
}
;
text-decoration: none;
transition: color 0.3s ease;
}}
a:hover {{
text-decoration: underline;
color:
{
self
.
LINK_COLOR_HOVER
}
;
text-decoration: none;
}}
.timestamp {{
font-size: 12px;
color: #777;
text-align: center;
margin-top: 20px;
animation: fadeInUp 0.5s ease-out 0.7s forwards;
opacity: 0;
}}
.nav-button {{
margin-top: 30px;
padding: 1em 2em;
font-size: larger;
text-transform: uppercase;
width: 12em;
text-align: center;
border: none;
border-radius: 5px;
background: linear-gradient(135deg, #ffff1e, #ffc400);
color: black;
cursor: pointer;
box-shadow: 0 10px 20px rgba(255, 255, 30, 0.3);
animation: fadeInUp 0.5s ease-out 0.8s forwards;
opacity: 0;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
position: relative;
overflow: hidden;
}}
.nav-button:hover {{
box-shadow: 0 15px 30px rgba(255, 255, 30, 0.4);
transform: translateY(-2px);
}}
.nav-button::before {{
content:
""
;
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.7s ease;
}}
.nav-button:hover::before {{
left: 100%;
}}
@keyframes fadeInUp {{
from {{
opacity: 0;
transform: translateY(20px);
}}
to {{
opacity: 1;
transform: translateY(0);
}}
}}
</style>
</head>
<body>
<h1>Formula Student
{
self
.
competition_type
}
Elo Rankings</h1>
<p>Rankings based on
{
len
(
self
.
competition_history
)
}
competitions.</p>
<div class=
"
content-container
"
>
<h1>Formula Student electric Elo Rankings</h1>
<p>Rankings based on 126 competitions.</p>
<div class=
"
chart-container
"
>
<img src=
"
images/top_teams_elo.png
"
alt=
"
Top Teams Elo Chart
"
>
...
...
@@ -311,7 +476,14 @@ class EloSystem:
<img src=
"
images/elo_distribution.png
"
alt=
"
Elo Distribution
"
>
</div>
<p><small>Generated on {}</small></p>
<div class=
"
timestamp
"
>
<p>Generated on {}</p>
</div>
<div style=
"
display: flex; justify-content: center;
"
>
<a href=
"
index.html
"
><button class=
"
nav-button
"
>Back to Home</button></a>
</div>
</div>
</body>
</html>
"""
.
format
(
datetime
.
now
().
strftime
(
"
%Y-%m-%d %H:%M:%S
"
))
...
...
@@ -349,48 +521,80 @@ class EloSystem:
}}
h1, h2 {{
color: #333;
animation: fadeInUp 0.5s ease-out forwards;
}}
table {{
border-collapse: collapse;
width: 100%;
margin-top: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
border-radius: 5px;
overflow: hidden;
animation: fadeInUp 0.5s ease-out forwards;
opacity: 0;
}}
th, td {{
border: 1px solid #ddd;
padding: 8px;
padding: 12px 15px;
text-align: left;
}}
th {{
background-color: #f2f2f2;
color: #333;
font-weight: bold;
text-transform: uppercase;
border-bottom: 2px solid
{
self
.
BORDER_COLOR
}
;
}}
tr {{
background-color: white;
transition: background-color 0.3s ease;
}}
tr:nth-child(even) {{
background-color: #f9f9f9;
}}
tr:hover {{
background-color: #f5f5f5;
}}
p {{
animation: fadeInUp 0.5s ease-out forwards;
}}
.chart-container {{
margin-top: 30px;
display: flex;
justify-content: center;
width: 100%;
animation: fadeInUp 0.5s ease-out forwards;
opacity: 0;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
border-radius: 5px;
overflow: hidden;
background-color: white;
padding: 20px;
box-sizing: border-box;
}}
.chart-container img {{
max-width: 100%;
height: auto;
}}
a {{
color:
#0066cc
;
color:
{
self
.
LINK_COLOR
}
;
text-decoration: none;
transition: color 0.3s ease;
}}
a:hover {{
text-decoration: underline;
color:
{
self
.
LINK_COLOR_HOVER
}
;
text-decoration: none;
}}
.summary-stats {{
display: flex;
justify-content: space-around;
margin: 20px 0;
flex-wrap: wrap;
animation: fadeInUp 0.5s ease-out forwards;
}}
.stat-box {{
padding: 15px;
...
...
@@ -417,6 +621,16 @@ class EloSystem:
.negative {{
color: red;
}}
@keyframes fadeInUp {{
from {{
opacity: 0;
transform: translateY(20px);
}}
to {{
opacity: 1;
transform: translateY(0);
}}
}}
</style>
</head>
<body>
...
...
@@ -478,7 +692,7 @@ class EloSystem:
<tr>
<td>
{
comp
[
'
date
'
]
}
</td>
<td>
{
comp
[
'
competition
'
]
}
</td>
<td>
{
comp
[
'
rank
'
]
}
</td>
<td>
{
comp
[
'
rank
'
]
}
/
{
comp
[
'
participants
'
]
}
</td>
<td>
{
int
(
comp
[
'
previous_elo
'
])
}
</td>
<td>
{
int
(
comp
[
'
new_elo
'
])
}
</td>
<td class=
"
{
change_class
}
"
>
{
'
+
'
if
elo_change
>
0
else
''
}{
elo_change
:
.
1
f
}
</td>
...
...
@@ -507,7 +721,8 @@ class EloSystem:
def
create_top_teams_chart
(
self
):
"""
Create a bar chart of the top teams by Elo rating
"""
plt
.
figure
(
figsize
=
(
12
,
8
))
fig
=
plt
.
figure
(
figsize
=
(
12
,
8
))
ax
=
fig
.
add_axes
([
0.3
,
0.1
,
0.6
,
0.8
])
# Get top 15 teams by Elo
top_teams
=
sorted
(
self
.
teams
.
items
(),
key
=
lambda
x
:
x
[
1
][
'
elo
'
],
reverse
=
True
)[:
15
]
...
...
@@ -516,16 +731,56 @@ class EloSystem:
elo_ratings
=
[
team
[
1
][
'
elo
'
]
for
team
in
top_teams
]
# Create bar chart
bars
=
plt
.
barh
(
team_names
,
elo_ratings
,
color
=
'
skyblu
e
'
)
bars
=
plt
.
barh
(
team_names
,
elo_ratings
,
color
=
'
non
e
'
)
# Add Elo values at the end of each bar
for
i
,
bar
in
enumerate
(
bars
):
plt
.
text
(
bar
.
get_width
()
+
5
,
bar
.
get_y
()
+
bar
.
get_height
()
/
2
,
f
"
{
int
(
elo_ratings
[
i
])
}
"
,
va
=
'
center
'
)
# Create custom colormap
colors
=
[
hex_to_normalized_rgb
(
self
.
LINK_COLOR
),
hex_to_normalized_rgb
(
self
.
BORDER_COLOR
)]
# RGB values for #ffff1e and #ffc400
custom_yellow_cmap
=
LinearSegmentedColormap
.
from_list
(
'
custom_yellow
'
,
colors
)
for
bar
in
bars
:
# Get the coordinates of the bar
x0
,
y0
=
bar
.
get_xy
()
width
=
bar
.
get_width
()
height
=
bar
.
get_height
()
# Create a gradient for the bar
gradient
=
np
.
linspace
(
0
,
1
,
100
).
reshape
(
1
,
-
1
)
# Get colors from the colormap
colors
=
custom_yellow_cmap
(
gradient
)
# Set the bar's face color to the gradient
bar
.
set_facecolor
(
'
none
'
)
# Remove default color
# Add the gradient rectangle
gradient_rect
=
patches
.
Rectangle
((
x0
,
y0
),
width
,
height
,
linewidth
=
0
,
fill
=
True
,
facecolor
=
custom_yellow_cmap
(
0.5
))
# To create a gradient, add an axial gradient transform
gradient_rect
.
set_facecolor
(
'
none
'
)
ax
.
add_patch
(
gradient_rect
)
# Create multiple thin rectangles to simulate a gradient
steps
=
50
for
i
in
range
(
steps
):
left
=
x0
+
(
i
/
steps
)
*
width
rect_width
=
width
/
steps
color
=
custom_yellow_cmap
(
i
/
steps
)
rect
=
patches
.
Rectangle
((
left
,
y0
),
rect_width
,
height
,
linewidth
=
0
,
color
=
color
)
ax
.
add_patch
(
rect
)
plt
.
xlabel
(
'
Elo Rating
'
)
plt
.
title
(
'
Top Teams by Elo Rating
'
)
plt
.
tight_layout
()
#
plt.tight_layout()
# Save the chart
plt
.
savefig
(
os
.
path
.
join
(
self
.
output_directory
,
'
images
'
,
'
top_teams_elo.png
'
),
dpi
=
100
)
...
...
@@ -538,8 +793,64 @@ class EloSystem:
# Get all Elo ratings
elo_ratings
=
[
team_data
[
'
elo
'
]
for
team_data
in
self
.
teams
.
values
()]
# Create custom yellow gradient colormap (#ffff1e to #ffc400)
colors
=
[
hex_to_normalized_rgb
(
self
.
LINK_COLOR
),
hex_to_normalized_rgb
(
self
.
BORDER_COLOR
)]
custom_yellow_cmap
=
LinearSegmentedColormap
.
from_list
(
'
custom_yellow
'
,
colors
)
# Create histogram
plt
.
hist
(
elo_ratings
,
bins
=
20
,
color
=
'
skyblue
'
,
edgecolor
=
'
black
'
)
counts
,
bin_edges
,
_
=
plt
.
hist
(
elo_ratings
,
bins
=
20
,
alpha
=
0
)
# Draw bars with gradients
bin_width
=
bin_edges
[
1
]
-
bin_edges
[
0
]
for
i
in
range
(
len
(
counts
)):
# For each bar in the histogram
left
=
bin_edges
[
i
]
bottom
=
0
width
=
bin_width
height
=
counts
[
i
]
# Create the gradient effect by stacking multiple thin rectangles
gradient_steps
=
50
for
step
in
range
(
gradient_steps
):
# Calculate position and width of each thin rectangle
rect_left
=
left
rect_width
=
width
# Calculate height of this piece of the gradient
rect_bottom
=
bottom
+
(
step
/
gradient_steps
)
*
height
rect_height
=
height
/
gradient_steps
# Get color from our custom colormap
color
=
custom_yellow_cmap
(
step
/
gradient_steps
)
# Add the rectangle patch
rect
=
patches
.
Rectangle
(
(
rect_left
,
rect_bottom
),
rect_width
,
rect_height
,
linewidth
=
0
,
facecolor
=
color
,
edgecolor
=
None
)
plt
.
gca
().
add_patch
(
rect
)
# Add black edges to each bar
for
i
in
range
(
len
(
counts
)):
left
=
bin_edges
[
i
]
width
=
bin_width
height
=
counts
[
i
]
rect
=
patches
.
Rectangle
(
(
left
,
0
),
width
,
height
,
fill
=
False
,
edgecolor
=
'
black
'
,
linewidth
=
1
)
plt
.
gca
().
add_patch
(
rect
)
# Set axis limits to make sure the entire histogram is visible
plt
.
xlim
(
bin_edges
[
0
],
bin_edges
[
-
1
])
plt
.
ylim
(
0
,
max
(
counts
)
*
1.1
)
plt
.
xlabel
(
'
Elo Rating
'
)
plt
.
ylabel
(
'
Number of Teams
'
)
...
...
@@ -564,7 +875,7 @@ class EloSystem:
elo_values
=
[
entry
[
'
new_elo
'
]
for
entry
in
team_history
]
# 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
=
'
gray
'
)
# Add competition labels
for
i
,
entry
in
enumerate
(
team_history
):
...
...
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