Skip to content

Commit bdfce16

Browse files
committed
add test records and update documentation
1 parent 16eeea6 commit bdfce16

File tree

6 files changed

+124
-18
lines changed

6 files changed

+124
-18
lines changed

README.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ Input Arguments:
155155
- ``physical`` (default=True): Flag that specifies whether to return signals in physical (True) or digital (False) units.
156156
- ``pbdir`` (default=None): Option used to stream data from Physiobank. The Physiobank database directory from which to find the required record files. eg. For record '100' in 'http://physionet.org/physiobank/database/mitdb', pbdir = 'mitdb'.
157157
- ``m2s`` (default=True): Flag used only for multi-segment records. Specifies whether to convert the returned wfdb.MultiRecord object into a wfdb.Record object (True) or not (False).
158+
- ``smoothframes`` (default=True): Flag used when reading records with signals having multiple samples per frame. Specifies whether to smooth the samples in signals with more than one sample per frame and return an mxn uniform numpy array as the d_signals or p_signals field (True), or to return a list of 1d numpy arrays containing every expanded sample as the e_d_signals or e_p_signals field (False).
158159

159160
Output Arguments:
160161

demo.ipynb

Lines changed: 98 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"cell_type": "markdown",
55
"metadata": {},
66
"source": [
7-
"## Demo Scripts for the wfdb-python package\n",
7+
"# Demo Scripts for the wfdb-python package\n",
88
"\n",
99
"Run this script from the base directory of the git repository to access the included demo files"
1010
]
@@ -23,11 +23,26 @@
2323
"from IPython.display import display"
2424
]
2525
},
26+
{
27+
"cell_type": "code",
28+
"execution_count": null,
29+
"metadata": {
30+
"collapsed": true
31+
},
32+
"outputs": [],
33+
"source": [
34+
"# See the help documentation for the read functions\n",
35+
"\n",
36+
"#help(wfdb.rdsamp)\n",
37+
"#help(wfdb.srdsamp)\n",
38+
"#help(wfdb.rdann)"
39+
]
40+
},
2641
{
2742
"cell_type": "markdown",
2843
"metadata": {},
2944
"source": [
30-
"### Reading Records and Annotations"
45+
"## Reading Records and Annotations"
3146
]
3247
},
3348
{
@@ -169,7 +184,32 @@
169184
"cell_type": "markdown",
170185
"metadata": {},
171186
"source": [
172-
"### Writing Records and Annotations"
187+
"### Multiple sample/frame examples\n",
188+
"\n",
189+
"Although there can only be one base sampling frequency per record, a single wfdb record can store multiple channels with different sampling frequencies, as long as their sampling frequencies can all be expressed by an integer multiple of a base value. This is done by using the `sampsperframe` attribute in each channel, which indicates the number of samples of each channel present in each frame.\n",
190+
"\n",
191+
"ie: To capture three signals with `fs = 120, 240, and 360 Hz` in a single record, they can be combined into a record with `fs = 120` and `sampsperframe = [1, 2, 3]`.\n",
192+
"\n",
193+
"#### Reading Options\n",
194+
"\n",
195+
"This package allows signals in records with multiple samples/frame to be read in two ways:\n",
196+
"1. smoothed - An uniform mxn numpy is returned as the d_signals or p_signals field. Channels with multiple samples/frame have their values averaged within each frame. This is like the behaviour of the `rdsamp` function of the original WFDB c package. Note that `wfdb.plotrec` only works if the record object has the `p_signals` field.\n",
197+
"2. expanded - A list of 1d numpy arrays is returned as the e_d_signals or e_p_signals field. All samples for each channel are returned in its respective numpy array. The arrays may have different lengths depending on their `sampsperframe` values. \n",
198+
"\n",
199+
"Set the `smoothframes` *(default=True)* option in `rdsamp` to return the desired signal type."
200+
]
201+
},
202+
{
203+
"cell_type": "code",
204+
"execution_count": null,
205+
"metadata": {
206+
"collapsed": false
207+
},
208+
"outputs": [],
209+
"source": [
210+
"# Demo 8 - Read a wfdb record in which one channel has multiple samples/frame. Return a smoothed uniform array.\n",
211+
"record = wfdb.rdsamp('sampledata/test01_00s_frame')\n",
212+
"wfdb.plotrec(record)"
173213
]
174214
},
175215
{
@@ -180,7 +220,32 @@
180220
},
181221
"outputs": [],
182222
"source": [
183-
"# Demo 8 - Read a WFDB record's digital samples and create a copy via the wrsamp() instance method \n",
223+
"# Demo 9 - Read a wfdb record in which one channel has multiple samples/frame. Return a list of all the expanded samples.\n",
224+
"record = wfdb.rdsamp('sampledata/test01_00s_frame', smoothframes = False)\n",
225+
"\n",
226+
"display(record.e_p_signals)\n",
227+
"# Show that different channels have different lengths. Channel 1 has 2 samples/frame, hence has 2x as many samples.\n",
228+
"print([len(s) for s in record.e_p_signals])\n",
229+
"\n",
230+
"# wfdb.plotrec doesn't work because the Record object is missing its p_signals field."
231+
]
232+
},
233+
{
234+
"cell_type": "markdown",
235+
"metadata": {},
236+
"source": [
237+
"## Writing Records and Annotations"
238+
]
239+
},
240+
{
241+
"cell_type": "code",
242+
"execution_count": null,
243+
"metadata": {
244+
"collapsed": false
245+
},
246+
"outputs": [],
247+
"source": [
248+
"# Demo 10 - Read a WFDB record's digital samples and create a copy via the wrsamp() instance method \n",
184249
"# of the Record object.\n",
185250
"\n",
186251
"# Read a record as a Record object.\n",
@@ -202,7 +267,7 @@
202267
},
203268
"outputs": [],
204269
"source": [
205-
"# Demo 9 - Write a WFDB record without using a Record object via the gateway wrsamp function.\n",
270+
"# Demo 11 - Write a WFDB record without using a Record object via the gateway wrsamp function.\n",
206271
"# This is the basic way to write physical signals to a WFDB file. \n",
207272
"\n",
208273
"# Read part of a record from Physiobank\n",
@@ -223,7 +288,28 @@
223288
},
224289
"outputs": [],
225290
"source": [
226-
"# Demo 10 - Read a WFDB annotation file and create a copy via the wrann() instance method\n",
291+
"# Demo 12 - Write a WFDB record with multiple samples/frame in a channel\n",
292+
"\n",
293+
"# Read a record as a Record object.\n",
294+
"record = wfdb.rdsamp('sampledata/test01_00s_frame', physical = False, smoothframes=False)\n",
295+
"record.recordname = 'test01_00s_framex'\n",
296+
"\n",
297+
"# Call the instance method of the object with expanded=True to write the record using the e_d_signals field\n",
298+
"record.wrsamp(expanded=True)\n",
299+
"\n",
300+
"# The new file can be read\n",
301+
"recordx = wfdb.rdsamp('test01_00s_framex')"
302+
]
303+
},
304+
{
305+
"cell_type": "code",
306+
"execution_count": null,
307+
"metadata": {
308+
"collapsed": false
309+
},
310+
"outputs": [],
311+
"source": [
312+
"# Demo 13 - Read a WFDB annotation file and create a copy via the wrann() instance method\n",
227313
"# of the Annotation object\n",
228314
"\n",
229315
"# Read an annotation from Physiobank\n",
@@ -245,7 +331,7 @@
245331
},
246332
"outputs": [],
247333
"source": [
248-
"# Demo 11 - Write a WFDB annotation file without using an Annotator object via the gateway wrann function.\n",
334+
"# Demo 14 - Write a WFDB annotation file without using an Annotator object via the gateway wrann function.\n",
249335
"\n",
250336
"# Read an annotation as an Annotation object\n",
251337
"annotation = wfdb.rdann('b001', 'atr', pbdir='cebsdb')\n",
@@ -265,15 +351,15 @@
265351
},
266352
"outputs": [],
267353
"source": [
268-
"# Demo 12 - View what the 'anntype' symbols mean in the standard WFDB library\n",
354+
"# Demo 15 - View what the 'anntype' symbols mean in the standard WFDB library\n",
269355
"wfdb.showanncodes()"
270356
]
271357
},
272358
{
273359
"cell_type": "markdown",
274360
"metadata": {},
275361
"source": [
276-
"### Downloading Content from Physiobank\n",
362+
"## Downloading Content from Physiobank\n",
277363
"\n",
278364
"- The downloads are made via http\n",
279365
"- See the above demos for examples on streaming WFDB files stored in Physiobank without downloading them to local disk\n",
@@ -288,7 +374,7 @@
288374
},
289375
"outputs": [],
290376
"source": [
291-
"# Demo 13 - List the Physiobank Databases\n",
377+
"# Demo 16 - List the Physiobank Databases\n",
292378
"\n",
293379
"dbs = wfdb.getdblist()\n",
294380
"display(dbs)"
@@ -302,7 +388,7 @@
302388
},
303389
"outputs": [],
304390
"source": [
305-
"# Demo 14 - Download all the WFDB records and annotations from a small Physiobank Database\n",
391+
"# Demo 17 - Download all the WFDB records and annotations from a small Physiobank Database\n",
306392
"\n",
307393
"# Make a temporary download directory in your current working directory\n",
308394
"cwd = os.getcwd()\n",
@@ -326,7 +412,7 @@
326412
},
327413
"outputs": [],
328414
"source": [
329-
"# Demo 15 - Download specified files from a Physiobank database\n",
415+
"# Demo 18 - Download specified files from a Physiobank database\n",
330416
"\n",
331417
"# The files to download\n",
332418
"filelist = ['STAFF-Studies-bibliography-2016.pdf', 'data/001a.hea', 'data/001a.dat', 'data/001b.hea', 'data/001b.dat']\n",

sampledata/test01_00s_frame.hea

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
test01_00s_frame 3 500 4000
2+
test01_00s.dat 16 100/mV 16 0 10 114 0 ECG_1
3+
test01_00s.dat 16x2 100/mV 16 0 -8 822 0 ECG_2
4+
test01_00s.dat 16 100/mV 16 0 -57 65135 0 ECG_3
5+
# <age>: 25 <sex>: M <diagnoses>: (none) <medications>: (none)
6+
# This is a copy of test01_00s with 2 samples/frame for channel 1, cutting out channel 3.

sampledata/test01_00s_skew.hea

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
test01_00s_skew 4 500 4000
2+
test01_00s.dat 16 100/mV 16 0 10 114 0 ECG_1
3+
test01_00s.dat 16:3 100/mV 16 0 -8 941 0 ECG_2
4+
test01_00s.dat 16 100/mV 16 0 -57 -119 0 ECG_3
5+
test01_00s.dat 16 100/mV 16 0 -66 -401 0 ECG_4
6+
# <age>: 25 <sex>: M <diagnoses>: (none) <medications>: (none)
7+
# This is a copy of test01_00s with a skew stated in channel 1.

sampledata/test01_00s_skewframe.hea

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
test01_00s_skewframe 3 500 4000
2+
test01_00s.dat 16:3 100/mV 16 0 10 114 0 ECG_1
3+
test01_00s.dat 16x2 100/mV 16 0 -8 822 0 ECG_2
4+
test01_00s.dat 16 100/mV 16 0 -57 65135 0 ECG_3
5+
# <age>: 25 <sex>: M <diagnoses>: (none) <medications>: (none)
6+
# This is a copy of test01_00s with 2 samples/frame for channel 1, cutting out channel 3. A skew is also stated in signal 0.

wfdb/records.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -431,14 +431,15 @@ def arrangefields(self, channels, expanded=False):
431431

432432
# Expanded signals - multiple samples per frame.
433433
if expanded:
434-
# clear checksum and initvalue for now
435-
self.checksum = [None]*len(self.e_d_signals)
436-
self.initvalue = [None]*len(self.e_d_signals)
434+
# Checksum and initvalue to be updated if present
435+
# unless the whole signal length was input
436+
if self.siglen != int(len(self.e_d_signals[0])/self.sampsperframe[0]):
437+
self.checksum = self.calc_checksum(expanded)
438+
self.initvalue = [s[0] for s in self.e_d_signals]
437439

438440
self.nsig = len(channels)
439441
self.siglen = int(len(self.e_d_signals[0])/self.sampsperframe[0])
440442

441-
442443
# MxN numpy array d_signals
443444
else:
444445
# Checksum and initvalue to be updated if present
@@ -458,7 +459,6 @@ def arrangefields(self, channels, expanded=False):
458459

459460

460461

461-
462462
# Class for multi segment WFDB records.
463463
class MultiRecord(BaseRecord, _headers.MultiHeadersMixin):
464464
"""
@@ -767,7 +767,7 @@ def rdsamp(recordname, sampfrom=0, sampto=None, channels = None, physical = True
767767
Record object (True).
768768
- smoothframes (default=True): Flag used when reading records with signals having multiple
769769
samples per frame. Specifies whether to smooth the samples in signals with more than
770-
one sample per frame to return an mxn uniform numpy array as the d_signals or p_signals
770+
one sample per frame and return an mxn uniform numpy array as the d_signals or p_signals
771771
field (True), or to return a list of 1d numpy arrays containing every expanded sample as
772772
the e_d_signals or e_p_signals field (False).
773773

0 commit comments

Comments
 (0)