Skip to content

Commit

Permalink
Add all old plots, along with a settings.json toggle setting
Browse files Browse the repository at this point in the history
  • Loading branch information
kangalio committed Nov 10, 2019
1 parent 91aa9ac commit 297bfd6
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 74 deletions.
136 changes: 93 additions & 43 deletions data_generators.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import datetime, timedelta
import numpy as np
from collections import Counter
import math
import numpy as np

import util
from util import parsedate, cache
Expand Down Expand Up @@ -140,48 +141,6 @@ def gen_plays_by_hour(xml):
#return {time(hour=i): num_plays[i] for i in range(24)}
return zip(*[(i, num_plays[i]) for i in range(24)])

def gen_idle_time_buckets(xml):
# Each bucket is 5 seconds. Total 10 minutes is tracked
buckets = [0] * 600

a, b = 0, 0

scores = []
for scoresat in xml.iter("ScoresAt"):
rate = float(scoresat.get("Rate"))
scores.extend(((score, rate) for score in scoresat.iter("Score")))

# Sort scores by datetime, oldest first
scores.sort(key=lambda pair: pair[0].findtext("DateTime"))

last_play_end = None
for score, rate in scores:
a+=1
datetime = util.parsedate(score.findtext("DateTime"))
survive_seconds = float(score.findtext("SurviveSeconds"))
#print(survive_seconds, rate)
length = timedelta(seconds=survive_seconds*rate)

