Skip to content

Commit a30ff5e

Browse files
author
Chen Xie
committed
more flat
1 parent af64b44 commit a30ff5e

File tree

2 files changed

+125
-69
lines changed

2 files changed

+125
-69
lines changed

devtests.ipynb

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,26 @@
99
},
1010
{
1111
"cell_type": "code",
12-
"execution_count": null,
12+
"execution_count": 1,
1313
"metadata": {
1414
"collapsed": false
1515
},
16-
"outputs": [],
16+
"outputs": [
17+
{
18+
"name": "stdout",
19+
"output_type": "stream",
20+
"text": [
21+
"[[ 988]\n",
22+
" [ 990]\n",
23+
" [ 989]\n",
24+
" ..., \n",
25+
" [ 951]\n",
26+
" [ 957]\n",
27+
" [1024]]\n",
28+
"649886\n"
29+
]
30+
}
31+
],
1732
"source": [
1833
"import numpy as np\n",
1934
"from wfdb import rdsamp\n",
@@ -138,11 +153,38 @@
138153
},
139154
{
140155
"cell_type": "code",
141-
"execution_count": null,
156+
"execution_count": 3,
142157
"metadata": {
143158
"collapsed": false
144159
},
145-
"outputs": [],
160+
"outputs": [
161+
{
162+
"name": "stdout",
163+
"output_type": "stream",
164+
"text": [
165+
"[[ nan nan]\n",
166+
" [ nan nan]\n",
167+
" [ nan nan]\n",
168+
" ..., \n",
169+
" [-21. nan]\n",
170+
" [-21. nan]\n",
171+
" [-20. nan]]\n",
172+
"(10000, 2)\n",
173+
"\n",
174+
"\n",
175+
"\n",
176+
"{'nsampseg': [0, 3261, 6750, 125, 1354, 512, 906259, 2604, 1304941, 1280, 512, 2210767, 172500, 5760000, 157500, 3900000, 4425, 18075, 7500, 37500, 22500], 'nsig': 4, 'fmt': [], 'fs': 125.0, 'nseg': 21, 'nsamp': 14518365, 'signame': [], 'units': [], 'skew': [], 'baseline': [], 'sampsperframe': [], 'initvalue': [], 'gain': [], 'comments': [' <age>: 60 <sex>: F'], 'basedate': '10/10/2896', 'filename': ['3975656_layout', '~', '3975656_0001', '3975656_0002', '3975656_0003', '3975656_0004', '3975656_0005', '~', '3975656_0006', '3975656_0007', '3975656_0008', '3975656_0009', '~', '3975656_0010', '~', '3975656_0011', '3975656_0012', '3975656_0013', '3975656_0014', '3975656_0015', '3975656_0016'], 'byteoffset': [], 'basetime': '00:31:25.894'}\n",
177+
"\n",
178+
"\n",
179+
"\n",
180+
"{'nsampseg': [], 'nsig': 4, 'fmt': ['0', '0', '0', '0'], 'fs': 125.0, 'nseg': 1, 'nsamp': 0, 'signame': ['II', 'V', 'MCL1', 'ABP'], 'units': ['mV', 'mV', 'mV', 'mmHg'], 'skew': [0, 0, 0, 0], 'baseline': [0, 0, 0, 0], 'sampsperframe': [1, 1, 1, 1], 'initvalue': [-16384, -8192, -1024, -256], 'gain': [83.0, 55.0, 122.0, 1.0], 'comments': [], 'basedate': '', 'filename': ['~', '~', '~', '~'], 'byteoffset': [0, 0, 0, 0], 'basetime': '31:25.894'}\n",
181+
"\n",
182+
"\n",
183+
"\n",
184+
"['Empty Segment', {'nsampseg': [], 'nsig': 1, 'fmt': ['80', 'No Channel'], 'fs': 125.0, 'nseg': 1, 'nsamp': 6750, 'signame': ['V', 'No Channel'], 'units': ['mV', 'No Channel'], 'skew': [0, 'No Channel'], 'baseline': [0, 'No Channel'], 'sampsperframe': [1, 'No Channel'], 'initvalue': [0, 'No Channel'], 'gain': [1.0, 'No Channel'], 'comments': [], 'basedate': '', 'filename': ['3975656_0001.dat', 'No Channel'], 'byteoffset': [0, 'No Channel'], 'basetime': '31:51.982'}]\n"
185+
]
186+
}
187+
],
146188
"source": [
147189
"# Testing rdsamp multi-segment variable layout record - stacksegments\n",
148190
"import numpy as np\n",
@@ -214,7 +256,7 @@
214256
},
215257
{
216258
"cell_type": "code",
217-
"execution_count": 1,
259+
"execution_count": 2,
218260
"metadata": {
219261
"collapsed": false
220262
},

wfdb/_rdsamp.py

Lines changed: 78 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -859,61 +859,9 @@ def loadconfig(fn):
859859
pass
860860
return config
861861

862-
def rdsamp(
863-
recordname,
864-
sampfrom=0,
865-
sampto=[],
866-
channels=[],
867-
physical=1,
868-
stacksegments=1):
869-
"""Read a WFDB record and return the signal as a numpy array and the metadata as a dictionary.
870-
871-
Usage:
872-
sig, fields = rdsamp(recordname, sampfrom, sampto, channels, physical, stacksegments)
873-
874-
Input arguments:
875-
- recordname (required): The name of the WFDB record to be read (without any file extensions).
876-
- sampfrom (default=0): The starting sample number to read for each channel.
877-
- sampto (default=length of entire signal): The final sample number to read for each channel.
878-
- channels (default=all channels): Indices specifying the channel to be returned.
879-
- physical (default=1): Flag that specifies whether to return signals in physical (1) or digital (0) units.
880-
- stacksegments (default=1): Flag used only for multi-segment files. Specifies whether to return the signal as a single stacked/concatenated numpy array (1) or as a list of one numpy array for each segment (0).
881-
882-
883-
Output variables:
884-
- sig: An nxm numpy array where n is the signal length and m is the number of channels.
885-
If the input record is a multi-segment record, depending on the input stacksegments flag,
886-
sig will either be a single stacked/concatenated numpy array (1) or a list of one numpy
887-
array for each segment (0). For empty segments, stacked format will contain Nan values,
888-
and non-stacked format will contain a single integer specifying the length of the empty segment.
889-
- fields: A dictionary of metadata about the record extracted or deduced from the header/signal file.
890-
If the input record is a multi-segment record, the output argument will be a list of dictionaries:
891-
: The first list element will be a dictionary of metadata about the master header.
892-
: If the record is in variable layout format, the next list element will be a dictionary
893-
of metadata about the layout specification header.
894-
: The last list element will be a list of dictionaries of metadata for each segment.
895-
For empty segments, the dictionary will be replaced by a single string: 'Empty Segment'
896-
"""
897-
898-
filestoremove = []
899-
config = loadconfig('wfdb.config')
900-
901-
if config.get('pbdownload','getpbfiles') == 1: # Flag specifying whether to allow downloading from physiobank
902-
recordname, dledfiles = checkrecordfiles(recordname, os.getcwd())
903-
904-
if int(config.get('pbdownload','keepdledfiles')) == 0: # Flag specifying whether to keep downloaded physiobank files
905-
filestoremove = dledfiles
906-
907-
fields = readheader(recordname) # Get the info from the header file
908862

909-
if fields["nsig"] == 0:
910-
sys.exit("This record has no signals. Use rdann to read annotations")
911-
if sampfrom < 0:
912-
sys.exit("sampfrom must be non-negative")
913-
dirname, baserecordname = os.path.split(recordname)
914-
915-
if fields["nseg"] == 1: # single segment file
916-
if (len(set(fields["filename"])) ==
863+
def processsegment(fields, dirname, baserecordname, sampfrom, sampto, channels, physical):
864+
if (len(set(fields["filename"])) ==
917865
1): # single dat (or binary) file in the segment
918866
# Signal length was not specified in the header, calculate it from
919867
# the file size.
@@ -967,8 +915,8 @@ def rdsamp(
967915
sig = np.subtract(sig, np.array(
968916
[float(i) for i in fields["baseline"]]))
969917
sig = np.divide(sig, np.array([fields["gain"]]))
970-
971-
else: # Multiple dat files in the segment. Read different dats and merge them channel wise.
918+
919+
else: # Multiple dat files in the segment. Read different dats and merge them channel wise.
972920

973921
if not channels: # Default all channels
974922
channels = list(range(0, fields["nsig"]))
@@ -1055,15 +1003,12 @@ def rdsamp(
10551003
fields["fmt"][ch]], ch] = np.nan
10561004
sig = np.subtract(sig, np.array(
10571005
[float(b) for b in fields["baseline"]]))
1058-
sig = np.divide(sig, np.array([fields["gain"]]))
1006+
sig = np.divide(sig, np.array([fields["gain"]]))
1007+
1008+
return sig, fields
10591009

1060-
# Multi-segment file. Preprocess and recursively call rdsamp on single
1061-
# segments.
1062-
else:
1063-
1064-
# Determine if this record is fixed or variable layout. startseg is the
1065-
# first signal segment.
1066-
if fields["nsampseg"][
1010+
def fixedorvariable:
1011+
if fields["nsampseg"][
10671012
0] == 0: # variable layout - first segment is layout specification file
10681013
startseg = 1
10691014
# Store the layout header info.
@@ -1073,6 +1018,75 @@ def rdsamp(
10731018
fields["filename"][0]))
10741019
else: # fixed layout - no layout specification file.
10751020
startseg = 0
1021+
layoutfields=[]
1022+
return startseg, layoutfields
1023+
1024+
def rdsamp(
1025+
recordname,
1026+
sampfrom=0,
1027+
sampto=[],
1028+
channels=[],
1029+
physical=1,
1030+
stacksegments=1):
1031+
"""Read a WFDB record and return the signal as a numpy array and the metadata as a dictionary.
1032+
1033+
Usage:
1034+
sig, fields = rdsamp(recordname, sampfrom, sampto, channels, physical, stacksegments)
1035+
1036+
Input arguments:
1037+
- recordname (required): The name of the WFDB record to be read (without any file extensions).
1038+
- sampfrom (default=0): The starting sample number to read for each channel.
1039+
- sampto (default=length of entire signal): The final sample number to read for each channel.
1040+
- channels (default=all channels): Indices specifying the channel to be returned.
1041+
- physical (default=1): Flag that specifies whether to return signals in physical (1) or digital (0) units.
1042+
- stacksegments (default=1): Flag used only for multi-segment files. Specifies whether to return the signal as a single stacked/concatenated numpy array (1) or as a list of one numpy array for each segment (0).
1043+
1044+
1045+
Output variables:
1046+
- sig: An nxm numpy array where n is the signal length and m is the number of channels.
1047+
If the input record is a multi-segment record, depending on the input stacksegments flag,
1048+
sig will either be a single stacked/concatenated numpy array (1) or a list of one numpy
1049+
array for each segment (0). For empty segments, stacked format will contain Nan values,
1050+
and non-stacked format will contain a single integer specifying the length of the empty segment.
1051+
- fields: A dictionary of metadata about the record extracted or deduced from the header/signal file.
1052+
If the input record is a multi-segment record, the output argument will be a list of dictionaries:
1053+
: The first list element will be a dictionary of metadata about the master header.
1054+
: If the record is in variable layout format, the next list element will be a dictionary
1055+
of metadata about the layout specification header.
1056+
: The last list element will be a list of dictionaries of metadata for each segment.
1057+
For empty segments, the dictionary will be replaced by a single string: 'Empty Segment'
1058+
"""
1059+
1060+
filestoremove = []
1061+
config = loadconfig('wfdb.config')
1062+
1063+
if config.get('pbdownload','getpbfiles') == 1: # Flag specifying whether to allow downloading from physiobank
1064+
recordname, dledfiles = checkrecordfiles(recordname, os.getcwd())
1065+
1066+
if int(config.get('pbdownload','keepdledfiles')) == 0: # Flag specifying whether to keep downloaded physiobank files
1067+
filestoremove = dledfiles
1068+
1069+
1070+
fields = readheader(recordname) # Get the info from the header file
1071+
1072+
1073+
if fields["nsig"] == 0:
1074+
sys.exit("This record has no signals. Use rdann to read annotations")
1075+
if sampfrom < 0:
1076+
sys.exit("sampfrom must be non-negative")
1077+
dirname, baserecordname = os.path.split(recordname)
1078+
1079+
1080+
if fields["nseg"] == 1: # single segment file
1081+
sig, fields = processsegment(fields, dirname, baserecordname, sampfrom, sampto, channels, physical)
1082+
1083+
# Multi-segment file. Preprocess and recursively call rdsamp on single
1084+
# segments.
1085+
else:
1086+
1087+
# Determine if this record is fixed or variable layout. startseg is the
1088+
# first signal segment.
1089+
startseg, layoutfields = fixedorvariable(fields)
10761090

10771091
# Determine the segments and samples that have to be read based on
10781092
# sampfrom and sampto

0 commit comments

Comments
 (0)