Data Science 3: Seeing Force
Let’s start making some comparisons under the assumption that you’ve read part one and part two. I have that one clean lap in the Falcon GT-F 351 (I finally confirmed the specific model). I also have a lap from the cover car, a Mercedes-AMG One, and a 240Z which is just a complete and utter classic and great car. But they’re all very very different. The Forza games have a concept of a performance class which is subdivisions of the performance index. The elevator pitch is that cars are rated based on specs with regards to their performance for one lap around a computed “index track”. I can assure you that this is a very generalized concept and that highly-rated cars can be nearly undriveable however it is an interesting measure.
Table Of Contents
The usual suspects
Let’s take a look at the dossiers of these vehicles.
Specs | Falcon GT-F 351 | Fairlady 240Z | Mercedes-AMG One |
---|---|---|---|
Manufacturer | Ford | Nissan | Mercedes-AMG |
Model Year | 2015 | 1969 | 2022? |
Car Class | A | A | X |
Performance Index | 799 | 800 | 999 |
Driveline | FR | FR | AWD* |
Weight | 3,344 lb | 1,927 lb | 3,133 lb |
Aspiration | Supercharged | Turbocharged | Turbocharged/Electric |
Horsepower | 471 hp | 371 hp | 1,319 hp |
Torque | 420 lb-ft | 271 lb-ft | 770 lb-ft |
Ford Falcon GT-F 351
For my American friends this car would seem to be unusual at best and all wrong at worst. We had a Ford Falcon back in the day that was particularly different however the Falcon name survived in Australia as it had a distinguished racing career. The Falcon GT has seen several iterations that, most succinctly, can be paralleled with Ford America’s Special Vehicle Team (SVT) that brought us such performance vehicles as the Mustang SVT Cobra and SVT Lightning. In Australia that was Tickford Vehicle Engineering (TVE) that morphed in the Ford Tickford Experience (FTE) which was eventually bought out by motorsports design firm Prodrive and a change in the collaboration’s branding to Ford Performance Vehicles (FPV).
The Miami V8 is the Australian-sourced, modified, and assembled Ford Coyote V8 and the version of the motor in the GT-F outputs anywhere from 460-540hp based on some variability on boost pressure (it’s my assumption that the ECU opens the bypass valve to avoid boost pressures that could damage the engine during extremely warm Australian weather but I’ve found zero information on this “overboost” functionality of a technical nature–that’s normally not a thing in supercharged engines unless they have some type of variable pulley system on the supercharger belt–and there are clutched superchargers and that’s just kinda weird).
It’s a heavier car coming in at around 4000lbs (in real life) with independent rear suspension. It’s 194" long and 73.5" wide which, for Americans, would be roughly equivalent in overall dimension to a late model Nissan Altima. Driving it in game, the car feels eminently predictable and controllable.
Mercedes-AMG One
The ONE is a car that is still, mostly, in development by Mercedes-AMG and is billed to be the only road-going Formula 1 car as it’s using a significant amount of technology from the Mercedes Formula 1 engine and race team programs. There is still some ambiguity as to how much power and how large it will be so, I would assume, Playground Games just made some reasonable assumptions and Mercedes-AMG didn’t have an issue with that. It’s been reported that the turbocharged V6 will make 748hp without the aid of the electric hybrid system. The electric powertrain will be a multi-component system that will perform various functions as well as outputting power through two electric motors–one at either side of the front of the car. This is the All-Wheel Drive* system referenced above and, with motors at either side, would allow for some extremely clever power manipulation to improve handling. It’s been reported that the electric system will be worth 600hp on its own.
The car will likely be long and wider than any of the others here as it is aerodynamically focused and will be made in limited quantities as opposed to being a mass-produced consumer vehicle. There are various active aerodynamic elements on the car and Forza Horizon 5 allows you to toggle between an “attack mode” high-downforce setting and a “slick mode” (these names are what I’ve called them) low-downforce setting that increases top speed and would, by all accounts, increase fuel economy. Game-wise the car drives fantastically in “attack mode” and drives quite poorly in “slick mode” (at least in comparison). I’m sure my lack of ability does it no favors and this may, in fact, be “too much car” for me.
Nissan Fairlady 240Z
I saved this one for last because, in the real world, it’s definitely my favorite of the three. The Z cars have a wonderful following and, at least in the US, have carved themselves into a niche as one of the most fun cars to drive. Of the three this is the only one I’ve ever seen in person or driven and I can say that both experiences were great (it was an NA high compression carbureted L28 which isn’t exactly a powerhouse by any recent standards but the car is so light that everything feels immediate and the immediacy makes it a genuinely engaging experience even if you’re not hooning it around [which I was not]). There’s a lot of history to the Z brand, and its ideals, but it was modeled after the gentlemanly sports cars of the time but then produced to be affordable and accessible.
This example of the 240Z in the game is highly modified to get it into the A class and is a bit of a handful. With a 90in wheelbase and an overall length of 173 inches the car is quite compact and thusly has a low moment of inertia. This is desirable when the car can be thoroughly and appropriately controlled however, in this particular game with the Xbox One controller, I am not fully able to do that. I did crash it a few times and that tended to come from the rear of the car losing grip when applying throttle during turn exit.
The early Z-cars, however, are not just cult classics. They are true classics and a genuine driving experience.
The plot thickens
There are various ways that you can compare these vehicles. Why don’t we just throw a lap up from each on a line graph and see what we see:
import pandas as pd
from matplotlib import pyplot as plt
from matplotlib.pyplot import figure
import matplotlib.ticker as tick
import numpy as np
WELCOME_TO_EARTH = 9.80665
# read csv file
amg_one = pd.read_csv('/content/drive/MyDrive/ForzaData/amg_one.csv')
z_car = pd.read_csv('/content/drive/MyDrive/ForzaData/240z_3.csv')
ford_falcon = pd.read_csv('/content/drive/MyDrive/ForzaData/ford_falcon.csv')
race_amg_one = amg_one.loc[(amg_one['IsRaceOn'] == 1)]
race_240z = z_car.loc[(z_car['IsRaceOn'] == 1)]
race_falcon = ford_falcon.loc[(ford_falcon['IsRaceOn'] == 1)]
fig, (ax1, ax2, ax3) = plt.subplots(3,1)
fig.set_figheight(16)
fig.set_figwidth(10)
lap_amg = race_amg_one.loc[(race_amg_one['Lap'] == 2)]
lap_240z = race_240z.loc[(race_240z['Lap'] == 2)]
lap_falcon = race_falcon.loc[(race_falcon['Lap'] == 2)]
ax1.plot(lap_amg['CurrentLapTime'],lap_amg['AccelerationX'] / WELCOME_TO_EARTH, color='cyan',label="AMG One")
ax1.plot(lap_240z['CurrentLapTime'],lap_240z['AccelerationX'] / WELCOME_TO_EARTH, color='red',label="240z")
ax1.plot(lap_falcon['CurrentLapTime'],lap_falcon['AccelerationX'] / WELCOME_TO_EARTH, color='green', label="Falcon FPV")
ax1.set_xlabel("Lap Time")
ax1.set_ylabel("Acceleration X")
ax1.legend()
ax2.plot(lap_amg['CurrentLapTime'],lap_amg['AccelerationY'] / WELCOME_TO_EARTH, color='cyan',label="AMG One")
ax2.plot(lap_240z['CurrentLapTime'],lap_240z['AccelerationY'] / WELCOME_TO_EARTH, color='red',label="240z")
ax2.plot(lap_falcon['CurrentLapTime'],lap_falcon['AccelerationY'] / WELCOME_TO_EARTH, color='green', label="Falcon FPV")
ax2.set_xlabel("Lap Time")
ax2.set_ylabel("Acceleration Y")
ax2.legend()
ax3.plot(lap_amg['CurrentLapTime'],lap_amg['AccelerationZ'] / WELCOME_TO_EARTH, color='cyan',label="AMG One")
ax3.plot(lap_240z['CurrentLapTime'],lap_240z['AccelerationZ'] / WELCOME_TO_EARTH, color='red',label="240z")
ax3.plot(lap_falcon['CurrentLapTime'],lap_falcon['AccelerationZ'] / WELCOME_TO_EARTH, color='green', label="Falcon FPV")
ax3.set_xlabel("Lap Time")
ax3.set_ylabel("Acceleration Z")
ax3.legend()
def racetime_fmt(x, y):
minutes, seconds = divmod(x, 60)
return "{:.0f}".format(minutes) + ':' + "{:.0f}".format(seconds)
ax1.xaxis.set_major_formatter(tick.FuncFormatter(racetime_fmt))
ax2.xaxis.set_major_formatter(tick.FuncFormatter(racetime_fmt))
ax3.xaxis.set_major_formatter(tick.FuncFormatter(racetime_fmt))
ax.legend()
Lines, lines, everywhere some lines
This is a busy graph. This is combining multiple lines on one plot and multiple plots in one image.
This is busy at best and confusing at worst. How is it confusing? While the x-axis is consistent the y-axis changes and it’s auto-formatted based on the values. This does make the graphs look better, yes, however it would be inappropriate to draw any objective comparisons based on how these plots look (which, if I haven’t mentioned, often happens). If you’re thinking “hey, in Data Science 2: Forza Boogaloo we already combined these values into ‘Total Acceleration’” well, you’re right, but I started working off an older plot that I had already done for this one just to prove a point. You can see that the Ford Falcon and Nissan 240Z show some similar acceleration numbers at similar points in time (ignore the part where I crashed in the 240Z).
Histogram? No, thistogram
A different way of presenting this type of data with a bit more density would be a histogram. Not the bar chart type, but a two-dimensional histogram. For the uninitiated, a histogram has a concept of bins which are categories of values and plots based on frequency of hits in those bins. If you’re into photography or video/cinematography then you’re probably very well of the color histograms which would give you an idea of what luminosity the number of pixels per color would be in. If you do pursue those hobbies but aren’t familiar with histograms then you may possibly benefit from looking into it. Let’s plot a couple of 2D histograms to see what type of representation we can manage.
import pandas as pd
from matplotlib import pyplot as plt
from matplotlib.pyplot import figure
import matplotlib.ticker as tick
import numpy as np
WELCOME_TO_EARTH = 9.80665
# read csv file
ford_falcon = pd.read_csv('/content/drive/MyDrive/ForzaData/ford_falcon_video.csv')
race_falcon = ford_falcon.loc[(ford_falcon['IsRaceOn'] == 1)]
lap = race_falcon.loc[(race_falcon['Lap'] == 2)]
race_amg_one = amg_one.loc[(ford_falcon['IsRaceOn'] == 1)]
lap_amg = race_amg_one.loc[(race_falcon['Lap'] == 2)]
plt.style.use('default')
fig, (ax, ax2) = plt.subplots(1,2,tight_layout=False, sharex=True,sharey=True)
fig.set_size_inches(14,6, forward=True)
hist = ax2.hist2d(lap['AccelerationX'] / WELCOME_TO_EARTH, lap['AccelerationY'] / WELCOME_TO_EARTH, bins=40)
ax2.set_facecolor('#440154') #the histogram would have white borders since we scaled up the axes to match the AMG
hist2 = ax.hist2d(lap_amg['AccelerationX'] / WELCOME_TO_EARTH, lap_amg['AccelerationY'] / WELCOME_TO_EARTH, bins=40)
ax.set_title("Mercedes-AMG One")
ax.set_xlabel("Acceleration X (g)")
ax.set_ylabel("Acceleration Y (g)")
ax2.set_title("Ford Falcon GT-F 351")
ax2.set_xlabel("Acceleration X (g)")
ax2.set_ylabel("Acceleration Y (g)")
def racetime_fmt(x, y):
minutes, seconds = divmod(x, 60)
return "{:.0f}".format(minutes) + ':' + "{:.0f}".format(seconds)
fig.suptitle("Acceleration Event Plot")
fig.show()
What is the ‘symbology’ of it all?
So we’re seeing a graphical representation of measurements taken from the overhead view of the car. Had I been prepared I would have superimposed a picture of the car over the origin. I was not prepared. I may go back and update this, though, but imaged the car being placed in the origin and facing upwards (I think it’s upwards. I’ll verify the cardinality the next time I get an opportunity to record telemetry.)
You’ll note that I needed to set ax2.set_facecolor('#440154')
because I had also set sharex=True,sharey=True
when declaring subplots()
because I wanted them to have the same axes to be “apples to apples”. So the great part about sharing x and y is that you can easily visually determine groupings and magnitudes and their differences. Also, matplotlib will leave the range that isn’t used as the background color. There might be a way to ensure the entire plot is filled with the background color but I didn’t find it. The colors also display groupings–the brighter the color the more events were recording within those values. That’s the bins=40
values I was talking about–40 bins allows for more resolution in a very practical and layperson’s experience.
It’s obvious (I mean, it was already obvious) that the AMG One has a wider total range but they both roughly have a similar form of many values vertically down the middle which represent acceleration and braking in a straight line. Then there are the two “wings” with the larger magnitude values (the ones further from the origin which is the exact middle of the plot) expanding at the positive y values (I can assume that this is the braking events).
Histograms don’t give you a moment in time but they do give you an idea of overall moments–if that makes sense. A good example: the “wings” on the Falcon’s plot are somewhat even but with more values on the left “wing” (it’s got brighter colors). The AMG One’s left “wing” is much longer than its right “wing” which gives the impression that those left-hand turns are:
- More hardcore than their right-hand turn counterparts (with regards to acceleration on the two axes). A more specific assumption here might be that the cars start coming into the long left-handers with a lot of speed and need to start slowing down in the turn–the AMG One just does all of those things more than the Falcon.
- The Falcon has basically reached its limits on the right-hand turns as well. It can’t produce any more acceleration than it has on the right-hand turns. The AMG One is a far more capable machine based on this as well as it’s car class and performance index.
Enter the scatter plot
I don’t necessarily know that this is better or worse than the histogram but scatter plots are great for reasons yet to be demonstrated. It’s going to look similar to a histogram but with some fun, albeit not terribly informational, colors.
import pandas as pd
from matplotlib import pyplot as plt
from matplotlib.pyplot import figure
import matplotlib.ticker as tick
import numpy as np
import matplotlib as mpl # new import
WELCOME_TO_EARTH = 9.80665
# read csv file
ford_falcon = pd.read_csv('/content/drive/MyDrive/ForzaData/ford_falcon_video.csv')
race_falcon = ford_falcon.loc[(ford_falcon['IsRaceOn'] == 1)]
lap = race_falcon.loc[(race_falcon['Lap'] == 2)]
race_amg_one = amg_one.loc[(ford_falcon['IsRaceOn'] == 1)]
lap_amg = race_amg_one.loc[(race_falcon['Lap'] == 2)]
## Add total acceleration to dataframe
lap = lap.assign(TotalAcceleration=lambda x: np.sqrt(pow(x['AccelerationX'],2) + pow(x['AccelerationY'],2))/ WELCOME_TO_EARTH)
lap_amg = lap_amg.assign(TotalAcceleration=lambda x: np.sqrt(pow(x['AccelerationX'],2) + pow(x['AccelerationY'],2))/ WELCOME_TO_EARTH)
plt.style.use('default')
fig, (ax, ax2) = plt.subplots(1,2,tight_layout=False, sharex=True,sharey=True)
fig.set_size_inches(14,6, forward=True)
cmap = plt.cm.jet
norm = plt.Normalize(vmin=np.min(abs(lap['TotalAcceleration'])), vmax=np.max(abs(lap_amg['TotalAcceleration'])))
scatter = ax2.scatter(lap['AccelerationX'] / WELCOME_TO_EARTH, lap['AccelerationY'] / WELCOME_TO_EARTH, color=cmap(norm(abs(lap['TotalAcceleration'])) ))
scatter_amg = ax.scatter(lap_amg['AccelerationX'] / WELCOME_TO_EARTH, lap_amg['AccelerationY'] / WELCOME_TO_EARTH, color=cmap(norm(abs(lap_amg['TotalAcceleration']))))
ax.set_title("Mercedes-AMG One")
ax.set_xlabel("Acceleration X (g)")
ax.set_ylabel("Acceleration Y (g)")
ax2.set_title("Ford Falcon GT-F 351")
ax2.set_xlabel("Acceleration X (g)")
ax2.set_ylabel("Acceleration Y (g)")
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
plt.colorbar(sm, ax=[ax, ax2],ticks=[np.min(lap['TotalAcceleration']),np.mean(lap['TotalAcceleration']),np.mean(lap_amg['TotalAcceleration']),np.max(lap_amg['TotalAcceleration'])],label="Total Acceleration(G)",orientation="horizontal")
fig.suptitle("Acceleration Event Plot")
fig.show()
An IT guy, a python, and a matplotlib walk into a colorbar
I think that the colorbar is very cool. There are a few components to it. The ColorMap is actually crucial and can be changed. This page shows a very complicated way to find out what they look like–there could be a simple command like listing the ColorMaps but this didn’t happen for these docs. You can try using dir(plt.cm)
(when using plt
as imported in the above examples or the linked notebook) and it should provide some insight as to what you may have available to you. The linked documentation gives you visual indicators of what the ColorMaps look like. We’re using plt.cm.jet
in this example.
If you’ve worked in audio you’ve heard of “normalization”. This isn’t the database term but more the statistical term and, unfortunately, the audio term seems to have been co-opted somewhat. There is definitely several posts about the audio term and they’re of varying practical and factual utility (and yes, they’re independent values if you can dig that) but the idea, from my experience, is to bring the loudest peak to your normalization target. Or to use a metric like LUFS over a time (that’s the integrated
value and a subject for another post) but here, in the statistical concept of normalization, what we’re looking for is to take the range of our data and match the minimum and the maximum of our data to the minimum and the maximum values of our ColorMap.
So cheers to norm
which uses plt.Normalize
to take vmin
and vmax
and stretch those values out. Good and easy stuff. Does it follow a biased distribution? No. Is that fully necessary for a human do acquire information from it? Also no. We’re going to use the ticks to provide some context anyway. Or we already did. Time is a flat circle. We drop this normalization into the variable norm
and the color
parameter (remember that after the main positional parameters in Python you can just be like parameter=blah
and it’s all good–very Pythonic) of plotting the .scatter
is going to use cmap
to take a norm
of the specific value of the column TotalAcceleration
from the DataFrame. This means that you can actually map scatter plot colors to independent variables. In this case they end up like a very well organized rainbow because they’re governed by magnitude and the further out from the origin they get the more the magnitude is. No big deal but it’s not necessarily the best example. The colorbar gives us context for what those values that map the colors are.
We use the parameter label
for the plt.colorbar
function to put the title on it and that’s actually super important in providing context. In this case we use sm
to set up the spectrum of values (from norm
) and applying the same ColorMap cmap
to set the spectrum of the color bar. It makes sense when you think about it but not necessarily when you read it. Thanks, Python. We get an array of these values (probably an array to represent some type of data structure–at this point I’m 19 layers in and not asking many questions) as well as the parameter ax
which means “which axes are you applying this colorbar to?” and to that query we respond [ax, ax2]
because we want it to apply to both plots equally (since we’re comparing them–apples to apples). This is why we defined vmin
as the minimum total acceleration of the Ford Falcon and vmax
as the maximum acceleration of the Mercedes-AMG One. Really catch all of the spectrum there. Not throwing shade at the Falcon–I really like driving that car it’s just very much Class A.
Importantly we also use the color
parameter to ax.scatter
and ax2.scatter
to ensure that they use the Color Map that we (I) have decided on. Just be warned because in all these nested functions here be parenthesis mismatches. Ensure that if you get an error you are meticulous about your brackets, parenthesis, quotes, and commas. I’ve heard people say that Python is easier because it doesn’t require semi-colons but that’s simply not true and the language lends itself to be written with less lines but with more complexity–and maybe it’s more the culture around it than the language itself but here we are–and, aside from spaces issues (yes, spaces. I’ve already lived that so I’m good with it but just use tabs. And ensure your tabs are good. Really. This could be the source of your frustration–something simultaneously less obvious and infinitely more simple than parenthesis and brackets) this is the most common source of “hey what the hell does that error mean?” so keep your wits about you.
SO we’re using a colormap based on the largest range of both plots as well as applying that same colormap to both plots. This makes logical visual sense to me. Logical and visual sense isn’t always the purpose of a plot (check out a pie chart if you’re bored) but it’s what I’m striving for. Using ticks
we are able to add the np.mean
of both the Falcon and the One (there is no spoon). It’s clever and maybe it’s even cute but I’m not entirely convinced that it’s helpful.
Standard nerds
I don’t think it’d be wise to try to extend Hugo to make a heading a link so here’s the link for the heading. But we aren’t talk about Sonic fan art–we’re talking about Standard Deviation. The standard deviation is the average amount that we expect a point to deviate from the average (and that’s something that no one in a University can tell you) and has a long and storied history of coming from the “variance”. This website has a banging image of what a normal distribution looks like however the function that we’re using, by my research, takes a biased number. I’ll take it as it comes but that’s the vibe that I get based on the distribution of the numbers. Plot’em if you got’em.
If you got it better plot it
Another plot and it’s two of America’s most wanted (stats) (no they aren’t) (I’m reaching).
np.std(lap['TotalAcceleration'])
totalaccel_falcon = lap['TotalAcceleration'].to_numpy()
totalaccel_amg = lap_amg['TotalAcceleration'].to_numpy()
total_totals = np.append(totalaccel_amg, totalaccel_falcon)
std_dev = np.std(total_totals)
std_rounded = round(np.std(total_totals),3)
print (std_rounded) #debugging
mintick = np.min(lap['TotalAcceleration'])
maxtick = np.max(lap_amg['TotalAcceleration'])
ticks = [mintick, maxtick, np.mean(lap['TotalAcceleration']), np.mean(lap_amg['TotalAcceleration'])]
i = mintick
while i < maxtick - std_dev:
i = i + std_dev
ticks.append(round(i, 3))
So this gives us our ticks (yes, you’ll need to import and have many other variables set up–check the notebook at the bottom if you haven’t already). It’s both non-obvious and incredibly straightforward. np.std
gives you that hot stuff based off of a DataFrame column (in this case ‘TotalAcceleration’) and there are a few false starts before I figure it out. Debugging in the raw. total_totals
is the total compilation of TotalAcceleration
readings from both the Falcon and the One. std_dev
is, roughly and based on the very simple way of thinking about it, the average amount that you’d expect a point to differ from the average. So with the average and the standard deviation you can make some guesses. I’ve read that np.std
returns a biased number that assists with the distribution of your particular dataset. Put it into action:
But that’s actually turbo busy down there. Not super helpful. It’s also difficult to label things on a color bar (from what I’ve seen it’s non-obvious). We can use what we learned about annotations to spice up the plot while keeping specific scalars separate from the colorbar.
Just slap a label on it
import pandas as pd
from matplotlib import pyplot as plt
from matplotlib.pyplot import figure
import matplotlib.ticker as tick
import numpy as np
import matplotlib as mpl # new import
WELCOME_TO_EARTH = 9.80665
# read csv file
ford_falcon = pd.read_csv('/content/drive/MyDrive/ForzaData/ford_falcon_video.csv')
race_falcon = ford_falcon.loc[(ford_falcon['IsRaceOn'] == 1)]
lap = race_falcon.loc[(race_falcon['Lap'] == 2)]
race_amg_one = amg_one.loc[(ford_falcon['IsRaceOn'] == 1)]
lap_amg = race_amg_one.loc[(race_falcon['Lap'] == 2)]
## Add total acceleration to dataframe
lap = lap.assign(TotalAcceleration=lambda x: np.sqrt(pow(x['AccelerationX'],2) + pow(x['AccelerationY'],2))/ WELCOME_TO_EARTH)
lap_amg = lap_amg.assign(TotalAcceleration=lambda x: np.sqrt(pow(x['AccelerationX'],2) + pow(x['AccelerationY'],2))/ WELCOME_TO_EARTH)
plt.style.use('default')
fig, (ax, ax2) = plt.subplots(1,2,tight_layout=False, sharex=True,sharey=True)
fig.set_size_inches(14,6, forward=True)
cmap = plt.cm.jet
norm = plt.Normalize(vmin=np.min(abs(lap['TotalAcceleration'])), vmax=np.max(abs(lap_amg['TotalAcceleration'])))
scatter = ax2.scatter(lap['AccelerationX'] / WELCOME_TO_EARTH, lap['AccelerationY'] / WELCOME_TO_EARTH, color=cmap(norm(abs(lap['TotalAcceleration'])) ))
scatter_amg = ax.scatter(lap_amg['AccelerationX'] / WELCOME_TO_EARTH, lap_amg['AccelerationY'] / WELCOME_TO_EARTH, color=cmap(norm(abs(lap_amg['TotalAcceleration']))))
ax.set_title("Mercedes-AMG One")
ax.set_xlabel("Acceleration X (g)")
ax.set_ylabel("Acceleration Y (g)")
ax2.set_title("Ford Falcon GT-F 351")
ax2.set_xlabel("Acceleration X (g)")
ax2.set_ylabel("Acceleration Y (g)")
# colorbar setup
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
# colorbar ticks setup
np.std(lap['TotalAcceleration'])
totalaccel_falcon = lap['TotalAcceleration'].to_numpy()
totalaccel_amg = lap_amg['TotalAcceleration'].to_numpy()
total_totals = np.append(totalaccel_amg, totalaccel_falcon)
std_dev = np.std(total_totals)
std_rounded = round(np.std(total_totals),3)
print (std_rounded) #debugging
mintick = np.min(lap['TotalAcceleration'])
maxtick = np.max(lap_amg['TotalAcceleration'])
ticks = [mintick, maxtick, np.mean(total_totals)]
mean_std = np.mean(total_totals)
print (mean_std)
ticks.append(round(mean_std - std_dev, 3))
ticks.append(round(mean_std + std_dev, 3))
#i = mintick
#while i < maxtick - std_dev:
#i = i + std_dev
#ticks.append(round(i, 3))
# then the colorbar
plt.colorbar(sm, ax=[ax, ax2],ticks=ticks,label="Total Acceleration(G)",orientation="horizontal")
def racetime_fmt(x, y):
minutes, seconds = divmod(x, 60)
return "{:.0f}".format(minutes) + ':' + "{:.0f}".format(seconds)
xlimmleft, xlimright = ax.axes.get_xlim()
ylimbottom, ylimtop = ax.axes.get_ylim()
amg_txt = ax.text( s ="{:.2f} g".format(np.mean(lap_amg['TotalAcceleration'])), x=.09, y=.065, transform=ax.transAxes,
ha="center", va="center", rotation=0, size=15,
bbox=dict(boxstyle="square,pad=0.3", fc=cmap(norm(np.mean(lap_amg['TotalAcceleration']))),
ec=cmap(norm(np.mean(lap_amg['TotalAcceleration']))), lw=2))
falcon_txt = ax2.text( s ="{:.2f} g".format(np.mean(lap['TotalAcceleration'])), x=.09, y=.065, transform=ax2.transAxes,
ha="center", va="center", rotation=0, size=15,
bbox=dict(boxstyle="square,pad=0.3", fc=cmap(norm(np.mean(lap['TotalAcceleration']))),
ec=cmap(norm(np.mean(lap['TotalAcceleration']))), lw=2))
fig.suptitle("Acceleration Event Plot")
fig.show()
It’s easier to read now even if it’s less dense. But you’ll note that the labels on the bottom left are:
- Huge. Sorry. There’s a lot of formatting that can be done here and I just took the W and kept going.
- Color-mapped to the value. That’s cool, right? Gives it some visual interest.
It’s a bit more involved than I would like to get this box of text on the plot (honestly) and it comes from that transform=ax.transAxes
which allows you to place the box in the coordinate plane of the plot. There are other parameters that would normally allow you to do that but those would apply to label
and not text
because they’re sooo different. boxstyle
is where you can apply, you guess it, box styles, to the boundary box (bbox
). You can definitely use boundary boxes to put actual boundaries into your plot–that’s actually their purpose this is just hacky. The fc
parameter is “face color” and that’s where we use the cmap
object to apply norm
to the average of the acceleration values over the lap. Bingo bango ha-ta-ta.
In the long run
This is just one of many ways to display this type of data. Is it the best way? That depends on what you’re objective is. My objective was to cheese my way into the next post. That one will be fun, I promise. Maybe. I thought it was particularly cool. I also came across an interesting little post on Gearing and the Force of Acceleration by Marc Haibeck which, aside from being interesting, projects a 485hp car with 4.10 gears on drag radials with no tire slip at 0.67 g of peak acceleration and traction does make all the difference. There was probably a better place for this but call it an Easter egg for reading all the way to the end.
./content/posts/data-science-3-seeing-speed.md 9:1 warning Try to avoid using Microsoft.We first-person plural like 'Let's'. 9:218 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 9:309 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 9:453 warning Consider removing 'very'. Microsoft.Adverbs 9:459 warning Consider removing 'very'. Microsoft.Adverbs 9:799 error Punctuation should be inside Microsoft.Quotes the quotes. 9:813 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 9:846 warning Consider removing 'very'. Microsoft.Adverbs 9:879 warning ' highly-' doesn't need a Microsoft.Hyphens hyphen. 9:905 warning Consider removing 'nearly'. Microsoft.Adverbs 9:932 error Use 'it's' instead of 'it is'. Microsoft.Contractions 13:1 warning Try to avoid using Microsoft.We first-person plural like 'Let's'. 16:14 error Punctuation should be inside Microsoft.Quotes the quotes. 18:9 error Punctuation should be inside Microsoft.Quotes the quotes. 19:9 error Punctuation should be inside Microsoft.Quotes the quotes. 20:9 error Punctuation should be inside Microsoft.Quotes the quotes. 25:18 error Punctuation should be inside Microsoft.Quotes the quotes. 26:28 error Punctuation should be inside Microsoft.Quotes the quotes. 27:26 error Punctuation should be inside Microsoft.Quotes the quotes. 31:18 error Punctuation should be inside Microsoft.Quotes the quotes. 32:28 error Punctuation should be inside Microsoft.Quotes the quotes. 33:26 error Punctuation should be inside Microsoft.Quotes the quotes. 37:18 error Punctuation should be inside Microsoft.Quotes the quotes. 38:28 error Punctuation should be inside Microsoft.Quotes the quotes. 39:26 error Punctuation should be inside Microsoft.Quotes the quotes. 43:18 error Punctuation should be inside Microsoft.Quotes the quotes. 44:28 error Punctuation should be inside Microsoft.Quotes the quotes. 45:26 error Punctuation should be inside Microsoft.Quotes the quotes. 49:18 error Punctuation should be inside Microsoft.Quotes the quotes. 50:28 error Punctuation should be inside Microsoft.Quotes the quotes. 51:26 error Punctuation should be inside Microsoft.Quotes the quotes. 55:18 error Punctuation should be inside Microsoft.Quotes the quotes. 56:28 error Punctuation should be inside Microsoft.Quotes the quotes. 56:30 error More than 3 commas! marktoso.TresComas 57:26 error Punctuation should be inside Microsoft.Quotes the quotes. 61:17 error Punctuation should be inside Microsoft.Quotes the quotes. 62:28 error Punctuation should be inside Microsoft.Quotes the quotes. 63:26 error Punctuation should be inside Microsoft.Quotes the quotes. 67:17 error Punctuation should be inside Microsoft.Quotes the quotes. 68:28 error Punctuation should be inside Microsoft.Quotes the quotes. 69:26 error Punctuation should be inside Microsoft.Quotes the quotes. 73:17 error Punctuation should be inside Microsoft.Quotes the quotes. 74:28 error Punctuation should be inside Microsoft.Quotes the quotes. 75:26 error Punctuation should be inside Microsoft.Quotes the quotes. 82:17 warning Avoid using acronyms in a Microsoft.HeadingAcronyms title or heading. 83:5 warning Use first person (such as Microsoft.FirstPerson 'my') sparingly. 83:91 warning Try to avoid using Microsoft.We first-person plural like 'We'. 83:390 warning Try to avoid using Microsoft.We first-person plural like 'us'. 85:203 warning Use first person (such as Microsoft.FirstPerson 'my') sparingly. 85:314 warning Consider removing 'extremely'. Microsoft.Adverbs 87:158 warning Consider removing 'roughly'. Microsoft.Adverbs 89:124 error Use 'it's' instead of 'It is'. Microsoft.Contractions 91:14 warning Avoid using acronyms in a Microsoft.HeadingAcronyms title or heading. 92:18 error Use 'that's' instead of 'that Microsoft.Contractions is'. 92:313 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 92:842 warning Consider removing 'extremely'. Microsoft.Adverbs 94:70 error Use 'it's' instead of 'it is'. Microsoft.Contractions 94:571 warning Consider removing 'poorly'. Microsoft.Adverbs 94:620 warning Use first person (such as Microsoft.FirstPerson 'I'm') sparingly. 94:629 warning Use first person (such as Microsoft.FirstPerson 'my') sparingly. 94:711 warning Use first person (such as Microsoft.FirstPerson 'me') sparingly. 98:1 warning Use first person (such as 'I Microsoft.FirstPerson ') sparingly. 98:71 warning Use first person (such as Microsoft.FirstPerson 'my') sparingly. 98:156 warning Try to avoid using Microsoft.We first-person plural like 'US'. 98:306 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 98:611 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 98:614 error Use 'wasn't' instead of 'was Microsoft.Contractions not'. 100:272 warning Consider removing Microsoft.Adverbs 'thoroughly'. 100:374 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 100:406 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 102:28 error Use 'aren't' instead of 'are Microsoft.Contractions not'. 102:56 error Use 'they're' instead of 'They Microsoft.Contractions are'. 108:71 warning Try to avoid using Microsoft.We first-person plural like 'we'. 108:133 warning Try to avoid using Microsoft.We first-person plural like 'we'. 171:46 error Use 'how's' instead of 'How Microsoft.Contractions is'. 171:126 error In general, don't hyphenate Microsoft.Auto 'auto-formatted'. 171:201 error More than 3 commas! marktoso.TresComas 171:318 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 171:480 warning Try to avoid using Microsoft.We first-person plural like 'we'. 171:563 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 171:604 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 171:796 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 173:12 warning Don't use end punctuation in Microsoft.HeadingPunctuation headings. 174:366 warning Consider removing 'very'. Microsoft.Adverbs 174:459 error Don't spell out the number in Microsoft.Units 'of pixels'. 174:613 warning Try to avoid using Microsoft.We first-person plural like 'Let's'. 174:685 warning Try to avoid using Microsoft.We first-person plural like 'we'. 219:87 warning Consider removing 'really'. Microsoft.Adverbs 221:5 error Use 'what's' instead of 'What Microsoft.Contractions is'. 221:37 warning Don't use end punctuation in Microsoft.HeadingPunctuation headings. 222:4 warning Try to avoid using Microsoft.We first-person plural like 'we'. 222:104 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 222:120 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 222:187 error Use 'wasn't' instead of 'was Microsoft.Contractions not'. 222:373 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 224:17 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 224:73 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 224:150 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 224:193 error Punctuation should be inside Microsoft.Quotes the quotes. 224:269 warning Consider removing 'easily'. Microsoft.Adverbs 224:508 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 224:664 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 224:726 warning Consider removing 'very'. Microsoft.Adverbs 226:102 warning Consider removing 'roughly'. Microsoft.Adverbs 229:287 warning Consider using 'all' instead Microsoft.Wordiness of 'all of'. 233:1 warning Use first person (such as 'I Microsoft.FirstPerson ') sparingly. 233:214 warning Consider removing 'terribly'. Microsoft.Adverbs 283:66 warning Consider removing 'Very'. Microsoft.Adverbs 285:8 warning Avoid using acronyms in a Microsoft.HeadingAcronyms title or heading. 286:1 warning Use first person (such as 'I Microsoft.FirstPerson ') sparingly. 286:30 warning Consider removing 'very'. Microsoft.Adverbs 286:364 warning Consider removing 'very'. Microsoft.Adverbs 286:787 warning Try to avoid using Microsoft.We first-person plural like 'We'. 288:43 error Punctuation should be inside Microsoft.Quotes the quotes. 288:124 warning Consider removing Microsoft.Adverbs 'unfortunately'. 288:377 warning Use first person (such as Microsoft.FirstPerson 'my') sparingly. 288:615 warning Try to avoid using Microsoft.We first-person plural like 'we'. 288:657 warning Try to avoid using Microsoft.We first-person plural like 'our'. 288:707 warning Try to avoid using Microsoft.We first-person plural like 'our'. 290:244 warning Try to avoid using Microsoft.We first-person plural like 'We'. 290:308 warning Try to avoid using Microsoft.We first-person plural like 'we'. 290:347 warning Try to avoid using Microsoft.We first-person plural like 'We'. 290:544 warning Consider removing 'very'. Microsoft.Adverbs 290:817 warning Consider removing 'very'. Microsoft.Adverbs 290:1029 warning Try to avoid using Microsoft.We first-person plural like 'us'. 292:1 warning Try to avoid using Microsoft.We first-person plural like 'We'. 292:156 warning Try to avoid using Microsoft.We first-person plural like 'we'. 292:384 warning Try to avoid using Microsoft.We first-person plural like 'We'. 292:491 warning Use first person (such as Microsoft.FirstPerson 'I'm') sparingly. 292:647 warning Try to avoid using Microsoft.We first-person plural like 'we'. 292:678 warning Try to avoid using Microsoft.We first-person plural like 'we'. 292:727 warning Try to avoid using Microsoft.We first-person plural like 'we'. 292:915 warning Consider removing 'Really'. Microsoft.Adverbs 292:928 warning Consider using 'all' instead Microsoft.Wordiness of 'all of'. 292:991 warning Consider removing 'really'. Microsoft.Adverbs 292:1030 warning Consider removing 'very'. Microsoft.Adverbs 294:13 warning Try to avoid using Microsoft.We first-person plural like 'we'. 294:124 warning Try to avoid using Microsoft.We first-person plural like 'we'. 294:612 error Use 'we're' instead of 'we Microsoft.Contractions are'. 294:612 warning Try to avoid using Microsoft.We first-person plural like 'we'. 294:691 warning Use first person (such as Microsoft.FirstPerson 'I'm') sparingly. 294:758 warning Consider removing 'Really'. Microsoft.Adverbs 296:4 warning Try to avoid using Microsoft.We first-person plural like 'we'. 296:157 warning Use first person (such as Microsoft.FirstPerson 'me') sparingly. 296:275 warning Use first person (such as Microsoft.FirstPerson 'I'm') sparingly. 296:307 warning Try to avoid using Microsoft.We first-person plural like 'we'. 296:307 error Use 'we're' instead of 'we Microsoft.Contractions are'. 296:433 warning Use first person (such as Microsoft.FirstPerson 'I'm') sparingly. 299:1 warning Use first person (such as 'I Microsoft.FirstPerson ') sparingly. 299:162 warning Try to avoid using Microsoft.We first-person plural like 'we'. 299:198 warning Try to avoid using Microsoft.We first-person plural like 'we'. 299:340 warning Try to avoid using Microsoft.We first-person plural like 'we'. 299:504 error Punctuation should be inside Microsoft.Quotes the quotes. 299:657 warning Try to avoid using Microsoft.We first-person plural like 'we'. 299:673 warning Use first person (such as Microsoft.FirstPerson 'my') sparingly. 299:760 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 302:78 warning Use first person (such as Microsoft.FirstPerson 'I'm') sparingly. 326:17 warning Try to avoid using Microsoft.We first-person plural like 'us'. 326:20 warning Try to avoid using Microsoft.We first-person plural like 'our'. 326:348 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 326:508 warning Consider removing 'roughly'. Microsoft.Adverbs 326:533 warning Consider removing 'very'. Microsoft.Adverbs 328:143 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 328:165 warning Use first person (such as Microsoft.FirstPerson 'me') sparingly. 328:189 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 328:236 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 330:154 warning Try to avoid using Microsoft.We first-person plural like 'We'. 330:170 warning Try to avoid using Microsoft.We first-person plural like 'we'. 419:70 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 422:30 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 422:81 warning Consider removing 'honestly'. Microsoft.Adverbs 422:632 warning Try to avoid using Microsoft.We first-person plural like 'we'. 425:1 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 425:123 warning Use first person (such as Microsoft.FirstPerson 'My') sparingly. 425:150 warning Use first person (such as Microsoft.FirstPerson 'my') sparingly. 425:200 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 425:256 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly. 427:210 warning Use first person (such as ' I Microsoft.FirstPerson ') sparingly.✖ 51 errors, 127 warnings and 0 suggestions in 1 file.