Skip to content
This repository has been archived by the owner on Oct 30, 2024. It is now read-only.

Commit

Permalink
Merge remote-tracking branch 'origin/main' into dashboard-01
Browse files Browse the repository at this point in the history
  • Loading branch information
lauraporta committed Jul 4, 2023
2 parents 2ef5e17 + 32ea410 commit f0987b0
Show file tree
Hide file tree
Showing 17 changed files with 777 additions and 462 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ Second, set up the environmental variables and the config file by executing:
```bash
python3 setup_for_demo.py
```
Then edit the config file with the correct paths to the data by overwriting `/path/to/`. The only path that matters at this stage is the `allen_dff` path, which should point to the folder where you stored the pre-processed data.
Then edit the config file with the correct paths to the data by overwriting `/path/to/`.
Please edit the `allen_dff` path to point to the folder where you stored the pre-processed data and the `output` path to point to the folder where you want to store the analysis output.

Finally, run `python3 demo_cli.py` to run the analysis. The script will create a file containing the analysis output which will then be used by the dashboard.

Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies = [
"scipy",
"numba",
"tqdm",
"GitPython",
]

[project.urls]
Expand All @@ -51,6 +52,8 @@ dev = [
"ruff",
"setuptools_scm",
"check-manifest",
"pytest-mock",
"GitPython",
]

