or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-data-structures.mddata-utilities.mdfile-io-support.mdindex.mdrawio-access.md

rawio-access.mddocs/

0

# Low-Level File Access

1

2

High-performance, memory-efficient access to electrophysiology files through Neo's RawIO interface. This low-level API provides direct access to signal chunks, header information, and metadata without creating full Neo objects, enabling efficient processing of large datasets and real-time applications.

3

4

## Capabilities

5

6

### RawIO Class Detection

7

8

Functions for automatically detecting and instantiating appropriate RawIO classes.

9

10

```python { .api }

11

def get_rawio(filename):

12

"""

13

Auto-detect file format and return appropriate RawIO class instance.

14

15

Parameters:

16

- filename (str): Path to file or directory

17

18

Returns:

19

RawIO instance for the detected file format

20

21

Raises:

22

IOError: If format cannot be determined or no compatible RawIO found

23

"""

24

```

25

26

### Core RawIO Interface

27

28

Base interface shared by all RawIO classes for consistent low-level data access.

29

30

```python { .api }

31

class BaseRawIO:

32

"""Base class defining the RawIO interface for all file formats."""

33

34

def parse_header(self):

35

"""Parse file header and extract metadata."""

36

37

def get_signal_size(self, block_index, seg_index, channel_indexes=None):

38

"""

39

Get the size of analog signal data.

40

41

Parameters:

42

- block_index (int): Block index in file

43

- seg_index (int): Segment index within block

44

- channel_indexes (list, optional): Specific channels

45

46

Returns:

47

int: Number of samples in the signal

48

"""

49

50

def get_analogsignal_chunk(self, block_index, seg_index,

51

i_start=None, i_stop=None,

52

channel_indexes=None):

53

"""

54

Get analog signal data chunk.

55

56

Parameters:

57

- block_index (int): Block index

58

- seg_index (int): Segment index

59

- i_start (int, optional): Start sample index

60

- i_stop (int, optional): Stop sample index

61

- channel_indexes (list, optional): Channel indices to read

62

63

Returns:

64

numpy.ndarray: Signal data chunk (samples x channels)

65

"""

66

67

def get_spike_timestamps(self, block_index, seg_index, unit_index):

68

"""

69

Get spike timestamps for a specific unit.

70

71

Parameters:

72

- block_index (int): Block index

73

- seg_index (int): Segment index

74

- unit_index (int): Unit/channel index

75

76

Returns:

77

numpy.ndarray: Spike timestamps

78

"""

79

80

def get_spike_raw_waveforms(self, block_index, seg_index, unit_index):

81

"""

82

Get raw spike waveform data.

83

84

Parameters:

85

- block_index (int): Block index

86

- seg_index (int): Segment index

87

- unit_index (int): Unit index

88

89

Returns:

90

numpy.ndarray: Waveform data (spikes x samples x channels)

91

"""

92

```

93

94

### Hardware-Specific RawIO Classes

95

96

Low-level access for major hardware manufacturer formats.

97

98

```python { .api }

99

class AxonRawIO:

100

"""

101

Low-level Axon file format access (.abf).

102

103

Provides efficient access to Molecular Devices pCLAMP and AxoScope

104

files with direct header parsing and signal chunk retrieval.

105

"""

106

def __init__(self, filename): ...

107

def parse_header(self): ...

108

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

109

110

# Axon-specific properties

111

header: dict # Parsed ABF header information

112

sampling_rate: float # Sampling rate in Hz

113

protocol_info: dict # Protocol and stimulus information

114

115

class BlackrockRawIO:

116

"""Low-level Blackrock Microsystems file access (.nev, .ns1-.ns6)."""

117

def __init__(self, filename): ...

118

def parse_header(self): ...

119

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

120

def get_spike_timestamps(self, block_index, seg_index, unit_index): ...

121

122

class PlexonRawIO:

123

"""Low-level Plexon file format access (.plx)."""

124

def __init__(self, filename): ...

125

def parse_header(self): ...

126

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

127

def get_spike_timestamps(self, block_index, seg_index, unit_index): ...

128

def get_spike_raw_waveforms(self, block_index, seg_index, unit_index): ...

129

130

class Plexon2RawIO:

131

"""Low-level Plexon2 file format access (.pl2)."""

132

def __init__(self, filename): ...

133

def parse_header(self): ...

134

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

135

136

class IntanRawIO:

137

"""Low-level Intan Technologies file access (.rhd, .rhs)."""

138

def __init__(self, filename): ...

139

def parse_header(self): ...

140

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

141

142

class NeuralynxRawIO:

143

"""Low-level Neuralynx file format access (.ncs, .nev, .nse, .ntt)."""

144

def __init__(self, dirname): ...

145

def parse_header(self): ...

146

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

147

def get_spike_timestamps(self, block_index, seg_index, unit_index): ...

148

149

class TdtRawIO:

150

"""Low-level Tucker-Davis Technologies file access (.tev, .tsq, .tnt)."""

151

def __init__(self, dirname): ...

152

def parse_header(self): ...

153

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

154

155

class Spike2RawIO:

156

"""Low-level Cambridge Electronic Design Spike2 access (.smr, .son)."""

157

def __init__(self, filename): ...

158

def parse_header(self): ...

159

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

160

```

