Skip to content

Commit 0f19848

Browse files
committed
rewrite checkfield for signal specs
1 parent 6eda4ff commit 0f19848

File tree

6 files changed

+404
-213
lines changed

6 files changed

+404
-213
lines changed

devtests.ipynb

Lines changed: 126 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,53 +9,145 @@
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+
"Help on function __init__ in module wfdb.records:\n",
22+
"\n",
23+
"__init__(self, p_signals=None, d_signals=None, recordname=None, nsig=None, fs=None, counterfreq=None, basecounter=None, siglen=None, basetime=None, basedate=None, filename=None, fmt=None, sampsperframe=None, skew=None, byteoffset=None, adcgain=None, baseline=None, units=None, adcres=None, adczero=None, initvalue=None, checksum=None, blocksize=None, signame=None, comments=None)\n",
24+
" Initialize self. See help(type(self)) for accurate signature.\n",
25+
"\n"
26+
]
27+
}
28+
],
1729
"source": [
18-
"import wfdb"
30+
"import wfdb\n",
31+
"\n",
32+
"help(wfdb.Record.__init__)"
1933
]
2034
},
2135
{
2236
"cell_type": "code",
23-
"execution_count": null,
37+
"execution_count": 1,
2438
"metadata": {
25-
"collapsed": false
39+
"collapsed": false,
40+
"scrolled": true
2641
},
27-
"outputs": [],
42+
"outputs": [
43+
{
44+
"name": "stdout",
45+
"output_type": "stream",
46+
"text": [
47+
"checking field: p_signals\n",
48+
"inner ch: None\n",
49+
"checking field: fmt\n",
50+
"inner ch: None\n"
51+
]
52+
},
53+
{
54+
"ename": "TypeError",
55+
"evalue": "list indices must be integers or slices, not NoneType",
56+
"output_type": "error",
57+
"traceback": [
58+
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
59+
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
60+
"\u001b[0;32m<ipython-input-1-e7b74ff452fd>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0msig\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfields\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwfdb\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msrdsamp\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'a103l'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msampfrom\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m50000\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mchannels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpbdir\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'challenge/2015/training'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;31m# Write a local WFDB record (manually inserting fields)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mwfdb\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwrsamp\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'ecgrecord'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m250\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0munits\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'mV'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'mV'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msignames\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'I'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'II'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mp_signals\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msig\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfmt\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'16'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'16'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
61+
"\u001b[0;32m/home/chen/Projects/wfdb-python/wfdb/records.py\u001b[0m in \u001b[0;36mwrsamp\u001b[0;34m(recordname, fs, units, signames, p_signals, d_signals, fmt, gain, baseline, comments)\u001b[0m\n\u001b[1;32m 944\u001b[0m signame = signames, adcgain = gain, baseline = baseline, comments = comments)\n\u001b[1;32m 945\u001b[0m \u001b[0;31m# Compute optimal fields to store the digital signal, carry out adc, and set the fields.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 946\u001b[0;31m \u001b[0mrecord\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_d_features\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdo_adc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 947\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 948\u001b[0m \u001b[0;31m# Create the Record object\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
62+
"\u001b[0;32m/home/chen/Projects/wfdb-python/wfdb/_signals.py\u001b[0m in \u001b[0;36mset_d_features\u001b[0;34m(self, do_adc, singlefmt)\u001b[0m\n\u001b[1;32m 123\u001b[0m \u001b[0;31m# If there is a fmt set\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 124\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 125\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcheckfield\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'fmt'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 126\u001b[0m \u001b[0;31m# Neither field set\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 127\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madcgain\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbaseline\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
63+
"\u001b[0;32m/home/chen/Projects/wfdb-python/wfdb/records.py\u001b[0m in \u001b[0;36mcheckfield\u001b[0;34m(self, field, ch)\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 46\u001b[0m \u001b[0;31m# Check the type of the field (and of its elements if it is to be a list)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 47\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcheckfieldtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfield\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mch\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 48\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 49\u001b[0m \u001b[0;31m# Individual specific field checks:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
64+
"\u001b[0;32m/home/chen/Projects/wfdb-python/wfdb/records.py\u001b[0m in \u001b[0;36mcheckfieldtype\u001b[0;34m(self, field, ch)\u001b[0m\n\u001b[1;32m 195\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mfield\u001b[0m \u001b[0;32min\u001b[0m \u001b[0m_headers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msigfieldspecs\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 196\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcheckfieldlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfield\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 197\u001b[0;31m \u001b[0mcheckitemtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfield\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mch\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfield\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_headers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msigfieldspecs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mfield\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mallowedtypes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 198\u001b[0m \u001b[0;31m# Segment specification field. List. elements cannot be None\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 199\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mfield\u001b[0m \u001b[0;32min\u001b[0m \u001b[0m_headers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msegfieldspecs\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
65+
"\u001b[0;31mTypeError\u001b[0m: list indices must be integers or slices, not NoneType"
66+
]
67+
}
68+
],
2869
"source": [
29-
"# Testing wrann\n",
3070
"import wfdb\n",
31-
"from IPython.display import display\n",
32-
"\n",
33-
"annsamp = [20, 40, 60, 80, 100, 800, 1200 , 3000, 3001, 8000, 100000]\n",
34-
"#annsamp = [20, 40, 60, 80, 100, 800, 1200]\n",
35-
"#annsamp = [20, 60, 2**31] # 2**31+100 is too large\n",
36-
"\n",
37-
"nsamp = len(annsamp)\n",
38-
"\n",
39-
"anntype = ['N', 'N', 'N', '3', 'N', '2', 'N', '1', 'N', 'N', 'g']\n",
40-
"sub = [1,0,0,0,5,3,2,5,0,0,0]\n",
41-
"chan = [2,2,3,2,9,2,2,2,2,2,2]\n",
42-
"num = [1,2,3,4,5,3,2,5,11,22,22]\n",
43-
"aux = [None, None, \"good\", \"good\", \"good\", \"good\", \"good\", \"good\", None, None, None]\n",
44-
"fs = 100\n",
45-
"fs = None\n",
46-
"\n",
47-
"a = wfdb.Annotation(recordname = 'firstann', annotator = 'atr', \n",
48-
" annsamp = annsamp, anntype = anntype, aux = aux, \n",
49-
" subtype = sub, chan = chan, num = num, fs = fs)\n",
50-
"a.type2aux()\n",
51-
"display(a.__dict__)\n",
52-
"a.wrann()\n",
53-
"\n",
54-
"b = wfdb.rdann('firstann', 'atr')\n",
55-
"b.__dict__\n",
56-
"\n",
71+
"# Read part of a record from Physiobank\n",
72+
"sig, fields = wfdb.srdsamp('a103l', sampfrom = 50000, channels = [0,1], pbdir = 'challenge/2015/training')\n",
73+
"# Write a local WFDB record (manually inserting fields)\n",
74+
"wfdb.wrsamp('ecgrecord', fs = 250, units = ['mV', 'mV'], signames = ['I', 'II'], p_signals = sig, fmt = ['16', '16'])"
75+
]
76+
},
77+
{
78+
"cell_type": "code",
79+
"execution_count": 3,
80+
"metadata": {
81+
"collapsed": false
82+
},
83+
"outputs": [
84+
{
85+
"data": {
86+
"text/plain": [
87+
"array([[-0.0709259 , 0.8128327 ],\n",
88+
" [-0.06733821, 0.81872624],\n",
89+
" [-0.06292259, 0.82851711],\n",
90+
" ..., \n",
91+
" [-0.04084449, 0.7493346 ],\n",
92+
" [-0.04719194, 0.7581749 ],\n",
93+
" [-0.04677798, 0.7615019 ]])"
94+
]
95+
},
96+
"execution_count": 3,
97+
"metadata": {},
98+
"output_type": "execute_result"
99+
}
100+
],
101+
"source": [
102+
"sig"
103+
]
104+
},
105+
{
106+
"cell_type": "code",
107+
"execution_count": 4,
108+
"metadata": {
109+
"collapsed": false
110+
},
111+
"outputs": [
112+
{
113+
"data": {
114+
"text/plain": [
115+
"{'comments': ['Asystole', 'False alarm'],\n",
116+
" 'fs': 250.0,\n",
117+
" 'signame': ['II', 'V'],\n",
118+
" 'units': ['mV', 'mV']}"
119+
]
120+
},
121+
"execution_count": 4,
122+
"metadata": {},
123+
"output_type": "execute_result"
124+
}
125+
],
126+
"source": [
57127
"\n",
58-
"# print([sd & 255, sd & 768, (sd & 768) >> 8])"
128+
"fields"
129+
]
130+
},
131+
{
132+
"cell_type": "code",
133+
"execution_count": 2,
134+
"metadata": {
135+
"collapsed": false
136+
},
137+
"outputs": [
138+
{
139+
"data": {
140+
"text/plain": [
141+
"[0, 1, 2]"
142+
]
143+
},
144+
"execution_count": 2,
145+
"metadata": {},
146+
"output_type": "execute_result"
147+
}
148+
],
149+
"source": [
150+
"list(range(len([1,2,3])))"
59151
]
60152
},
61153
{

tests/test_annotations.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def test_1(self):
5656
pbannotation = wfdb.rdann('100', 'atr', pbdir = 'mitdb')
5757

5858
# Test file writing
59-
annotation.wrsamp()
59+
annotation.wrann()
6060
annotationwrite = wfdb.rdann('100', 'atr')
6161

6262
assert (comp == [True] * 6)
@@ -106,7 +106,7 @@ def test_2(self):
106106
pbannotation = wfdb.rdann('12726', 'anI', pbdir = 'prcp')
107107

108108
# Test file writing
109-
annotation.wrsamp()
109+
annotation.wrann()
110110
annotationwrite = wfdb.rdann('12726', 'anI')
111111

112112
assert (comp == [True] * 6)

wfdb/_headers.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ def setdefaults(self):
2424

2525
# Helper function for getwritefields
2626
# specfields is the set of specification fields
27+
# For record specs, it returns a list of all fields needed.
28+
# For signal specs, it returns a dictionary of all fields needed,
29+
# with keys = field and value = list of 1 or 0 indicating channel for the field
2730
def getwritesubset(self, specfields):
2831

2932
# record specification fields
@@ -50,7 +53,10 @@ def getwritesubset(self, specfields):
5053

5154
# signal spec field. Need to return a potentially different list for each channel.
5255
elif specfields == 'signal':
56+
# List of lists for each channel
5357
writefields=[]
58+
59+
allwritefields=[]
5460
fieldspecs = OrderedDict(reversed(list(sigfieldspecs.items())))
5561

5662
for ch in range(self.nsig):
@@ -71,6 +77,21 @@ def getwritesubset(self, specfields):
7177
rf=fieldspecs[rf].dependency
7278

7379
writefields.append(writefieldsch)
80+
81+
# Convert the list of lists to a single dictionary.
82+
# keys = field and value = list of 1 or 0 indicating channel for the field
83+
dictwritefields = {}
84+
85+
# For fields present in any channel:
86+
for f in set([i for wsub in writefields for i in wsub]):
87+
dictwritefields[f] = [0]*self.nsig
88+
89+
for ch in range(self.nsig):
90+
if f in writefields[ch]:
91+
dictwritefields[f][ch] = 1
92+
93+
writefields = dictwritefields
94+
7495

7596
return writefields
7697

@@ -91,13 +112,12 @@ def wrheader(self):
91112
for f in recwritefields:
92113
self.checkfield(f)
93114

94-
# Signal specification fields. Check by channel.
95-
for ch in range(self.nsig):
96-
for f in sigwritefields[ch]:
97-
self.checkfield(f, ch)
115+
# Signal specification fields.
116+
for f in sigwritefields:
117+
self.checkfield(f, sigwritefields[f])
98118

99119
# Check the cohesion of fields used to write the header
100-
self.checkfieldcohesion(recwritefields, sigwritefields)
120+
self.checkfieldcohesion(recwritefields, list(sigwritefields))
101121

102122
# Write the header file using the specified fields
103123
self.wrheaderfile(recwritefields, sigwritefields)
@@ -168,20 +188,14 @@ def checkfieldcohesion(self, recwritefields, sigwritefields):
168188

169189
# If there are no signal specification fields, there is nothing to check.
170190
if self.nsig>0:
171-
# Get the set of signal spec fields used in any of the channels
172-
allsigwritefields = []
173-
for s in sigwritefields:
174-
allsigwritefields += s
175-
176-
allsigwritefields = list(set(allsigwritefields))
177191

178192
# The length of all signal specification fields must match nsig
179193
# even if some of its elements are None.
180-
for f in allsigwritefields:
194+
for f in sigwritefields:
181195
if len(getattr(self, f)) != self.nsig:
182-
sys.exit('The length of field: '+f+' does not match field nsig.')
196+
sys.exit('The length of field: '+f+' must match field nsig.')
183197

184-
# Each filename must correspond to only one fmt, and only one byte offset (if defined).
198+
# Each filename must correspond to only one fmt, (and only one byte offset if defined).
185199
datfmts = {}
186200
for ch in range(self.nsig):
187201
if self.filename[ch] not in datfmts:

0 commit comments

Comments
 (0)