Understanding The Data -- Kovaaks Aim Trainer
A Deep Dive into the automatically saved csvs from Kovaaks Aim Trainer
Kovaaks
Kovaaks is a "game" that allows to directly practice mouse control in 3d fps games. There are hundreds of different mini-games to practice with, each having a different focus.
This guide is best for getting perspective or understanding how and why to use Kovaaks
File Names
Kovaaks has a great feature in which it saves every mini-game's stats to a csv. The format of the csv's names is as follows
<scenario name> - <Challenge or Freeplay> - YYYY.MM.DD-HH.MM.SS Stats.csv
Example:
Tile Frenzy - Challenge - 2020.12.14-08.46.00 Stats.csv
Some of the scenario names also have dashes so just checking against the first dash will not work
Tile Frenzy - Strafing - 03 - Challenge - 2020.12.14-08.34.31 Stats.csv
import pandas as pd
import matplotlib.pyplot as plt
from urllib.request import urlopen
from io import StringIO
import plotly.express as px
from IPython.display import HTML
Each part of the data has different formats and headers. Here are the Headers/keys in python
keys_kills=["Date","Kill #","Timestamp","Bot","Weapon","TTK","Shots","Hits","Accuracy","Damage Done","Damage Possible","Efficiency","Cheated"]
keys_weapon=["Date","Weapon","Shots","Hits","Damage Done","Damage Possible"]
keys_info=["Date","Kills","Deaths","Fight Time","Avg TTK","Damage Done","Damage Taken","Midairs","Midaired","Directs","Directed","Distance Traveled","Score","Scenario","Hash","Game Version","Challenge Start","Input Lag","Max FPS (config)","Sens Scale","Horiz Sens","Vert Sens","FOV","Hide Gun","Crosshair","Crosshair Scale","Crosshair Color","Resolution","Avg FPS","Resolution Scale"]
keys_info_no_colon=["Resolution","Avg FPS","Resolution Scale"]
#HELPERS
def split_format_file(section, output, date):
split_section = section.split('\n')
# if output == "":
# output = split_section[0]
# TODO: Add date to each line
for i in range(len(split_section[1:])):
if split_section[i+1][-1] == ',':
split_section[i+1] = split_section[i+1][:-1]
split_section[i+1] = date + "," + split_section[i+1]
section = '\n'.join(split_section[1:])
output = output + '\n' + section
return output
def format_info(info, output, date):
info_lines = info.split('\n')
data = []
for key in keys_info:
if key == "Date":
found_key = True
data.append(date)
else:
found_key = False
for line in info_lines:
if any(key in line for key in keys_info_no_colon):
split_line = line.split(',')
if len(split_line) > 1:
if split_line[0] == key:
found_key = True
data.append(split_line[1])
else:
split_line = line.split(':', 1)
if len(split_line) > 1:
if split_line[0] == key:
found_key = True
data.append(split_line[1][1:])
if not found_key:
data.append('')
output = output + '\n' + ','.join(data)
return output
# Current online directory for my stats
stat_dir = "https://jprier.github.io/stats/"
stat_filenames_url = "https://jprier.github.io/stats/filenames.txt"
stat_filenames = urlopen(stat_filenames_url).read().decode('utf-8').split('\n')
kills = ','.join(keys_kills)
weapon = ','.join(keys_weapon)
info = ','.join(keys_info)
for filename in stat_filenames:
# TODO: parse filename for challenge name and date
try:
filename = filename.replace(' ', '%20')
file = urlopen(stat_dir + filename).read().decode('utf-8').split('\n\n')
if len(file) > 1:
date = filename.split('%20')[-2]
# TODO: Add challenge name and date to each as columns
kills = split_format_file(file[0], kills, date)
# file[1] --> df_weapon
weapon = split_format_file(file[1], weapon, date)
# file[2,3] --> df_info
info = format_info(file[2]+"\n"+file[3], info, date)
except Exception as err:
print(err)
df_kills = pd.read_csv(StringIO(kills), sep=",")
df_weapons = pd.read_csv(StringIO(weapon), sep=",")
df_info = pd.read_csv(StringIO(info), sep=",")
df_kills["Date"] = pd.to_datetime(df_kills.Date, format='%Y.%m.%d-%H.%M.%S')#df_kills["Date"].dt.strftime("%Y.%d.%m-%H.%M.%S")
df_weapons["Date"] = pd.to_datetime(df_weapons.Date, format='%Y.%m.%d-%H.%M.%S')#df_weapons["Date"].dt.strftime("%Y.%d.%m-%H.%M.%S")
df_info["Date"] = pd.to_datetime(df_info.Date, format='%Y.%m.%d-%H.%M.%S')#df_info["Date"].dt.strftime("%Y.%d.%m-%H.%M.%S")
scenarios = df_info['Scenario'].unique()
scenario, scenarios = scenarios[0], scenarios[1:]
df_info_max = df_info.loc[df_info['Scenario'] == scenario].resample('D')['Score'].agg(['max'])
df_info_max['Scenario'] = scenario
for scenario in scenarios:
df_info_max_scenario = df_info.loc[df_info['Scenario'] == scenario].resample('D')['Score'].agg(['max'])
df_info_max_scenario = df_info_max_scenario[df_info_max_scenario['max'].notna()]
if df_info_max_scenario.size > 3:
df_info_max_scenario['Scenario'] = scenario
df_info_max = df_info_max.append(df_info_max_scenario)
with pd.option_context('display.max_rows', 10, 'display.max_columns', None):
display(df_info_max)
fig = px.line(df_info_max, x=df_info_max.index, y="max", color='Scenario')
fig1 = px.scatter(df_info, x=df_info.index, y="Score", trendline='lowess', color='Scenario')