161

162

### Multi-Electrode Array RawIO

163

164

Specialized low-level access for high-channel-count MEA and probe recordings.

165

166

```python { .api }

167

class BiocamRawIO:

168

"""Low-level 3Brain Biocam file access (.brw, .bxr)."""

169

def __init__(self, filename): ...

170

def parse_header(self): ...

171

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

172

173

class MaxwellRawIO:

174

"""Low-level Maxwell Biosystems file access (.raw.h5)."""

175

def __init__(self, filename): ...

176

def parse_header(self): ...

177

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

178

179

class NeuroNexusRawIO:

180

"""Low-level NeuroNexus probe file access (.xdaq)."""

181

def __init__(self, filename): ...

182

def parse_header(self): ...

183

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

184

185

class SpikeGadgetsRawIO:

186

"""Low-level SpikeGadgets file access (.rec, .mda)."""

187

def __init__(self, filename): ...

188

def parse_header(self): ...

189

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

190

191

class RawMCSRawIO:

192

"""Low-level Multi Channel Systems raw file access (.raw)."""

193

def __init__(self, filename): ...

194

def parse_header(self): ...

195

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

196

```

197

198

### Open Source Format RawIO

199

200

Low-level access for open source and analysis software formats.

201

202

```python { .api }

203

class OpenEphysRawIO:

204

"""Low-level Open Ephys GUI file access (.continuous, .events, .spikes)."""

205

def __init__(self, dirname): ...

206

def parse_header(self): ...

207

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

208

209

class OpenEphysBinaryRawIO:

210

"""Low-level Open Ephys binary file access (.dat)."""

211

def __init__(self, dirname): ...

212

def parse_header(self): ...

213

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

214

215

class PhyRawIO:

216

"""Low-level Phy spike sorting file access (params.py, cluster_info.tsv)."""

217

def __init__(self, dirname): ...

218

def parse_header(self): ...

219

def get_spike_timestamps(self, block_index, seg_index, unit_index): ...

220

def get_spike_raw_waveforms(self, block_index, seg_index, unit_index): ...

221

222

class SpikeGLXRawIO:

223

"""Low-level SpikeGLX acquisition file access (.bin, .meta)."""

224

def __init__(self, dirname): ...

225

def parse_header(self): ...

226

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

227

```

228

229

### Standard Format RawIO

230

231

Low-level access for industry-standard and research formats.

232

233

```python { .api }

234

class EDFRawIO:

235

"""Low-level European Data Format access (.edf, .bdf)."""

236

def __init__(self, filename): ...

237

def parse_header(self): ...

238

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

239

240

class NIXRawIO:

241

"""Low-level NIX format access (.nix)."""

242

def __init__(self, filename): ...

243

def parse_header(self): ...

244

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

245

246

class RawBinarySignalRawIO:

247

"""

248

Low-level raw binary signal file access (.raw, .bin).

249

250

Flexible access to custom binary formats with user-specified

251

data layout parameters.

252

"""

253

def __init__(self, filename, dtype='float32', sampling_rate=1.0,

254

nb_channel=1, signal_gain=1.0, signal_offset=0.0): ...

255

def parse_header(self): ...

256

def get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop, channel_indexes): ...

257

```

258

259

## Usage Examples

260

261

### Basic RawIO Operations

262

263

```python

264

import neo.rawio

265

import numpy as np

266

267

# Auto-detect and create RawIO instance

268

rawio = neo.rawio.get_rawio('recording.abf')

269

270

# Parse file header

271

rawio.parse_header()

272

273

# Examine file structure

274

print(f"Number of blocks: {rawio.block_count()}")

275

print(f"Number of segments: {rawio.segment_count(0)}")

276

print(f"Number of channels: {rawio.signal_channels_count()}")

277

print(f"Sampling rate: {rawio.get_signal_sampling_rate()}")

278

279

# Get channel information

280

signal_channels = rawio.header['signal_channels']

281

for i, ch in enumerate(signal_channels):

282

print(f"Channel {i}: {ch['name']} ({ch['units']})")

283

```

284

285

### Efficient Data Access

286

287

