spacr 0.3.33__py3-none-any.whl → 0.3.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spacr/gui_utils.py +1 -1
- spacr/ml.py +34 -1
- spacr/plot.py +301 -211
- spacr/settings.py +1 -0
- {spacr-0.3.33.dist-info → spacr-0.3.34.dist-info}/METADATA +1 -1
- {spacr-0.3.33.dist-info → spacr-0.3.34.dist-info}/RECORD +10 -10
- {spacr-0.3.33.dist-info → spacr-0.3.34.dist-info}/LICENSE +0 -0
- {spacr-0.3.33.dist-info → spacr-0.3.34.dist-info}/WHEEL +0 -0
- {spacr-0.3.33.dist-info → spacr-0.3.34.dist-info}/entry_points.txt +0 -0
- {spacr-0.3.33.dist-info → spacr-0.3.34.dist-info}/top_level.txt +0 -0
spacr/gui_utils.py
CHANGED
@@ -508,7 +508,7 @@ def run_function_gui(settings_type, settings, q, fig_queue, stop_requested):
|
|
508
508
|
imports = 1
|
509
509
|
elif settings_type == 'ml_analyze':
|
510
510
|
function = generate_ml_scores
|
511
|
-
imports =
|
511
|
+
imports = 1
|
512
512
|
elif settings_type == 'cellpose_masks':
|
513
513
|
function = identify_masks_finetune
|
514
514
|
imports = 1
|
spacr/ml.py
CHANGED
@@ -730,7 +730,7 @@ def process_scores(df, dependent_variable, plate, min_cell_count=25, agg_type='m
|
|
730
730
|
|
731
731
|
def generate_ml_scores(settings):
|
732
732
|
|
733
|
-
from .io import _read_and_merge_data
|
733
|
+
from .io import _read_and_merge_data, _read_db
|
734
734
|
from .plot import plot_plates
|
735
735
|
from .utils import get_ml_results_paths
|
736
736
|
from .settings import set_default_analyze_screen
|
@@ -753,6 +753,38 @@ def generate_ml_scores(settings):
|
|
753
753
|
nuclei_limit,
|
754
754
|
pathogen_limit,
|
755
755
|
uninfected)
|
756
|
+
|
757
|
+
if settings['annotation_column'] is not None:
|
758
|
+
|
759
|
+
settings['location_column'] = settings['annotation_column']
|
760
|
+
|
761
|
+
png_list_df = _read_db(db_loc[0], tables=['png_list'])[0]
|
762
|
+
if not {'prcfo', settings['annotation_column']}.issubset(png_list_df.columns):
|
763
|
+
raise ValueError("The 'png_list_df' DataFrame must contain 'prcfo' and 'test' columns.")
|
764
|
+
annotated_df = png_list_df[['prcfo', settings['annotation_column']]].set_index('prcfo')
|
765
|
+
df = annotated_df.merge(df, left_index=True, right_index=True)
|
766
|
+
display(df)
|
767
|
+
unique_values = df[settings['annotation_column']].dropna().unique()
|
768
|
+
if len(unique_values) == 1:
|
769
|
+
unannotated_rows = df[df[settings['annotation_column']].isna()].index
|
770
|
+
existing_value = unique_values[0]
|
771
|
+
next_value = existing_value + 1
|
772
|
+
|
773
|
+
settings['positive_control'] = str(existing_value)
|
774
|
+
settings['negative_control'] = str(next_value)
|
775
|
+
|
776
|
+
existing_count = df[df[settings['annotation_column']] == existing_value].shape[0]
|
777
|
+
num_to_select = min(existing_count, len(unannotated_rows))
|
778
|
+
selected_rows = np.random.choice(unannotated_rows, size=num_to_select, replace=False)
|
779
|
+
df.loc[selected_rows, settings['annotation_column']] = next_value
|
780
|
+
|
781
|
+
# Print the counts for existing_value and next_value
|
782
|
+
existing_count_final = df[df[settings['annotation_column']] == existing_value].shape[0]
|
783
|
+
next_count_final = df[df[settings['annotation_column']] == next_value].shape[0]
|
784
|
+
|
785
|
+
print(f"Number of rows with value {existing_value}: {existing_count_final}")
|
786
|
+
print(f"Number of rows with value {next_value}: {next_count_final}")
|
787
|
+
df[settings['annotation_column']] = df[settings['annotation_column']].apply(str)
|
756
788
|
|
757
789
|
if settings['channel_of_interest'] in [0,1,2,3]:
|
758
790
|
|
@@ -847,6 +879,7 @@ def ml_analysis(df, channel_of_interest=3, location_column='col', positive_contr
|
|
847
879
|
if verbose:
|
848
880
|
print(f'Found {len(features)} numerical features in the dataframe')
|
849
881
|
print(f'Features used in training: {features}')
|
882
|
+
|
850
883
|
df = pd.concat([df, df_metadata[location_column]], axis=1)
|
851
884
|
|
852
885
|
# Subset the dataframe based on specified column values
|
spacr/plot.py
CHANGED
@@ -23,6 +23,9 @@ import pingouin as pg
|
|
23
23
|
from ipywidgets import IntSlider, interact
|
24
24
|
from IPython.display import Image as ipyimage
|
25
25
|
|
26
|
+
import matplotlib.patches as patches
|
27
|
+
from collections import defaultdict
|
28
|
+
|
26
29
|
def plot_image_mask_overlay(file, channels, cell_channel, nucleus_channel, pathogen_channel, figuresize=10, normalize=True, thickness=3, save_pdf=True):
|
27
30
|
"""Plot image and mask overlays."""
|
28
31
|
|
@@ -1930,7 +1933,7 @@ def jitterplot_by_annotation(src, x_column, y_column, plot_title='Jitter Plot',
|
|
1930
1933
|
|
1931
1934
|
return balanced_df
|
1932
1935
|
|
1933
|
-
def create_grouped_plot(df, grouping_column, data_column, graph_type='bar', summary_func='mean', order=None, colors=None, output_dir='./output', save=False,
|
1936
|
+
def create_grouped_plot(df, grouping_column, data_column, graph_type='bar', summary_func='mean', order=None, colors=None, output_dir='./output', save=False, y_lim=None, error_bar_type='std'):
|
1934
1937
|
"""
|
1935
1938
|
Create a grouped plot, perform statistical tests, and optionally export the results along with the plot.
|
1936
1939
|
|
@@ -1944,7 +1947,7 @@ def create_grouped_plot(df, grouping_column, data_column, graph_type='bar', summ
|
|
1944
1947
|
- colors: List of colors for each group.
|
1945
1948
|
- output_dir: Directory where the figure and test results will be saved if `save=True`.
|
1946
1949
|
- save: Boolean flag indicating whether to save the plot and results to files.
|
1947
|
-
-
|
1950
|
+
- y_lim: Optional y-axis min and max.
|
1948
1951
|
- error_bar_type: Type of error bars to plot, either 'std' for standard deviation or 'sem' for standard error of the mean.
|
1949
1952
|
|
1950
1953
|
Outputs:
|
@@ -2068,10 +2071,8 @@ def create_grouped_plot(df, grouping_column, data_column, graph_type='bar', summ
|
|
2068
2071
|
results_df = pd.DataFrame(test_results)
|
2069
2072
|
|
2070
2073
|
# Set y-axis start if provided
|
2071
|
-
if
|
2072
|
-
plt.ylim(
|
2073
|
-
else:
|
2074
|
-
plt.ylim(0, None) # Default to starting at 0 if no custom start value is provided
|
2074
|
+
if isinstance(y_lim, list) and len(y_lim) == 2:
|
2075
|
+
plt.ylim(y_lim)
|
2075
2076
|
|
2076
2077
|
# If save is True, save the plot and results as PNG and CSV
|
2077
2078
|
if save:
|
@@ -2095,12 +2096,14 @@ def create_grouped_plot(df, grouping_column, data_column, graph_type='bar', summ
|
|
2095
2096
|
|
2096
2097
|
class spacrGraph:
|
2097
2098
|
def __init__(self, df, grouping_column, data_column, graph_type='bar', summary_func='mean',
|
2098
|
-
order=None, colors=None, output_dir='./output', save=False,
|
2099
|
+
order=None, colors=None, output_dir='./output', save=False, y_lim=None,
|
2099
2100
|
error_bar_type='std', remove_outliers=False, theme='pastel', representation='object',
|
2100
2101
|
paired=False, all_to_all=True, compare_group=None):
|
2102
|
+
|
2101
2103
|
"""
|
2102
2104
|
Class for creating grouped plots with optional statistical tests and data preprocessing.
|
2103
2105
|
"""
|
2106
|
+
|
2104
2107
|
self.df = df
|
2105
2108
|
self.grouping_column = grouping_column
|
2106
2109
|
self.data_column = data_column if isinstance(data_column, list) else [data_column]
|
@@ -2110,7 +2113,6 @@ class spacrGraph:
|
|
2110
2113
|
self.colors = colors
|
2111
2114
|
self.output_dir = output_dir
|
2112
2115
|
self.save = save
|
2113
|
-
self.y_axis_start = y_axis_start
|
2114
2116
|
self.error_bar_type = error_bar_type
|
2115
2117
|
self.remove_outliers = remove_outliers
|
2116
2118
|
self.theme = theme
|
@@ -2118,14 +2120,15 @@ class spacrGraph:
|
|
2118
2120
|
self.paired = paired
|
2119
2121
|
self.all_to_all = all_to_all
|
2120
2122
|
self.compare_group = compare_group
|
2121
|
-
|
2123
|
+
self.y_lim = y_lim
|
2122
2124
|
self.results_df = pd.DataFrame()
|
2123
2125
|
self.sns_palette = None
|
2124
|
-
self.fig = None
|
2126
|
+
self.fig = None
|
2127
|
+
|
2128
|
+
self.results_name = str(self.data_column[0])+'_'+str(self.grouping_column)+'_'+str(self.graph_type)
|
2125
2129
|
|
2126
|
-
# Preprocess and set palette
|
2127
2130
|
self._set_theme()
|
2128
|
-
self.raw_df = self.df.copy()
|
2131
|
+
self.raw_df = self.df.copy()
|
2129
2132
|
self.df = self.preprocess_data()
|
2130
2133
|
|
2131
2134
|
def _set_theme(self):
|
@@ -2150,19 +2153,15 @@ class spacrGraph:
|
|
2150
2153
|
"""Preprocess the data: remove NaNs, sort/order the grouping column, and optionally group by 'prc'."""
|
2151
2154
|
# Remove NaNs in both the grouping column and each data column
|
2152
2155
|
df = self.df.dropna(subset=[self.grouping_column] + self.data_column) # Handle multiple data columns
|
2153
|
-
|
2154
2156
|
# Group by 'prc' column if representation is 'well'
|
2155
2157
|
if self.representation == 'well':
|
2156
2158
|
df = df.groupby(['prc', self.grouping_column])[self.data_column].agg(self.summary_func).reset_index()
|
2157
|
-
|
2158
2159
|
if self.order:
|
2159
2160
|
df[self.grouping_column] = pd.Categorical(df[self.grouping_column], categories=self.order, ordered=True)
|
2160
2161
|
else:
|
2161
2162
|
df[self.grouping_column] = pd.Categorical(df[self.grouping_column], categories=sorted(df[self.grouping_column].unique()), ordered=True)
|
2162
|
-
|
2163
2163
|
return df
|
2164
2164
|
|
2165
|
-
|
2166
2165
|
def remove_outliers_from_plot(self):
|
2167
2166
|
"""Remove outliers from the plot but keep them in the data."""
|
2168
2167
|
filtered_df = self.df.copy()
|
@@ -2181,13 +2180,11 @@ class spacrGraph:
|
|
2181
2180
|
"""Perform normality tests for each group and each data column."""
|
2182
2181
|
unique_groups = self.df[self.grouping_column].unique()
|
2183
2182
|
normality_results = []
|
2184
|
-
|
2185
2183
|
for column in self.data_column:
|
2186
2184
|
grouped_data = [self.df.loc[self.df[self.grouping_column] == group, column] for group in unique_groups]
|
2187
2185
|
normal_p_values = [normaltest(data).pvalue for data in grouped_data]
|
2188
2186
|
normal_stats = [normaltest(data).statistic for data in grouped_data]
|
2189
2187
|
is_normal = all(p > 0.05 for p in normal_p_values) # Test if all groups are normal
|
2190
|
-
|
2191
2188
|
for group, stat, p_value in zip(unique_groups, normal_stats, normal_p_values):
|
2192
2189
|
normality_results.append({
|
2193
2190
|
'Comparison': f'Normality test for {group} on {column}',
|
@@ -2197,10 +2194,8 @@ class spacrGraph:
|
|
2197
2194
|
'Column': column,
|
2198
2195
|
'n': len(self.df[self.df[self.grouping_column] == group]) # Sample size
|
2199
2196
|
})
|
2200
|
-
|
2201
2197
|
return is_normal, normality_results
|
2202
2198
|
|
2203
|
-
|
2204
2199
|
def perform_levene_test(self, unique_groups):
|
2205
2200
|
"""Perform Levene's test for equal variance."""
|
2206
2201
|
grouped_data = [self.df.loc[self.df[self.grouping_column] == group, self.data_column] for group in unique_groups]
|
@@ -2208,57 +2203,48 @@ class spacrGraph:
|
|
2208
2203
|
return stat, p_value
|
2209
2204
|
|
2210
2205
|
def perform_statistical_tests(self, unique_groups, is_normal):
|
2211
|
-
"""Perform statistical tests
|
2212
|
-
|
2213
|
-
|
2214
|
-
|
2215
|
-
|
2216
|
-
|
2206
|
+
"""Perform statistical tests separately for each data column."""
|
2207
|
+
test_results = []
|
2208
|
+
for column in self.data_column: # Iterate over each data column
|
2209
|
+
grouped_data = [self.df.loc[self.df[self.grouping_column] == group, column] for group in unique_groups]
|
2210
|
+
if len(unique_groups) == 2: # For two groups: class_0 vs class_1
|
2211
|
+
if is_normal:
|
2212
|
+
if self.paired:
|
2213
|
+
stat, p = pg.ttest(grouped_data[0], grouped_data[1], paired=True).iloc[0][['T', 'p-val']]
|
2214
|
+
test_name = 'Paired T-test'
|
2215
|
+
else:
|
2216
|
+
stat, p = ttest_ind(grouped_data[0], grouped_data[1])
|
2217
|
+
test_name = 'T-test'
|
2217
2218
|
else:
|
2218
|
-
|
2219
|
-
|
2219
|
+
if self.paired:
|
2220
|
+
stat, p = pg.wilcoxon(grouped_data[0], grouped_data[1]).iloc[0][['T', 'p-val']]
|
2221
|
+
test_name = 'Paired Wilcoxon test'
|
2222
|
+
else:
|
2223
|
+
stat, p = mannwhitneyu(grouped_data[0], grouped_data[1])
|
2224
|
+
test_name = 'Mann-Whitney U test'
|
2220
2225
|
else:
|
2221
|
-
if
|
2222
|
-
|
2223
|
-
test_name = '
|
2226
|
+
if is_normal:
|
2227
|
+
stat, p = f_oneway(*grouped_data)
|
2228
|
+
test_name = 'One-way ANOVA'
|
2224
2229
|
else:
|
2225
|
-
|
2226
|
-
test_name = '
|
2227
|
-
else:
|
2228
|
-
if is_normal:
|
2229
|
-
stat_test = f_oneway
|
2230
|
-
test_name = 'One-way ANOVA'
|
2231
|
-
else:
|
2232
|
-
stat_test = kruskal
|
2233
|
-
test_name = 'Kruskal-Wallis test'
|
2234
|
-
|
2235
|
-
comparisons = list(itertools.combinations(unique_groups, 2))
|
2236
|
-
test_results = []
|
2237
|
-
for (group1, group2) in comparisons:
|
2238
|
-
data1 = self.df[self.df[self.grouping_column] == group1][self.data_column]
|
2239
|
-
data2 = self.df[self.df[self.grouping_column] == group2][self.data_column]
|
2240
|
-
raw_data1 = self.raw_df[self.raw_df[self.grouping_column] == group1][self.data_column]
|
2241
|
-
raw_data2 = self.raw_df[self.raw_df[self.grouping_column] == group2][self.data_column]
|
2242
|
-
|
2243
|
-
if self.paired:
|
2244
|
-
stat, p = stat_test(data1, data2, paired=True)
|
2245
|
-
else:
|
2246
|
-
stat, p = stat_test(data1, data2)
|
2230
|
+
stat, p = kruskal(*grouped_data)
|
2231
|
+
test_name = 'Kruskal-Wallis test'
|
2247
2232
|
|
2248
2233
|
test_results.append({
|
2249
|
-
'Comparison': f'{
|
2234
|
+
'Comparison': f'{unique_groups[0]} vs {unique_groups[1]} ({column})',
|
2250
2235
|
'Test Statistic': stat,
|
2251
2236
|
'p-value': p,
|
2252
2237
|
'Test Name': test_name,
|
2253
|
-
'
|
2254
|
-
'
|
2255
|
-
|
2238
|
+
'Column': column,
|
2239
|
+
'n_object': len(grouped_data[0]) + len(grouped_data[1]),
|
2240
|
+
'n_well': len(self.df[self.df[self.grouping_column] == unique_groups[0]]) +
|
2241
|
+
len(self.df[self.df[self.grouping_column] == unique_groups[1]])})
|
2242
|
+
|
2256
2243
|
return test_results
|
2257
2244
|
|
2258
2245
|
def perform_posthoc_tests(self, is_normal, unique_groups):
|
2259
2246
|
"""Perform post-hoc tests for multiple groups based on all_to_all flag."""
|
2260
2247
|
if is_normal and len(unique_groups) > 2 and self.all_to_all:
|
2261
|
-
# Tukey HSD Post-hoc when comparing all to all
|
2262
2248
|
tukey_result = pairwise_tukeyhsd(self.df[self.data_column], self.df[self.grouping_column], alpha=0.05)
|
2263
2249
|
posthoc_results = []
|
2264
2250
|
for comparison, p_value in zip(tukey_result._results_table.data[1:], tukey_result.pvalues):
|
@@ -2271,12 +2257,10 @@ class spacrGraph:
|
|
2271
2257
|
'p-value': p_value,
|
2272
2258
|
'Test Name': 'Tukey HSD Post-hoc',
|
2273
2259
|
'n_object': len(raw_data1) + len(raw_data2),
|
2274
|
-
'n_well': len(self.df[self.df[self.grouping_column] == comparison[0]]) + len(self.df[self.df[self.grouping_column] == comparison[1]])
|
2275
|
-
})
|
2260
|
+
'n_well': len(self.df[self.df[self.grouping_column] == comparison[0]]) + len(self.df[self.df[self.grouping_column] == comparison[1]])})
|
2276
2261
|
return posthoc_results
|
2277
2262
|
|
2278
2263
|
elif len(unique_groups) > 2 and not self.all_to_all and self.compare_group:
|
2279
|
-
# Dunn's post-hoc test using Pingouin
|
2280
2264
|
dunn_result = pg.pairwise_tests(data=self.df, dv=self.data_column, between=self.grouping_column, padjust='bonf', test='dunn')
|
2281
2265
|
posthoc_results = []
|
2282
2266
|
for idx, row in dunn_result.iterrows():
|
@@ -2287,38 +2271,146 @@ class spacrGraph:
|
|
2287
2271
|
'p-value': row['p-val'],
|
2288
2272
|
'Test Name': 'Dunn’s Post-hoc',
|
2289
2273
|
'n_object': None,
|
2290
|
-
'n_well': None
|
2291
|
-
|
2274
|
+
'n_well': None})
|
2275
|
+
|
2292
2276
|
return posthoc_results
|
2293
2277
|
return []
|
2294
|
-
|
2278
|
+
|
2295
2279
|
def create_plot(self, ax=None):
|
2296
2280
|
"""Create and display the plot based on the chosen graph type."""
|
2281
|
+
|
2282
|
+
def _generate_tabels(unique_groups):
|
2283
|
+
"""Generate row labels and a symbol table for multi-level grouping."""
|
2284
|
+
# Create row labels: Include the grouping column and data columns
|
2285
|
+
row_labels = [self.grouping_column] + self.data_column
|
2286
|
+
|
2287
|
+
# Initialize table data
|
2288
|
+
table_data = []
|
2289
|
+
|
2290
|
+
# Create the grouping row: Alternate each group for every data column
|
2291
|
+
grouping_row = []
|
2292
|
+
for _ in self.data_column:
|
2293
|
+
for group in unique_groups:
|
2294
|
+
grouping_row.append(group)
|
2295
|
+
table_data.append(grouping_row) # Add the grouping row to the table
|
2296
|
+
|
2297
|
+
# Create symbol rows for each data column
|
2298
|
+
for column in self.data_column:
|
2299
|
+
column_row = [] # Initialize a row for this column
|
2300
|
+
for data_col in self.data_column: # Iterate over data columns to align with the structure
|
2301
|
+
for group in unique_groups:
|
2302
|
+
# Assign '+' if the column matches, otherwise assign '-'
|
2303
|
+
if column == data_col:
|
2304
|
+
column_row.append('+')
|
2305
|
+
else:
|
2306
|
+
column_row.append('-')
|
2307
|
+
table_data.append(column_row) # Add this row to the table
|
2308
|
+
|
2309
|
+
# Transpose the table to align with the plot layout
|
2310
|
+
transposed_table = list(map(list, zip(*table_data)))
|
2311
|
+
return row_labels, transposed_table
|
2312
|
+
|
2313
|
+
def _place_symbols(row_labels, transposed_table, x_positions, ax):
|
2314
|
+
|
2315
|
+
# Get the bottom of the y-axis (y=0) in data coordinates and convert to display coordinates
|
2316
|
+
y_axis_min = ax.get_ylim()[0] # Minimum y-axis value (usually 0)
|
2317
|
+
symbol_start_y = ax.transData.transform((0, y_axis_min))[1] - 30 # Slightly below the x-axis line
|
2318
|
+
|
2319
|
+
# Convert to figure coordinates
|
2320
|
+
symbol_start_y_fig = ax.transAxes.inverted().transform((0, symbol_start_y))[1]
|
2321
|
+
|
2322
|
+
# Calculate y-spacing for the table rows (adjust as needed)
|
2323
|
+
y_spacing = 0.02 # Control vertical spacing between elements
|
2324
|
+
|
2325
|
+
# X-coordinate for the row labels at the y-axis and x-axis intersection
|
2326
|
+
label_x_pos = ax.get_xlim()[0] - 0.5 # Slightly offset from the y-axis
|
2327
|
+
|
2328
|
+
# Place the row titles at the y-axis intersection
|
2329
|
+
for row_idx, title in enumerate(row_labels):
|
2330
|
+
y_pos = symbol_start_y_fig - (row_idx * y_spacing) # Align with row index
|
2331
|
+
ax.text(label_x_pos, y_pos, title, ha='right', va='center', fontsize=12, fontweight='regular')
|
2332
|
+
|
2333
|
+
# Place the symbols under each bar
|
2334
|
+
for idx, (x_pos, column_data) in enumerate(zip(x_positions, transposed_table)):
|
2335
|
+
for row_idx, text in enumerate(column_data):
|
2336
|
+
y_pos = symbol_start_y_fig - (row_idx * y_spacing)
|
2337
|
+
ax.text(x_pos, y_pos, text, ha='center', va='center', fontsize=12)
|
2338
|
+
|
2339
|
+
def _get_positions(self, ax):
|
2340
|
+
if self.graph_type == 'bar':
|
2341
|
+
x_positions = [np.mean(bar.get_paths()[0].vertices[:, 0]) for bar in ax.collections if hasattr(bar, 'get_paths')]
|
2342
|
+
|
2343
|
+
elif self.graph_type == 'violin':
|
2344
|
+
x_positions = [np.mean(violin.get_paths()[0].vertices[:, 0]) for violin in ax.collections if hasattr(violin, 'get_paths')]
|
2345
|
+
|
2346
|
+
elif self.graph_type == 'box':
|
2347
|
+
x_positions = list(set(line.get_xdata().mean() for line in ax.lines if line.get_linestyle() == '-'))
|
2348
|
+
|
2349
|
+
elif self.graph_type == 'jitter':
|
2350
|
+
x_positions = [np.mean(collection.get_offsets()[:, 0]) for collection in ax.collections if collection.get_offsets().size > 0]
|
2351
|
+
return x_positions
|
2352
|
+
|
2353
|
+
def _draw_comparison_lines(ax, x_positions):
|
2354
|
+
"""Draw comparison lines and annotate significance based on results_df."""
|
2355
|
+
if self.results_df.empty:
|
2356
|
+
print("No comparisons available to annotate.")
|
2357
|
+
return
|
2358
|
+
|
2359
|
+
y_max = max([bar.get_height() for bar in ax.patches])
|
2360
|
+
ax.set_ylim(0, y_max * 1.3)
|
2361
|
+
|
2362
|
+
for idx, row in self.results_df.iterrows():
|
2363
|
+
group1, group2 = row['Comparison'].split(' vs ')
|
2364
|
+
p_value = row['p-value']
|
2365
|
+
|
2366
|
+
# Determine significance marker
|
2367
|
+
if p_value <= 0.001:
|
2368
|
+
significance = '***'
|
2369
|
+
elif p_value <= 0.01:
|
2370
|
+
significance = '**'
|
2371
|
+
elif p_value <= 0.05:
|
2372
|
+
significance = '*'
|
2373
|
+
else:
|
2374
|
+
significance = 'ns'
|
2375
|
+
|
2376
|
+
# Find the x positions of the compared groups
|
2377
|
+
x1 = x_positions[unique_groups.tolist().index(group1)]
|
2378
|
+
x2 = x_positions[unique_groups.tolist().index(group2)]
|
2379
|
+
|
2380
|
+
# Stagger lines to avoid overlap
|
2381
|
+
line_y = y_max + (0.1 * y_max) * (idx + 1)
|
2382
|
+
|
2383
|
+
# Draw the comparison line
|
2384
|
+
ax.plot([x1, x1, x2, x2], [line_y - 0.02, line_y, line_y, line_y - 0.02], lw=1.5, c='black')
|
2385
|
+
|
2386
|
+
# Add the significance marker
|
2387
|
+
ax.text((x1 + x2) / 2, line_y, significance, ha='center', va='bottom', fontsize=12)
|
2388
|
+
|
2297
2389
|
# Optional: Remove outliers for plotting
|
2298
2390
|
if self.remove_outliers:
|
2299
2391
|
self.df = self.remove_outliers_from_plot()
|
2300
2392
|
|
2301
|
-
|
2393
|
+
self.df_melted = pd.melt(self.df, id_vars=[self.grouping_column], value_vars=self.data_column,var_name='Data Column', value_name='Value')
|
2302
2394
|
unique_groups = self.df[self.grouping_column].unique()
|
2395
|
+
is_normal, normality_results = self.perform_normality_tests()
|
2396
|
+
levene_stat, levene_p = self.perform_levene_test(unique_groups)
|
2397
|
+
test_results = self.perform_statistical_tests(unique_groups, is_normal)
|
2398
|
+
posthoc_results = self.perform_posthoc_tests(is_normal, unique_groups)
|
2399
|
+
self.results_df = pd.DataFrame(normality_results + test_results + posthoc_results)
|
2303
2400
|
|
2304
|
-
|
2305
|
-
self.df_melted = pd.melt(self.df, id_vars=[self.grouping_column], value_vars=self.data_column,
|
2306
|
-
var_name='Data Column', value_name='Value')
|
2307
|
-
|
2308
|
-
# Dynamically set figure dimensions based on the number of unique groups and data columns
|
2309
|
-
num_groups = len(self.df_melted[self.grouping_column].unique())
|
2401
|
+
num_groups = len(self.data_column)*len(self.grouping_column)
|
2310
2402
|
num_data_columns = len(self.data_column)
|
2311
|
-
bar_width =
|
2312
|
-
spacing_between_groups = 0.
|
2313
|
-
|
2314
|
-
fig_width = (num_groups * num_data_columns * bar_width) + (spacing_between_groups * num_groups)
|
2315
|
-
fig_height = 10
|
2403
|
+
self.bar_width = 0.4
|
2404
|
+
spacing_between_groups = self.bar_width/0.5
|
2316
2405
|
|
2406
|
+
self.fig_width = (num_groups * self.bar_width) + (spacing_between_groups * num_groups)
|
2407
|
+
self.fig_height = self.fig_width/2
|
2408
|
+
|
2317
2409
|
if ax is None:
|
2318
|
-
self.fig, ax = plt.subplots(figsize=(
|
2410
|
+
self.fig, ax = plt.subplots(figsize=(self.fig_height, self.fig_width))
|
2319
2411
|
else:
|
2320
2412
|
self.fig = ax.figure
|
2321
|
-
|
2413
|
+
|
2322
2414
|
if len(self.data_column) == 1:
|
2323
2415
|
self.hue=self.grouping_column
|
2324
2416
|
self.jitter_bar_dodge = False
|
@@ -2328,9 +2420,8 @@ class spacrGraph:
|
|
2328
2420
|
|
2329
2421
|
# Handle the different plot types based on `graph_type`
|
2330
2422
|
if self.graph_type == 'bar':
|
2331
|
-
self._create_bar_plot(
|
2423
|
+
self._create_bar_plot(ax)
|
2332
2424
|
elif self.graph_type == 'jitter':
|
2333
|
-
#transparent_palette = [(r, g, b, 0.6) for (r, g, b) in sns.color_palette(self.sns_palette, n_colors=len(df_melted[hue].unique()))]
|
2334
2425
|
self._create_jitter_plot(ax)
|
2335
2426
|
elif self.graph_type == 'box':
|
2336
2427
|
self._create_box_plot(ax)
|
@@ -2339,159 +2430,154 @@ class spacrGraph:
|
|
2339
2430
|
else:
|
2340
2431
|
raise ValueError(f"Unknown graph type: {self.graph_type}")
|
2341
2432
|
|
2342
|
-
if ax is None:
|
2343
|
-
self.fig, ax = plt.subplots(figsize=(fig_width, fig_height))
|
2344
|
-
else:
|
2345
|
-
self.fig = ax.figure
|
2346
|
-
|
2347
|
-
|
2348
2433
|
# Set y-axis start
|
2349
|
-
if self.
|
2350
|
-
|
2434
|
+
if isinstance(self.y_lim, list):
|
2435
|
+
if len(self.y_lim) == 2:
|
2436
|
+
ax.set_ylim(self.y_lim[0], self.y_lim[1])
|
2437
|
+
elif len(self.y_lim) == 1:
|
2438
|
+
ax.set_ylim(self.y_lim[0], None)
|
2351
2439
|
|
2352
|
-
# Remove top and right spines
|
2353
2440
|
sns.despine(ax=ax, top=True, right=True)
|
2354
|
-
|
2355
|
-
# Adjust the position of the plot to move it closer to the table
|
2356
|
-
ax.set_position([0.1, 0.25, 0.8, 0.45]) # Adjust the height of the plot, leaving more space for the table
|
2357
|
-
|
2358
|
-
# Move the legend outside the plot
|
2359
|
-
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5), title='Data Column')
|
2360
|
-
|
2441
|
+
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5), title='Data Column') # Move the legend outside the plot
|
2361
2442
|
ax.set_xlabel('')
|
2443
|
+
x_positions = _get_positions(self, ax)
|
2362
2444
|
|
2363
2445
|
if len(self.data_column) == 1:
|
2364
2446
|
ax.legend().remove()
|
2365
2447
|
ax.set_xticklabels(ax.get_xticklabels(), rotation=45, ha='right')
|
2366
2448
|
|
2367
|
-
|
2368
|
-
if len(self.data_column) > 1:
|
2449
|
+
elif len(self.data_column) > 1:
|
2369
2450
|
ax.set_xticks([])
|
2370
2451
|
ax.tick_params(bottom=False)
|
2371
2452
|
ax.set_xticklabels([])
|
2372
|
-
|
2373
|
-
legend_ax = self.fig.add_axes([0.1, 0.02, 0.8, 0.2]) # Position the table closer to the graph
|
2453
|
+
legend_ax = self.fig.add_axes([0.1, -0.2, 0.62, 0.2]) # Position the table closer to the graph
|
2374
2454
|
legend_ax.set_axis_off()
|
2375
2455
|
|
2376
|
-
|
2377
|
-
row_labels
|
2378
|
-
|
2379
|
-
|
2380
|
-
|
2381
|
-
for column in self.data_column:
|
2382
|
-
column_bars = []
|
2383
|
-
for group in unique_groups:
|
2384
|
-
# For each group and data column, assign a '+' or '-'
|
2385
|
-
for bar_position in range(num_data_columns): # Loop through each data_column position
|
2386
|
-
if bar_position == self.data_column.index(column):
|
2387
|
-
column_bars.append('+')
|
2388
|
-
else:
|
2389
|
-
column_bars.append('-')
|
2390
|
-
table_data.append(column_bars)
|
2391
|
-
|
2392
|
-
# Display the table directly below the graph with white background and thicker row height
|
2393
|
-
legend_table = legend_ax.table(cellText=table_data,
|
2394
|
-
rowLabels=row_labels,
|
2395
|
-
loc='center',
|
2396
|
-
cellLoc='center',
|
2397
|
-
rowColours=['white'] * len(row_labels), # Set background to white
|
2398
|
-
cellColours=[['white'] * len(column_bars) for _ in table_data], # White cell background
|
2399
|
-
edges='closed', # Show edges around cells
|
2400
|
-
bbox=[0, 0, 1, 1])
|
2401
|
-
|
2402
|
-
# Adjust the row height for readability
|
2403
|
-
for key, cell in legend_table.get_celld().items():
|
2404
|
-
cell.set_height(0.5) # Increase row height
|
2405
|
-
cell.set_edgecolor('white') # Set grid color to white
|
2406
|
-
cell.set_linewidth(2) # Set line thickness
|
2407
|
-
|
2456
|
+
row_labels, table_data = _generate_tabels(unique_groups)
|
2457
|
+
_place_symbols(row_labels, table_data, x_positions, ax)
|
2458
|
+
|
2459
|
+
#_draw_comparison_lines(ax, x_positions)
|
2460
|
+
|
2408
2461
|
if self.save:
|
2409
2462
|
self._save_results()
|
2410
2463
|
|
2411
|
-
|
2412
|
-
plt.show() # Ensure the plot is shown, but plt.show() doesn't clear the figure context
|
2413
|
-
|
2414
|
-
def get_figure(self):
|
2415
|
-
"""Return the generated figure."""
|
2416
|
-
return self.fig
|
2464
|
+
ax.margins(x=0.12)
|
2417
2465
|
|
2418
|
-
def _create_bar_plot(self,
|
2466
|
+
def _create_bar_plot(self, ax):
|
2419
2467
|
"""Helper method to create a bar plot with consistent bar thickness and centered error bars."""
|
2420
|
-
|
2421
|
-
|
2422
|
-
|
2423
|
-
|
2424
|
-
|
2425
|
-
|
2426
|
-
summary_df = df_melted.groupby([self.grouping_column, 'Data Column']).agg({self.summary_func: ['mean', 'std', 'sem']}).reset_index()
|
2427
|
-
summary_df.columns = ['_'.join(col).strip() if isinstance(col, tuple) else col for col in summary_df.columns]
|
2428
|
-
|
2429
|
-
# Determine which type of error bars to show (std or sem)
|
2430
|
-
if self.error_bar_type == 'std':
|
2431
|
-
error_bars = summary_df[f'{self.summary_func}_std']
|
2432
|
-
elif self.error_bar_type == 'sem':
|
2433
|
-
error_bars = summary_df[f'{self.summary_func}_sem']
|
2468
|
+
# Flatten DataFrame: Combine grouping column and data column into one group if needed
|
2469
|
+
if len(self.data_column) > 1:
|
2470
|
+
self.df_melted['Combined Group'] = (self.df_melted[self.grouping_column].astype(str) + " - " + self.df_melted['Data Column'].astype(str))
|
2471
|
+
x_axis_column = 'Combined Group'
|
2472
|
+
hue = None
|
2473
|
+
ax.set_ylabel('Value')
|
2434
2474
|
else:
|
2435
|
-
|
2436
|
-
|
2437
|
-
|
2438
|
-
|
2439
|
-
|
2440
|
-
|
2441
|
-
barplot =
|
2442
|
-
|
2443
|
-
|
2444
|
-
|
2445
|
-
|
2446
|
-
|
2447
|
-
|
2448
|
-
|
2449
|
-
|
2450
|
-
|
2451
|
-
|
2452
|
-
#
|
2453
|
-
|
2454
|
-
|
2455
|
-
|
2456
|
-
|
2457
|
-
|
2458
|
-
# Ensure error bars are aligned with correct bars
|
2459
|
-
for bar, (_, row) in zip(bars, summary_df_sorted.iterrows()):
|
2460
|
-
x_bar = bar.get_x() + bar.get_width() / 2 # Center of the bar
|
2461
|
-
err = row[f'{self.summary_func}_{self.error_bar_type}'] # Get the correct error value for this bar
|
2462
|
-
|
2475
|
+
x_axis_column = self.grouping_column
|
2476
|
+
ax.set_ylabel(self.data_column[0])
|
2477
|
+
hue = None
|
2478
|
+
|
2479
|
+
summary_df = self.df_melted.groupby([x_axis_column]).agg(mean=('Value', 'mean'),std=('Value', 'std'),sem=('Value', 'sem')).reset_index()
|
2480
|
+
error_bars = summary_df[self.error_bar_type] if self.error_bar_type in ['std', 'sem'] else None
|
2481
|
+
sns.barplot(data=self.df_melted, x=x_axis_column, y='Value', hue=self.hue, palette=self.sns_palette, ax=ax, dodge=self.jitter_bar_dodge, ci=None)
|
2482
|
+
|
2483
|
+
# Adjust the bar width manually
|
2484
|
+
if len(self.data_column) > 1:
|
2485
|
+
bars = [bar for bar in ax.patches if isinstance(bar, plt.Rectangle)]
|
2486
|
+
target_width = self.bar_width * 2
|
2487
|
+
for bar in bars:
|
2488
|
+
bar.set_width(target_width) # Set new width
|
2489
|
+
# Center the bar on its x-coordinate
|
2490
|
+
bar.set_x(bar.get_x() - target_width / 2)
|
2491
|
+
|
2492
|
+
# Adjust error bars alignment with bars
|
2493
|
+
bars = [bar for bar in ax.patches if isinstance(bar, plt.Rectangle)]
|
2494
|
+
for bar, (_, row) in zip(bars, summary_df.iterrows()):
|
2495
|
+
x_bar = bar.get_x() + bar.get_width() / 2
|
2496
|
+
err = row[self.error_bar_type]
|
2463
2497
|
ax.errorbar(x=x_bar, y=bar.get_height(), yerr=err, fmt='none', c='black', capsize=5, lw=2)
|
2464
|
-
|
2465
|
-
# Set
|
2466
|
-
if hue:
|
2467
|
-
ax.legend(title="Data Column", loc='center left', bbox_to_anchor=(1, 0.5))
|
2468
|
-
else:
|
2469
|
-
if len(self.data_column) > 1:
|
2470
|
-
ax.legend_.remove()
|
2471
|
-
|
2472
|
-
# Set labels
|
2498
|
+
|
2499
|
+
# Set legend and labels
|
2473
2500
|
ax.set_xlabel(self.grouping_column)
|
2474
|
-
ax.set_ylabel(self.summary_func)
|
2475
|
-
|
2476
2501
|
|
2477
2502
|
def _create_jitter_plot(self, ax):
|
2478
|
-
"""Helper method to create a jitter plot (strip plot)
|
2479
|
-
|
2480
|
-
|
2503
|
+
"""Helper method to create a jitter plot (strip plot) with consistent spacing."""
|
2504
|
+
# Combine grouping column and data column if needed
|
2505
|
+
if len(self.data_column) > 1:
|
2506
|
+
self.df_melted['Combined Group'] = (self.df_melted[self.grouping_column].astype(str) + " - " + self.df_melted['Data Column'].astype(str))
|
2507
|
+
x_axis_column = 'Combined Group'
|
2508
|
+
hue = None # Disable hue to avoid two-level grouping
|
2509
|
+
ax.set_ylabel('Value')
|
2510
|
+
else:
|
2511
|
+
x_axis_column = self.grouping_column
|
2512
|
+
ax.set_ylabel(self.data_column[0])
|
2513
|
+
hue = None
|
2514
|
+
|
2515
|
+
# Create the jitter plot
|
2516
|
+
sns.stripplot(data=self.df_melted,x=x_axis_column,y='Value',hue=self.hue, palette=self.sns_palette, dodge=self.jitter_bar_dodge, jitter=self.bar_width, ax=ax,alpha=0.6)
|
2517
|
+
|
2518
|
+
# Adjust legend and labels
|
2519
|
+
ax.set_xlabel(self.grouping_column)
|
2520
|
+
|
2521
|
+
# Manage the legend
|
2522
|
+
handles, labels = ax.get_legend_handles_labels()
|
2523
|
+
unique_labels = dict(zip(labels, handles))
|
2524
|
+
ax.legend(unique_labels.values(), unique_labels.keys(), loc='best')
|
2525
|
+
|
2481
2526
|
def _create_box_plot(self, ax):
|
2482
|
-
"""Helper method to create a box plot
|
2483
|
-
|
2527
|
+
"""Helper method to create a box plot with consistent spacing."""
|
2528
|
+
# Combine grouping column and data column if needed
|
2529
|
+
if len(self.data_column) > 1:
|
2530
|
+
self.df_melted['Combined Group'] = (self.df_melted[self.grouping_column].astype(str) + " - " + self.df_melted['Data Column'].astype(str))
|
2531
|
+
x_axis_column = 'Combined Group'
|
2532
|
+
hue = None
|
2533
|
+
ax.set_ylabel('Value')
|
2534
|
+
else:
|
2535
|
+
x_axis_column = self.grouping_column
|
2536
|
+
ax.set_ylabel(self.data_column[0])
|
2537
|
+
hue = None
|
2538
|
+
|
2539
|
+
# Create the box plot
|
2540
|
+
sns.boxplot(data=self.df_melted,x=x_axis_column,y='Value',hue=self.hue,palette=self.sns_palette,ax=ax)
|
2541
|
+
|
2542
|
+
# Adjust legend and labels
|
2543
|
+
ax.set_xlabel(self.grouping_column)
|
2484
2544
|
|
2545
|
+
# Manage the legend
|
2546
|
+
handles, labels = ax.get_legend_handles_labels()
|
2547
|
+
unique_labels = dict(zip(labels, handles))
|
2548
|
+
ax.legend(unique_labels.values(), unique_labels.keys(), loc='best')
|
2549
|
+
|
2485
2550
|
def _create_violin_plot(self, ax):
|
2486
|
-
"""Helper method to create a violin plot
|
2487
|
-
|
2551
|
+
"""Helper method to create a violin plot with consistent spacing."""
|
2552
|
+
# Combine grouping column and data column if needed
|
2553
|
+
if len(self.data_column) > 1:
|
2554
|
+
self.df_melted['Combined Group'] = (self.df_melted[self.grouping_column].astype(str) + " - " + self.df_melted['Data Column'].astype(str))
|
2555
|
+
x_axis_column = 'Combined Group'
|
2556
|
+
hue = None
|
2557
|
+
ax.set_ylabel('Value')
|
2558
|
+
else:
|
2559
|
+
x_axis_column = self.grouping_column
|
2560
|
+
ax.set_ylabel(self.data_column[0])
|
2561
|
+
hue = None
|
2562
|
+
|
2563
|
+
# Create the violin plot
|
2564
|
+
sns.violinplot(data=self.df_melted,x=x_axis_column,y='Value', hue=self.hue,palette=self.sns_palette,ax=ax)
|
2565
|
+
|
2566
|
+
# Adjust legend and labels
|
2567
|
+
ax.set_xlabel(self.grouping_column)
|
2568
|
+
ax.set_ylabel('Value')
|
2569
|
+
|
2570
|
+
# Manage the legend
|
2571
|
+
handles, labels = ax.get_legend_handles_labels()
|
2572
|
+
unique_labels = dict(zip(labels, handles))
|
2573
|
+
ax.legend(unique_labels.values(), unique_labels.keys(), loc='best')
|
2488
2574
|
|
2489
2575
|
def _save_results(self):
|
2490
2576
|
"""Helper method to save the plot and results."""
|
2491
2577
|
os.makedirs(self.output_dir, exist_ok=True)
|
2492
|
-
plot_path = os.path.join(self.output_dir,
|
2493
|
-
self.fig.savefig(plot_path)
|
2494
|
-
results_path = os.path.join(self.output_dir,
|
2578
|
+
plot_path = os.path.join(self.output_dir, f"{self.results_name}.pdf")
|
2579
|
+
self.fig.savefig(plot_path, bbox_inches='tight', dpi=600, transparent=True, format='pdf')
|
2580
|
+
results_path = os.path.join(self.output_dir, f"{self.results_name}.csv")
|
2495
2581
|
self.results_df.to_csv(results_path, index=False)
|
2496
2582
|
print(f"Plot saved to {plot_path}")
|
2497
2583
|
print(f"Test results saved to {results_path}")
|
@@ -2500,9 +2586,13 @@ class spacrGraph:
|
|
2500
2586
|
"""Return the results dataframe."""
|
2501
2587
|
return self.results_df
|
2502
2588
|
|
2589
|
+
def get_figure(self):
|
2590
|
+
"""Return the generated figure."""
|
2591
|
+
return self.fig
|
2592
|
+
|
2503
2593
|
def plot_data_from_db(settings):
|
2504
2594
|
from .io import _read_db
|
2505
|
-
from
|
2595
|
+
from .utils import annotate_conditions
|
2506
2596
|
"""
|
2507
2597
|
Extracts the specified table from the SQLite database and plots a specified column.
|
2508
2598
|
|
@@ -2530,18 +2620,17 @@ def plot_data_from_db(settings):
|
|
2530
2620
|
df['prc'] = df['plate'].astype(str) + '_' + df['row'].astype(str) + '_' + df['col'].astype(str)
|
2531
2621
|
df = df.dropna(subset=settings['column_name'])
|
2532
2622
|
df['class'] = df['png_path'].apply(lambda x: 'class_1' if 'class_1' in x else ('class_0' if 'class_0' in x else None))
|
2533
|
-
|
2534
|
-
# Initialize the spacrGraph class with your DataFrame and desired parameters
|
2623
|
+
|
2535
2624
|
spacr_graph = spacrGraph(
|
2536
|
-
df=df,
|
2625
|
+
df=df, # Your DataFrame
|
2537
2626
|
grouping_column=settings['grouping_column'], # Column for grouping the data (x-axis)
|
2538
2627
|
data_column=settings['column_name'], # Column for the data (y-axis)
|
2539
2628
|
graph_type=settings['graph_type'], # Type of plot ('bar', 'box', 'violin', 'jitter')
|
2540
2629
|
summary_func='mean', # Function to summarize data (e.g., 'mean', 'median')
|
2541
2630
|
colors=None, # Custom colors for the plot (optional)
|
2542
|
-
output_dir=settings['dst'],
|
2543
|
-
save=settings['save'],
|
2544
|
-
|
2631
|
+
output_dir=settings['dst'], # Directory to save the plot and results
|
2632
|
+
save=settings['save'], # Whether to save the plot and results
|
2633
|
+
y_lim=settings['y_lim'], # Starting point for y-axis (optional)
|
2545
2634
|
error_bar_type='std', # Type of error bar ('std' or 'sem')
|
2546
2635
|
representation='well',
|
2547
2636
|
theme=settings['theme'], # Seaborn color palette theme (e.g., 'pastel', 'muted')
|
@@ -2552,8 +2641,9 @@ def plot_data_from_db(settings):
|
|
2552
2641
|
|
2553
2642
|
# Get the figure object if needed
|
2554
2643
|
fig = spacr_graph.get_figure()
|
2644
|
+
plt.show()
|
2555
2645
|
|
2556
2646
|
# Optional: Get the results DataFrame containing statistical test results
|
2557
2647
|
results_df = spacr_graph.get_results()
|
2558
|
-
|
2559
|
-
return fig
|
2648
|
+
|
2649
|
+
return fig, results_df
|
spacr/settings.py
CHANGED
@@ -278,6 +278,7 @@ def get_measure_crop_settings(settings={}):
|
|
278
278
|
|
279
279
|
def set_default_analyze_screen(settings):
|
280
280
|
settings.setdefault('src', 'path')
|
281
|
+
settings.setdefault('annotation_column', None)
|
281
282
|
settings.setdefault('model_type_ml','xgboost')
|
282
283
|
settings.setdefault('heatmap_feature','predictions')
|
283
284
|
settings.setdefault('grouping','mean')
|
@@ -13,16 +13,16 @@ spacr/deep_spacr.py,sha256=HdOcNU8cHcE_19nP7_5uTz-ih3E169ffr2Hm--NvMvA,43255
|
|
13
13
|
spacr/gui.py,sha256=ARyn9Q_g8HoP-cXh1nzMLVFCKqthY4v2u9yORyaQqQE,8230
|
14
14
|
spacr/gui_core.py,sha256=LV_HX5zreu3Bye6sQFDbOuk8Dfj4StMoohy6hsrDEXA,41363
|
15
15
|
spacr/gui_elements.py,sha256=puDqf7PJJ_UMA01fjqODk-zsfSmvzVXpvaZ1BYV988w,136554
|
16
|
-
spacr/gui_utils.py,sha256=
|
16
|
+
spacr/gui_utils.py,sha256=7e9DsZIuV7-jh97kEf7v1In_cFzlFueV4SGcGYGpTxw,45454
|
17
17
|
spacr/io.py,sha256=AARmqn1fMmTgVDwWy8bEYK6SjH-6DZIulgCSPdBTyf0,143370
|
18
18
|
spacr/logger.py,sha256=lJhTqt-_wfAunCPl93xE65Wr9Y1oIHJWaZMjunHUeIw,1538
|
19
19
|
spacr/measure.py,sha256=BThn_sALgKrwGKnLOGpT4FyoJeRVoTZoP9SXbCtCMRw,54857
|
20
20
|
spacr/mediar.py,sha256=FwLvbLQW5LQzPgvJZG8Lw7GniA2vbZx6Jv6vIKu7I5c,14743
|
21
|
-
spacr/ml.py,sha256=
|
21
|
+
spacr/ml.py,sha256=8i2D9YEC9rSYdbgkuuMLl6adwivWn2Z5BEUjPRsW4t4,48983
|
22
22
|
spacr/openai.py,sha256=5vBZ3Jl2llYcW3oaTEXgdyCB2aJujMUIO5K038z7w_A,1246
|
23
|
-
spacr/plot.py,sha256=
|
23
|
+
spacr/plot.py,sha256=PtCSoBmLFlGC7ebmsk-vMlyd7q2ahXgRVaTtAq3w_po,116513
|
24
24
|
spacr/sequencing.py,sha256=t18mgpK6rhWuB1LtFOsPxqgpFXxuUmrD06ecsaVQ0Gw,19655
|
25
|
-
spacr/settings.py,sha256=
|
25
|
+
spacr/settings.py,sha256=7rAvzPkfyfbpY6JQqcTe6PcCghEvLebUgsfKMVBtNyU,75879
|
26
26
|
spacr/sim.py,sha256=1xKhXimNU3ukzIw-3l9cF3Znc_brW8h20yv8fSTzvss,71173
|
27
27
|
spacr/submodules.py,sha256=AB7s6-cULsaqz-haAaCtXfGEIi8uPZGT4xoCslUJC3Y,18391
|
28
28
|
spacr/timelapse.py,sha256=FSYpUtAVy6xc3lwprRYgyDTT9ysUhfRQ4zrP9_h2mvg,39465
|
@@ -150,9 +150,9 @@ spacr/resources/icons/umap.png,sha256=dOLF3DeLYy9k0nkUybiZMe1wzHQwLJFRmgccppw-8b
|
|
150
150
|
spacr/resources/images/plate1_E01_T0001F001L01A01Z01C02.tif,sha256=Tl0ZUfZ_AYAbu0up_nO0tPRtF1BxXhWQ3T3pURBCCRo,7958528
|
151
151
|
spacr/resources/images/plate1_E01_T0001F001L01A02Z01C01.tif,sha256=m8N-V71rA1TT4dFlENNg8s0Q0YEXXs8slIn7yObmZJQ,7958528
|
152
152
|
spacr/resources/images/plate1_E01_T0001F001L01A03Z01C03.tif,sha256=Pbhk7xn-KUP6RSIhJsxQcrHFImBm3GEpLkzx7WOc-5M,7958528
|
153
|
-
spacr-0.3.
|
154
|
-
spacr-0.3.
|
155
|
-
spacr-0.3.
|
156
|
-
spacr-0.3.
|
157
|
-
spacr-0.3.
|
158
|
-
spacr-0.3.
|
153
|
+
spacr-0.3.34.dist-info/LICENSE,sha256=SR-2MeGc6SCM1UORJYyarSWY_A-JaOMFDj7ReSs9tRM,1083
|
154
|
+
spacr-0.3.34.dist-info/METADATA,sha256=6C9_x3YSb9ycM9cXRufcPTJG809Cf-hkNYDOdEVXMT0,5949
|
155
|
+
spacr-0.3.34.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
|
156
|
+
spacr-0.3.34.dist-info/entry_points.txt,sha256=BMC0ql9aNNpv8lUZ8sgDLQMsqaVnX5L535gEhKUP5ho,296
|
157
|
+
spacr-0.3.34.dist-info/top_level.txt,sha256=GJPU8FgwRXGzKeut6JopsSRY2R8T3i9lDgya42tLInY,6
|
158
|
+
spacr-0.3.34.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|