#print("Datetime:", datetime)
#print("Play length:", str(length)[:-7], "(according to SurviveSeconds)")
if last_play_end is not None:
idle_time = datetime - last_play_end
if idle_time >= timedelta():
bucket_index = int(idle_time.total_seconds() // 5)
if bucket_index < len(buckets):
buckets[bucket_index] += 1
else:
#print("Negative idle time!")
b+=1

last_play_end = datetime + length
#print("Finished", last_play_end)
#print()

# ~ keys = [i * 5 for i in range(len(buckets))]
keys = range(len(buckets))
return (keys, buckets)

def gen_most_played_charts(xml, num_charts):
charts_num_plays = []
for chart in xml.iter("Chart"):
Expand Down Expand Up @@ -237,10 +196,101 @@ def calc_average_hours_per_day(xml, timespan=timedelta(days=365/2)):

return total_hours / timespan.days

# OPTIONAL PLOTS BEGINNING

def gen_hit_distribution(xml, analysis):
buckets = analysis.offset_buckets
return (list(buckets.keys()), list(buckets.values()))

def gen_idle_time_buckets(xml):
# Each bucket is 5 seconds. Total 10 minutes is tracked
buckets = [0] * 600

a, b = 0, 0

scores = []
for scoresat in xml.iter("ScoresAt"):
rate = float(scoresat.get("Rate"))
scores.extend(((score, rate) for score in scoresat.iter("Score")))

# Sort scores by datetime, oldest first
scores.sort(key=lambda pair: pair[0].findtext("DateTime"))

last_play_end = None
for score, rate in scores:
a+=1
datetime = util.parsedate(score.findtext("DateTime"))
survive_seconds = float(score.findtext("SurviveSeconds"))
#print(survive_seconds, rate)
length = timedelta(seconds=survive_seconds*rate)

#print("Datetime:", datetime)
#print("Play length:", str(length)[:-7], "(according to SurviveSeconds)")
if last_play_end is not None:
idle_time = datetime - last_play_end
if idle_time >= timedelta():
bucket_index = int(idle_time.total_seconds() // 5)
if bucket_index < len(buckets):
buckets[bucket_index] += 1
else:
#print("Negative idle time!")
b+=1

last_play_end = datetime + length
#print("Finished", last_play_end)
#print()

# ~ keys = [i * 5 for i in range(len(buckets))]
keys = range(len(buckets))
return (keys, buckets)

def gen_session_length(xml):
sessions = divide_into_sessions(xml)
x, y = [], []
for s in sessions:
x.append(s[0][1]) # Datetime [1] of first play [0] in session
y.append((s[-1][1]-s[0][1]).total_seconds() / 60) # Length in minutes

return (x, y)

def gen_session_plays(xml):
sessions = divide_into_sessions(xml)
nums_plays = [len(session) for session in sessions]
nums_sessions_with_x_plays = Counter(nums_plays)
return (list(nums_sessions_with_x_plays.keys()),
list(nums_sessions_with_x_plays.values()))

def gen_cb_probability(xml, analysis):
# {combo length: (base number, cb number)
base, cbs = analysis.combo_occurences, analysis.cbs_on_combo_len

# Find first combo that was never reached (0), starting with combo 1
max_combo = base.index(0, 1)
result = {i: int(cbs[i]/base[i]) for i in range(max_combo)[:10] if base[i] >= 0}
x_list = range(max_combo)
return (x_list, [cbs[i]/base[i] for i in x_list])

def gen_plays_per_week(xml):
datetimes = [parsedate(s.findtext("DateTime")) for s in xml.iter("Score")]
datetimes.sort()

weeks = {}
week_end = datetimes[0]
week_start = week_end - timedelta(weeks=1)
i = 0
while i < len(datetimes):
if datetimes[i] < week_end:
weeks[week_start] += 1
i += 1
else:
week_start += timedelta(weeks=1)
week_end += timedelta(weeks=1)
weeks[week_start] = 0

return (list(weeks.keys()), list(weeks.values()))

# OPTIONAL PLOTS END

def calc_ratings_for_sessions(xml):
if cache("calc_ratings_for_sessions"):
return cache("calc_ratings_for_sessions")
Expand Down
16 changes: 11 additions & 5 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def __init__(self, state):

# Start
w, h = 1600, 2500
if state.enable_all_plots: h = 3800 # More plots -> more room
root.setMinimumSize(1000, h)
window.resize(w, h)
self.window.show()
Expand Down Expand Up @@ -113,7 +114,7 @@ def setup_widgets(self, layout):
lambda: QMessageBox.about(None, "About", ABOUT_TEXT))

# Add plot frame (that thing that contains all the plots)
self.plotter = Plotter(infobar)
self.plotter = Plotter(infobar, self.state.enable_all_plots)
layout.addWidget(self.plotter.frame)

# Returns path to Etterna.xml
Expand All @@ -136,13 +137,15 @@ def choose_replays(self):
class Application:
etterna_xml = None
replays_dir = None
enable_all_plots = None
ui = None
plotter = None

def __init__(self):
self.enable_all_plots = False # Default value
self.load_settings() # Apply settings
self.ui = UI(self) # Init UI
self.plotter = self.ui.plotter
self.load_settings() # Apply settings

# If Etterna.xml isn't already defined, search it
if self.etterna_xml is None:
Expand Down Expand Up @@ -225,8 +228,10 @@ def load_settings(self):

try:
settings = json.load(open(SETTINGS_PATH))
if settings.get("enable-all-plots"):
self.enable_all_plots = settings["enable-all-plots"]
self.etterna_xml = settings["etterna-xml"]
if settings["replays-dir"]:
if not settings.get("replays-dir") is None:
self.set_replays(settings["replays-dir"])
except Exception as e:
util.logger.exception("Loading settings")
Expand All @@ -239,9 +244,10 @@ def write_settings(self):
try:
settings = {
"etterna-xml": self.etterna_xml,
"replays-dir": self.replays_dir
"replays-dir": self.replays_dir,
"enable-all-plots": self.enable_all_plots,
}
json.dump(settings, open(SETTINGS_PATH, "w"))
json.dump(settings, open(SETTINGS_PATH, "w"), indent=4)
except Exception as e:
util.logger.exception("Writing settings")
msgbox = QMessageBox.warning(None, "Warning",
Expand Down
38 changes: 28 additions & 10 deletions plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def __init__(self, plot, data_generator, analysis_requirement):
self.analysis_requirement = analysis_requirement

class Plotter:
def __init__(self, infobar):
def __init__(self, infobar, enable_all_plots):
frame = PlotFrame(infobar)
self.frame = frame

Expand Down Expand Up @@ -93,15 +93,33 @@ def __init__(self, infobar):
plots.append(PlotEntry(_, g.gen_session_rating_improvement, "no"))
self.frame.next_row()

# ~ _ = Plot(self, frame, 30, title="Distribution of hit offset")
# ~ _.set_args(cmap[6], type_="bar")
# ~ plots.append(PlotEntry(_, g.gen_hit_distribution, "yes"))
# ~ self.frame.next_row()

# ~ _ = Plot(self, frame, 30, title="Idle time between plays")
# ~ _.set_args(cmap[6], type_="bar")
# ~ plots.append(PlotEntry(_, g.gen_idle_time_buckets, "no"))
# ~ self.frame.next_row()
if enable_all_plots:
_ = Plot(self, frame, 30, title="Distribution of hit offset")
_.set_args(cmap[6], type_="bar")
plots.append(PlotEntry(_, g.gen_hit_distribution, "yes"))

_ = Plot(self, frame, 30, title="Idle time between plays (a bit broken)")
_.set_args(cmap[6], type_="bar")
plots.append(PlotEntry(_, g.gen_idle_time_buckets, "no"))
self.frame.next_row()

_ = Plot(self, frame, 30, title="CB probability based on combo length")
_.set_args(cmap[6], type_="bar")
plots.append(PlotEntry(_, g.gen_cb_probability, "yes"))

_ = Plot(self, frame, 30, title="Number of sessions with specific score amount")
_.set_args(cmap[6], type_="bar")
plots.append(PlotEntry(_, g.gen_session_plays, "no"))
self.frame.next_row()

_ = Plot(self, frame, 30, flags="time_xaxis", title="Session length over time")
_.set_args(cmap[6])
plots.append(PlotEntry(_, g.gen_session_length, "no"))

_ = Plot(self, frame, 30, flags="time_xaxis", title="Number of scores each week")
_.set_args(cmap[6], type_="bar", width=604800*0.8)
plots.append(PlotEntry(_, g.gen_plays_per_week, "no"))
self.frame.next_row()

_ = Plot(self, frame, 30, title="Number of plays per hour of day")
_.set_args(cmap[4], type_="bar")
Expand Down
37 changes: 21 additions & 16 deletions replays_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class ReplaysAnalysis:
longest_combo = [0, None] # Combo variables are lists of `[combo length, chart]`
longest_mcombo = [0, None]
num_near_hits = 0

combo_occurences = [0] * 10000
cbs_on_combo_len = [0] * 10000

# This function is responsible for replay analysis. Every chart that
# uses replay data uses the data generated from this function.
Expand All @@ -36,7 +39,8 @@ def do_combo_end(combo, longest):
replay = util.read_replay(replays, score.get("Key"))
if replay is None:
continue


r.combo_occurences[0] += 1 # I have no idea
previous_time = 0
num_total = 0
num_manipulated = 0
Expand All @@ -59,21 +63,22 @@ def do_combo_end(combo, longest):
bucket_key = round(offset * 1000)
r.offset_buckets[bucket_key] = r.offset_buckets.get(bucket_key, 0) + 1

if column < 4: # Ignore 6-and-up-key scores
if abs(offset) > 0.09:
do_combo_end(combo, r.longest_combo)
combo = 0
r.cbs_per_column[column] += 1
else:
combo += 1

if abs(offset) > 0.0225:
do_combo_end(mcombo, r.longest_mcombo)
mcombo = 0
else:
mcombo += 1

r.notes_per_column[column] += 1
if abs(offset) > 0.09:
do_combo_end(combo, r.longest_combo)
r.cbs_on_combo_len[combo] += 1
combo = 0
if column < 4: r.cbs_per_column[column] += 1
else:
combo += 1
r.combo_occurences[combo] += 1

if abs(offset) > 0.0225:
do_combo_end(mcombo, r.longest_mcombo)
mcombo = 0
else:
mcombo += 1

if column < 4: r.notes_per_column[column] += 1

num_total += 1
do_combo_end(combo, r.longest_combo)
Expand Down

0 comments on commit 297bfd6

Please sign in to comment.