```python

288

# Get signal chunk without loading entire file

289

chunk = rawio.get_analogsignal_chunk(

290

block_index=0,

291

seg_index=0,

292

i_start=1000, # Start at sample 1000

293

i_stop=2000, # End at sample 2000

294

channel_indexes=[0, 1, 2] # Only channels 0, 1, 2

295

)

296

297

print(f"Chunk shape: {chunk.shape}") # (1000 samples, 3 channels)

298

print(f"Data type: {chunk.dtype}")

299

print(f"Memory usage: {chunk.nbytes} bytes")

300

301

# Stream processing of large files

302

chunk_size = 10000 # Process 10k samples at a time

303

total_samples = rawio.get_signal_size(0, 0)

304

n_chunks = total_samples // chunk_size

305

306

processed_data = []

307

for i in range(n_chunks):

308

i_start = i * chunk_size

309

i_stop = min((i + 1) * chunk_size, total_samples)

310

311

chunk = rawio.get_analogsignal_chunk(0, 0, i_start, i_stop)

312

313

# Process chunk (e.g., filter, downsample, etc.)

314

processed_chunk = np.mean(chunk, axis=1) # Example: channel average

315

processed_data.append(processed_chunk)

316

317

final_result = np.concatenate(processed_data)

318

print(f"Processed {len(final_result)} samples")

319

```

320

321

### Spike Data Access

322

323

```python

324

# Access spike data (for formats that support it)

325

if rawio.spike_channels_count() > 0:

326

spike_channels = rawio.header['spike_channels']

327

328

for unit_index in range(len(spike_channels)):

329

# Get spike timestamps

330

spike_times = rawio.get_spike_timestamps(

331

block_index=0,

332

seg_index=0,

333

unit_index=unit_index

334

)

335

336

print(f"Unit {unit_index}: {len(spike_times)} spikes")

337

338

# Get spike waveforms if available

339

if hasattr(rawio, 'get_spike_raw_waveforms'):

340

waveforms = rawio.get_spike_raw_waveforms(0, 0, unit_index)

341

if waveforms is not None:

342

print(f" Waveform shape: {waveforms.shape}")

343

```

344

345

### Memory-Efficient Analysis

346

347

```python

348

def analyze_signal_statistics(rawio, block_index=0, seg_index=0):

349

"""Compute signal statistics without loading entire file."""

350

351

total_samples = rawio.get_signal_size(block_index, seg_index)

352

n_channels = rawio.signal_channels_count()

353

chunk_size = 50000 # Adjust based on available memory

354

355

# Initialize accumulators

356

running_sum = np.zeros(n_channels)

357

running_sum_squares = np.zeros(n_channels)

358

sample_count = 0

359

360

# Process file in chunks

361

for i_start in range(0, total_samples, chunk_size):

362

i_stop = min(i_start + chunk_size, total_samples)

363

364

chunk = rawio.get_analogsignal_chunk(

365

block_index, seg_index, i_start, i_stop

366

)

367

368

# Update statistics

369

running_sum += np.sum(chunk, axis=0)

370

running_sum_squares += np.sum(chunk**2, axis=0)

371

sample_count += chunk.shape[0]

372

373

# Compute final statistics

374

means = running_sum / sample_count

375

variances = (running_sum_squares / sample_count) - means**2

376

stds = np.sqrt(variances)

377

378

return means, stds

379

380

# Use the function

381

means, stds = analyze_signal_statistics(rawio)

382

print(f"Channel means: {means}")

383

print(f"Channel standard deviations: {stds}")

384

```

385

386

### Real-Time Processing Simulation

387

388

```python

389

def simulate_realtime_processing(rawio, chunk_duration=0.1):

390

"""Simulate real-time processing by reading time-based chunks."""

391

392

sampling_rate = rawio.get_signal_sampling_rate()

393

chunk_samples = int(chunk_duration * sampling_rate)

394

total_samples = rawio.get_signal_size(0, 0)

395

396

print(f"Simulating real-time processing:")

397

print(f" Chunk duration: {chunk_duration}s")

398

print(f" Chunk size: {chunk_samples} samples")

399

print(f" Total duration: {total_samples / sampling_rate:.2f}s")

400

401

for i_start in range(0, total_samples, chunk_samples):

402

i_stop = min(i_start + chunk_samples, total_samples)

403

404

# Read chunk

405

chunk = rawio.get_analogsignal_chunk(0, 0, i_start, i_stop)

406

407

# Simulate processing time

408

import time

409

processing_start = time.time()

410

411

# Example processing: detect threshold crossings

412

threshold = 3 * np.std(chunk, axis=0)

413

crossings = np.sum(np.abs(chunk) > threshold, axis=0)

414

415

processing_time = time.time() - processing_start

416

chunk_time = i_start / sampling_rate

417

418

print(f" t={chunk_time:.1f}s: {crossings.sum()} crossings, "

419

f"processed in {processing_time*1000:.1f}ms")

420

421

simulate_realtime_processing(rawio, chunk_duration=1.0)

422

```

