def import_subjects_from_project(self): """ import subjects from another project """ try: fn = QFileDialog().getOpenFileName( self, "Import subjects from project file", "", ("Project files (*.boris *.boris.gz);;" "All files (*)")) file_name = fn[0] if type(fn) is tuple else fn if file_name: _, _, project, _ = project_functions.open_project_json(file_name) if "error" in project: logging.debug(project["error"]) QMessageBox.critical(self, programName, project["error"]) return # configuration of behaviours if SUBJECTS in project and project[SUBJECTS]: if self.twSubjects.rowCount(): response = dialog.MessageDialog( programName, ("There are subjects already configured. " "Do you want to append subjects or replace them?"), ['Append', 'Replace', 'Cancel']) if response == "Replace": self.twSubjects.setRowCount(0) if response == CANCEL: return for idx in utilities.sorted_keys(project[SUBJECTS]): self.twSubjects.setRowCount(self.twSubjects.rowCount() + 1) for idx2, sbjField in enumerate(subjectsFields): if sbjField in project[SUBJECTS][idx]: self.twSubjects.setItem( self.twSubjects.rowCount() - 1, idx2, QTableWidgetItem( project[SUBJECTS][idx][sbjField])) else: self.twSubjects.setItem( self.twSubjects.rowCount() - 1, idx2, QTableWidgetItem("")) self.twSubjects.resizeColumnsToContents() else: QMessageBox.warning( self, programName, "No subjects configuration found in project") except Exception: dialog.error_message(sys._getframe().f_code.co_name, sys.exc_info())
def import_subjects_from_clipboard(self): """ import subjects from clipboard """ try: cb = QApplication.clipboard() cb_text = cb.text() if not cb_text: QMessageBox.warning(None, programName, "The clipboard is empty", QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton) return if self.twSubjects.rowCount(): response = dialog.MessageDialog( programName, "Some subjects are already configured. Do you want to append subjects or replace them?", ["Append", "Replace", CANCEL]) if response == CANCEL: return if response == "Replace": self.twSubjects.setRowCount(0) cb_text_splitted = cb_text.split("\n") if len(set([len(x.split("\t")) for x in cb_text_splitted])) != 1: QMessageBox.warning(None, programName, ( "The clipboard content does not have a constant number of fields.<br>" "From your spreadsheet: CTRL + A (select all cells), CTRL + C (copy to clipboard)" ), QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton) return for row in cb_text_splitted: if set(row.split("\t")) != set([""]): subject = {} for idx, field in enumerate(row.split("\t")): if idx == 0: subject["key"] = field.strip() if len( field.strip()) == 1 else "" if idx == 1: subject[SUBJECT_NAME] = field.strip() if idx == 2: subject["description"] = field.strip() self.twSubjects.setRowCount(self.twSubjects.rowCount() + 1) for idx, field_name in enumerate(subjectsFields): item = QTableWidgetItem(subject.get(field_name, "")) self.twSubjects.setItem(self.twSubjects.rowCount() - 1, idx, item) except Exception: dialog.error_message(sys._getframe().f_code.co_name, sys.exc_info())
def export_events_jwatcher(parameters: list, obsId: str, observation: list, ethogram: dict, file_name: str, output_format: str): """ export events jwatcher .dat format Args: parameters (dict): subjects, behaviors obsId (str): observation id observation (dict): observation ethogram (dict): ethogram of project file_name (str): file name for exporting events output_format (str): Not used for compatibility with export_events function Returns: bool: result: True if OK else False str: error message """ try: for subject in parameters["selected subjects"]: # select events for current subject events = [] for event in observation[EVENTS]: if event[SUBJECT_EVENT_FIELD] == subject or (subject == "No focal subject" and event[SUBJECT_EVENT_FIELD] == ""): events.append(event) if not events: continue total_length = 0 # in seconds if observation[EVENTS]: total_length = observation[EVENTS][-1][0] - observation[EVENTS][0][0] # last event time - first event time file_name_subject = str(pathlib.Path(file_name).parent / pathlib.Path(file_name).stem) + "_" + subject + ".dat" rows = ["FirstLineOfData"] # to be completed rows.append("#-----------------------------------------------------------") rows.append(f"# Name: {pathlib.Path(file_name_subject).name}") rows.append("# Format: Focal Data File 1.0") rows.append(f"# Updated: {datetime.datetime.now().isoformat()}") rows.append("#-----------------------------------------------------------") rows.append("") rows.append(f"FocalMasterFile={pathlib.Path(file_name_subject).with_suffix('.fmf')}") rows.append("") rows.append(f"# Observation started: {observation['date']}") try: start_time = datetime.datetime.strptime(observation["date"], '%Y-%m-%dT%H:%M:%S') except ValueError: start_time = datetime.datetime(1970, 1, 1, 0, 0) start_time_epoch = int((start_time - datetime.datetime(1970, 1, 1, 0, 0)).total_seconds() * 1000) rows.append(f"StartTime={start_time_epoch}") stop_time = (start_time + datetime.timedelta(seconds=float(total_length))).isoformat() stop_time_epoch = int(start_time_epoch + float(total_length) * 1000) rows.append(f"# Observation stopped: {stop_time}") rows.append(f"StopTime={stop_time_epoch}") rows.extend([""] * 3) rows.append("#BEGIN DATA") rows[0] = f"FirstLineOfData={len(rows) + 1}" all_observed_behaviors = [] mem_number_of_state_events = {} for event in events: behav_code = event[EVENT_BEHAVIOR_FIELD_IDX] try: behavior_key = [ethogram[k][BEHAVIOR_KEY] for k in ethogram if ethogram[k][BEHAVIOR_CODE] == behav_code][0] except Exception: # coded behavior not defined in ethogram continue if [ethogram[k][TYPE] for k in ethogram if ethogram[k][BEHAVIOR_CODE] == behav_code] == [STATE_EVENT]: if behav_code in mem_number_of_state_events: mem_number_of_state_events[behav_code] += 1 else: mem_number_of_state_events[behav_code] = 1 # skip the STOP event in case of STATE if mem_number_of_state_events[behav_code] % 2 == 0: continue rows.append(f"{int(event[EVENT_TIME_FIELD_IDX] * 1000)}, {behavior_key}") if (event[EVENT_BEHAVIOR_FIELD_IDX], behavior_key) not in all_observed_behaviors: all_observed_behaviors.append((event[EVENT_BEHAVIOR_FIELD_IDX], behavior_key)) rows.append(f"{int(events[-1][0] * 1000)}, EOF\n") try: with open(file_name_subject, "w") as f_out: f_out.write("\n".join(rows)) except Exception: return False, f"File DAT not created for subject {subject}: {sys.exc_info()[1]}" # create fmf file fmf_file_path = pathlib.Path(file_name_subject).with_suffix(".fmf") fmf_creation_answer = "" if fmf_file_path.exists(): fmf_creation_answer = dialog.MessageDialog( programName, (f"The {fmf_file_path} file already exists.<br>" "What do you want to do?"), [OVERWRITE, "Skip file creation", CANCEL]) if fmf_creation_answer == CANCEL: return True, "" rows = [] rows.append("#-----------------------------------------------------------") rows.append(f"# Name: {pathlib.Path(file_name_subject).with_suffix('.fmf').name}") rows.append("# Format: Focal Master File 1.0") rows.append(f"# Updated: {datetime.datetime.now().isoformat()}") rows.append("#-----------------------------------------------------------") for (behav, key) in all_observed_behaviors: rows.append(f"Behaviour.name.{key}={behav}") behav_description = [ethogram[k][DESCRIPTION] for k in ethogram if ethogram[k][BEHAVIOR_CODE] == behav][0] rows.append(f"Behaviour.description.{key}={behav_description}") rows.append(f"DurationMilliseconds={int(float(total_length) * 1000)}") rows.append("CountUp=false") rows.append("Question.1=") rows.append("Question.2=") rows.append("Question.3=") rows.append("Question.4=") rows.append("Question.5=") rows.append("Question.6=") rows.append("Notes=") rows.append("Supplementary=\n") if fmf_creation_answer == OVERWRITE or fmf_creation_answer == "": try: with open(fmf_file_path, "w") as f_out: f_out.write("\n".join(rows)) except Exception: return False, f"File FMF not created: {sys.exc_info()[1]}" # create FAF file faf_file_path = pathlib.Path(file_name_subject).with_suffix(".faf") faf_creation_answer = "" if faf_file_path.exists(): faf_creation_answer = dialog.MessageDialog(programName, (f"The {faf_file_path} file already exists.<br>" "What do you want to do?"), [OVERWRITE, "Skip file creation", CANCEL]) if faf_creation_answer == CANCEL: return True, "" rows = [] rows.append("#-----------------------------------------------------------") rows.append("# Name: {}".format(pathlib.Path(file_name_subject).with_suffix(".faf").name)) rows.append("# Format: Focal Analysis Master File 1.0") rows.append("# Updated: {}".format(datetime.datetime.now().isoformat())) rows.append("#-----------------------------------------------------------") rows.append("FocalMasterFile={}".format(str(pathlib.Path(file_name_subject).with_suffix(".fmf")))) rows.append("") rows.append("TimeBinDuration=0.0") rows.append("EndWithLastCompleteBin=true") rows.append("") rows.append("ScoreFromBeginning=true") rows.append("ScoreFromBehavior=false") rows.append("ScoreFromFirstBehavior=false") rows.append("ScoreFromOffset=false") rows.append("") rows.append("Offset=0.0") rows.append("BehaviorToScoreFrom=") rows.append("") rows.append("OutOfSightCode=") rows.append("") rows.append("Report.StateNaturalInterval.Occurrence=false") rows.append("Report.StateNaturalInterval.TotalTime=false") rows.append("Report.StateNaturalInterval.Average=false") rows.append("Report.StateNaturalInterval.StandardDeviation=false") rows.append("Report.StateNaturalInterval.ProportionOfTime=false") rows.append("Report.StateNaturalInterval.ProportionOfTimeInSight=false") rows.append("Report.StateNaturalInterval.ConditionalProportionOfTime=false") rows.append("") rows.append("Report.StateNaturalDuration.Occurrence=false") rows.append("Report.StateNaturalDuration.TotalTime=false") rows.append("Report.StateNaturalDuration.Average=false") rows.append("Report.StateNaturalDuration.StandardDeviation=false") rows.append("Report.StateNaturalDuration.ProportionOfTime=false") rows.append("Report.StateNaturalDuration.ProportionOfTimeInSight=false") rows.append("Report.StateNaturalDuration.ConditionalProportionOfTime=false") rows.append("") rows.append("Report.StateAllInterval.Occurrence=false") rows.append("Report.StateAllInterval.TotalTime=false") rows.append("Report.StateAllInterval.Average=false") rows.append("Report.StateAllInterval.StandardDeviation=false") rows.append("Report.StateAllInterval.ProportionOfTime=false") rows.append("Report.StateAllInterval.ProportionOfTimeInSight=false") rows.append("Report.StateAllInterval.ConditionalProportionOfTime=false") rows.append("") rows.append("Report.StateAllDuration.Occurrence=true") rows.append("Report.StateAllDuration.TotalTime=true") rows.append("Report.StateAllDuration.Average=true") rows.append("Report.StateAllDuration.StandardDeviation=false") rows.append("Report.StateAllDuration.ProportionOfTime=false") rows.append("Report.StateAllDuration.ProportionOfTimeInSight=true") rows.append("Report.StateAllDuration.ConditionalProportionOfTime=false") rows.append("") rows.append("Report.EventNaturalInterval.EventCount=false") rows.append("Report.EventNaturalInterval.Occurrence=false") rows.append("Report.EventNaturalInterval.Average=false") rows.append("Report.EventNaturalInterval.StandardDeviation=false") rows.append("Report.EventNaturalInterval.ConditionalNatEventCount=false") rows.append("Report.EventNaturalInterval.ConditionalNatRate=false") rows.append("Report.EventNaturalInterval.ConditionalNatIntervalOccurance=false") rows.append("Report.EventNaturalInterval.ConditionalNatIntervalAverage=false") rows.append("Report.EventNaturalInterval.ConditionalNatIntervalStandardDeviation=false") rows.append("Report.EventNaturalInterval.ConditionalAllEventCount=false") rows.append("Report.EventNaturalInterval.ConditionalAllRate=false") rows.append("Report.EventNaturalInterval.ConditionalAllIntervalOccurance=false") rows.append("Report.EventNaturalInterval.ConditionalAllIntervalAverage=false") rows.append("Report.EventNaturalInterval.ConditionalAllIntervalStandardDeviation=false") rows.append("") rows.append("AllCodesMutuallyExclusive=true") rows.append("") for (behav, key) in all_observed_behaviors: rows.append(f"Behavior.isModified.{key}=false") rows.append(f"Behavior.isSubtracted.{key}=false") rows.append(f"Behavior.isIgnored.{key}=false") rows.append(f"Behavior.isEventAnalyzed.{key}=false") rows.append(f"Behavior.switchesOff.{key}=") rows.append("") if faf_creation_answer == "" or faf_creation_answer == OVERWRITE: try: with open(pathlib.Path(file_name_subject).with_suffix(".faf"), "w") as f_out: f_out.write("\n".join(rows)) except Exception: return False, f"File FAF not created: {sys.exc_info()[1]}" return True, "" except Exception: logging.critical("Error during exporting the events for JWatcher") dialog.error_message("exporting the events for JWatcher", sys.exc_info()) return False, ""
def import_indep_variables_from_project(self): """ import independent variables from another project """ try: fn = QFileDialog().getOpenFileName( self, "Import independent variables from project file", "", ("Project files (*.boris *.boris.gz);;" "All files (*)")) file_name = fn[0] if type(fn) is tuple else fn if file_name: _, _, project, _ = project_functions.open_project_json(file_name) if "error" in project: logging.debug(project["error"]) QMessageBox.critical(self, programName, project["error"]) return # independent variables if INDEPENDENT_VARIABLES in project and project[ INDEPENDENT_VARIABLES]: # check if variables are already present existing_var = [] for r in range(self.twVariables.rowCount()): existing_var.append( self.twVariables.item(r, 0).text().strip().upper()) for i in utilities.sorted_keys(project[INDEPENDENT_VARIABLES]): self.twVariables.setRowCount(self.twVariables.rowCount() + 1) flag_renamed = False for idx, field in enumerate(tw_indVarFields): item = QTableWidgetItem() if field in project[INDEPENDENT_VARIABLES][i]: if field == "label": txt = project[INDEPENDENT_VARIABLES][i][ "label"].strip() while txt.upper() in existing_var: txt += "_2" flag_renamed = True else: txt = project[INDEPENDENT_VARIABLES][i][ field].strip() item.setText(txt) else: item.setText("") self.twVariables.setItem( self.twVariables.rowCount() - 1, idx, item) self.twVariables.resizeColumnsToContents() if flag_renamed: QMessageBox.information( self, programName, "Some variables already present were renamed") else: QMessageBox.warning( self, programName, "No independent variables found in project") except Exception: dialog.error_message("Import independent variable from project", sys.exc_info())
def import_from_text_file(self): if self.twBehaviors.rowCount(): response = dialog.MessageDialog( programName, "There are behaviors already configured. Do you want to append behaviors or replace them?", ['Append', 'Replace', CANCEL]) if response == CANCEL: return fn = QFileDialog().getOpenFileName( self, "Import behaviors from text file", "", "Text files (*.txt *.tsv *.csv);;All files (*)") fileName = fn[0] if type(fn) is tuple else fn if fileName: if self.twBehaviors.rowCount() and response == "Replace": self.twBehaviors.setRowCount(0) try: with open(fileName, mode="rb") as f: rows_b = f.read().splitlines() rows = [] idx = 1 for row in rows_b: try: rows.append(row.decode("utf-8")) except Exception: QMessageBox.critical( None, programName, (f"Error while reading file\nThe line # {idx}\n" f"{row}\ncontains characters that are not readable."), QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton) return idx += 1 fieldSeparator, fieldsNumber = check_text_file_type(rows) logging.debug( f"fields separator: {fieldSeparator} fields number: {fieldsNumber}" ) if fieldSeparator is None: QMessageBox.critical( self, programName, "Separator character not found! Use plain text file and TAB or comma as value separator" ) else: for row in rows: type_, key, code, description = "", "", "", "" if fieldsNumber == 3: # fields: type, key, code type_, key, code = row.split(fieldSeparator) description = "" if fieldsNumber == 4: # fields: type, key, code, description type_, key, code, description = row.split( fieldSeparator) if fieldsNumber > 4: type_, key, code, description = row.split( fieldSeparator)[:4] behavior = { "key": key, "code": code, "description": description, "modifiers": "", "excluded": "", "coding map": "", "category": "" } self.twBehaviors.setRowCount(self.twBehaviors.rowCount() + 1) for field_type in behavioursFields: if field_type == TYPE: item = QTableWidgetItem(DEFAULT_BEHAVIOR_TYPE) # add type combobox if POINT in type_.upper(): item = QTableWidgetItem(POINT_EVENT) if STATE in type_.upper(): item = QTableWidgetItem(STATE_EVENT) else: item = QTableWidgetItem(behavior[field_type]) if field_type not in ETHOGRAM_EDITABLE_FIELDS: item.setFlags(Qt.ItemIsEnabled) item.setBackground(QColor(230, 230, 230)) self.twBehaviors.setItem( self.twBehaviors.rowCount() - 1, behavioursFields[field_type], item) except Exception: dialog.error_message(sys._getframe().f_code.co_name, sys.exc_info())
def import_behaviors_from_project(self): try: fn = QFileDialog().getOpenFileName( self, "Import behaviors from project file", "", ("Project files (*.boris *.boris.gz);;" "All files (*)")) file_name = fn[0] if type(fn) is tuple else fn if file_name: _, _, project, _ = project_functions.open_project_json(file_name) # import behavioral_categories if BEHAVIORAL_CATEGORIES in project: self.pj[BEHAVIORAL_CATEGORIES] = list( project[BEHAVIORAL_CATEGORIES]) # configuration of behaviours if ETHOGRAM in project and project[ETHOGRAM]: if self.twBehaviors.rowCount(): response = dialog.MessageDialog( programName, ("Some behaviors are already configured. " "Do you want to append behaviors or replace them?"), ["Append", "Replace", CANCEL]) if response == "Replace": self.twBehaviors.setRowCount(0) if response == CANCEL: return behaviors_to_import = select_behaviors( title="Select the behaviors to import", text="Behaviors", behavioral_categories=list(project[BEHAVIORAL_CATEGORIES]), ethogram=dict(project[ETHOGRAM]), behavior_type=[STATE_EVENT, POINT_EVENT]) for i in utilities.sorted_keys(project[ETHOGRAM]): if project[ETHOGRAM][i][ BEHAVIOR_CODE] not in behaviors_to_import: continue self.twBehaviors.setRowCount(self.twBehaviors.rowCount() + 1) for field in project[ETHOGRAM][i]: item = QTableWidgetItem() if field == TYPE: item.setText(project[ETHOGRAM][i][field]) item.setFlags(Qt.ItemIsEnabled) item.setBackground(QColor(230, 230, 230)) else: if field == "modifiers" and isinstance( project[ETHOGRAM][i][field], str): modif_set_dict = {} if project[ETHOGRAM][i][field]: modif_set_list = project[ETHOGRAM][i][ field].split("|") for modif_set in modif_set_list: modif_set_dict[str( len(modif_set_dict))] = { "name": "", "type": SINGLE_SELECTION, "values": modif_set.split(",") } project[ETHOGRAM][i][field] = dict( modif_set_dict) item.setText(str(project[ETHOGRAM][i][field])) if field not in ETHOGRAM_EDITABLE_FIELDS: item.setFlags(Qt.ItemIsEnabled) item.setBackground(QColor(230, 230, 230)) self.twBehaviors.setItem( self.twBehaviors.rowCount() - 1, behavioursFields[field], item) self.twBehaviors.resizeColumnsToContents() else: QMessageBox.warning( self, programName, "No behaviors configuration found in project") except Exception: dialog.error_message(sys._getframe().f_code.co_name, sys.exc_info())
def select_behaviors(title="Record value from external data file", text="Behaviors", behavioral_categories=[], ethogram={}, behavior_type=[STATE_EVENT, POINT_EVENT]): """ allow user to select behaviors to import Args: title (str): title of dialog box text (str): text of dialog box behavioral_categories (list): behavioral categories ethogram (dict): ethogram """ try: paramPanelWindow = param_panel.Param_panel() paramPanelWindow.resize(800, 600) paramPanelWindow.setWindowTitle(title) paramPanelWindow.lbBehaviors.setText(text) for w in [ paramPanelWindow.lwSubjects, paramPanelWindow.pbSelectAllSubjects, paramPanelWindow.pbUnselectAllSubjects, paramPanelWindow.pbReverseSubjectsSelection, paramPanelWindow.lbSubjects, paramPanelWindow.cbIncludeModifiers, paramPanelWindow.cbExcludeBehaviors, paramPanelWindow.frm_time ]: w.setVisible(False) if behavioral_categories: categories = behavioral_categories # check if behavior not included in a category if "" in [ ethogram[idx][BEHAVIOR_CATEGORY] for idx in ethogram if BEHAVIOR_CATEGORY in ethogram[idx] ]: categories += [""] else: categories = ["###no category###"] for category in categories: if category != "###no category###": if category == "": paramPanelWindow.item = QListWidgetItem("No category") paramPanelWindow.item.setData(34, "No category") else: paramPanelWindow.item = QListWidgetItem(category) paramPanelWindow.item.setData(34, category) font = QFont() font.setBold(True) paramPanelWindow.item.setFont(font) paramPanelWindow.item.setData(33, "category") paramPanelWindow.item.setData(35, False) paramPanelWindow.lwBehaviors.addItem(paramPanelWindow.item) # check if behavior type must be shown for behavior in [ ethogram[x][BEHAVIOR_CODE] for x in utilities.sorted_keys(ethogram) ]: if ((categories == ["###no category###"]) or (behavior in [ ethogram[x][BEHAVIOR_CODE] for x in ethogram if BEHAVIOR_CATEGORY in ethogram[x] and ethogram[x][BEHAVIOR_CATEGORY] == category ])): paramPanelWindow.item = QListWidgetItem(behavior) paramPanelWindow.item.setCheckState(Qt.Unchecked) if category != "###no category###": paramPanelWindow.item.setData(33, "behavior") if category == "": paramPanelWindow.item.setData(34, "No category") else: paramPanelWindow.item.setData(34, category) paramPanelWindow.lwBehaviors.addItem(paramPanelWindow.item) if paramPanelWindow.exec_(): return paramPanelWindow.selectedBehaviors return [] except Exception: dialog.error_message(sys._getframe().f_code.co_name, sys.exc_info())
def import_from_JWatcher(self): """ import behaviors configuration from JWatcher (GDF file) """ try: if self.twBehaviors.rowCount(): response = dialog.MessageDialog( programName, "There are behaviors already configured. Do you want to append behaviors or replace them?", ["Append", "Replace", CANCEL]) if response == CANCEL: return fn = QFileDialog().getOpenFileName( self, "Import behaviors from JWatcher", "", "Global Definition File (*.gdf);;All files (*)") fileName = fn[0] if type(fn) is tuple else fn if fileName: if self.twBehaviors.rowCount() and response == "Replace": self.twBehaviors.setRowCount(0) with open(fileName, "r") as f: rows = f.readlines() for idx, row in enumerate(rows): if row and row[0] == "#": continue if "Behavior.name." in row and "=" in row: key, code = row.split('=') key = key.replace("Behavior.name.", "") # read description if idx < len(rows) and "Behavior.description." in rows[ idx + 1]: description = rows[idx + 1].split("=")[-1] behavior = { "key": key, "code": code, "description": description, "modifiers": "", "excluded": "", "coding map": "", "category": "" } self.twBehaviors.setRowCount(self.twBehaviors.rowCount() + 1) for field_type in behavioursFields: if field_type == TYPE: item = QTableWidgetItem(DEFAULT_BEHAVIOR_TYPE) else: item = QTableWidgetItem(behavior[field_type]) if field_type in [ TYPE, "excluded", "category", "coding map", "modifiers" ]: item.setFlags(Qt.ItemIsEnabled) item.setBackground(QColor(230, 230, 230)) self.twBehaviors.setItem( self.twBehaviors.rowCount() - 1, behavioursFields[field_type], item) except Exception: dialog.error_message(sys._getframe().f_code.co_name, sys.exc_info())
def import_behaviors_from_clipboard(self): """ import ethogram from clipboard """ try: cb = QApplication.clipboard() cb_text = cb.text() if not cb_text: QMessageBox.warning(None, programName, "The clipboard is empty", QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton) return if self.twBehaviors.rowCount(): response = dialog.MessageDialog( programName, "Some behaviors are already configured. Do you want to append behaviors or replace them?", ["Append", "Replace", CANCEL]) if response == CANCEL: return if response == "Replace": self.twBehaviors.setRowCount(0) cb_text_splitted = cb_text.split("\n") while "" in cb_text_splitted: cb_text_splitted.remove("") if len(set([len(x.split("\t")) for x in cb_text_splitted])) != 1: QMessageBox.warning(None, programName, ( "The clipboard content does not have a constant number of fields.<br>" "From your spreadsheet: CTRL + A (select all cells), CTRL + C (copy to clipboard)" ), QMessageBox.Ok | QMessageBox.Default, QMessageBox.NoButton) return for row in cb_text_splitted: if set(row.split("\t")) != set([""]): behavior = {"type": DEFAULT_BEHAVIOR_TYPE} for idx, field in enumerate(row.split("\t")): if idx == 0: behavior["type"] = STATE_EVENT if STATE in field.upper( ) else (POINT_EVENT if POINT in field.upper() else "") if idx == 1: behavior["key"] = field.strip() if len( field.strip()) == 1 else "" if idx == 2: behavior["code"] = field.strip() if idx == 3: behavior["description"] = field.strip() if idx == 4: behavior["category"] = field.strip() self.twBehaviors.setRowCount(self.twBehaviors.rowCount() + 1) for field_type in behavioursFields: if field_type == TYPE: item = QTableWidgetItem( behavior.get("type", DEFAULT_BEHAVIOR_TYPE)) else: item = QTableWidgetItem(behavior.get(field_type, "")) if field_type not in ETHOGRAM_EDITABLE_FIELDS: # [TYPE, "excluded", "coding map", "modifiers", "category"]: item.setFlags(Qt.ItemIsEnabled) item.setBackground(QColor(230, 230, 230)) self.twBehaviors.setItem(self.twBehaviors.rowCount() - 1, behavioursFields[field_type], item) except Exception: dialog.error_message(sys._getframe().f_code.co_name, sys.exc_info())
def time_budget_analysis(ethogram: dict, cursor, selected_observations: list, parameters: dict, by_category: bool = False): """ extract number of occurrences, total duration, mean ... if start_time = 0 and end_time = 0 all events are extracted Args: ethogram (dict): project ethogram cursor: cursor on temporary database selected_observations (list): selected observations parameters (dict): parameters for analysis by_category (bool): True for grouping in category else False Returns: list: results dict: """ try: categories, out = {}, [] for subject in parameters[SELECTED_SUBJECTS]: out_cat, categories[subject] = [], {} for behavior in parameters[SELECTED_BEHAVIORS]: if parameters[INCLUDE_MODIFIERS]: cursor.execute( "SELECT DISTINCT modifiers FROM events WHERE subject = ? AND code = ?", (subject, behavior)) distinct_modifiers = list(cursor.fetchall()) if not distinct_modifiers: if not parameters[EXCLUDE_BEHAVIORS]: if STATE in project_functions.event_type( behavior, ethogram): out.append({ "subject": subject, "behavior": behavior, "modifiers": "", "duration": 0, "duration_mean": 0, "duration_stdev": "NA", "number": "0", "inter_duration_mean": "NA", "inter_duration_stdev": "NA" }) else: # point out.append({ "subject": subject, "behavior": behavior, "modifiers": "", "duration": 0, "duration_mean": 0, "duration_stdev": "NA", "number": "0", "inter_duration_mean": "NA", "inter_duration_stdev": "NA" }) continue if POINT in project_functions.event_type( behavior, ethogram): for modifier in distinct_modifiers: cursor.execute( ("SELECT occurence, observation FROM events " "WHERE subject = ? " "AND code = ? " "AND modifiers = ? " "ORDER BY observation, occurence"), (subject, behavior, modifier[0])) rows = cursor.fetchall() # inter events duration all_event_interdurations = [] for idx, row in enumerate(rows): if idx and row[1] == rows[idx - 1][1]: all_event_interdurations.append( float(row[0]) - float(rows[idx - 1][0])) out_cat.append({ "subject": subject, "behavior": behavior, "modifiers": modifier[0], "duration": NA, "duration_mean": NA, "duration_stdev": NA, "number": len(rows), "inter_duration_mean": round( statistics.mean(all_event_interdurations), 3) if len(all_event_interdurations) else NA, "inter_duration_stdev": round( statistics.stdev(all_event_interdurations), 3) if len(all_event_interdurations) > 1 else NA }) if STATE in project_functions.event_type( behavior, ethogram): for modifier in distinct_modifiers: cursor.execute( ("SELECT occurence, observation FROM events " "WHERE subject = ? " "AND code = ? " "AND modifiers = ? " "ORDER BY observation, occurence"), (subject, behavior, modifier[0])) rows = list(cursor.fetchall()) if len(rows) % 2: out.append({ "subject": subject, "behavior": behavior, "modifiers": modifier[0], "duration": UNPAIRED, "duration_mean": UNPAIRED, "duration_stdev": UNPAIRED, "number": UNPAIRED, "inter_duration_mean": UNPAIRED, "inter_duration_stdev": UNPAIRED }) else: all_event_durations, all_event_interdurations = [], [] for idx, row in enumerate(rows): # event if idx % 2 == 0: new_init, new_end = float( row[0]), float(rows[idx + 1][0]) all_event_durations.append(new_end - new_init) # inter event if same observation if idx % 2 and idx != len( rows) - 1 and row[1] == rows[idx + 1][1]: if (parameters["start time"] <= row[0] <= parameters["end time"] and parameters["start time"] <= rows[idx + 1][0] <= parameters["end time"]): all_event_interdurations.append( float(rows[idx + 1][0]) - float(row[0])) out_cat.append({ "subject": subject, "behavior": behavior, "modifiers": modifier[0], "duration": round(sum(all_event_durations), 3), "duration_mean": round(statistics.mean(all_event_durations), 3) if len(all_event_durations) else "NA", "duration_stdev": round( statistics.stdev(all_event_durations), 3) if len(all_event_durations) > 1 else "NA", "number": len(all_event_durations), "inter_duration_mean": round( statistics.mean( all_event_interdurations), 3) if len(all_event_interdurations) else "NA", "inter_duration_stdev": round( statistics.stdev( all_event_interdurations), 3) if len(all_event_interdurations) > 1 else "NA" }) else: # no modifiers if POINT in project_functions.event_type( behavior, ethogram): cursor.execute(( "SELECT occurence,observation FROM events " "WHERE subject = ? AND code = ? ORDER BY observation, occurence" ), (subject, behavior)) rows = list(cursor.fetchall()) if len(selected_observations) == 1: new_rows = [] for occurence, observation in rows: new_occurence = max( float(parameters["start time"]), occurence) new_occurence = min( new_occurence, float(parameters["end time"])) new_rows.append([new_occurence, observation]) rows = list(new_rows) # include behaviors without events if not len(rows): if not parameters[EXCLUDE_BEHAVIORS]: out.append({ "subject": subject, "behavior": behavior, "modifiers": "", "duration": NA, "duration_mean": NA, "duration_stdev": NA, "number": "0", "inter_duration_mean": NA, "inter_duration_stdev": NA }) continue # inter events duration all_event_interdurations = [] for idx, row in enumerate(rows): if idx and row[1] == rows[idx - 1][1]: all_event_interdurations.append( float(row[0]) - float(rows[idx - 1][0])) out_cat.append({ "subject": subject, "behavior": behavior, "modifiers": "", "duration": NA, "duration_mean": NA, "duration_stdev": NA, "number": len(rows), "inter_duration_mean": round(statistics.mean(all_event_interdurations), 3) if len(all_event_interdurations) else NA, "inter_duration_stdev": round(statistics.stdev(all_event_interdurations), 3) if len(all_event_interdurations) > 1 else NA }) if STATE in project_functions.event_type( behavior, ethogram): cursor.execute(( "SELECT occurence, observation FROM events " "WHERE subject = ? AND code = ? ORDER BY observation, occurence" ), (subject, behavior)) rows = list(cursor.fetchall()) if not len(rows): if not parameters[ EXCLUDE_BEHAVIORS]: # include behaviors without events out.append({ "subject": subject, "behavior": behavior, "modifiers": "", "duration": 0, "duration_mean": 0, "duration_stdev": "NA", "number": 0, "inter_duration_mean": "-", "inter_duration_stdev": "-" }) continue if len(rows) % 2: out.append({ "subject": subject, "behavior": behavior, "modifiers": "", "duration": UNPAIRED, "duration_mean": UNPAIRED, "duration_stdev": UNPAIRED, "number": UNPAIRED, "inter_duration_mean": UNPAIRED, "inter_duration_stdev": UNPAIRED }) else: all_event_durations, all_event_interdurations = [], [] for idx, row in enumerate(rows): # event if idx % 2 == 0: new_init, new_end = float(row[0]), float( rows[idx + 1][0]) all_event_durations.append(new_end - new_init) # inter event if same observation if idx % 2 and idx != len(rows) - 1 and row[ 1] == rows[idx + 1][1]: if (parameters["start time"] <= row[0] <= parameters["end time"] and parameters["start time"] <= rows[idx + 1][0] <= parameters["end time"]): all_event_interdurations.append( float(rows[idx + 1][0]) - float(row[0])) out_cat.append({ "subject": subject, "behavior": behavior, "modifiers": "", "duration": round(sum(all_event_durations), 3), "duration_mean": round(statistics.mean(all_event_durations), 3) if len(all_event_durations) else NA, "duration_stdev": round(statistics.stdev(all_event_durations), 3) if len(all_event_durations) > 1 else NA, "number": len(all_event_durations), "inter_duration_mean": round( statistics.mean(all_event_interdurations), 3) if len(all_event_interdurations) else NA, "inter_duration_stdev": round( statistics.stdev(all_event_interdurations), 3) if len(all_event_interdurations) > 1 else NA }) out += out_cat if by_category: # and flagCategories: for behav in out_cat: try: category = [ ethogram[x]["category"] for x in ethogram if "category" in ethogram[x] and ethogram[x]["code"] == behav['behavior'] ][0] except Exception: category = "" if category in categories[subject]: if behav["duration"] not in ["-", "NA"] and categories[ subject][category]["duration"] not in [ "-", "NA" ]: categories[subject][category]["duration"] += behav[ "duration"] else: categories[subject][category]["duration"] = "-" categories[subject][category]["number"] += behav[ "number"] else: categories[subject][category] = { "duration": behav["duration"], "number": behav["number"] } out_sorted = [] for subject in parameters[SELECTED_SUBJECTS]: for behavior in parameters[SELECTED_BEHAVIORS]: for row in out: if row["subject"] == subject and row[ "behavior"] == behavior: out_sorted.append(row) # http://stackoverflow.com/questions/673867/python-arbitrary-order-by return out_sorted, categories except Exception: dialog.error_message("time_budget", sys.exc_info()) return [], []
def synthetic_time_budget(pj: dict, selected_observations: list, parameters_obs: dict): """ create a synthetic time budget Args: pj (dict): project dictionary selected_observations (list): list of observations to include in time budget parameters_obs (dict): Returns: bool: True if everything OK str: message tablib.Dataset: dataset containing synthetic time budget data """ try: selected_subjects = parameters_obs[SELECTED_SUBJECTS] selected_behaviors = parameters_obs[SELECTED_BEHAVIORS] include_modifiers = parameters_obs[INCLUDE_MODIFIERS] interval = parameters_obs["time"] start_time = parameters_obs["start time"] end_time = parameters_obs["end time"] parameters = [ ["duration", "Total duration"], ["number", "Number of occurrences"], ["duration mean", "Duration mean"], ["duration stdev", "Duration std dev"], ["proportion of time", "Proportion of time"], ] data_report = tablib.Dataset() data_report.title = "Synthetic time budget" 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 db_connector.create_aggregate("stdev", 1, StdevFunc) cursor = db_connector.cursor() # modifiers if include_modifiers: cursor.execute( "SELECT distinct behavior, modifiers FROM aggregated_events") distinct_behav_modif = [[rows["behavior"], rows["modifiers"]] for rows in cursor.fetchall()] else: cursor.execute("SELECT distinct behavior FROM aggregated_events") distinct_behav_modif = [[rows["behavior"], ""] 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, ""]) behaviors = init_behav_modif(pj[ETHOGRAM], selected_subjects, distinct_behav_modif, include_modifiers, parameters) param_header = ["Observations id", "Total length (s)"] subj_header, behav_header, modif_header = [""] * len(param_header), [ "" ] * len(param_header), [""] * len(param_header) subj_header[1] = "Subjects:" behav_header[1] = "Behaviors:" modif_header[1] = "Modifiers:" for subj in selected_subjects: for behavior_modifiers in distinct_behav_modif: behavior, modifiers = behavior_modifiers behavior_modifiers_str = "|".join( behavior_modifiers) if modifiers else behavior for param in parameters: subj_header.append(subj) behav_header.append(behavior) modif_header.append(modifiers) param_header.append(param[1]) ''' if parameters_obs["group observations"]: cursor.execute("UPDATE aggregated_events SET observation = 'all' " ) #selected_observations = ["all"] ''' data_report.append(subj_header) data_report.append(behav_header) if include_modifiers: data_report.append(modif_header) data_report.append(param_header) # select time interval for obs_id in selected_observations: ok, msg, db_connector = db_functions.load_aggregated_events_in_db( pj, selected_subjects, [obs_id], selected_behaviors) if not ok: return False, msg, None db_connector.create_aggregate("stdev", 1, StdevFunc) cursor = db_connector.cursor() # if modifiers not to be included set modifiers to "" if not include_modifiers: 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 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) # adapt start and stop to the selected time interval 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, )) cursor.execute( "DELETE FROM aggregated_events WHERE observation = ? AND (start < ? AND stop < ?) OR (start > ? AND stop > ?)", ( obs_id, min_time, min_time, max_time, max_time, )) for subject in selected_subjects: # check if behaviors are to exclude from total time time_to_subtract = 0 if EXCLUDED_BEHAVIORS in parameters_obs: for excluded_behav in parameters_obs[EXCLUDED_BEHAVIORS]: cursor.execute(( "SELECT SUM(stop-start) " "FROM aggregated_events " "WHERE observation = ? AND subject = ? AND behavior = ? " ), ( obs_id, subject, excluded_behav, )) for row in cursor.fetchall(): if row[0] is not None: time_to_subtract += row[0] for behavior_modifiers in distinct_behav_modif: behavior, modifiers = behavior_modifiers behavior_modifiers_str = "|".join( behavior_modifiers) if modifiers else behavior cursor.execute(( "SELECT SUM(stop-start), COUNT(*), AVG(stop-start), stdev(stop-start) " "FROM aggregated_events " "WHERE observation = ? AND subject = ? AND behavior = ? AND modifiers = ? " ), ( obs_id, subject, behavior, modifiers, )) for row in cursor.fetchall(): behaviors[subject][behavior_modifiers_str][ "duration"] = (0 if row[0] is None else f"{row[0]:.3f}") behaviors[subject][behavior_modifiers_str][ "number"] = 0 if row[1] is None else row[1] behaviors[subject][behavior_modifiers_str][ "duration mean"] = (0 if row[2] is None else f"{row[2]:.3f}") behaviors[subject][behavior_modifiers_str][ "duration stdev"] = (0 if row[3] is None else f"{row[3]:.3f}") if behavior not in parameters_obs[EXCLUDED_BEHAVIORS]: try: behaviors[subject][behavior_modifiers_str][ "proportion of time"] = ( 0 if row[0] is None else f"{row[0] / ((max_time - min_time) - time_to_subtract):.3f}" ) except ZeroDivisionError: behaviors[subject][behavior_modifiers_str][ "proportion of time"] = "-" else: # behavior subtracted behaviors[subject][behavior_modifiers_str][ "proportion of time"] = ( 0 if row[0] is None else f"{row[0] / (max_time - min_time):.3f}") columns = [obs_id, f"{max_time - min_time:0.3f}"] for subj in selected_subjects: for behavior_modifiers in distinct_behav_modif: behavior, modifiers = behavior_modifiers behavior_modifiers_str = "|".join( behavior_modifiers) if modifiers else behavior for param in parameters: columns.append( behaviors[subj][behavior_modifiers_str][param[0]]) data_report.append(columns) except Exception: dialog.error_message("synthetic_time_budget", sys.exc_info()) return (False, msg, tablib.Dataset()) return True, msg, data_report
def synthetic_time_budget_bin(pj: dict, selected_observations: list, parameters_obs: dict): """ create a synthetic time budget divised in time bin Args: pj (dict): project dictionary selected_observations (list): list of observations to include in time budget parameters_obs (dict): Returns: bool: True if everything OK str: message tablib.Dataset: dataset containing synthetic time budget data """ def interval_len(interval): if interval.empty: return dec(0) else: return sum([x.upper - x.lower for x in interval]) def interval_number(interval): if interval.empty: return dec(0) else: return len(interval) def interval_mean(interval): if interval.empty: return dec(0) else: return sum([x.upper - x.lower for x in interval]) / len(interval) def interval_std_dev(interval): if interval.empty: return "NA" else: try: return round( statistics.stdev([x.upper - x.lower for x in interval]), 3) except: return "NA" try: selected_subjects = parameters_obs[SELECTED_SUBJECTS] selected_behaviors = parameters_obs[SELECTED_BEHAVIORS] include_modifiers = parameters_obs[INCLUDE_MODIFIERS] time_interval = parameters_obs["time"] start_time = parameters_obs[START_TIME] end_time = parameters_obs[END_TIME] time_bin_size = dec(parameters_obs[TIME_BIN_SIZE]) parameters = [ ["duration", "Total duration"], ["number", "Number of occurrences"], ["duration mean", "Duration mean"], ["duration stdev", "Duration std dev"], ["proportion of time", "Proportion of time"], ] data_report = tablib.Dataset() data_report.title = "Synthetic time budget with time bin" distinct_behav_modif = [] for obs_id in selected_observations: for event in pj[OBSERVATIONS][obs_id][EVENTS]: if include_modifiers: if (event[EVENT_BEHAVIOR_FIELD_IDX], event[EVENT_MODIFIER_FIELD_IDX] ) not in distinct_behav_modif: distinct_behav_modif.append( (event[EVENT_BEHAVIOR_FIELD_IDX], event[EVENT_MODIFIER_FIELD_IDX])) else: if (event[EVENT_BEHAVIOR_FIELD_IDX], "") not in distinct_behav_modif: distinct_behav_modif.append( (event[EVENT_BEHAVIOR_FIELD_IDX], "")) distinct_behav_modif.sort() ''' print("distinct_behav_modif", distinct_behav_modif) ''' # 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, ""]) ''' print("distinct_behav_modif with not observed behav", distinct_behav_modif) ''' behaviors = init_behav_modif_bin(pj[ETHOGRAM], selected_subjects, distinct_behav_modif, include_modifiers, parameters) ''' print("init behaviors", behaviors) print(f"selected subjects: {selected_subjects}") ''' param_header = [ "Observations id", "Total length (s)", "Time interval (s)" ] subj_header, behav_header, modif_header = [""] * len(param_header), [ "" ] * len(param_header), [""] * len(param_header) subj_header[1] = "Subjects:" behav_header[1] = "Behaviors:" modif_header[1] = "Modifiers:" for subj in selected_subjects: for behavior_modifiers in distinct_behav_modif: behavior, modifiers = behavior_modifiers behavior_modifiers_str = "|".join( behavior_modifiers) if modifiers else behavior for param in parameters: subj_header.append(subj) behav_header.append(behavior) modif_header.append(modifiers) param_header.append(param[1]) data_report.append(subj_header) data_report.append(behav_header) if include_modifiers: data_report.append(modif_header) data_report.append(param_header) state_events_list = [ pj[ETHOGRAM][x][BEHAVIOR_CODE] for x in pj[ETHOGRAM] if STATE in pj[ETHOGRAM][x][TYPE].upper() ] # select time interval for obs_id in selected_observations: obs_length = project_functions.observation_total_length( pj[OBSERVATIONS][obs_id]) if obs_length == -1: obs_length = 0 if time_interval == TIME_FULL_OBS: min_time = dec(0) max_time = dec(obs_length) if time_interval == TIME_EVENTS: try: min_time = dec(pj[OBSERVATIONS][obs_id][EVENTS][0][0]) except Exception: min_time = dec(0) try: max_time = dec(pj[OBSERVATIONS][obs_id][EVENTS][-1][0]) except Exception: max_time = dec(obs_length) if time_interval == TIME_ARBITRARY_INTERVAL: min_time = dec(start_time) max_time = dec(end_time) #print("observation:", obs_id) events_interval = {} mem_events_interval = {} for event in pj[OBSERVATIONS][obs_id][EVENTS]: if event[EVENT_SUBJECT_FIELD_IDX] == "": current_subject = NO_FOCAL_SUBJECT else: current_subject = event[EVENT_SUBJECT_FIELD_IDX] if current_subject not in selected_subjects: continue if current_subject not in events_interval: events_interval[current_subject] = {} mem_events_interval[current_subject] = {} if include_modifiers: modif = event[EVENT_MODIFIER_FIELD_IDX] else: modif = "" if (event[EVENT_BEHAVIOR_FIELD_IDX], modif) not in distinct_behav_modif: continue if (event[EVENT_BEHAVIOR_FIELD_IDX], modif) not in events_interval[current_subject]: events_interval[current_subject][( event[EVENT_BEHAVIOR_FIELD_IDX], modif)] = I.empty() mem_events_interval[current_subject][( event[EVENT_BEHAVIOR_FIELD_IDX], modif)] = [] if event[EVENT_BEHAVIOR_FIELD_IDX] in state_events_list: mem_events_interval[current_subject][( event[EVENT_BEHAVIOR_FIELD_IDX], modif)].append(event[EVENT_TIME_FIELD_IDX]) if len(mem_events_interval[current_subject][( event[EVENT_BEHAVIOR_FIELD_IDX], modif)]) == 2: events_interval[current_subject][(event[EVENT_BEHAVIOR_FIELD_IDX], modif)] |= \ I.closedopen(mem_events_interval[current_subject][(event[EVENT_BEHAVIOR_FIELD_IDX], modif)][0], mem_events_interval[current_subject][(event[EVENT_BEHAVIOR_FIELD_IDX], modif)][1]) mem_events_interval[current_subject][( event[EVENT_BEHAVIOR_FIELD_IDX], modif)] = [] else: events_interval[current_subject][( event[EVENT_BEHAVIOR_FIELD_IDX], modif)] |= I.singleton(event[EVENT_TIME_FIELD_IDX]) ''' print("\n\n events interval", events_interval) ''' time_bin_start = min_time if time_bin_size: time_bin_end = time_bin_start + time_bin_size if time_bin_end > max_time: time_bin_end = max_time else: time_bin_end = max_time ''' print("time_bin_start type", type(time_bin_start)) ''' while True: for subject in events_interval: # check behavior to exclude from total time time_to_subtract = 0 if EXCLUDED_BEHAVIORS in parameters_obs: for behav in events_interval[subject]: if behav[0] in parameters_obs.get( EXCLUDED_BEHAVIORS, []): interval_intersec = events_interval[ subject][behav] & I.closed( time_bin_start, time_bin_end) time_to_subtract += interval_len( interval_intersec) for behav in events_interval[subject]: interval_intersec = events_interval[subject][ behav] & I.closed(time_bin_start, time_bin_end) dur = interval_len(interval_intersec) nocc = interval_number(interval_intersec) mean = interval_mean(interval_intersec) ''' print(interval_intersec) print(subject, behav) print("duration", dur) print("n. occ", nocc) print("mean", mean) print("std dev", interval_std_dev(interval_intersec)) print(time_bin_start) print(time_bin_end) print(time_to_subtract) ''' if behav[0] in parameters_obs.get( EXCLUDED_BEHAVIORS, []): proportion = dur / ( (time_bin_end - time_bin_start)) else: proportion = dur / ( (time_bin_end - time_bin_start) - time_to_subtract) behaviors[subject][behav]["duration"] = dur behaviors[subject][behav]["number"] = nocc behaviors[subject][behav]["duration mean"] = mean behaviors[subject][behav][ "duration stdev"] = interval_std_dev( interval_intersec) behaviors[subject][behav][ "proportion of time"] = f"{proportion:.3f}" columns = [ obs_id, f"{max_time - min_time:0.3f}", f"{time_bin_start:.3f}-{time_bin_end:.3f}" ] for subject in selected_subjects: for behavior_modifiers in distinct_behav_modif: behavior, modifiers = behavior_modifiers #behavior_modifiers_str = "|".join(behavior_modifiers) if modifiers else behavior behavior_modifiers_str = behavior_modifiers for param in parameters: columns.append(behaviors[subject] [behavior_modifiers_str][param[0]]) #columns.append(behaviors[subject][behavior][param[0]]) data_report.append(columns) time_bin_start = time_bin_end time_bin_end = time_bin_start + time_bin_size if time_bin_end > max_time: time_bin_end = max_time #print(f"start: {time_bin_start} end: {time_bin_end} max time: {max_time}") if time_bin_start == time_bin_end: break except Exception: dialog.error_message("synthetic_time_budget_bin", sys.exc_info()) return (False, tablib.Dataset()) return True, data_report