create_measurement_list.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. from view.python_core.measurement_list import MeasurementList
  2. from view.python_core.measurement_list.importers import get_importer_class
  3. from view.python_core.flags import FlagsManager
  4. from collections import OrderedDict
  5. import pandas as pd
  6. import logging
  7. import re
  8. logging.basicConfig(level=logging.INFO)
  9. # ------------------- Some parameters about experimental setup, data structure and output file type --------------------
  10. # 3 for single wavelength Till Photonics Measurements
  11. # 4 for two wavelength Till Photonics Measurements
  12. # 20 for Zeiss Confocal Measurements
  13. LE_loadExp = 3
  14. # Mother of all Folders of your dataset
  15. # On Windows, if you copy paths from the file explorer, make sure the string below is always of the form r"......"
  16. STG_MotherOfAllFolders = r"/home/aj/SharedWithWindows/view_test_data/MR_Till"
  17. # path of the "Data" folder in VIEW organization containing the data
  18. # On Windows, if you copy paths from the file explorer, make sure the string below is always of the form r"......"
  19. STG_Datapath = r"data"
  20. # path of the "Lists" folder in VIEW organization containing the list files
  21. # On Windows, if you copy paths from the file explorer, make sure the string below is always of the form r"......"
  22. STG_OdorInfoPath = r"IDLlist"
  23. # Choose measurement list output extension among ".lst", ".lst.xls", ".settings.xls"
  24. measurement_output_extension = ".lst.xls"
  25. # ------------------- A dictionary containing default values for metadata.----------------------------------------------
  26. # ------------------- Only metadata included in this dictionary will be written ----------------------------------------
  27. # ----Note that columns of the output measeurement list files will have the same order as below.------------------------
  28. default_values = OrderedDict()
  29. default_values['Measu'] = 0 # unique identifier for each line, corresponds to item in TILL photonics log file
  30. default_values['Label'] = "none"
  31. default_values['Odour'] = 'odor?' # stimulus name, maybe extracted from label in the function "custom_func" below
  32. default_values['OConc'] = 0 # odor concentration, maybe extracted from label in the function "custom_func" below
  33. default_values['Analyze'] = -1 # whether to analyze in VIEWoff. Default 1
  34. default_values['Cycle'] = 0 # how many ms per frame
  35. default_values['DBB1'] = 'none' # file name of raw data
  36. default_values['UTC'] = 0 # recording time, extracted from file
  37. default_values['PxSzX'] = '4.6' # um per pixel, 1.5625 for 50x air objective, measured by Hanna Schnell July 2017 on Till vision system, with a binning of 8
  38. default_values['PxSzY'] = '4.6' # um per pixel, 1.5625 for 50x air objective, measured by Hanna Schnell July 2017 on Till vision system, with a binning of 8
  39. default_values['Lambda'] = 0 # wavelength of stimulus. In TILL, from .log file, In Zeiss LSM, from .lsm file
  40. # These will be automatically filed for LE_loadExp=4
  41. default_values['dbb2'] = 'none' # file name of raw data in dual wavelength recordings (FURA)
  42. # To include more columns, uncomment entries below and specify a default value.
  43. # #
  44. # block for first stimulus
  45. # default_values['StimON'] = -1 # stimulus onset, unit: frames, count starts at frame 1.
  46. # default_values['StimOFF'] = -1 # stimulus offset, unit: frames, count starts at frame 1.
  47. # default_values['StimLen'] = 0 # stimulus onset in ms from beginning - alternative to StimON
  48. # default_values['StimONms'] = -1 # stimulus length in ms - alternative to StimOFF
  49. # #
  50. # block for second stimulus
  51. # default_values['Stim2ON'] = 0 # stimulus onset, unit: frames, count starts at frame 1.
  52. # default_values['Stim2OFF'] = 0 # stimulus offset, unit: frames, count starts at frame 1.
  53. # default_values['Stim2Len'] = 0 # stimulus onset in ms from beginning - alternative to StimON
  54. # default_values['Stim2ONms'] = -1 # stimulus length in ms - alternative to StimOFF
  55. # #
  56. # default_values['Age'] = -1
  57. # default_values['Sex'] = 'o'
  58. # default_values['Side'] = 'none'
  59. # default_values['Comment'] = 'none'
  60. # #
  61. # default_values['MTime'] = 0
  62. # default_values['Control'] = 0
  63. # default_values['Pharma'] = 'none'
  64. # default_values['PhTime'] = 0
  65. # default_values['PhConc'] = 0
  66. # default_values['ShiftX'] = 0
  67. # default_values['ShiftY'] = 0
  68. # default_values['StimISI'] = 0
  69. # default_values['setting'] = 'none'
  70. # default_values['dbb3'] = 'none'
  71. # default_values['PosZ'] = 0
  72. # default_values['Countl'] = 0
  73. # default_values['slvFlip'] = 0
  74. # ----------------------------------------------------------------------------------------------------------------------
  75. # ----------------- A function used to modify list entries after automatic parsing of metadata -------------------------
  76. # ----------------- This function indicates what needs to be done for a row --------------------------------------------
  77. # ----------------- The same is internally applied to all rows of the measurement list----------------------------------
  78. def custom_func(list_row: pd.Series, animal_tag: str) -> pd.Series:
  79. # Examples:
  80. # list_row["StimON"] = 25
  81. _, _, stim_conc_str = list_row["Label"].split("_")
  82. stim, conc_str = stim_conc_str.split("-")
  83. list_row["Odour"] = stim
  84. conc_without_minus = conc_str[:-1] if not conc_str[-1].isdigit() else conc_str
  85. list_row["OConc"] = int(f"-{conc_without_minus}")
  86. # list_row["Odour"] = get_odor_from_label(list_row["Label"])
  87. # if list_row["Measu"]
  88. # get Odor from another file based on the value of <animal_tag> and list_row["Label"]
  89. return list_row
  90. # ----------------------------------------------------------------------------------------------------------------------
  91. # ------------------ A function defining the criteria for excluding measurements ---------------------------------------
  92. # ------------------ Currently applicable only for tillvision setups ---------------------------------------------------
  93. def measurement_filter(s):
  94. # exclude blocks that have in the name "Snapshot" or "Delta"
  95. # or that do not have any "_"
  96. name = s["Label"]
  97. label_not_okay = name.count('Snapshot') > 0 or name.count('Delta') > 0 or name.count('_') < 1
  98. label_okay = not label_not_okay
  99. # exclude blocks with less than two frames or no calibration
  100. atleast_two_frames = False
  101. if type(s["Timing_ms"]) is str:
  102. if len(s["Timing_ms"].split(' ')) >= 2 and s["Timing_ms"] != "(No calibration available)":
  103. atleast_two_frames = True
  104. return label_okay and atleast_two_frames
  105. # ______________________________________________________________________________________________________________________
  106. # ------------------ names of columns that will be overwritten by old values -------------------------------------------
  107. # ------ these will only be used if a measurement list file with the same name as current output file exists -----------
  108. overwrite_old_values = ["Line", "PxSzX", "PxSzY", "Age", "Sex", "Prefer",
  109. "Comment", "Analyze", "Odour", "OConc"]
  110. # ______________________________________________________________________________________________________________________
  111. if __name__ == "__main__":
  112. # initialize a FlagsManager object with values specified above
  113. flags = FlagsManager()
  114. flags.update_flags({"STG_MotherOfAllFolders": STG_MotherOfAllFolders,
  115. "STG_OdorInfoPath": STG_OdorInfoPath,
  116. "STG_Datapath": STG_Datapath})
  117. # initialize importer
  118. importer_class = get_importer_class(LE_loadExp)
  119. importer = importer_class(default_values)
  120. # open a dialog for choosing raw data files
  121. # this returns a dictionary where keys are animal tags (STG_ReportTag) and
  122. # values are lists of associated raw data files
  123. animal_tag_raw_data_mapping = importer.ask_for_files(default_dir=flags["STG_Datapath"])
  124. # make sure some files were chosen
  125. assert len(animal_tag_raw_data_mapping) > 0, IOError("No files were chosen!")
  126. for animal_tag, raw_data_files in animal_tag_raw_data_mapping.items():
  127. # automatically parse metadata
  128. metadata_df = importer.import_metadata(raw_data_files=raw_data_files,
  129. measurement_filter=measurement_filter)
  130. # inform user if no usable measurements were found
  131. if metadata_df.shape[0] == 0:
  132. logging.info(f"No usable measurements we found among the files "
  133. f"chosen for the animal {animal_tag}. Not creating a list file")
  134. else:
  135. # create a new Measurement list object from parsed metadata
  136. measurement_list = MeasurementList.create_from_df(LE_loadExp=LE_loadExp,
  137. df=metadata_df)
  138. # set anaylze to 0 if raw data files don't exist
  139. flags.update_flags({"STG_ReportTag": animal_tag})
  140. measurement_list.sanitize(flags=flags,
  141. data_file_extension=importer.movie_data_extension)
  142. # apply custom modifications
  143. measurement_list.update_from_custom_func(custom_func=custom_func, animal_tag=animal_tag)
  144. # construct the name of the output file
  145. out_file = f"{flags.get_lst_file_stem()}{measurement_output_extension}"
  146. # write measurement file to list
  147. measurement_list.write_to_list_file(lst_fle=out_file, columns2write=default_values.keys(),
  148. overwrite_old_values=overwrite_old_values)