[build-system]
Expand Down
202 changes: 102 additions & 100 deletions rsp_vision/analysis/spatial_freq_temporal_freq.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,123 +527,125 @@ def get_median_subtracted_response_and_params(
return median_subtracted_response_2d_matrix, sf_0, tf_0, peak_response

def get_gaussian_fits_for_roi(self, roi_id: int) -> dict:
"""Calculate the best fit parameters for each ROI and direction.
This method takes as input the ROI index, and loops over the
directions to calculate the best fit parameters for each spatial
and temporal frequency. First, it calls the
get_median_subtracted_response_and_params method to extract the
median subtracted response matrix for the given ROI and direction.
Then, the method performs a 2D Gaussian fit to the 2D response
matrix using the fit_2D_gaussian_to_data method. The resulting best
fit parameters are stored in a dictionary, where the keys are the
directions, and the values are tuples containing the
preferred spatial and temporal frequencies and the peak response
amplitude, the best fit parameter values obtained from the Gaussian
fit, and the median-subtracted response matrix.
There are 10 possible attempts to fit the data. If the fit fails,
the method will try again with a different initial guess. If all
attempts fail, the fitting parameters will be set to 1.
Args:
roi_id (int): The index of the ROI for which to calculate the best
fit parameters.
"""Calculates the best fit parameters for each direction and for
the pooled data for a given ROI. It calls the `manage_fitting`
method in oredr to find the best fit parameters.
Returns:
dict: A dictionary with the best fit parameters for each direction.
The keys are the directions, and the values are tuples
containing the preferred spatial and temporal frequencies
and the peak response amplitude, the best fit parameter values
obtained from the Gaussian fit, and the median-subtracted
response matrix.
"""

def manage_fitting(
responses: np.ndarray,
roi_id: int,
spatial_frequencies: np.ndarray,
temporal_frequencies: np.ndarray,
pool_directions: bool = False,
direction: float = sys.float_info.min,
) -> Tuple[Tuple[float, float, float], np.ndarray, np.ndarray]:
# Internal method to avoid code repetition
(
response_matrix,
sf_0,
tf_0,
peak_response,
) = self.get_median_subtracted_response_and_params(
responses=responses,
roi_id=roi_id,
sfs=spatial_frequencies,
tfs=temporal_frequencies,
dir=direction,
pool_directions=pool_directions,
)

initial_parameters = [
peak_response,
sf_0,
tf_0,
np.std(spatial_frequencies, ddof=1),
np.std(temporal_frequencies, ddof=1),
self.data.config["fitting"]["power_law_exp"],
]

best_result = None
tentatives = 0
while best_result is None and tentatives < 10:
best_result = fit_2D_gaussian_to_data(
spatial_frequencies,
temporal_frequencies,
response_matrix,
initial_parameters,
self.data.config,
)
if best_result is None:
logging.warning(
f"ROI {roi_id} and direction {dir} failed to fit."
+ f"Trying again... Tentative {tentatives + 1} of 10"
)
tentatives += 1

if best_result is None:
logging.warning(
f"ROI {roi_id} and direction {dir} failed to fit."
+ "Skipping..."
)
best_result = OptimizeResult()
best_result.x = np.nan * np.ones(6)
Parameters
----------
roi_id (int)
The index of the ROI for which to calculate the best
fit parameters.
return (
(sf_0, tf_0, peak_response),
best_result.x,
response_matrix,
)
Returns
-------
dict
A dictionary with the best fit parameters for each direction
and for the pooled data.The keys are the directions, and the
values are tuples containing the preferred spatial and temporal
frequencies and the peak response amplitude, the best fit parameter
values obtained from the Gaussian fit, and the median-subtracted
response matrix.
"""

roi_data = {}
for dir in self.data.directions:
roi_data[dir] = manage_fitting(
responses=self.data.responses,
roi_data[dir] = self.manage_fitting(
roi_id=roi_id,
direction=dir,
spatial_frequencies=self.data.spatial_frequencies,
temporal_frequencies=self.data.temporal_frequencies,
pool_directions=False,
)

# now the same by pooling directions
roi_data["pooled"] = manage_fitting(
responses=self.data.responses,
roi_data["pooled"] = self.manage_fitting(
roi_id=roi_id,
spatial_frequencies=self.data.spatial_frequencies,
temporal_frequencies=self.data.temporal_frequencies,
pool_directions=True,
)

return roi_data

def manage_fitting(
self,
roi_id: int,
pool_directions: bool = False,
direction: float = sys.float_info.min,
) -> Tuple[Tuple[float, float, float], np.ndarray, np.ndarray]:
"""
This method is called by the get_gaussian_fits_for_roi method.
It calls the get_median_subtracted_response_and_params method to
extract the median subtracted response matrix for the given ROI and
direction. Then, it calls the fit_2D_gaussian_to_data method to
perform a 2D Gaussian fit to the 2D response matrix. The resulting
best fit parameters are stored in a tuple, where the first element
is the peak response amplitude, the second element is the best fit
parameter values obtained from the Gaussian fit, and the third
element is the median-subtracted response matrix.
Parameters
----------
roi_id : int
The ID of the ROI to extract the response matrix from.
pool_directions : bool, optional
Whether to extract the response matrix for a single direction,
by default False
direction : float, optional
The direction to extract the response matrix from. To be used
only if `pool_directions` is set to False. By default
sys.float_info.min
Returns
-------
Tuple[Tuple[float, float, float], np.ndarray, np.ndarray]
A tuple containing the peak response amplitude and its
corresponding SF and TF, the best fit parameter values
obtained from the Gaussian fit, and the median-subtracted
response matrix.
"""
(
response_matrix,
sf_0,
tf_0,
peak_response,
) = self.get_median_subtracted_response_and_params(
responses=self.data.responses,
roi_id=roi_id,
sfs=self.data.spatial_frequencies,
tfs=self.data.temporal_frequencies,
dir=direction,
pool_directions=pool_directions,
)

initial_parameters = [
peak_response,
sf_0,
tf_0,
np.std(self.data.spatial_frequencies, ddof=1),
np.std(self.data.temporal_frequencies, ddof=1),
self.data.config["fitting"]["power_law_exp"],
]

best_result = fit_2D_gaussian_to_data(
self.data.spatial_frequencies,
self.data.temporal_frequencies,
response_matrix,
initial_parameters,
self.data.config,
)

if best_result is None:
logging.warning(
f"ROI {roi_id} and direction {dir} failed to fit."
+ "Skipping..."
)
best_result = OptimizeResult()
best_result.x = np.nan * np.ones(6)

return (
(sf_0, tf_0, peak_response),
best_result.x,
response_matrix,
)

def get_all_fits(self) -> None:
"""Calculate the Gaussian fits for all ROIs using multiprocessing.
Expand Down
19 changes: 10 additions & 9 deletions rsp_vision/objects/SWC_Blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def __init__(
project_name: str,
raw_data: bool = False,
derivatives: bool = True,
local_path: Path = Path("."),
local_path: Path = Path().absolute(),
) -> None:
"""
Create a `SWC_Blueprint_Spec` object to specify the location of the
Expand All @@ -30,7 +30,7 @@ def __init__(
by default True.
local_path : Path, optional
The path to the local folder where you want to create the project,
by default Path(".").
by default Path().absolute().
"""
self.project_name = project_name
self.raw_data = raw_data
Expand Down Expand Up @@ -72,7 +72,8 @@ class SubjectFolder:
Create a `SubjectFolder` object from a `FolderNamingSpecs` object
and a subject number.
make_from_table_row(table_row)
Create a `SubjectFolder` object from a table row.
Create a `SubjectFolder` object from a row of a table (a dict which
corresponds to a row of the "analysis_log.csv" file).
Raises
------
Expand All @@ -84,7 +85,7 @@ def __init__(
self,
swc_blueprint_spec: SWC_Blueprint_Spec,
folder_or_table: Union[FolderNamingSpecs, dict],
sub_num: int = 0,
sub_num: int,
):
self.swc_blueprint_spec = swc_blueprint_spec
if isinstance(folder_or_table, FolderNamingSpecs):
Expand Down Expand Up @@ -119,9 +120,9 @@ def make_from_table_row(self, table_row: dict):
self.sub_folder_name = (
f"sub-{self.sub_num:03d}"
+ "_line-"
+ table_row["mouse line"]
+ table_row["mouse_line"]
+ "_id-"
+ str(table_row["mouse id"])
+ str(table_row["mouse_id"])
)


Expand Down Expand Up @@ -168,7 +169,7 @@ def __init__(
self,
subject_folder: SubjectFolder,
folder_or_table: Union[FolderNamingSpecs, dict],
ses_num: int = 0,
ses_num: int,
):
self.subject_folder = subject_folder
if isinstance(folder_or_table, FolderNamingSpecs):
Expand Down Expand Up @@ -217,13 +218,13 @@ def make_from_folder_naming_specs(

def make_from_table_row(self, table_row: dict):
self.ses_num = int(table_row["ses"])
self.monitor = table_row["monitor position"]
self.monitor = table_row["monitor_position"]
self.ses_folder_name = (
f"ses-{self.ses_num:03d}"
+ "_hemisphere-"
+ table_row["hemisphere"]
+ "_region-"
+ table_row["brain region"]
+ table_row["brain_region"]
+ "_monitor-"
+ self.monitor
+ (
Expand Down
3 changes: 2 additions & 1 deletion rsp_vision/objects/photon_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,9 @@ def make_signal_dataframe(self, data_raw: DataRaw) -> pd.DataFrame:
direction_tmp = []
stimulus_onset_tmp = []

# Use list comprehension to create the dataframe
# Use lists to create the dataframe
# reduces the complexity from O(n^2) to O(n)
# n = number of frames
for session in range(self.n_sessions):
for roi in range(self.n_roi):
if self.total_n_days in (2, 3):
Expand Down
Loading

0 comments on commit f0987b0

Please sign in to comment.