diff --git a/docs/FAQ.md b/docs/FAQ.md index e0986df6c..ac3d971ae 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -427,6 +427,13 @@ Then try to relaunch simba by typing `simba`. +### 28. I'm on a mac M2 computer and SimBA won't install properly! + +
+ Show solutions! +

+ +Sorry - I don't have a straightforward installation process for M2's. But [THIS](https://github.com/sgoldenlab/simba/issues/372#issuecomment-2313396518) procedure is reported to work. diff --git a/docs/_static/img/flip_videos.webm b/docs/_static/img/flip_videos.webm new file mode 100644 index 000000000..5bac34358 Binary files /dev/null and b/docs/_static/img/flip_videos.webm differ diff --git a/docs/index.rst b/docs/index.rst index b27a8f395..8fbfb392c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,9 +6,6 @@ Welcome to SimBA's documentation! :width: 1000 :align: center - - - These docs are under development. For detailed tutorials, code, and more extensive documentation see the `SimBA GitHub repository `_. diff --git a/docs/tutorials_rst/img/index/landing_1.webm b/docs/tutorials_rst/img/index/landing_1.webm new file mode 100644 index 000000000..72b38d376 Binary files /dev/null and b/docs/tutorials_rst/img/index/landing_1.webm differ diff --git a/simba/SimBA.py b/simba/SimBA.py index 6be73878a..8045c6ef7 100644 --- a/simba/SimBA.py +++ b/simba/SimBA.py @@ -16,7 +16,6 @@ from tkinter.messagebox import askyesno import PIL.Image -from PIL import ImageTk from simba.bounding_box_tools.boundary_menus import BoundaryMenus from simba.cue_light_tools.cue_light_menues import CueLightAnalyzerMenu diff --git a/simba/data_processors/cuda/circular_statistics.py b/simba/data_processors/cuda/circular_statistics.py index 8c6dfc80f..bcfaaa132 100644 --- a/simba/data_processors/cuda/circular_statistics.py +++ b/simba/data_processors/cuda/circular_statistics.py @@ -498,7 +498,7 @@ def _instantaneous_angular_velocity(x, stride, results): def instantaneous_angular_velocity(x: np.ndarray, stride: Optional[int] = 1) -> np.ndarray: - """ + r""" Calculate the instantaneous angular velocity between angles in a given array. This function uses CUDA to perform parallel computations on the GPU. @@ -650,8 +650,8 @@ def sliding_angular_diff(x: np.ndarray, \text{difference} = \pi - |\pi - |a_1 - a_2|| Where: - - \( a_1 \) is the angle at position `x`. - - \( a_2 \) is the angle at position `x - \text{stride}`. + - :math:`a_1` is the angle at position `x`. + - :math:`a_2` is the angle at position `x - \text{stride}`. :param np.ndarray x: 1D array of angles in degrees. :param np.ndarray time_windows: 1D array of time windows in seconds to determine the stride (distance in frames) between angles. diff --git a/simba/data_processors/cuda/statistics.py b/simba/data_processors/cuda/statistics.py index 5ca8a7e7f..807f57864 100644 --- a/simba/data_processors/cuda/statistics.py +++ b/simba/data_processors/cuda/statistics.py @@ -522,7 +522,7 @@ def dunn_index(x: np.ndarray, y: np.ndarray) -> float: .. math:: - Dunn\ Index = \\frac{\\min_{i \\neq j} \\delta(c_i, c_j)}{\\max_k \\Delta(c_k)} + Dunn\ Index = \frac{\\min_{i \\neq j} \\delta(c_i, c_j)}{\\max_k \\Delta(c_k)} Where: - :math:`\\delta(c_i, c_j)` is the distance between clusters :math:`c_i` and :math:`c_j`. diff --git a/simba/mixins/circular_statistics.py b/simba/mixins/circular_statistics.py index 96cf5487f..7d8346cab 100644 --- a/simba/mixins/circular_statistics.py +++ b/simba/mixins/circular_statistics.py @@ -63,7 +63,7 @@ def mean_resultant_vector_length(data: np.ndarray) -> float: R = \\frac{{\\sqrt{{\\sum_{{i=1}}^N \\cos(\\theta_i - \\bar{\theta})^2 + \\sum_{{i=1}}^N \\sin(\theta_i - \\bar{\\theta})^2}}}}{{N}} - where \(N\) is the number of data points, \(\theta_i\) is the angle of the ith data point, and \(\bar{\theta}\) is the mean angle. + where :math:`N` is the number of data points, :math:`\\theta_i` is the angle of the ith data point, and \(\bar{\theta}\) is the mean angle. .. seealso:: :func:`simba.data_processors.cuda.circular_statistics.sliding_resultant_vector_length`, @@ -131,16 +131,19 @@ def sliding_mean_resultant_vector_length(data: np.ndarray, fps: float, time_wind @staticmethod @njit("(float32[:],)") def circular_mean(data: np.ndarray) -> float: - """ + + r""" Jitted compute of the circular mean of single sample. + .. math:: - \mu = \text{atan2}\left(\frac{1}{N} \sum_{i=1}^{N} \sin(\theta_i), \frac{1}{N} \sum_{i=1}^{N} \cos(\theta_i)\right) + + \\mu = \\text{atan2}\\left(\\frac{1}{N} \\sum_{i=1}^{N} \\sin(\\theta_i), \\frac{1}{N} \\sum_{i=1}^{N} \\cos(\\theta_i)\\right) Where: - - :math:`\mu` is the circular mean in degrees. - - :math:`\theta_i` are the individual angles in degrees. - - :math:`N` is the number of samples. + - :math:`\\theta_i` are the angles in radians within the sliding window + - :math:`N` is the number of samples in the window + :param np.ndarray data: 1D array of size len(frames) representing angles in degrees. :returns: The circular mean of the angles in degrees. @@ -226,10 +229,10 @@ def circular_std(data: np.ndarray) -> float: \\sigma_{\\text{circular}} = \\text{rad2deg}\\left(\\sqrt{-2 \\cdot \\log\\left(|\text{mean}(\\exp(j \\cdot \\theta))|\\right)}\\right) - where \\(\\theta\\) represents the angles in radians + where :math:`\\theta` represents the angles in radians - :parameter ndarray data: 1D array of size len(frames) with angles in degrees + :param ndarray data: 1D array of size len(frames) with angles in degrees :returns: The standard deviation of the data sample in degrees. :rtype: float @@ -249,7 +252,8 @@ def circular_std(data: np.ndarray) -> float: def sliding_circular_std( data: np.ndarray, fps: int, time_windows: np.ndarray ) -> np.ndarray: - """ + + r""" Compute standard deviation of angular data in sliding time windows. .. image:: _static/img/angle_stdev.png @@ -261,9 +265,9 @@ def sliding_circular_std( :func:`simba.mixins.circular_statistics.CircularStatisticsMixin.circular_std` - :parameter ndarray data: 1D array of size len(frames) representing degrees. - :parameter np.ndarray time_window: Sliding time-window as float in seconds. - :parameter int fps: fps of the recorded video + :param ndarray data: 1D array of size len(frames) representing degrees. + :param np.ndarray time_window: Sliding time-window as float in seconds. + :param int fps: fps of the recorded video :returns: Size data.shape[0] x time_windows.shape[0] with angular standard deviations in rolling time windows in degrees. :rtype: np.ndarray @@ -564,11 +568,21 @@ def sliding_rayleigh_z(data: np.ndarray, time_windows: np.ndarray, fps: int) -> @staticmethod @njit("(float32[:], float32[:],)") def circular_correlation(sample_1: np.ndarray, sample_2: np.ndarray) -> float: - """ + + r""" Jitted compute of circular correlation coefficient of two samples using the cross-correlation coefficient. Ranges from -1 to 1: 1 indicates perfect positive correlation, -1 indicates perfect negative correlation, 0 indicates no correlation. + .. math:: + R = \\frac{\\sum \\sin(\\theta_1 - \\bar{\\theta}_1) \\sin(\\theta_2 - \\bar{\\theta}_2)} + {\\sqrt{\\sum \\sin^2(\\theta_1 - \\bar{\\theta}_1) \\sum \\sin^2(\\theta_2 - \\bar{\\theta}_2)}} + + where: + + - :math:`\\theta_1` and :math:`\\theta_2` are the angles (in radians) from `sample_1` and `sample_2`, respectively. + - :math:`\\bar{\\theta}_1` and :math:`\\bar{\\theta}_2` are the mean directions of `sample_1` and `sample_2`, respectively. + .. note:: Adapted from ``astropy.stats.circstats.circcorrcoef``. @@ -579,9 +593,10 @@ def circular_correlation(sample_1: np.ndarray, sample_2: np.ndarray) -> float: .. seealso: :func:`simba.mixins.circular_statistics.CircularStatisticsMixin.sliding_circular_correlation` - :parameter np.ndarray sample_1: Angular data for e.g., Animal 1 - :parameter np.ndarray sample_1: Angular data for e.g., Animal 2 - :parameter float circular_correlation: The correlation between the two distributions. + :param np.ndarray sample_1: Angular data for e.g., Animal 1 + :param np.ndarray sample_1: Angular data for e.g., Animal 2 + :return: The correlation between the two distributions. + :rtype: float :example: >>> sample_1 = np.array([50, 90, 20, 60, 20, 90]).astype(np.float32) @@ -594,15 +609,13 @@ def circular_correlation(sample_1: np.ndarray, sample_2: np.ndarray) -> float: m1 = np.arctan2(np.mean(np.sin(sample_1)), np.mean(np.cos(sample_1))) m2 = np.arctan2(np.mean(np.sin(sample_2)), np.mean(np.cos(sample_2))) sin_1, sin_2 = np.sin(sample_1 - m1), np.sin(sample_2 - m2) - return np.abs( - np.sum(sin_1 * sin_2) - / np.sqrt(np.sum(sin_1 * sin_1) * np.sum(sin_2 * sin_2)) - ) + return np.abs(np.sum(sin_1 * sin_2) / np.sqrt(np.sum(sin_1 * sin_1) * np.sum(sin_2 * sin_2))) @staticmethod @njit("(float32[:], float32[:], float64[:], int64)") def sliding_circular_correlation(sample_1: np.ndarray, sample_2: np.ndarray, time_windows: np.ndarray, fps: float) -> np.ndarray: - """ + + r""" Jitted compute of correlations between two angular distributions in sliding time-windows using the cross-correlation coefficient. @@ -803,9 +816,9 @@ def sliding_rao_spacing(data: np.ndarray, time_windows: np.ndarray, fps: int) -> """ Jitted compute of the uniformity of a circular dataset in sliding windows. - :parameter ndarray data: 1D array of size len(frames) representing degrees. - :parameter np.ndarray time_window: Rolling time-window as float in seconds. - :parameter int fps: fps of the recorded video + :param ndarray data: 1D array of size len(frames) representing degrees. + :param np.ndarray time_window: Rolling time-window as float in seconds. + :param int fps: fps of the recorded video :return np.ndarray: representing rao-spacing U in every sliding windows [-window:n] .. image:: _static/img/raospacing.png @@ -868,7 +881,7 @@ def kuipers_two_sample_test(sample_1: np.ndarray, sample_2: np.ndarray) -> float Where: - :math:`F_1(\theta)` and :math:`F_2(\theta)` are the empirical cumulative distribution functions (CDFs) of the two circular samples. - - :math:`\theta` are the sorted angles in the two samples. + - :math:`\\theta` are the sorted angles in the two samples. .. note:: Adapted from `Kuiper `__ by `Anne Archibald `_. @@ -979,20 +992,29 @@ def watsons_u(data: np.ndarray): @staticmethod @njit("(float32[:],)") def circular_range(data: np.ndarray) -> float: - """ + r""" Jitted compute of circular range in degrees. The range is defined as the angular span of the shortest arc that can contain all the data points. A smaller range indicates a more concentrated distribution, while a larger range suggests a more dispersed distribution. + .. math:: + \text{Range} = \min \left( 2\pi - \max(\delta \theta_i), \theta_{\text{max}} - \theta_{\text{min}} \right) + + where: + + - :math:`\delta \theta_i` is the difference between consecutive angular data points. + - :math:`\theta_{\text{max}}` and :math:`\theta_{\text{min}}` are the maximum and minimum angles in the data. + + .. image:: _static/img/circular_range.png - :width: 600 + :width: 400 :align: center .. seealso:: :func:`simba.data_processors.cuda.circular_statistics.sliding_circular_range`, :func:`simba.mixins.circular_statistics.CircularStatisticsMixin.sliding_circular_range` - :parameter ndarray data: 1D array of circular data measured in degrees + :param ndarray data: 1D array of circular data measured in degrees :return: The circular range in degrees. :rtype: np.ndarray diff --git a/simba/mixins/statistics_mixin.py b/simba/mixins/statistics_mixin.py index 0eaa54c8e..6fb94851f 100644 --- a/simba/mixins/statistics_mixin.py +++ b/simba/mixins/statistics_mixin.py @@ -145,7 +145,8 @@ def independent_samples_t( sample_2: np.ndarray, critical_values: Optional[np.ndarray] = None, ) -> (float, Union[None, bool]): - """ + + r""" Jitted compute independent-samples t-test statistic and boolean significance between two distributions. .. note:: @@ -3781,10 +3782,10 @@ def adjusted_rand(x: np.ndarray, y: np.ndarray) -> float: ARI = \\frac{TP + TN}{TP + FP + FN + TN} where: - - TP (True Positive) is the number of pairs of elements that are in the same cluster in both x and y, - - FP (False Positive) is the number of pairs of elements that are in the same cluster in y but not in x, - - FN (False Negative) is the number of pairs of elements that are in the same cluster in x but not in y, - - TN (True Negative) is the number of pairs of elements that are in different clusters in both x and y. + - :math:`TP` (True Positive) is the number of pairs of elements that are in the same cluster in both x and y, + - :math:`FP` (False Positive) is the number of pairs of elements that are in the same cluster in y but not in x, + - :math:`FN` (False Negative) is the number of pairs of elements that are in the same cluster in x but not in y, + - :math:`TN` (True Negative) is the number of pairs of elements that are in different clusters in both x and y. The ARI value ranges from -1 to 1. A value of 1 indicates perfect clustering agreement, 0 indicates random clustering, and negative values indicate disagreement between the clusterings. @@ -4052,7 +4053,7 @@ def wave_hedges_distance(self, x: np.ndarray, y: np.ndarray) -> float: :example: >>> x = np.random.randint(0, 500, (1000,)) >>> y = np.random.randint(0, 500, (1000,)) - >>> wave_hedges_distance(x=x, y=y) + >>> Statistics().wave_hedges_distance(x=x, y=y) """ check_valid_array(data=x, source=f'{Statistics.wave_hedges_distance.__name__} x', accepted_ndims=(1,), accepted_dtypes=Formats.NUMERIC_DTYPES.value) diff --git a/simba/mixins/timeseries_features_mixin.py b/simba/mixins/timeseries_features_mixin.py index 8fbdb2687..b9b559e7f 100644 --- a/simba/mixins/timeseries_features_mixin.py +++ b/simba/mixins/timeseries_features_mixin.py @@ -701,8 +701,8 @@ def permutation_entropy(data: np.ndarray, dimension: int, delay: int) -> float: PE = - \\sum(p_i \\log(p_i)) where: - - PE is the permutation entropy. - - p_i is the probability of each unique order pattern. + :math:`PE` is the permutation entropy. + :math:`p_i` is the probability of each unique order pattern. :param numpy.ndarray data: The time series data for which permutation entropy is calculated. :param int dimension: It specifies the length of the order patterns to be considered. @@ -762,9 +762,9 @@ def line_length(data: np.ndarray) -> float: LL = \sum_{i=1}^{N-1} |x[i] - x[i-1]| where: - - LL is the line length. - - N is the number of elements in the input data array. - - x[i] represents the value of the data at index i. + :math:`LL` is the line length. + :math:`N` is the number of elements in the input data array. + :math:`x[i]` represents the value of the data at index i. .. image:: _static/img/line_length.png @@ -882,12 +882,20 @@ def sliding_descriptive_statistics(data: np.ndarray, window_sizes: np.ndarray, s :return np.ndarray: Array containing the selected descriptive statistics for each window size, data point, and statistic type. The shape of the result array is (len(statistics), data.shape[0], window_sizes.shape[0). .. note:: - - The `statistics` parameter should be a list containing one or more of the following statistics: - 'var' (variance), 'max' (maximum), 'min' (minimum), 'std' (standard deviation), 'median' (median), - 'mean' (mean), 'mad' (median absolute deviation), 'sum' (sum), 'mac' (mean absolute change), - 'rms' (root mean square), 'absenergy' (absolute energy). - - If the statistics list is ['var', 'max', 'mean'], the - 3rd dimension order in the result array will be: [variance, maximum, mean] + The `statistics` parameter should be a list containing one or more of the following statistics: + * 'var' (variance) + * 'max' (maximum) + * 'min' (minimum) + * 'std' (standard deviation) + * 'median' (median) + * 'mean' (mean) + * 'mad' (median absolute deviation) + * 'sum' (sum) + * 'mac' (mean absolute change) + * 'rms' (root mean square) + * 'absenergy' (absolute energy) + + E.g., If the statistics list is ['var', 'max', 'mean'], the 3rd dimension order in the result array will be: [variance, maximum, mean] :example: >>> data = np.array([1, 4, 2, 3, 5, 6, 8, 7, 9, 10]).astype(np.float32) @@ -1792,7 +1800,7 @@ def sliding_pct_in_top_n( Compute the percentage of elements in the top 'n' frequencies in sliding windows of the input array. .. note:: - To compute percentage of elements in the top 'n' frequencies in entire array, use ``simba.mixins.statistics_mixin.Statistics.pct_in_top_n``. + To compute percentage of elements in the top 'n' frequencies in entire array, use :func:`simba.mixins.statistics_mixin.Statistics.pct_in_top_n()`. :param np.ndarray x: Input 1D array. :param np.ndarray windows: Array of window sizes in seconds. @@ -1831,9 +1839,7 @@ def sliding_pct_in_top_n( results = np.full((x.shape[0], windows.shape[0]), -1.0) for i in range(windows.shape[0]): W_s = int(windows[i] * fps) - for cnt, (l, r) in enumerate( - zip(range(0, x.shape[0] + 1), range(W_s, x.shape[0] + 1)) - ): + for cnt, (l, r) in enumerate(zip(range(0, x.shape[0] + 1), range(W_s, x.shape[0] + 1))): sample = x[l:r] cnts = np.sort(np.unique(sample, return_counts=True)[1])[-n:] results[int(r - 1), i] = np.sum(cnts) / sample.shape[0] diff --git a/simba/video_processors/video_processing.py b/simba/video_processors/video_processing.py index d7837658b..52bda5635 100644 --- a/simba/video_processors/video_processing.py +++ b/simba/video_processors/video_processing.py @@ -110,7 +110,7 @@ def convert_to_jpeg(path: Union[str, os.PathLike, List[Union[str, os.PathLike]]] Quality above 95 should be avoided; 100 disables portions of the JPEG compression algorithm, and results in large files with hardly any gain in image quality .. image:: _static/img/convert_to_jpeg.jpeg - :width: 600 + :width: 400 :align: center :parameter Union[str, os.PathLike] directory: Path to directory holding image files, a single image file, or a list of paths to image files. @@ -323,7 +323,7 @@ def convert_to_webp(path: Union[str, os.PathLike], Convert the file type of all image files within a directory to WEBP format of passed quality. .. image:: _static/img/convert_to_webp.webp - :width: 600 + :width: 400 :align: center :parameter Union[str, os.PathLike] directory: Path to directory holding image files @@ -4259,7 +4259,7 @@ def flip_videos(video_path: Union[str, os.PathLike], Flip a video or directory of videos horizontally, vertically, or both, and save them to the specified directory. .. video:: _static/img/flip_videos.webm - :width: 900 + :width: 700 :loop: :autoplay: