def behav_color(behav): """ return color corresponding to behavior if color not found returns "darkgray" see BEHAVIORS_PLOT_COLORS list in config.py """ if behav in all_behaviors: return utilities.behavior_color(plot_colors, all_behaviors.index(behav)) else: return "darkgray"
def create_events_plot(pj, selected_observations, parameters, plot_colors=BEHAVIORS_PLOT_COLORS, plot_directory="", file_format="png"): selected_subjects = parameters[SELECTED_SUBJECTS] selected_behaviors = parameters[SELECTED_BEHAVIORS] include_modifiers = parameters[INCLUDE_MODIFIERS] interval = parameters[TIME_INTERVAL] start_time = parameters[START_TIME] end_time = parameters[END_TIME] ok, msg, db_connector = db_functions.load_aggregated_events_in_db( pj, selected_subjects, selected_observations, selected_behaviors) if not ok: return False, msg, None cursor = db_connector.cursor() # if modifiers not to be included set modifiers to "" if not include_modifiers: cursor.execute("UPDATE aggregated_events SET modifiers = ''") cursor.execute( "SELECT distinct behavior, modifiers FROM aggregated_events") distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()] # add selected behaviors that are not observed for behav in selected_behaviors: if [x for x in distinct_behav_modif if x[0] == behav] == []: distinct_behav_modif.append([behav, "-"]) distinct_behav_modif = sorted(distinct_behav_modif) max_len = len(distinct_behav_modif) all_behaviors = [ pj[ETHOGRAM][x][BEHAVIOR_CODE] for x in utilities.sorted_keys(pj[ETHOGRAM]) ] par1 = 1 bar_height = 0.5 init = dt.datetime(2017, 1, 1) for obs_id in selected_observations: if len(selected_subjects) > 1: fig, axs = plt.subplots(figsize=(20, 8), nrows=len(selected_subjects), ncols=1, sharex=True) else: fig, ax = plt.subplots(figsize=(20, 8), nrows=len(selected_subjects), ncols=1, sharex=True) axs = np.ndarray(shape=(1), dtype=type(ax)) axs[0] = ax ok, msg, db_connector = db_functions.load_aggregated_events_in_db( pj, selected_subjects, [obs_id], selected_behaviors) cursor = db_connector.cursor() # if modifiers not to be included set modifiers to "" if not include_modifiers: cursor.execute("UPDATE aggregated_events SET modifiers = ''") cursor = db_connector.cursor() cursor.execute( "SELECT distinct behavior, modifiers FROM aggregated_events") distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()] # add selected behaviors that are not observed if not parameters["exclude behaviors"]: for behav in selected_behaviors: if [x for x in distinct_behav_modif if x[0] == behav] == []: distinct_behav_modif.append([behav, "-"]) distinct_behav_modif = sorted(distinct_behav_modif) max_len = len(distinct_behav_modif) # time obs_length = project_functions.observation_total_length( pj[OBSERVATIONS][obs_id]) if obs_length == -1: # media length not available interval = TIME_EVENTS if interval == TIME_FULL_OBS: min_time = float(0) max_time = float(obs_length) if interval == TIME_EVENTS: try: min_time = float(pj[OBSERVATIONS][obs_id][EVENTS][0][0]) except Exception: min_time = float(0) try: max_time = float(pj[OBSERVATIONS][obs_id][EVENTS][-1][0]) except Exception: max_time = float(obs_length) if interval == TIME_ARBITRARY_INTERVAL: min_time = float(start_time) max_time = float(end_time) cursor.execute( "UPDATE aggregated_events SET start = ? WHERE observation = ? AND start < ? AND stop BETWEEN ? AND ?", ( min_time, obs_id, min_time, min_time, max_time, )) cursor.execute( "UPDATE aggregated_events SET stop = ? WHERE observation = ? AND stop > ? AND start BETWEEN ? AND ?", ( max_time, obs_id, max_time, min_time, max_time, )) cursor.execute( "UPDATE aggregated_events SET start = ?, stop = ? WHERE observation = ? AND start < ? AND stop > ?", ( min_time, max_time, obs_id, min_time, max_time, )) ylabels = [" ".join(x) for x in distinct_behav_modif] for ax_idx, subject in enumerate(selected_subjects): if parameters["exclude behaviors"]: cursor.execute( "SELECT distinct behavior, modifiers FROM aggregated_events WHERE subject = ?", (subject, )) distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()] # add selected behaviors that are not observed if not parameters["exclude behaviors"]: for behav in selected_behaviors: if [x for x in distinct_behav_modif if x[0] == behav] == []: distinct_behav_modif.append([behav, "-"]) distinct_behav_modif = sorted(distinct_behav_modif) max_len = len(distinct_behav_modif) ylabels = [" ".join(x) for x in distinct_behav_modif] if not ax_idx: axs[ax_idx].set_title(f"Observation {obs_id}\n{subject}", fontsize=14) else: axs[ax_idx].set_title(subject, fontsize=14) bars = {} i = 0 for behavior_modifiers in distinct_behav_modif: behavior, modifiers = behavior_modifiers behavior_modifiers_str = "|".join( behavior_modifiers) if modifiers else behavior bars[behavior_modifiers_str] = [] # total duration cursor.execute(( "SELECT start,stop FROM aggregated_events " "WHERE observation = ? AND subject = ? AND behavior = ? AND modifiers = ?" ), ( obs_id, subject, behavior, modifiers, )) for row in cursor.fetchall(): bars[behavior_modifiers_str].append( (row["start"], row["stop"])) start_date = matplotlib.dates.date2num(init + dt.timedelta( seconds=row["start"])) end_date = matplotlib.dates.date2num(init + dt.timedelta( seconds=row["stop"] + POINT_EVENT_PLOT_DURATION * (row["stop"] == row["start"]))) try: bar_color = utilities.behavior_color( plot_colors, all_behaviors.index(behavior)) except Exception: bar_color = "darkgray" bar_color = POINT_EVENT_PLOT_COLOR if row["stop"] == row[ "start"] else bar_color # sage colors removed from matplotlib colors list if bar_color in ["sage", "darksage", "lightsage"]: bar_color = { "darksage": "#598556", "lightsage": "#bcecac", "sage": "#87ae73" }[bar_color] try: axs[ax_idx].barh((i * par1) + par1, end_date - start_date, left=start_date, height=bar_height, align="center", edgecolor=bar_color, color=bar_color, alpha=1) except Exception: axs[ax_idx].barh((i * par1) + par1, end_date - start_date, left=start_date, height=bar_height, align="center", edgecolor="darkgray", color="darkgray", alpha=1) i += 1 axs[ax_idx].set_ylim(bottom=0, top=(max_len * par1) + par1) pos = np.arange(par1, max_len * par1 + par1 + 1, par1) axs[ax_idx].set_yticks(pos[:len(ylabels)]) axs[ax_idx].set_yticklabels(ylabels, fontdict={"fontsize": 10}) axs[ax_idx].set_ylabel("Behaviors" + " (modifiers)" * include_modifiers, fontdict={"fontsize": 10}) axs[ax_idx].set_xlim( left=matplotlib.dates.date2num(init + dt.timedelta(seconds=min_time)), right=matplotlib.dates.date2num(init + dt.timedelta( seconds=max_time + 1))) axs[ax_idx].grid(color="g", linestyle=":") axs[ax_idx].xaxis_date() axs[ax_idx].xaxis.set_major_formatter(DateFormatter("%H:%M:%S")) axs[ax_idx].set_xlabel("Time (HH:MM:SS)", fontdict={"fontsize": 12}) axs[ax_idx].invert_yaxis() fig.autofmt_xdate() plt.tight_layout() if len(selected_observations) > 1: output_file_name = str( pathlib.Path( pathlib.Path(plot_directory) / utilities.safeFileName(obs_id)).with_suffix("." + file_format)) plt.savefig(output_file_name) else: plt.show()
def create_behaviors_bar_plot(pj: dict, selected_observations: list, param: dict, plot_directory: str, output_format: str, plot_colors:list=BEHAVIORS_PLOT_COLORS): """ time budget bar plot Args: pj (dict): project param (dict): parameters plot_directory (str): path of directory output_format (str): image format Returns: dict: """ selected_subjects = param[SELECTED_SUBJECTS] selected_behaviors = param[SELECTED_BEHAVIORS] time_interval = param["time"] start_time = param[START_TIME] end_time = param[END_TIME] parameters = ["duration", "number of occurences"] ok, msg, db_connector = db_functions.load_aggregated_events_in_db(pj, selected_subjects, selected_observations, selected_behaviors) if not ok: return {"error": True, "message": msg} try: # extract all behaviors from ethogram for colors in plot all_behaviors = [pj[ETHOGRAM][x][BEHAVIOR_CODE] for x in utilities.sorted_keys(pj[ETHOGRAM])] for obs_id in selected_observations: cursor = db_connector.cursor() # distinct behaviors cursor.execute("SELECT distinct behavior FROM aggregated_events WHERE observation = ?", (obs_id,)) distinct_behav = [rows["behavior"] for rows in cursor.fetchall()] # add selected behaviors that are not observed ''' if not param[EXCLUDE_BEHAVIORS]: for behavior in selected_behaviors: if [x for x in distinct_behav if x == behavior] == []: distinct_behav.append(behavior) ''' # distinct subjects cursor.execute("SELECT distinct subject FROM aggregated_events WHERE observation = ?", (obs_id,)) distinct_subjects = [rows["subject"] for rows in cursor.fetchall()] behaviors = init_behav(pj[ETHOGRAM], distinct_subjects, distinct_behav, parameters) # plot creation if len(distinct_subjects) > 1: fig, axs = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True) fig2, axs2 = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True) else: fig, ax = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True) axs = np.ndarray(shape=(1), dtype=type(ax)) axs[0] = ax fig2, ax2 = plt.subplots(nrows=1, ncols=len(distinct_subjects), sharey=True) axs2 = np.ndarray(shape=(1), dtype=type(ax2)) axs2[0] = ax2 fig.suptitle("Durations of behaviors") fig2.suptitle("Number of occurences of behaviors") # if modifiers not to be included set modifiers to "" cursor.execute("UPDATE aggregated_events SET modifiers = ''") # time obs_length = project_functions.observation_total_length(pj[OBSERVATIONS][obs_id]) if obs_length == -1: obs_length = 0 if param["time"] == TIME_FULL_OBS: min_time = float(0) max_time = float(obs_length) if param["time"] == TIME_EVENTS: try: min_time = float(pj[OBSERVATIONS][obs_id][EVENTS][0][0]) except Exception: min_time = float(0) try: max_time = float(pj[OBSERVATIONS][obs_id][EVENTS][-1][0]) except Exception: max_time = float(obs_length) if param["time"] == TIME_ARBITRARY_INTERVAL: min_time = float(start_time) max_time = float(end_time) cursor.execute("UPDATE aggregated_events SET start = ? WHERE observation = ? AND start < ? AND stop BETWEEN ? AND ?", (min_time, obs_id, min_time, min_time, max_time, )) cursor.execute("UPDATE aggregated_events SET stop = ? WHERE observation = ? AND stop > ? AND start BETWEEN ? AND ?", (max_time, obs_id, max_time, min_time, max_time, )) cursor.execute("UPDATE aggregated_events SET start = ?, stop = ? WHERE observation = ? AND start < ? AND stop > ?", (min_time, max_time, obs_id, min_time, max_time, )) for ax_idx, subject in enumerate(sorted(distinct_subjects)): for behavior in distinct_behav: # number of occurences cursor.execute(("SELECT COUNT(*) AS count FROM aggregated_events " "WHERE observation = ? AND subject = ? AND behavior = ?"), (obs_id, subject, behavior, )) for row in cursor.fetchall(): behaviors[subject][behavior]["number of occurences"] = 0 if row["count"] is None else row["count"] # total duration if STATE in project_functions.event_type(behavior, pj[ETHOGRAM]): cursor.execute(("SELECT SUM(stop - start) AS duration FROM aggregated_events " "WHERE observation = ? AND subject = ? AND behavior = ?"), (obs_id, subject, behavior, )) for row in cursor.fetchall(): behaviors[subject][behavior]["duration"] = 0 if row["duration"] is None else row["duration"] durations, n_occurences, colors, x_labels, colors_duration, x_labels_duration = [], [], [], [], [], [] for behavior in sorted(distinct_behav): if param[EXCLUDE_BEHAVIORS] and behaviors[subject][behavior]["number of occurences"] == 0: continue n_occurences.append(behaviors[subject][behavior]["number of occurences"]) x_labels.append(behavior) try: colors.append(utilities.behavior_color(plot_colors, all_behaviors.index(behavior))) except Exception: colors.append("darkgray") if STATE in project_functions.event_type(behavior, pj[ETHOGRAM]): durations.append(behaviors[subject][behavior]["duration"]) x_labels_duration.append(behavior) try: colors_duration.append(utilities.behavior_color(plot_colors, all_behaviors.index(behavior))) except Exception: colors_duration.append("darkgray") #width = 0.35 # the width of the bars: can also be len(x) sequence axs2[ax_idx].bar(np.arange(len(n_occurences)), n_occurences, #width, color=colors ) axs[ax_idx].bar(np.arange(len(durations)), durations, #width, color=colors_duration ) if ax_idx == 0: axs[ax_idx].set_ylabel("Duration (s)") axs[ax_idx].set_xlabel("Behaviors") axs[ax_idx].set_title(f"{subject}") axs[ax_idx].set_xticks(np.arange(len(durations))) axs[ax_idx].set_xticklabels(x_labels_duration, rotation='vertical', fontsize=8) if ax_idx == 0: axs2[ax_idx].set_ylabel("Number of occurences") axs2[ax_idx].set_xlabel("Behaviors") axs2[ax_idx].set_title(f"{subject}") axs2[ax_idx].set_xticks(np.arange(len(n_occurences))) axs2[ax_idx].set_xticklabels(x_labels, rotation='vertical', fontsize=8) fig.align_labels() fig.tight_layout(rect=[0, 0.03, 1, 0.95]) fig2.align_labels() fig2.tight_layout(rect=[0, 0.03, 1, 0.95]) if plot_directory: # output_file_name = f"{pathlib.Path(plot_directory) / utilities.safeFileName(obs_id)}.{output_format}" fig.savefig(f"{pathlib.Path(plot_directory) / utilities.safeFileName(obs_id)}.duration.{output_format}") fig2.savefig(f"{pathlib.Path(plot_directory) / utilities.safeFileName(obs_id)}.number_of_occurences.{output_format}") plt.close() else: fig.show() fig2.show() return {} except Exception: error_type, error_file_name, error_lineno = utilities.error_info(sys.exc_info()) logging.critical(f"Error in time budget bar plot: {error_type} {error_file_name} {error_lineno}") return {"error": True, "exception": sys.exc_info()}
def test_idx1000(self): assert utilities.behavior_color(config.BEHAVIORS_PLOT_COLORS, 1000) == "midnightblue"
def test_idx0(self): assert utilities.behavior_color(config.BEHAVIORS_PLOT_COLORS, 0) == "tab:blue"
def test_idx1000(self): assert utilities.behavior_color(config.BEHAVIORS_PLOT_COLORS, 1000) == "midnightblue"
def test_idx0(self): assert utilities.behavior_color(config.BEHAVIORS_PLOT_COLORS, 0) == "tab:blue"
def plot_time_ranges(pj, time_format, plot_colors, obs, obsId, minTime, videoLength, excludeBehaviorsWithoutEvents, line_width): """ create "hlines" matplotlib plot used by plot_event function (legacy) """ def on_draw(event): # http://matplotlib.org/faq/howto_faq.html#move-the-edge-of-an-axes-to-make-room-for-tick-labels bboxes = [] for label in labels: bbox = label.get_window_extent() bboxi = bbox.inverse_transformed(fig.transFigure) bboxes.append(bboxi) bbox = mtransforms.Bbox.union(bboxes) if fig.subplotpars.left < bbox.width: fig.subplots_adjust(left=1.1 * bbox.width) fig.canvas.draw() return False LINE_WIDTH = line_width all_behaviors, observedBehaviors = [], [] maxTime = 0 # max time in all events of all subjects # all behaviors defined in project without modifiers all_project_behaviors = [ pj[ETHOGRAM][idx]["code"] for idx in utilities.sorted_keys(pj[ETHOGRAM]) ] all_project_subjects = [NO_FOCAL_SUBJECT] + [ pj[SUBJECTS][idx]["name"] for idx in utilities.sorted_keys(pj[SUBJECTS]) ] for subject in obs: for behavior_modifiers_json in obs[subject]: behavior_modifiers = json.loads(behavior_modifiers_json) if not excludeBehaviorsWithoutEvents: observedBehaviors.append(behavior_modifiers_json) else: if obs[subject][behavior_modifiers_json]: observedBehaviors.append(behavior_modifiers_json) if not behavior_modifiers_json in all_behaviors: all_behaviors.append(behavior_modifiers_json) for t1, t2 in obs[subject][behavior_modifiers_json]: maxTime = max(maxTime, t1, t2) observedBehaviors.append("") lbl = [] if excludeBehaviorsWithoutEvents: for behav_modif_json in observedBehaviors: if not behav_modif_json: lbl.append("") continue behav_modif = json.loads(behav_modif_json) if len(behav_modif) == 2: lbl.append("{0} ({1})".format(behav_modif[0], behav_modif[1])) else: lbl.append(behav_modif[0]) else: all_behaviors.append('[""]') # empty json list element for behav_modif_json in all_behaviors: behav_modif = json.loads(behav_modif_json) if len(behav_modif) == 2: lbl.append("{0} ({1})".format(behav_modif[0], behav_modif[1])) else: lbl.append(behav_modif[0]) lbl = lbl[:] * len(obs) lbl = lbl[:-1] # remove last empty line fig = plt.figure(figsize=(20, 10)) fig.suptitle("Time diagram of observation {}".format(obsId), fontsize=14) ax = fig.add_subplot(111) labels = ax.set_yticklabels(lbl) ax.set_ylabel("Behaviors") if time_format == HHMMSS: fmtr = matplotlib.dates.DateFormatter("%H:%M:%S") # %H:%M:%S:%f ax.xaxis.set_major_formatter(fmtr) ax.set_xlabel("Time (hh:mm:ss)") else: ax.set_xlabel("Time (s)") plt.ylim(len(lbl), -0.5) if not videoLength: videoLength = maxTime if pj[OBSERVATIONS][obsId]["time offset"]: t0 = round(pj[OBSERVATIONS][obsId]["time offset"] + minTime) t1 = round(pj[OBSERVATIONS][obsId]["time offset"] + videoLength + 2) else: t0 = round(minTime) t1 = round(videoLength) subjectPosition = t0 + (t1 - t0) * 0.05 if time_format == HHMMSS: t0d = dt.datetime(1970, 1, 1, int(t0 / 3600), int((t0 - int(t0 / 3600) * 3600) / 60), int(t0 % 60), round(round(t0 % 1, 3) * 1000000)) t1d = dt.datetime(1970, 1, 1, int(t1 / 3600), int((t1 - int(t1 / 3600) * 3600) / 60), int(t1 % 60), round(round(t1 % 1, 3) * 1000000)) subjectPositiond = dt.datetime( 1970, 1, 1, int(subjectPosition / 3600), int((subjectPosition - int(subjectPosition / 3600) * 3600) / 60), int(subjectPosition % 60), round(round(subjectPosition % 1, 3) * 1000000)) if time_format == S: t0d, t1d = t0, t1 subjectPositiond = subjectPosition plt.xlim(t0d, t1d) plt.yticks(range(len(lbl) + 1), np.array(lbl)) count = 0 flagFirstSubject = True for subject in all_project_subjects: if subject not in obs: continue if not flagFirstSubject: if excludeBehaviorsWithoutEvents: count += 1 ax.axhline(y=(count - 1), linewidth=1, color="black") ax.hlines(np.array([count]), np.array([0]), np.array([0]), lw=LINE_WIDTH, color=col) else: flagFirstSubject = False ax.text(subjectPositiond, count - 0.5, subject) behaviors = obs[subject] x1, x2, y, col, pointsx, pointsy, guide = [], [], [], [], [], [], [] col_count = 0 for bm_json in all_behaviors: if bm_json in obs[subject]: if obs[subject][bm_json]: for t1, t2 in obs[subject][bm_json]: if t1 == t2: pointsx.append(t1) pointsy.append(count) ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) else: x1.append(t1) x2.append(t2) y.append(count) col.append( utilities.behavior_color( plot_colors, all_project_behaviors.index( json.loads(bm_json)[0]))) ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) count += 1 else: x1.append(0) x2.append(0) y.append(count) col.append("white") ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) count += 1 else: if not excludeBehaviorsWithoutEvents: x1.append(0) x2.append(0) y.append(count) col.append("white") ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) count += 1 col_count += 1 if time_format == HHMMSS: ax.hlines(np.array(y), np.array([ dt.datetime(1970, 1, 1, int(p / 3600), int((p - int(p / 3600) * 3600) / 60), int(p % 60), round(round(p % 1, 3) * 1e6)) for p in x1 ]), np.array([ dt.datetime(1970, 1, 1, int(p / 3600), int((p - int(p / 3600) * 3600) / 60), int(p % 60), round(round(p % 1, 3) * 1e6)) for p in x2 ]), lw=LINE_WIDTH, color=col) if time_format == S: ax.hlines(np.array(y), np.array(x1), np.array(x2), lw=LINE_WIDTH, color=col) if time_format == HHMMSS: ax.plot( np.array([ dt.datetime(1970, 1, 1, int(p / 3600), int((p - int(p / 3600) * 3600) / 60), int(p % 60), round(round(p % 1, 3) * 1e6)) for p in pointsx ]), pointsy, "r^") if time_format == S: ax.plot(pointsx, pointsy, "r^") fig.canvas.mpl_connect("draw_event", on_draw) plt.show() return True
def create_events_plot(pj, selected_observations, parameters, plot_colors=BEHAVIORS_PLOT_COLORS, plot_directory="", file_format="png"): selected_subjects = parameters["selected subjects"] selected_behaviors = parameters["selected behaviors"] include_modifiers = parameters["include modifiers"] interval = parameters[TIME_INTERVAL] start_time = parameters[START_TIME] end_time = parameters[END_TIME] ok, msg, db_connector = db_functions.load_aggregated_events_in_db(pj, selected_subjects, selected_observations, selected_behaviors) if not ok: return False, msg, None cursor = db_connector.cursor() # if modifiers not to be included set modifiers to "" if not include_modifiers: cursor.execute("UPDATE aggregated_events SET modifiers = ''") cursor.execute("SELECT distinct behavior, modifiers FROM aggregated_events") distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()] # add selected behaviors that are not observed for behav in selected_behaviors: if [x for x in distinct_behav_modif if x[0] == behav] == []: distinct_behav_modif.append([behav, "-"]) distinct_behav_modif = sorted(distinct_behav_modif) max_len = len(distinct_behav_modif) all_behaviors = [pj[ETHOGRAM][x][BEHAVIOR_CODE] for x in utilities.sorted_keys(pj[ETHOGRAM])] par1 = 1 bar_height = 0.5 init = dt.datetime(2017, 1, 1) for obs_id in selected_observations: if len(selected_subjects) > 1: fig, axs = plt.subplots(figsize=(20, 8), nrows=len(selected_subjects), ncols=1, sharex=True) else: fig, ax = plt.subplots(figsize=(20, 8), nrows=len(selected_subjects), ncols=1, sharex=True) axs = np.ndarray(shape=(1), dtype=type(ax)) axs[0] = ax ok, msg, db_connector = db_functions.load_aggregated_events_in_db( pj, selected_subjects, [obs_id], selected_behaviors) cursor = db_connector.cursor() # if modifiers not to be included set modifiers to "" if not include_modifiers: cursor.execute("UPDATE aggregated_events SET modifiers = ''") cursor = db_connector.cursor() cursor.execute("SELECT distinct behavior, modifiers FROM aggregated_events") distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()] # add selected behaviors that are not observed if not parameters["exclude behaviors"]: for behav in selected_behaviors: if [x for x in distinct_behav_modif if x[0] == behav] == []: distinct_behav_modif.append([behav, "-"]) distinct_behav_modif = sorted(distinct_behav_modif) max_len = len(distinct_behav_modif) # time obs_length = project_functions.observation_total_length(pj[OBSERVATIONS][obs_id]) if obs_length == -1: # media length not available interval = TIME_EVENTS if interval == TIME_FULL_OBS: min_time = float(0) max_time = float(obs_length) if interval == TIME_EVENTS: try: min_time = float(pj[OBSERVATIONS][obs_id][EVENTS][0][0]) except Exception: min_time = float(0) try: max_time = float(pj[OBSERVATIONS][obs_id][EVENTS][-1][0]) except Exception: max_time = float(obs_length) if interval == TIME_ARBITRARY_INTERVAL: min_time = float(start_time) max_time = float(end_time) cursor.execute("UPDATE aggregated_events SET start = ? WHERE observation = ? AND start < ? AND stop BETWEEN ? AND ?", (min_time, obs_id, min_time, min_time, max_time, )) cursor.execute("UPDATE aggregated_events SET stop = ? WHERE observation = ? AND stop > ? AND start BETWEEN ? AND ?", (max_time, obs_id, max_time, min_time, max_time, )) cursor.execute("UPDATE aggregated_events SET start = ?, stop = ? WHERE observation = ? AND start < ? AND stop > ?", (min_time, max_time, obs_id, min_time, max_time, )) ylabels = [" ".join(x) for x in distinct_behav_modif] for ax_idx, subject in enumerate(selected_subjects): if parameters["exclude behaviors"]: cursor.execute("SELECT distinct behavior, modifiers FROM aggregated_events WHERE subject = ?", (subject, )) distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()] # add selected behaviors that are not observed if not parameters["exclude behaviors"]: for behav in selected_behaviors: if [x for x in distinct_behav_modif if x[0] == behav] == []: distinct_behav_modif.append([behav, "-"]) distinct_behav_modif = sorted(distinct_behav_modif) max_len = len(distinct_behav_modif) ylabels = [" ".join(x) for x in distinct_behav_modif] if not ax_idx: axs[ax_idx].set_title("Observation {}\n{}".format(obs_id, subject), fontsize=14) else: axs[ax_idx].set_title(subject, fontsize=14) bars = {} i = 0 for behavior_modifiers in distinct_behav_modif: behavior, modifiers = behavior_modifiers behavior_modifiers_str = "|".join(behavior_modifiers) if modifiers else behavior bars[behavior_modifiers_str] = [] # total duration cursor.execute(("SELECT start,stop FROM aggregated_events " "WHERE observation = ? AND subject = ? AND behavior = ? AND modifiers = ?"), (obs_id, subject, behavior, modifiers,)) for row in cursor.fetchall(): bars[behavior_modifiers_str].append((row["start"], row["stop"])) start_date = matplotlib.dates.date2num(init + dt.timedelta(seconds=row["start"])) end_date = matplotlib.dates.date2num( init + dt.timedelta(seconds=row["stop"] + POINT_EVENT_PLOT_DURATION * (row["stop"] == row["start"]))) try: bar_color = utilities.behavior_color(plot_colors, all_behaviors.index(behavior)) except Exception: bar_color = "darkgray" bar_color = POINT_EVENT_PLOT_COLOR if row["stop"] == row["start"] else bar_color # sage colors removed from matplotlib colors list if bar_color in ["sage", "darksage", "lightsage"]: bar_color = {"darksage": "#598556", "lightsage": "#bcecac", "sage": "#87ae73"}[bar_color] try: axs[ax_idx].barh((i * par1) + par1, end_date - start_date, left=start_date, height=bar_height, align="center", edgecolor=bar_color, color=bar_color, alpha=1) except Exception: axs[ax_idx].barh((i * par1) + par1, end_date - start_date, left=start_date, height=bar_height, align="center", edgecolor="darkgray", color="darkgray", alpha=1) i += 1 axs[ax_idx].set_ylim(bottom=0, top=(max_len * par1) + par1) pos = np.arange(par1, max_len * par1 + par1 + 1, par1) axs[ax_idx].set_yticks(pos[:len(ylabels)]) axs[ax_idx].set_yticklabels(ylabels, fontdict={"fontsize": 10}) axs[ax_idx].set_ylabel("Behaviors" + " (modifiers)" * include_modifiers, fontdict={"fontsize": 10}) axs[ax_idx].set_xlim(left=matplotlib.dates.date2num(init + dt.timedelta(seconds=min_time)), right=matplotlib.dates.date2num(init + dt.timedelta(seconds=max_time + 1))) axs[ax_idx].grid(color="g", linestyle=":") axs[ax_idx].xaxis_date() axs[ax_idx].xaxis.set_major_formatter(DateFormatter("%H:%M:%S")) axs[ax_idx].set_xlabel("Time (HH:MM:SS)", fontdict={"fontsize": 12}) axs[ax_idx].invert_yaxis() fig.autofmt_xdate() plt.tight_layout() if len(selected_observations) > 1: output_file_name = str(pathlib.Path(pathlib.Path(plot_directory) / utilities.safeFileName(obs_id)).with_suffix( "." + file_format)) plt.savefig(output_file_name) else: plt.show()
def plot_time_ranges(pj, time_format, plot_colors, obs, obsId, minTime, videoLength, excludeBehaviorsWithoutEvents, line_width): """ create "hlines" matplotlib plot used by plot_event function (legacy) """ def on_draw(event): # http://matplotlib.org/faq/howto_faq.html#move-the-edge-of-an-axes-to-make-room-for-tick-labels bboxes = [] for label in labels: bbox = label.get_window_extent() bboxi = bbox.inverse_transformed(fig.transFigure) bboxes.append(bboxi) bbox = mtransforms.Bbox.union(bboxes) if fig.subplotpars.left < bbox.width: fig.subplots_adjust(left=1.1*bbox.width) fig.canvas.draw() return False LINE_WIDTH = line_width all_behaviors, observedBehaviors = [], [] maxTime = 0 # max time in all events of all subjects # all behaviors defined in project without modifiers all_project_behaviors = [pj[ETHOGRAM][idx]["code"] for idx in utilities.sorted_keys(pj[ETHOGRAM])] all_project_subjects = [NO_FOCAL_SUBJECT] + [pj[SUBJECTS][idx]["name"] for idx in utilities.sorted_keys(pj[SUBJECTS])] for subject in obs: for behavior_modifiers_json in obs[subject]: behavior_modifiers = json.loads(behavior_modifiers_json) if not excludeBehaviorsWithoutEvents: observedBehaviors.append(behavior_modifiers_json) else: if obs[subject][behavior_modifiers_json]: observedBehaviors.append(behavior_modifiers_json) if not behavior_modifiers_json in all_behaviors: all_behaviors.append(behavior_modifiers_json) for t1, t2 in obs[subject][behavior_modifiers_json]: maxTime = max(maxTime, t1, t2) observedBehaviors.append("") lbl = [] if excludeBehaviorsWithoutEvents: for behav_modif_json in observedBehaviors: if not behav_modif_json: lbl.append("") continue behav_modif = json.loads(behav_modif_json) if len(behav_modif) == 2: lbl.append("{0} ({1})".format(behav_modif[0], behav_modif[1])) else: lbl.append(behav_modif[0]) else: all_behaviors.append('[""]') # empty json list element for behav_modif_json in all_behaviors: behav_modif = json.loads(behav_modif_json) if len(behav_modif) == 2: lbl.append("{0} ({1})".format(behav_modif[0], behav_modif[1])) else: lbl.append(behav_modif[0]) lbl = lbl[:] * len(obs) lbl = lbl[:-1] # remove last empty line fig = plt.figure(figsize=(20, 10)) fig.suptitle("Time diagram of observation {}".format(obsId), fontsize=14) ax = fig.add_subplot(111) labels = ax.set_yticklabels(lbl) ax.set_ylabel("Behaviors") if time_format == HHMMSS: fmtr = matplotlib.dates.DateFormatter("%H:%M:%S") # %H:%M:%S:%f ax.xaxis.set_major_formatter(fmtr) ax.set_xlabel("Time (hh:mm:ss)") else: ax.set_xlabel("Time (s)") plt.ylim(len(lbl), -0.5) if not videoLength: videoLength = maxTime if pj[OBSERVATIONS][obsId]["time offset"]: t0 = round(pj[OBSERVATIONS][obsId]["time offset"] + minTime) t1 = round(pj[OBSERVATIONS][obsId]["time offset"] + videoLength + 2) else: t0 = round(minTime) t1 = round(videoLength) subjectPosition = t0 + (t1 - t0) * 0.05 if time_format == HHMMSS: t0d = dt.datetime(1970, 1, 1, int(t0 / 3600), int((t0 - int(t0 / 3600) * 3600) / 60), int(t0 % 60), round(round(t0 % 1, 3) * 1000000)) t1d = dt.datetime(1970, 1, 1, int(t1 / 3600), int((t1 - int(t1 / 3600) * 3600) / 60), int(t1 % 60), round(round(t1 % 1, 3) * 1000000)) subjectPositiond = dt.datetime(1970, 1, 1, int(subjectPosition / 3600), int((subjectPosition - int(subjectPosition / 3600) * 3600) / 60), int(subjectPosition % 60), round(round(subjectPosition % 1, 3) * 1000000)) if time_format == S: t0d, t1d = t0, t1 subjectPositiond = subjectPosition plt.xlim(t0d, t1d) plt.yticks(range(len(lbl) + 1), np.array(lbl)) count = 0 flagFirstSubject = True for subject in all_project_subjects: if subject not in obs: continue if not flagFirstSubject: if excludeBehaviorsWithoutEvents: count += 1 ax.axhline(y=(count-1), linewidth=1, color="black") ax.hlines(np.array([count]), np.array([0]), np.array([0]), lw=LINE_WIDTH, color=col) else: flagFirstSubject = False ax.text(subjectPositiond, count - 0.5, subject) behaviors = obs[subject] x1, x2, y, col, pointsx, pointsy, guide = [], [], [], [], [], [], [] col_count = 0 for bm_json in all_behaviors: if bm_json in obs[subject]: if obs[subject][bm_json]: for t1, t2 in obs[subject][bm_json]: if t1 == t2: pointsx.append(t1) pointsy.append(count) ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) else: x1.append(t1) x2.append(t2) y.append(count) col.append(utilities.behavior_color(plot_colors, all_project_behaviors.index(json.loads(bm_json)[0]))) ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) count += 1 else: x1.append(0) x2.append(0) y.append(count) col.append("white") ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) count += 1 else: if not excludeBehaviorsWithoutEvents: x1.append(0) x2.append(0) y.append(count) col.append("white") ax.axhline(y=count, linewidth=1, color="lightgray", zorder=-1) count += 1 col_count += 1 if time_format == HHMMSS: ax.hlines(np.array(y), np.array([dt.datetime(1970, 1, 1, int(p / 3600), int((p - int(p / 3600) * 3600) / 60), int(p % 60), round(round(p % 1, 3) * 1e6)) for p in x1]), np.array([dt.datetime(1970, 1, 1, int(p / 3600), int((p - int(p / 3600) * 3600) / 60), int(p % 60), round(round(p % 1, 3) * 1e6)) for p in x2]), lw=LINE_WIDTH, color=col) if time_format == S: ax.hlines(np.array(y), np.array(x1), np.array(x2), lw=LINE_WIDTH, color=col) if time_format == HHMMSS: ax.plot( np.array([ dt.datetime(1970, 1, 1, int(p / 3600), int((p - int(p / 3600) * 3600) / 60), int(p % 60), round(round(p % 1, 3) * 1e6)) for p in pointsx ]), pointsy, "r^") if time_format == S: ax.plot(pointsx, pointsy, "r^") fig.canvas.mpl_connect("draw_event", on_draw) plt.show() return True