@@ -320,20 +320,79 @@ def get_shape(self, segment_index: int | None = None) -> tuple[int, ...]:
320320
321321 def save (self , format = "binary" , verbose : bool = False , ** save_kwargs ):
322322 """
323- TODO: each object.save should have extensive docstring with all the options and examples
323+ Save a `BaseRecording` object to a specified format:
324+
325+ * "binary"
326+ * "zarr"
327+ * "memory"
328+
329+ Parameters
330+ ----------
331+ format : str, default: "binary"
332+ The format to save the recording in. Options are:
333+ - "binary": Saves the recording in binary format.
334+ - "zarr": Saves the recording in Zarr format.
335+ - "memory": Saves the recording in memory (shared memory or numpy array).
336+ verbose : bool, default: False
337+ If True, prints additional information during the save process.
338+ **save_kwargs : dict
339+ Additional keyword arguments specific to the chosen format.
340+ All formats support job_kwargs for parallel processing
341+ (see `si.get_global_job_kwargs()` for default values).
342+
343+ * "binary" format:
344+ - folder : str or Path
345+ The folder where the binary files will be saved.
346+ - overwrite : bool, default: False
347+ If True, existing files in the folder will be overwritten.
348+ - dtype : str, optional
349+ The data type to use for saving the recording. If not provided, the recording's dtype
350+ will be used.
351+ * "zarr" format:
352+ - folder : str or Path
353+ The folder where the Zarr files will be saved.
354+ - overwrite: bool, default: False
355+ If True, the folder is removed if it already exists
356+ - storage_options: dict or None, default: None
357+ Storage options for zarr `store`. E.g., if "s3://" or "gcs://" they can provide authentication methods, etc.
358+ For cloud storage locations, this should not be None (in case of default values, use an empty dict)
359+ - channel_chunk_size: int or None, default: None
360+ Channels per chunk (only for BaseRecording)
361+ - compressor: numcodecs.Codec or None, default: None
362+ Global compressor. If None, Blosc-zstd, level 5, with bit shuffle is used
363+ - filters: list[numcodecs.Codec] or None, default: None
364+ Global filters for zarr (global)
365+ - compressor_by_dataset: dict or None, default: None
366+ Optional compressor per dataset:
367+ - traces
368+ - times
369+ If None, the global compressor is used
370+ - filters_by_dataset: dict or None, default: None
371+ Optional filters per dataset:
372+ - traces
373+ - times
374+ If None, the global filters are used
375+ * "memory" format:
376+ - sharedmem : bool, default: True
377+ If True, the recording is saved in shared memory. If False, it is saved as
378+ a numpy array in memory.
379+
380+ Returns
381+ -------
382+ BaseRecording
383+ The saved recording object in the specified format.
324384 """
325385 kwargs , job_kwargs = split_job_kwargs (save_kwargs )
326386
327- # TODO: add overwrite option to binary/zarr save
328-
329387 if format == "binary" :
330388 if "folder" not in kwargs :
331389 raise ValueError ("Missing folder in recording.save(folder='...')" )
332390
333391 from .binaryfolder import BinaryFolderRecording
334392
393+ folder = kwargs .pop ("folder" )
335394 cached = BinaryFolderRecording .write_recording (
336- self , folder = kwargs [ " folder" ], dtype = kwargs . get ( "dtype" , None ) , ** job_kwargs
395+ self , folder_path = folder , verbose = verbose , ** kwargs , ** job_kwargs
337396 )
338397
339398 elif format == "memory" :
@@ -348,49 +407,22 @@ def save(self, format="binary", verbose: bool = False, **save_kwargs):
348407
349408 cached = NumpyRecording .from_recording (self , with_metadata = True , with_time_vector = True , ** job_kwargs )
350409
351- # self.copy_metadata(cached)
352-
353- # # timestamps are not saved in memory, so we have to set them explicitly
354- # for segment_index in range(self.get_num_segments()):
355- # if self.has_time_vector(segment_index):
356- # # the use of get_times is preferred since timestamps are converted to array
357- # time_vector = self.get_times(segment_index=segment_index)
358- # cached.set_times(time_vector, segment_index=segment_index)
359-
360410 elif format == "zarr" :
361411 if "folder" not in kwargs :
362412 raise ValueError ("Missing folder in recording.save(folder='...')" )
413+ folder_path = kwargs .pop ("folder" )
363414
364415 from .zarrextractors import ZarrRecordingExtractor
365416
366- folder_path = kwargs ["folder" ]
367- if isinstance (folder_path , Path ) and folder_path .suffix != "zarr" :
368- # automatically add the zarr suffix
369- folder_path = folder_path .with_suffix (".zarr" )
370-
371- storage_options = kwargs .pop ("storage_options" , None )
372- ZarrRecordingExtractor .write_recording (
373- self , folder_path , storage_options , verbose = verbose , ** kwargs , ** job_kwargs
417+ cached = ZarrRecordingExtractor .write_recording (
418+ self , folder_path = folder_path , verbose = verbose , ** kwargs , ** job_kwargs
374419 )
375- cached = ZarrRecordingExtractor (folder_path , storage_options )
376- # timestamps are saved and restored in zarr, so no need to set them explicitly
377420
378421 else :
379422 raise ValueError (f"format { format } not supported" )
380423
381424 return cached
382425
383- def _extra_metadata_from_folder (self , folder ):
384- # load probe
385- super ()._extra_metadata_from_folder (folder )
386-
387- # load time vector if any
388- for segment_index , rs in enumerate (self .segments ):
389- time_file = folder / f"times_cached_seg{ segment_index } .npy"
390- if time_file .is_file ():
391- time_vector = np .load (time_file , mmap_mode = "r" )
392- rs ._time_vector = time_vector
393-
394426 def select_channels (self , channel_ids : list | np .ndarray | tuple ) -> "BaseRecording" :
395427 """
396428 Returns a new recording object with a subset of channels.
0 commit comments