PyOPIA 2.5.10__tar.gz → 2.5.11__tar.gz
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.
- {pyopia-2.5.10 → pyopia-2.5.11}/PKG-INFO +1 -1
- pyopia-2.5.11/pyopia/__init__.py +1 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/cli.py +16 -2
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/io.py +133 -16
- pyopia-2.5.10/pyopia/__init__.py +0 -1
- {pyopia-2.5.10 → pyopia-2.5.11}/LICENSE +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/README.md +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/background.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/classify.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/exampledata.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/instrument/__init__.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/instrument/common.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/instrument/holo.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/instrument/silcam.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/instrument/uvp.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/pipeline.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/plotting.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/process.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/simulator/__init__.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/simulator/silcam.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/statistics.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/tests/__init__.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/tests/test_classify.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/tests/test_notebooks.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyopia/tests/test_pipeline.py +0 -0
- {pyopia-2.5.10 → pyopia-2.5.11}/pyproject.toml +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.5.11'
|
|
@@ -186,7 +186,8 @@ def process(config_filename: str, num_chunks: int = 1):
|
|
|
186
186
|
|
|
187
187
|
|
|
188
188
|
@app.command()
|
|
189
|
-
def merge_mfdata(path_to_data: str, prefix='*'
|
|
189
|
+
def merge_mfdata(path_to_data: str, prefix='*', overwrite_existing_partials: bool = True,
|
|
190
|
+
chunk_size: int = None):
|
|
190
191
|
'''Combine a multi-file directory of STATS.nc files into a single '-STATS.nc' file
|
|
191
192
|
that can then be loaded with {func}`pyopia.io.load_stats`
|
|
192
193
|
|
|
@@ -198,8 +199,21 @@ def merge_mfdata(path_to_data: str, prefix='*'):
|
|
|
198
199
|
prefix : str
|
|
199
200
|
Prefix to multi-file dataset (for replacing the wildcard in '*Image-D*-STATS.nc').
|
|
200
201
|
Defaults to '*'
|
|
202
|
+
|
|
203
|
+
overwrite_existing_partials : bool
|
|
204
|
+
Do not reprocess existing merged netcdf files for each chunk if False.
|
|
205
|
+
Otherwise reprocess (load) and overwrite. This can be used to restart
|
|
206
|
+
or continue a previous merge operation as new files become available.
|
|
207
|
+
|
|
208
|
+
chunk_size : int
|
|
209
|
+
Process this many files together and store as partially merged netcdf files, which
|
|
210
|
+
are then merged at the end. Default: None, process all files together.
|
|
201
211
|
'''
|
|
202
|
-
|
|
212
|
+
setup_logging({'general': {}})
|
|
213
|
+
|
|
214
|
+
pyopia.io.merge_and_save_mfdataset(path_to_data, prefix=prefix,
|
|
215
|
+
overwrite_existing_partials=overwrite_existing_partials,
|
|
216
|
+
chunk_size=chunk_size)
|
|
203
217
|
|
|
204
218
|
|
|
205
219
|
def setup_logging(pipeline_config):
|
|
@@ -3,7 +3,7 @@ Module containing tools for datafile and metadata handling
|
|
|
3
3
|
'''
|
|
4
4
|
|
|
5
5
|
from datetime import datetime
|
|
6
|
-
|
|
6
|
+
import numpy as np
|
|
7
7
|
import h5py
|
|
8
8
|
import pandas as pd
|
|
9
9
|
import toml
|
|
@@ -11,6 +11,7 @@ import xarray
|
|
|
11
11
|
import os
|
|
12
12
|
from glob import glob
|
|
13
13
|
import xarray as xr
|
|
14
|
+
from tqdm.auto import tqdm
|
|
14
15
|
|
|
15
16
|
from pyopia import __version__ as pyopia_version
|
|
16
17
|
|
|
@@ -178,7 +179,7 @@ def load_stats(datafilename):
|
|
|
178
179
|
'''
|
|
179
180
|
|
|
180
181
|
if datafilename.endswith('.nc'):
|
|
181
|
-
with xarray.open_dataset(datafilename) as xstats:
|
|
182
|
+
with xarray.open_dataset(datafilename, engine=NETCDF_ENGINE) as xstats:
|
|
182
183
|
xstats.load()
|
|
183
184
|
elif datafilename.endswith('.h5'):
|
|
184
185
|
logger.warning('In future, load_stats will only take .nc files')
|
|
@@ -194,7 +195,11 @@ def load_stats(datafilename):
|
|
|
194
195
|
|
|
195
196
|
|
|
196
197
|
def combine_stats_netcdf_files(path_to_data, prefix='*'):
|
|
197
|
-
'''
|
|
198
|
+
'''.. deprecated:: 2.4.11
|
|
199
|
+
:class:`pyopia.io.combine_stats_netcdf_files` will be removed in version 3.0.0, it is replaced by
|
|
200
|
+
:class:`pyopia.io.concat_stats_netcdf_files`.
|
|
201
|
+
|
|
202
|
+
Combine a multi-file directory of STATS.nc files into a 'stats' xarray dataset created by :func:`pyopia.io.write_stats`
|
|
198
203
|
when using 'append = false'
|
|
199
204
|
|
|
200
205
|
Parameters
|
|
@@ -231,7 +236,63 @@ def combine_stats_netcdf_files(path_to_data, prefix='*'):
|
|
|
231
236
|
return xstats, image_stats
|
|
232
237
|
|
|
233
238
|
|
|
234
|
-
def
|
|
239
|
+
def concat_stats_netcdf_files(sorted_filelist):
|
|
240
|
+
'''Concatenate specified list of STATS.nc files into one 'xstats' xarray dataset
|
|
241
|
+
created by :func:`pyopia.io.write_stats when using 'append = false'.
|
|
242
|
+
|
|
243
|
+
Existing files are first loaded and then combined, so memory usage will go up with longer file lists.
|
|
244
|
+
|
|
245
|
+
Parameters
|
|
246
|
+
----------
|
|
247
|
+
sorted_filelist : str
|
|
248
|
+
List of files to be combined into single dataset
|
|
249
|
+
|
|
250
|
+
Returns
|
|
251
|
+
-------
|
|
252
|
+
xstats : xarray.Dataset or None
|
|
253
|
+
Particle statistics and metatdata from processing steps
|
|
254
|
+
image_stats : xarray.Dataset or None
|
|
255
|
+
Summary statistics of each raw image (including those with no particles)
|
|
256
|
+
'''
|
|
257
|
+
if len(sorted_filelist) < 1:
|
|
258
|
+
logger.error('No files found to concatenate, doing nothing.')
|
|
259
|
+
return None, None
|
|
260
|
+
|
|
261
|
+
# We load one dataset at the time into a list for later merge
|
|
262
|
+
datasets = []
|
|
263
|
+
datasets_image_stats = []
|
|
264
|
+
|
|
265
|
+
# Check if we have image statistics in first file, if not, we skip checking the rest
|
|
266
|
+
skip_image_stats = False
|
|
267
|
+
try:
|
|
268
|
+
with xr.open_dataset(sorted_filelist[0], group='image_stats', engine=NETCDF_ENGINE) as ds:
|
|
269
|
+
ds.load()
|
|
270
|
+
except OSError:
|
|
271
|
+
logger.info('Could get image_stats from netcdf files for merging, returning None for this.')
|
|
272
|
+
skip_image_stats = True
|
|
273
|
+
|
|
274
|
+
# Load datasets from each file into the lists
|
|
275
|
+
for f in tqdm(sorted_filelist, desc='Loading datasets'):
|
|
276
|
+
with xr.open_dataset(f, engine=NETCDF_ENGINE) as ds:
|
|
277
|
+
ds.load()
|
|
278
|
+
datasets.append(ds)
|
|
279
|
+
|
|
280
|
+
if not skip_image_stats:
|
|
281
|
+
with xr.open_dataset(f, group='image_stats', engine=NETCDF_ENGINE) as ds:
|
|
282
|
+
ds.load()
|
|
283
|
+
datasets_image_stats.append(ds)
|
|
284
|
+
|
|
285
|
+
# Combine the individual datasets loaded above
|
|
286
|
+
logging.info('Combining datasets')
|
|
287
|
+
xstats = xr.concat(datasets, dim='index')
|
|
288
|
+
image_stats = None
|
|
289
|
+
if not skip_image_stats:
|
|
290
|
+
image_stats = xr.concat(datasets_image_stats, dim='timestamp')
|
|
291
|
+
|
|
292
|
+
return xstats, image_stats
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def merge_and_save_mfdataset(path_to_data, prefix='*', overwrite_existing_partials=False, chunk_size=None):
|
|
235
296
|
'''Combine a multi-file directory of STATS.nc files into a single '-STATS.nc' file
|
|
236
297
|
that can then be loaded with :func:`pyopia.io.load_stats`
|
|
237
298
|
|
|
@@ -243,24 +304,80 @@ def merge_and_save_mfdataset(path_to_data, prefix='*'):
|
|
|
243
304
|
prefix : str
|
|
244
305
|
Prefix to multi-file dataset (for replacing the wildcard in '*Image-D*-STATS.nc').
|
|
245
306
|
Defaults to '*'
|
|
307
|
+
|
|
308
|
+
overwrite_existing_partials : bool
|
|
309
|
+
Do not reprocess existing merged netcdf files for each chunk if False.
|
|
310
|
+
Otherwise reprocess (load) and overwrite. This can be used to restart
|
|
311
|
+
or continue a previous merge operation as new files become available.
|
|
312
|
+
|
|
313
|
+
chunk_size : int
|
|
314
|
+
Number of files to be loaded and merged in each step. Produces a number
|
|
315
|
+
of intermediate/partially merged netcdf files equal to the total number
|
|
316
|
+
of input files divided by chunk_size. The last chunk may contain less
|
|
317
|
+
files than specified, depending on the total number of files.
|
|
318
|
+
Default: None, which processes all files together.
|
|
246
319
|
'''
|
|
320
|
+
logging.info(f'Combine stats netcdf files from {path_to_data}')
|
|
321
|
+
if (chunk_size is not None) and (chunk_size < 1):
|
|
322
|
+
raise ValueError(f'Invalid chunk size, must be greater than 0, was {chunk_size}')
|
|
247
323
|
|
|
248
|
-
|
|
249
|
-
|
|
324
|
+
# Get sorted list of per-image stats netcdf files
|
|
325
|
+
sorted_filelist = sorted(glob(os.path.join(path_to_data, prefix + 'Image-D*-STATS.nc')))
|
|
250
326
|
|
|
327
|
+
# Chunk the file list into smaller parts if specified
|
|
328
|
+
num_files = len(sorted_filelist)
|
|
329
|
+
chunk_size_used = num_files if chunk_size is None else min(chunk_size, num_files)
|
|
330
|
+
num_chunks = int(np.ceil(num_files / chunk_size_used))
|
|
331
|
+
filelist_chunks = [sorted_filelist[i*chunk_size_used:min(num_files, (i+1)*chunk_size_used)] for i in range(num_chunks)]
|
|
332
|
+
infostr = f'Processing {num_chunks} partial file lists of {chunk_size_used} files each'
|
|
333
|
+
infostr += ', based on a total of {num_files} files.'
|
|
334
|
+
logging.info(infostr)
|
|
335
|
+
|
|
336
|
+
# Get config from first file in list
|
|
337
|
+
xstats = load_stats(sorted_filelist[0])
|
|
251
338
|
settings = steps_from_xstats(xstats)
|
|
252
|
-
|
|
253
339
|
prefix_out = os.path.basename(settings['steps']['output']['output_datafile'])
|
|
254
|
-
output_name = os.path.join(path_to_data, prefix_out)
|
|
255
|
-
|
|
256
|
-
logging.info(f'writing {output_name}')
|
|
257
|
-
# save the particle statistics (xstats) to NetCDF
|
|
258
340
|
encoding = setup_xstats_encoding(xstats)
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
341
|
+
|
|
342
|
+
def process_store(i, filelist_):
|
|
343
|
+
output_name = os.path.join(path_to_data, f'part-{i:04d}-{prefix_out}-STATS.nc')
|
|
344
|
+
|
|
345
|
+
# Skip this chunk if the merged output file exists and overwrite is set to False
|
|
346
|
+
if os.path.exists(output_name) and not overwrite_existing_partials:
|
|
347
|
+
logging.info(f'File exists ({output_name}), skipping')
|
|
348
|
+
return output_name
|
|
349
|
+
|
|
350
|
+
# Load the individual datasets
|
|
351
|
+
xstats, image_stats = concat_stats_netcdf_files(filelist_)
|
|
352
|
+
|
|
353
|
+
# Save the particle statistics (xstats) to NetCDF
|
|
354
|
+
logging.info(f'Writing {output_name}')
|
|
355
|
+
if xstats is not None:
|
|
356
|
+
xstats.to_netcdf(output_name, mode='w', encoding=encoding, engine=NETCDF_ENGINE, format='NETCDF4')
|
|
357
|
+
|
|
358
|
+
# If summary data for each raw image are available (image_stats), save this into the image_stats group
|
|
359
|
+
if image_stats is not None:
|
|
360
|
+
image_stats.to_netcdf(output_name, group='image_stats', mode='a', engine=NETCDF_ENGINE)
|
|
361
|
+
logging.info(f'Writing {output_name} done.')
|
|
362
|
+
|
|
363
|
+
return output_name
|
|
364
|
+
|
|
365
|
+
# Loop over filelist chunkst and created merged netcdf files for each
|
|
366
|
+
merged_files = []
|
|
367
|
+
for i, filelist_ in enumerate(filelist_chunks):
|
|
368
|
+
output_name = process_store(i, filelist_)
|
|
369
|
+
merged_files.append(output_name)
|
|
370
|
+
|
|
371
|
+
# Finally, merge the partially merged files
|
|
372
|
+
logging.info('Doing final merge of partially merged files')
|
|
373
|
+
output_name = os.path.join(path_to_data, prefix_out + '-STATS.nc')
|
|
374
|
+
with xr.open_mfdataset(merged_files, concat_dim='index', combine='nested') as ds:
|
|
375
|
+
ds.to_netcdf(output_name, mode='w', encoding=encoding, engine=NETCDF_ENGINE, format='NETCDF4')
|
|
376
|
+
|
|
377
|
+
with xr.open_mfdataset(merged_files, group='image_stats', concat_dim='timestamp', combine='nested') as ds:
|
|
378
|
+
ds.to_netcdf(output_name, mode='a', group='image_stats', engine=NETCDF_ENGINE)
|
|
379
|
+
|
|
380
|
+
logging.info(f'Writing {output_name} done.')
|
|
264
381
|
|
|
265
382
|
|
|
266
383
|
def steps_from_xstats(xstats):
|
pyopia-2.5.10/pyopia/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.5.10'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|