spacr 0.3.37__py3-none-any.whl → 0.3.41__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/toxo.py CHANGED
@@ -4,8 +4,9 @@ import numpy as np
4
4
  from adjustText import adjust_text
5
5
  import pandas as pd
6
6
  from scipy.stats import fisher_exact
7
+ from IPython.display import display
7
8
 
8
- def custom_volcano_plot(data_path, metadata_path, metadata_column='tagm_location', string_list=[], point_size=50, figsize=20):
9
+ def custom_volcano_plot_v1(data_path, metadata_path, metadata_column='tagm_location', point_size=50, figsize=20, threshold=0):
9
10
  """
10
11
  Create a volcano plot with the ability to control the shape of points based on a categorical column,
11
12
  color points based on a string list, annotate specific points based on p-value and coefficient thresholds,
@@ -19,7 +20,8 @@ def custom_volcano_plot(data_path, metadata_path, metadata_column='tagm_location
19
20
  - point_size: Fixed value to control the size of points.
20
21
  - figsize: Width of the plot (height is half the width).
21
22
  """
22
-
23
+
24
+
23
25
  filename = 'volcano_plot.pdf'
24
26
 
25
27
  # Load the data
@@ -42,46 +44,65 @@ def custom_volcano_plot(data_path, metadata_path, metadata_column='tagm_location
42
44
  metadata['gene_nr'] = metadata['gene_nr'].astype(str)
43
45
  data['gene_nr'] = data['gene_nr'].astype(str)
44
46
 
47
+
45
48
  # Merge data and metadata on 'gene_nr'
46
49
  merged_data = pd.merge(data, metadata[['gene_nr', 'tagm_location']], on='gene_nr', how='left')
47
-
48
- # Controls handling
49
- controls = ['000000', '000001', '000002', '000003', '000004', '000005', '000006', '000007', '000008', '000009', '000010', '000011']
50
- merged_data.loc[merged_data['gene_nr'].isin(controls), metadata_column] = 'control'
50
+
51
51
  merged_data.loc[merged_data['gene_nr'].str.startswith('4'), metadata_column] = 'GT1_gene'
52
52
  merged_data.loc[merged_data['gene_nr'] == 'Intercept', metadata_column] = 'Intercept'
53
53
 
54
- # Create a 'highlight_color' column based on the string_list
55
- merged_data['highlight_color'] = merged_data['gene_nr'].apply(lambda x: 'red' if any(s in x for s in string_list) else 'blue')
56
-
57
54
  # Create the volcano plot
58
55
  figsize_2 = figsize / 2
59
56
  plt.figure(figsize=(figsize_2, figsize))
57
+
58
+ palette = {
59
+ 'pc': 'red',
60
+ 'nc': 'green',
61
+ 'control': 'black',
62
+ 'other': 'gray'
63
+ }
64
+
65
+ merged_data['condition'] = pd.Categorical(
66
+ merged_data['condition'],
67
+ categories=['pc', 'nc', 'control', 'other'],
68
+ ordered=True
69
+ )
60
70
 
71
+ display(merged_data)
72
+
61
73
  # Create the scatter plot with fixed point size
62
74
  sns.scatterplot(
63
75
  data=merged_data,
64
76
  x='coefficient',
65
77
  y='-log10(p_value)',
66
- hue='highlight_color',
67
- style=metadata_column if metadata_column else None, # Control point shape with metadata_column
78
+ hue='condition', # Controls color
79
+ style=metadata_column if metadata_column else None, # Controls point shape
68
80
  s=point_size, # Fixed size for all points
69
- palette={'red': 'red', 'blue': 'blue'}
81
+ palette=palette, # Color palette
82
+ alpha=1.0 # Transparency
70
83
  )
71
84
 
72
85
  # Set the plot title and labels
73
86
  plt.title('Custom Volcano Plot of Coefficients')
74
87
  plt.xlabel('Coefficient')
75
88
  plt.ylabel('-log10(p-value)')
89
+
90
+ if threshold > 0:
91
+ plt.gca().axvline(x=-abs(threshold), linestyle='--', color='black')
92
+ plt.gca().axvline(x=abs(threshold), linestyle='--', color='black')
76
93
 
77
94
  # Horizontal line at p-value threshold (0.05)
78
- plt.axhline(y=-np.log10(0.05), color='red', linestyle='--')
95
+ plt.axhline(y=-np.log10(0.05), color='black', linestyle='--')
79
96
 
80
- # Annotate points where p_value <= 0.05 and coefficient >= 0.25
81
97
  texts = []
82
98
  for i, row in merged_data.iterrows():
83
- if row['p_value'] <= 0.05 and row['coefficient'] >= 0.25:
84
- texts.append(plt.text(row['coefficient'], -np.log10(row['p_value']), row['gene_nr'], fontsize=9))
99
+ if row['p_value'] <= 0.05 and abs(row['coefficient']) >= abs(threshold):
100
+ texts.append(plt.text(
101
+ row['coefficient'],
102
+ -np.log10(row['p_value']),
103
+ row['variable'],
104
+ fontsize=8
105
+ ))
85
106
 
86
107
  # Adjust text positions to avoid overlap
87
108
  adjust_text(texts, arrowprops=dict(arrowstyle='-', color='black'))
@@ -96,6 +117,171 @@ def custom_volcano_plot(data_path, metadata_path, metadata_column='tagm_location
96
117
  # Show the plot
97
118
  plt.show()
98
119
 
120
+ def custom_volcano_plot(data_path, metadata_path, metadata_column='tagm_location', point_size=50, figsize=20, threshold=0, split_axis_lims = [10, None, None, 10]):
121
+ """
122
+ Create a volcano plot with the ability to control the shape of points based on a categorical column,
123
+ color points based on a condition, annotate specific points based on p-value and coefficient thresholds,
124
+ and control the size of points.
125
+ """
126
+
127
+ filename = 'volcano_plot.pdf'
128
+
129
+ # Load the data
130
+ if isinstance(data_path, pd.DataFrame):
131
+ data = data_path
132
+ else:
133
+ data = pd.read_csv(data_path)
134
+
135
+ data['variable'] = data['feature'].str.extract(r'\[(.*?)\]')
136
+ data['variable'].fillna(data['feature'], inplace=True)
137
+ split_columns = data['variable'].str.split('_', expand=True)
138
+ data['gene_nr'] = split_columns[0]
139
+
140
+ # Load metadata
141
+ if isinstance(metadata_path, pd.DataFrame):
142
+ metadata = metadata_path
143
+ else:
144
+ metadata = pd.read_csv(metadata_path)
145
+
146
+ metadata['gene_nr'] = metadata['gene_nr'].astype(str)
147
+ data['gene_nr'] = data['gene_nr'].astype(str)
148
+
149
+ # Merge data and metadata on 'gene_nr'
150
+ merged_data = pd.merge(data, metadata[['gene_nr', 'tagm_location']], on='gene_nr', how='left')
151
+
152
+ merged_data.loc[merged_data['gene_nr'].str.startswith('4'), metadata_column] = 'GT1_gene'
153
+ merged_data.loc[merged_data['gene_nr'] == 'Intercept', metadata_column] = 'Intercept'
154
+ merged_data.loc[merged_data['condition'] == 'control', metadata_column] = 'control'
155
+
156
+ # Categorize condition for coloring
157
+ merged_data['condition'] = pd.Categorical(
158
+ merged_data['condition'],
159
+ categories=['other','pc', 'nc', 'control'],
160
+ ordered=True)
161
+
162
+ # Create subplots with a broken y-axis
163
+ figsize_2 = figsize / 2
164
+ fig, (ax1, ax2) = plt.subplots(
165
+ 2, 1, figsize=(figsize_2, figsize),
166
+ sharex=True, gridspec_kw={'height_ratios': [1, 3]}
167
+ )
168
+
169
+ # Define color palette
170
+ palette = {
171
+ 'pc': 'red',
172
+ 'nc': 'green',
173
+ 'control': 'white',
174
+ 'other': 'gray'}
175
+
176
+ # Scatter plot on both axes
177
+ sns.scatterplot(
178
+ data=merged_data,
179
+ x='coefficient',
180
+ y='-log10(p_value)',
181
+ hue='condition',
182
+ style=metadata_column if metadata_column else None,
183
+ s=point_size,
184
+ edgecolor='black',
185
+ palette=palette,
186
+ alpha=0.8,
187
+ ax=ax2 # Lower plot
188
+ )
189
+
190
+ sns.scatterplot(
191
+ data=merged_data[merged_data['-log10(p_value)'] > 10],
192
+ x='coefficient',
193
+ y='-log10(p_value)',
194
+ hue='condition',
195
+ style=metadata_column if metadata_column else None,
196
+ s=point_size,
197
+ palette=palette,
198
+ edgecolor='black',
199
+ alpha=0.8,
200
+ ax=ax1 # Upper plot
201
+ )
202
+
203
+ if isinstance(split_axis_lims, list):
204
+ if len(split_axis_lims) == 4:
205
+ ylim_min_ax1 = split_axis_lims[0]
206
+ if split_axis_lims[1] is None:
207
+ ylim_max_ax1 = merged_data['-log10(p_value)'].max() + 5
208
+ else:
209
+ ylim_max_ax1 = split_axis_lims[1]
210
+ ylim_min_ax2 = split_axis_lims[2]
211
+ ylim_max_ax2 = split_axis_lims[3]
212
+ else:
213
+ ylim_min_ax1 = None
214
+ ylim_max_ax1 = merged_data['-log10(p_value)'].max() + 5
215
+ ylim_min_ax2 = 0
216
+ ylim_max_ax2 = None
217
+
218
+ # Set axis limits and hide unnecessary parts
219
+ ax1.set_ylim(ylim_min_ax1, ylim_max_ax1)
220
+ ax2.set_ylim(0, ylim_max_ax2)
221
+ ax1.spines['bottom'].set_visible(False)
222
+ ax2.spines['top'].set_visible(False)
223
+ ax1.tick_params(labelbottom=False)
224
+
225
+ ax1.legend_.remove()
226
+ if ax1.get_legend() is not None:
227
+ ax1.get_legend().remove()
228
+ ax1.tick_params(axis='x', which='both', bottom=False, top=False, labelbottom=False)
229
+
230
+ # Add vertical threshold lines to both plots
231
+ if threshold > 0:
232
+ for ax in (ax1, ax2):
233
+ ax.axvline(x=-abs(threshold), linestyle='--', color='black')
234
+ ax.axvline(x=abs(threshold), linestyle='--', color='black')
235
+
236
+ # Add a horizontal line at p-value threshold (0.05)
237
+ ax2.axhline(y=-np.log10(0.05), color='black', linestyle='--')
238
+
239
+ # Annotate significant points on both axes
240
+ texts_ax1 = []
241
+ texts_ax2 = []
242
+
243
+ for i, row in merged_data.iterrows():
244
+ if row['p_value'] <= 0.05 and abs(row['coefficient']) >= abs(threshold):
245
+ # Select the appropriate axis for the annotation
246
+ #ax = ax1 if row['-log10(p_value)'] > 10 else ax2
247
+
248
+ ax = ax1 if row['-log10(p_value)'] >= ax1.get_ylim()[0] else ax2
249
+
250
+
251
+ # Create the annotation on the selected axis
252
+ text = ax.text(
253
+ row['coefficient'],
254
+ -np.log10(row['p_value']),
255
+ row['variable'],
256
+ fontsize=8,
257
+ ha='center',
258
+ va='bottom',
259
+ )
260
+
261
+ # Store the text annotation in the correct list
262
+ if ax == ax1:
263
+ texts_ax1.append(text)
264
+ else:
265
+ texts_ax2.append(text)
266
+
267
+ # Adjust text positions to avoid overlap for both axes
268
+ adjust_text(texts_ax1, arrowprops=dict(arrowstyle='-', color='black'), ax=ax1)
269
+ adjust_text(texts_ax2, arrowprops=dict(arrowstyle='-', color='black'), ax=ax2)
270
+
271
+ # Move the legend outside the lower plot
272
+ ax2.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0.)
273
+
274
+ # Adjust the spacing between subplots and move the title
275
+ plt.subplots_adjust(hspace=0.05)
276
+ fig.suptitle('Custom Volcano Plot of Coefficients', y=1.02, fontsize=16) # Title above the top plot
277
+
278
+ # Save the plot as PDF
279
+ plt.savefig(filename, format='pdf', bbox_inches='tight')
280
+ print(f'Saved Volcano plot: {filename}')
281
+
282
+ # Show the plot
283
+ plt.show()
284
+
99
285
  def go_term_enrichment_by_column(significant_df, metadata_path, go_term_columns=['Computed GO Processes', 'Curated GO Components', 'Curated GO Functions', 'Curated GO Processes']):
100
286
  """
101
287
  Perform GO term enrichment analysis for each GO term column and generate plots.
spacr/utils.py CHANGED
@@ -326,6 +326,8 @@ def save_settings(settings, name='settings', show=False):
326
326
 
327
327
  if isinstance(settings['src'], list):
328
328
  src = settings['src'][0]
329
+ #if os.path.exists(src):
330
+
329
331
  name = f"{name}_list"
330
332
  else:
331
333
  src = settings['src']
@@ -4712,10 +4714,10 @@ def merge_regression_res_with_metadata(results_file, metadata_file, name='_metad
4712
4714
  df_metadata['gene'] = df_metadata['Gene ID'].apply(lambda x: x.split('_')[1] if '_' in x else None)
4713
4715
 
4714
4716
  # Drop rows where gene extraction failed
4715
- df_results = df_results.dropna(subset=['gene'])
4717
+ #df_results = df_results.dropna(subset=['gene'])
4716
4718
 
4717
4719
  # Merge the two dataframes on the gene column
4718
- merged_df = pd.merge(df_results, df_metadata, on='gene')
4720
+ merged_df = pd.merge(df_results, df_metadata, on='gene', how='left')
4719
4721
 
4720
4722
  # Generate the new file name
4721
4723
  base, ext = os.path.splitext(results_file)
@@ -5047,6 +5049,7 @@ def add_column_to_database(settings):
5047
5049
  """
5048
5050
  Adds a new column to the database table by matching on a common column from the DataFrame.
5049
5051
  If the column already exists in the database, it adds the column with a suffix.
5052
+ NaN values will remain as NULL in the database.
5050
5053
 
5051
5054
  Parameters:
5052
5055
  - settings: A dictionary containing the following keys:
@@ -5060,7 +5063,7 @@ def add_column_to_database(settings):
5060
5063
  # Read the DataFrame from the provided CSV path
5061
5064
  df = pd.read_csv(settings['csv_path'])
5062
5065
 
5063
- # Check for any 0 values in the update column and replace them with 2
5066
+ # Replace 0 values with 2 in the update column
5064
5067
  if (df[settings['update_column']] == 0).any():
5065
5068
  print("Replacing all 0 values with 2 in the update column.")
5066
5069
  df[settings['update_column']].replace(0, 2, inplace=True)
@@ -5073,12 +5076,10 @@ def add_column_to_database(settings):
5073
5076
  cursor.execute(f"PRAGMA table_info({settings['table_name']})")
5074
5077
  columns_in_db = [col[1] for col in cursor.fetchall()]
5075
5078
 
5076
- # Check if the update column already exists in the database
5079
+ # Add a suffix if the update column already exists in the database
5077
5080
  if settings['update_column'] in columns_in_db:
5078
- # Add a suffix to the column name (e.g., '_new', '_1', or similar)
5079
5081
  suffix = 1
5080
5082
  new_column_name = f"{settings['update_column']}_{suffix}"
5081
- # Ensure uniqueness by incrementing the suffix if needed
5082
5083
  while new_column_name in columns_in_db:
5083
5084
  suffix += 1
5084
5085
  new_column_name = f"{settings['update_column']}_{suffix}"
@@ -5086,8 +5087,8 @@ def add_column_to_database(settings):
5086
5087
  else:
5087
5088
  new_column_name = settings['update_column']
5088
5089
 
5089
- # Add the new column to the database table
5090
- cursor.execute(f"ALTER TABLE {settings['table_name']} ADD COLUMN {new_column_name} TEXT")
5090
+ # Add the new column with INTEGER type to the database table
5091
+ cursor.execute(f"ALTER TABLE {settings['table_name']} ADD COLUMN {new_column_name} INTEGER")
5091
5092
  print(f"Added new column '{new_column_name}' to the table '{settings['table_name']}'.")
5092
5093
 
5093
5094
  # Iterate over the DataFrame and update the new column in the database
@@ -5095,6 +5096,10 @@ def add_column_to_database(settings):
5095
5096
  value_to_update = row[settings['update_column']]
5096
5097
  match_value = row[settings['match_column']]
5097
5098
 
5099
+ # Handle NaN values by converting them to None (SQLite equivalent of NULL)
5100
+ if pd.isna(value_to_update):
5101
+ value_to_update = None
5102
+
5098
5103
  # Prepare and execute the SQL update query
5099
5104
  query = f"""
5100
5105
  UPDATE {settings['table_name']}
@@ -5107,7 +5112,4 @@ def add_column_to_database(settings):
5107
5112
  conn.commit()
5108
5113
  conn.close()
5109
5114
 
5110
- print(f"Updated '{new_column_name}' in '{settings['table_name']}' using '{settings['match_column']}'.")
5111
-
5112
-
5113
-
5115
+ print(f"Updated '{new_column_name}' in '{settings['table_name']}' using '{settings['match_column']}'.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spacr
3
- Version: 0.3.37
3
+ Version: 0.3.41
4
4
  Summary: Spatial phenotype analysis of crisp screens (SpaCr)
5
5
  Home-page: https://github.com/EinarOlafsson/spacr
6
6
  Author: Einar Birnir Olafsson
@@ -8,26 +8,26 @@ spacr/app_measure.py,sha256=_K7APYIeOKpV6e_LcqabBjvEi7mfq9Fch8175x1x0k8,162
8
8
  spacr/app_sequencing.py,sha256=DjG26jy4cpddnV8WOOAIiExtOe9MleVMY4MFa5uTo5w,157
9
9
  spacr/app_umap.py,sha256=ZWAmf_OsIKbYvolYuWPMYhdlVe-n2CADoJulAizMiEo,153
10
10
  spacr/cellpose.py,sha256=zv4BzhaP2O-mtQ-pUfYvpOyxgn1ke_bDWgdHD5UWm9I,13942
11
- spacr/core.py,sha256=G_x-w7FRIHNfSOoPaIZPSf_A7mVj7PA7o9HQZ4nIu5o,48231
11
+ spacr/core.py,sha256=dW9RrAKFLfVsFhX0-kaVMc2T7b47Ky0pTXK-CEVOeWQ,48235
12
12
  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=w-S1MZdyxt5O3DsNAHNNXy_WGfwBPg0NhwQtCsJeiao,137071
16
16
  spacr/gui_utils.py,sha256=7e9DsZIuV7-jh97kEf7v1In_cFzlFueV4SGcGYGpTxw,45454
17
- spacr/io.py,sha256=AARmqn1fMmTgVDwWy8bEYK6SjH-6DZIulgCSPdBTyf0,143370
17
+ spacr/io.py,sha256=LN_gJq_oqjbf8y-lBtLLZtJi8DLbNdyoGEcBYyOjbhQ,143606
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=ItibDL_q0cKwEsJdwpBtVqfpRQGPXGbb0BX5UB5iH5s,49342
21
+ spacr/ml.py,sha256=e6nUQaiKBPwcDN_aZZKsbZG6qEa5k9B42wtuL8ipv3Q,50287
22
22
  spacr/openai.py,sha256=5vBZ3Jl2llYcW3oaTEXgdyCB2aJujMUIO5K038z7w_A,1246
23
- spacr/plot.py,sha256=QYj2bV0-6UqKRTWeSj3eOOgr6dNMmCCc4TdRGTniQ4c,118083
23
+ spacr/plot.py,sha256=TDGMwiIHjvk6v94WFlIvemU-6JfEik_GmSez51vyvCc,135869
24
24
  spacr/sequencing.py,sha256=t18mgpK6rhWuB1LtFOsPxqgpFXxuUmrD06ecsaVQ0Gw,19655
25
25
  spacr/settings.py,sha256=AzP9NGiXI1MqT69bHObxwDSCUk0kdstBVvl1JpcD_-w,75960
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
29
- spacr/toxo.py,sha256=us3pQyULtMTyfTq0MWPn4QJTTmQ6BwAJKChNf75jo3I,10082
30
- spacr/utils.py,sha256=3SBf5yeeU3u9MVsIWeYmcHjhwqs8LJ6m9UF0wBSNq8M,216304
29
+ spacr/toxo.py,sha256=7dUJe5_HSvDCP16OIXtbYLyshh9LXb2JQ80Vtn-XdPk,15979
30
+ spacr/utils.py,sha256=_8OxwGVCZaMNBiweB4_YOxBkqQX1LR9YstPSIFmeQKA,216420
31
31
  spacr/version.py,sha256=axH5tnGwtgSnJHb5IDhiu4Zjk5GhLyAEDRe-rnaoFOA,409
32
32
  spacr/resources/MEDIAR/.gitignore,sha256=Ff1q9Nme14JUd-4Q3jZ65aeQ5X4uttptssVDgBVHYo8,152
33
33
  spacr/resources/MEDIAR/LICENSE,sha256=yEj_TRDLUfDpHDNM0StALXIt6mLqSgaV2hcCwa6_TcY,1065
@@ -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.37.dist-info/LICENSE,sha256=SR-2MeGc6SCM1UORJYyarSWY_A-JaOMFDj7ReSs9tRM,1083
154
- spacr-0.3.37.dist-info/METADATA,sha256=Y65Rn3Py1AHsgydWiBFLsd0_vPVhxubiYizcIpfFIbE,5949
155
- spacr-0.3.37.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
156
- spacr-0.3.37.dist-info/entry_points.txt,sha256=BMC0ql9aNNpv8lUZ8sgDLQMsqaVnX5L535gEhKUP5ho,296
157
- spacr-0.3.37.dist-info/top_level.txt,sha256=GJPU8FgwRXGzKeut6JopsSRY2R8T3i9lDgya42tLInY,6
158
- spacr-0.3.37.dist-info/RECORD,,
153
+ spacr-0.3.41.dist-info/LICENSE,sha256=SR-2MeGc6SCM1UORJYyarSWY_A-JaOMFDj7ReSs9tRM,1083
154
+ spacr-0.3.41.dist-info/METADATA,sha256=zGZO-9iZjljHTjnVg9kAxQJjr2vqpobl7S7ZSQlgxP8,5949
155
+ spacr-0.3.41.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
156
+ spacr-0.3.41.dist-info/entry_points.txt,sha256=BMC0ql9aNNpv8lUZ8sgDLQMsqaVnX5L535gEhKUP5ho,296
157
+ spacr-0.3.41.dist-info/top_level.txt,sha256=GJPU8FgwRXGzKeut6JopsSRY2R8T3i9lDgya42tLInY,6
158
+ spacr-0.3.41.dist-info/RECORD,,
File without changes