From 02fd1b64e7a8f8470c8a9d458c16092d2479bac2 Mon Sep 17 00:00:00 2001 From: Stepan Bahnik Date: Sun, 10 Aug 2014 20:03:21 +0200 Subject: [PATCH] added strategies function, add tags tool --- Stuff/Modules/cm.py | 80 ++++++++++++++++++++++++++++++++++++++++----- Stuff/Modules/controller.py | 1 + Stuff/Modules/explorer.py | 16 +++++++-- Stuff/Modules/graphs.py | 2 +- Stuff/Modules/menu.py | 18 +++++++--- Stuff/Modules/parameters.py | 5 +-- Stuff/Modules/processor.py | 2 +- Stuff/Modules/tools.py | 25 ++++++++++++++ Stuff/Modules/version.py | 2 +- 9 files changed, 131 insertions(+), 20 deletions(-) diff --git a/Stuff/Modules/cm.py b/Stuff/Modules/cm.py index 5088382..dbe53b1 100644 --- a/Stuff/Modules/cm.py +++ b/Stuff/Modules/cm.py @@ -949,10 +949,10 @@ def removeReflections(self, points = None, deleteSame = True, bothframes = True) def countBadPoints(self, time = 20, startTime = 0): + start = self.findStart(startTime) + time *= 60000 startTime *= 60000 - - start = self.findStart(startTime) count = 0 bad = 0 @@ -965,9 +965,8 @@ def countBadPoints(self, time = 20, startTime = 0): bad += 1 proportion = (bad / count) * 100 - - return round(proportion, 2) - + return format(proportion, "0.2f") + def countOutsidePoints(self, time = 20, startTime = 0, distance = 1): "returns number of data points where an animal was outside the arena" @@ -1025,6 +1024,7 @@ def findStart(self, startTime): def recognizeAfterShockStrategy(self, i0, i1, minAngle): + "characterizes strategy after a shock" cx, cy = self.centerX, self.centerY x0, y0 = self.data[i0][self.indices] x1, y1 = self.data[i1][self.indices] @@ -1041,6 +1041,7 @@ def recognizeAfterShockStrategy(self, i0, i1, minAngle): def recognizeStrategy(self, i0, i1, minSpeed, percentSize): + "characterizes strategy in absence of a shock" x0, y0 = self.data[i0][self.indices] x1, y1 = self.data[i1][self.indices] t0, t1 = self.data[i0][1], self.data[i1][1] @@ -1066,8 +1067,19 @@ def recognizeStrategy(self, i0, i1, minSpeed, percentSize): def getStrategies(self, time = 20, startTime = 0, rows = 25, minSpeed = 10, minAngle = 7, - borderPercentSize = 50, indices = False): - i1, t1 = self.data[self.findStart(time)][0:2] + borderPercentSize = 50, indices = False, summary = True): + """characterizes strategies in a given time interval + rows - number of rows in a bin + minSpeed - minimal speed counted as movement in absence of shock + minAngle - minimal angular speed counted as movement after shock + borderPercentSize - boundary for center area + indices - return indices or times + summary - return summary for results or dict with indices or times + """ + try: + i1, t1 = self.data[self.findStart(time)][0:2] + except IndexError: + i1, t1 = self.data[-1][0:2] time = time * 60000 start = self.findStart(startTime) i0, t0 = self.data[start][0:2] @@ -1120,11 +1132,63 @@ def getStrategies(self, time = 20, startTime = 0, rows = 25, minSpeed = 10, minA else: t = self.data[i1 - 1][1] strategies[lastStrategy].append((beginning, t)) + + if summary: + order = ("reaction_counterclockwise", "reaction_clockwise", "no_reaction", + "counterclockwise", "clockwise", "immobile", "center") + times = [sum([i[1] - i[0] for i in strategies[s]]) for s in order] + sumOfTimes = sum(times) + proportions = [format(time / sumOfTimes, "0.4f") for time in times] + return "|".join(proportions) + else: + return strategies + + + def getProportionOfStrategies(self, time = 20, startTime = 0, numerator = [], denominator = []): + "returns proportion of times of strategies in nominator and denominator" + strategies = self.getStrategies(time, startTime) + num = 0 + denom = 0 + for strategy in numerator: + num += sum([i[1] - i[0] for i in strategies[strategy]]) + for strategy in denominator: + denom += sum([i[1] - i[0] for i in strategies[strategy]]) + + return format(num / denom, "0.3f") + + + def getRotationSpeed(self, time = 20, startTime = 0, rows = 25): + "returns speed of arena rotation in degrees per minute" + start = self.findStart(startTime) + end = self.findStart(time) + + cx, cy = self.centerX, self.centerY + speeds = [] + + ax0, ay0 = self.data[start][7:9] + t0, rx0, ry0 = self.data[start][1:4] + for content in self.data[(start+rows):end:rows]: + ax1, ay1 = content[7:9] + t1, rx1, ry1 = content[1:4] + + arenaAngle = ((degrees(atan2(ax1 - cx, ay1 - cy + 0.0000001)) - + degrees(atan2(ax0 - cx, ay0 - cy + 0.0000001)) + 180) % 360) - 180 + roomAngle = ((degrees(atan2(rx1 - cx, ry1 - cy + 0.0000001)) - + degrees(atan2(rx0 - cx, ry0 - cy + 0.0000001)) + 180) % 360) - 180 + speeds.append(((roomAngle - arenaAngle) * 60000) / (t1 - t0)) + + ax0, ay0, t0, rx0, ry0 = ax1, ay1, t1, rx1, ry1 + + if speeds: + return format(sum(speeds) / len(speeds), "0.1f") + else: + return "NA" + - return strategies def main(): + return start = 9 end = 12 diff = end - start diff --git a/Stuff/Modules/controller.py b/Stuff/Modules/controller.py index 5872deb..45d5a3e 100644 --- a/Stuff/Modules/controller.py +++ b/Stuff/Modules/controller.py @@ -553,6 +553,7 @@ def assessImportance(self, cm, control, file): value = results elif method == "Bad Points": + results = float(results) if results > 10: importance = "Problem" elif results > 5: diff --git a/Stuff/Modules/explorer.py b/Stuff/Modules/explorer.py index 3871240..c8e49a4 100644 --- a/Stuff/Modules/explorer.py +++ b/Stuff/Modules/explorer.py @@ -27,6 +27,7 @@ import os.path +from showtracks import ShowTracks from filestorage import FileStorageFrame from commonframes import TimeFrame, returnName from image import SVG @@ -995,13 +996,15 @@ def graphPopUp(self, event): def parametersPopUp(self, event): "shows menu for selection of parameters to be shown in parameters frame" menu = Menu(self, tearoff = 0) - notAvailable = ["Total distance", "Entrances", "Time in sectors", "Time in quadrants"] + notAvailable = ["Total distance", "Entrances", "Time in sectors", "Time in quadrants", + "Strategies"] + available = ["Bad points", "Outside points", "Reflections"] menu.add_radiobutton(label = "Don't show anything", variable = self.selectedParameter, value = "nothing", command = self._changedSelectedParameter) menu.add_separator() for name, par in m.parameters.items(): - if par.group not in ["info", "custom"] and name not in notAvailable: + if par.group not in ["info", "custom"] and name not in notAvailable or name in available: menu.add_radiobutton(label = 'Show {}'.format(name.lower()), variable = self.selectedParameter, value = name, command = self._changedSelectedParameter) @@ -1024,7 +1027,8 @@ def _changedSelectedParameter(self): "Time": "t.", "time": "t.", "Circular": "Circ.", - "variance": "var." + "variance": "var.", + "points": "pts." } for key, value in mapping.items(): name = name.replace(key, value) @@ -1324,6 +1328,11 @@ def refresh(self): "refreshes the tree after adding a comment" self.root._showComment(self.selected) self.drawTree(selected = self.selected) + + + def _showTrack(self, file): + "opens ShowTracks; accessed by right click on file" + ShowTracks(self, self.fileStorage.arenafiles, file) def popUp(self, event): @@ -1340,6 +1349,7 @@ def popUp(self, event): else: menu.add_command(label = "Add tag", command = lambda: self.tagFun(index = int(item))) + menu.add_command(label = "Show track", command = lambda: self._showTrack(file)) menu.post(event.x_root, event.y_root) diff --git a/Stuff/Modules/graphs.py b/Stuff/Modules/graphs.py index 64b6017..188760a 100644 --- a/Stuff/Modules/graphs.py +++ b/Stuff/Modules/graphs.py @@ -260,7 +260,7 @@ def distance(line): strategies = cm.getStrategies(time = self.maxTime / 60000, startTime = self.minTime / 60000, rows = rows, minSpeed = minSpeed, minAngle = minAngle, - borderPercentSize = borderPercentSize) + borderPercentSize = borderPercentSize, summary = False) colors = {"counterclockwise": "green", "clockwise": "dodger blue", "no_reaction": "deep pink", diff --git a/Stuff/Modules/menu.py b/Stuff/Modules/menu.py index 05d2974..a39c1c7 100644 --- a/Stuff/Modules/menu.py +++ b/Stuff/Modules/menu.py @@ -31,7 +31,7 @@ from optionwrite import optionWrite from optionget import optionGet from helpcmm import HelpCM -from tools import saveFileStorage, loadFileStorage +from tools import saveFileStorage, loadFileStorage, addTags from window import placeWindow from filestorage import FileStorage import version @@ -50,14 +50,14 @@ def __init__(self, root): self.menu_file = Menu(self) self.menu_options = Menu(self) - #self.menu_tools = Menu(self) # for future + self.menu_tools = Menu(self) self.menu_task = Menu(self) self.menu_help = Menu(self) menuWidth = 8 self.add_cascade(menu = self.menu_file, label = "{:^{}}".format("File", menuWidth)) self.add_cascade(menu = self.menu_options, label = "{:^{}}".format("Options", menuWidth)) - #self.add_cascade(menu = self.menu_tools, label = "{:^{}}".format("Tools", menuWidth)) + self.add_cascade(menu = self.menu_tools, label = "{:^{}}".format("Tools", menuWidth)) self.add_cascade(menu = self.menu_task, label = "{:^{}}".format("Task", menuWidth)) self.add_cascade(menu = self.menu_help, label = "{:^{}}".format("Help", menuWidth)) @@ -73,6 +73,7 @@ def __init__(self, root): self.menu_options.add_command(label = "General options", command = self.generalOptions) self.menu_options.add_separator() self.menu_options.add_command(label = "Reset all options", command = self.resetOptions) + self.menu_tools.add_command(label = "Add tags", command = self.addTagsHelper) for task, name in m.fullname.items(): self.menu_task.add_radiobutton(label = name, variable = self.task, value = task, command = self.changedTask) @@ -107,6 +108,13 @@ def saveLoadedFiles(self): def loadSavedFiles(self): loadFileStorage(self.root) + def addTagsHelper(self): + try: + addTags(self.root) + except Exception as e: + messagebox.showinfo(message = "Sorry, something went wrong.", detail = e, + title = "Error", icon = "error") + def resetOptions(self): text = ("Are you sure that you want to reset all options (including parameter settings)" " to default settings?") @@ -278,7 +286,9 @@ def checkNewVersion(self): newVersion = self.returnSiteContent(url).split(".") for i in range(3): if int(newVersion[i]) > int(self.version[i]): - return newVersion + return newVersion + elif int(newVersion[i]) < int(self.version[i]): + return None else: return None diff --git a/Stuff/Modules/parameters.py b/Stuff/Modules/parameters.py index 9bba1a9..5a51ac4 100644 --- a/Stuff/Modules/parameters.py +++ b/Stuff/Modules/parameters.py @@ -107,12 +107,13 @@ def __init__(self): "borderPercentSize": (Opt('borderPercentSizeStrategies', 50, ['int', 'float']), "Annulus width [in percents]") }) - self["Bad points"] = Par("countBadPoints", "info", {}) + self["Percent bad points"] = Par("countBadPoints", "info", {}) self["Reflections"] = Par("countReflections", "info", {}) self["Outside points"] = Par("countOutsidePoints", "info", { "distance": (Opt('OutsidePointsDistance', 1, 'int'), "Distance from margin counted [in pixels]"), }) + self["Rotation speed"] = Par("getRotationSpeed", "info", {}) #self.findParameters() @@ -223,7 +224,7 @@ class ParametersCMSF(OrderedDict): def __init__(self): super().__init__() for name, parameter in ParametersCM().items(): - if name not in ("Room frame filename",): + if name not in ("Room frame filename", "Rotation speed"): self[name] = parameter diff --git a/Stuff/Modules/processor.py b/Stuff/Modules/processor.py index a53a213..583adb4 100644 --- a/Stuff/Modules/processor.py +++ b/Stuff/Modules/processor.py @@ -172,7 +172,7 @@ def processFun(self): if not self.useBatchTimeVar.get(): methods[name] = [methodcaller(par.method, startTime = startTime, time = time, **options)] - elif par.group != "info": + elif par.group != "info" or name == "Rotation speed": methods[name] = [methodcaller(par.method, startTime = times[0], time = times[1], **options) for times in batchTime] diff --git a/Stuff/Modules/tools.py b/Stuff/Modules/tools.py index 4e8b0bb..e2fb94a 100644 --- a/Stuff/Modules/tools.py +++ b/Stuff/Modules/tools.py @@ -301,7 +301,32 @@ def _roundDecimal(self, d): return int(d) else: return round(float(d), 3) + + +def addTags(root): + "adds tags to files loaded from a text file" + # currently is not safe to possible problems + infile = askopenfilename(filetypes = [("Text", "*.txt")], initialdir = os.getcwd()) + with open(infile) as f: + for line in f: + basename = os.path.basename(line.split()[0].strip()) + for arenafile in m.fs[m.mode]: + if os.path.basename(arenafile) == basename: + if arenafile not in m.fs[m.mode].tagged: + m.fs[m.mode].tagged.append(arenafile) + break + root.checkProcessing() + + + + + + + + + + diff --git a/Stuff/Modules/version.py b/Stuff/Modules/version.py index f8e3bcd..da18f1b 100644 --- a/Stuff/Modules/version.py +++ b/Stuff/Modules/version.py @@ -21,7 +21,7 @@ def version(): return ['0', '4', '0'] def date(): - return "9 August 2014" + return "10 August 2014" def copyleft(): return "2013, 2014"