423

424

### Format-Specific Operations

425

426

```python

427

# Axon-specific operations

428

if isinstance(rawio, neo.rawio.AxonRawIO):

429

print("Axon ABF file detected")

430

print(f"Protocol name: {rawio.header.get('protocol', 'Unknown')}")

431

432

# Access ABF-specific metadata

433

if hasattr(rawio, '_axon_info'):

434

axon_info = rawio._axon_info

435

print(f"Episode count: {axon_info.get('lEpisodesPerRun', 'Unknown')}")

436

print(f"Sample interval: {axon_info.get('fADCSampleInterval', 'Unknown')} μs")

437

438

# Blackrock-specific operations

439

elif isinstance(rawio, neo.rawio.BlackrockRawIO):

440

print("Blackrock file detected")

441

442

# Access NEV event data if available

443

if rawio.event_channels_count() > 0:

444

events = rawio.get_event_timestamps(0, 0, 0)

445

print(f"Found {len(events)} digital events")

446

447

# SpikeGLX-specific operations

448

elif isinstance(rawio, neo.rawio.SpikeGLXRawIO):

449

print("SpikeGLX file detected")

450

451

# Access probe geometry information

452

if hasattr(rawio, '_geometry'):

453

geometry = rawio._geometry

454

print(f"Probe has {len(geometry)} recording sites")

455

```

456

457

### Error Handling and Validation

458

459

```python

460

try:

461

rawio = neo.rawio.get_rawio('data_file.ns5')

462

rawio.parse_header()

463

464

except Exception as e:

465

print(f"Failed to open file: {e}")

466

467

# Validate file integrity

468

try:

469

# Test reading small chunk

470

test_chunk = rawio.get_analogsignal_chunk(0, 0, 0, 100)

471

print(f"File validation successful: {test_chunk.shape}")

472

473

except Exception as e:

474

print(f"File validation failed: {e}")

475

476

# Check for required capabilities

477

if rawio.signal_channels_count() == 0:

478

print("Warning: No analog signal channels found")

479

480

if rawio.spike_channels_count() == 0:

481

print("Warning: No spike channels found")

482

483

# Memory usage estimation

484

total_samples = rawio.get_signal_size(0, 0)

485

n_channels = rawio.signal_channels_count()

486

bytes_per_sample = 4 # Assuming float32

487

total_memory = total_samples * n_channels * bytes_per_sample

488

print(f"Full file would require {total_memory / 1e9:.2f} GB memory")

489

```

490

491

## Performance Considerations

492

493

### Memory Management

494

- RawIO avoids creating full Neo objects, reducing memory usage by 5-10x

495

- Use chunked processing for files larger than available RAM

496

- Optimal chunk sizes: 10k-100k samples depending on channel count

497

498

### I/O Efficiency

499

- Channel selection reduces disk I/O and memory allocation

500

- Sequential access patterns are faster than random access

501

- Consider file system caching for repeated access to same regions

502

503

### Format-Specific Optimizations

504

- **ABF files**: Use lazy loading for multi-episode files

505

- **Blackrock files**: Leverage separate .ns files for different sampling rates

506

- **SpikeGLX**: Take advantage of memory-mapped file access

507

- **Binary formats**: Specify exact data types to avoid unnecessary conversions

508

509

## Types

510

511

```python { .api }

512

# RawIO class types

513

RawIOClass = type[BaseRawIO] # Base RawIO class type

514

RawIOInstance = BaseRawIO # RawIO instance type

515

516

# Index and size types

517

BlockIndex = int # Block index in file

518

SegmentIndex = int # Segment index within block

519

ChannelIndex = int # Channel index

520

SampleIndex = int # Sample index

521

ChannelIndexes = list[int] | slice # Channel selection

522

SampleRange = tuple[int, int] # Sample start and stop indices

523

524

# Data chunk types

525

SignalChunk = np.ndarray # Signal data chunk (samples x channels)

526

SpikeTimestamps = np.ndarray # Spike timestamp array

527

SpikeWaveforms = np.ndarray # Spike waveform data (spikes x samples x channels)

528

EventData = np.ndarray # Event timestamp and code data

529

530

# Header and metadata types

531

HeaderDict = dict[str, Any] # Parsed header information

532

ChannelInfo = dict[str, Any] # Channel metadata

533

SamplingRate = float # Sampling rate in Hz

534

SignalUnits = str # Physical units of signal

535

536

# File format types

537

FileName = str # File path string

538

DirectoryName = str # Directory path string (for multi-file formats)

539

FileExtension = str # File extension string

540

```