add support for user list stimulus parameters
KBenthall opened this issue ยท 20 comments
I'm trying to plot my stimulus file for a protocol in which I used the 'list' function in Clampex to define current steps, and using sweepC for this plots incorrect step amplitudes. It seems that pyabf is not able to determine the true current step amplitude when 'list' is used in the protocol editor.
@KBenthall Can you attach an example file?
Yes, thank you, would it be possible for me to send via email instead of posting here?
Yes, thank you, would it be possible for me to send via email instead of posting here?
It would really help if you could submit a sample ABF publicly (here) so we can all work on it ๐
Sorry, I'm trying to upload but the .abf and .atf files aren't supported in the comment section. Is there another place I'm supposed to upload instead?
Here's a google drive link in case that's helpful:
https://drive.google.com/drive/folders/1xbwefwMMQ0z_iCLlCjx1KoP6rvHMJArt?usp=sharing
Hi @KBenthall, thanks for the additional information! Unfortunately it looks like you sen that email to pyABF@noreply.github.com
so the ABF never made it.
If you e-mail the ABF to my personal email address swharden@gmail.com
I can upload the ABF here and @t-b and I can take a closer look
For those of us less familiar with what a "user list" is, this is what the manual says:
The User List, on each of the Stimulus tabs, provides a way of customizing one of a range of
analog and digital output features, overriding the generalized settings made elsewhere in the
Edit Protocol dialog. The selected parameter can be set to arbitrary values for each sweep in
a run by entering the desired values in the List of parameter values field. So, for example, rather than having sweep start-to-start times remain constant for every sweep in a run, specific sweep start-to-start times can be set for each sweep. Alternatively, rather than being forced to increase or decrease the duration of a waveform epoch in regular steps with each successive sweep by setting a duration delta value, you can set independent epoch durations for each sweep. All aspects of the conditioning train, the number of subsweeps in P/N Leak Subtraction, and command waveform amplitudes and durations, besides other output features, can be overridden from the User List.
TLDR: the user list is a set of values which can override one feature of the stimulus waveform by manually defining it as a list of numbers
@KBenthall user lists are definitely not supported by pyABF (yet). I've never seen an ABF file which uses this feature. However, your ABF may help us add support for it.
A sample ABF with a user list stimulus property is now available for download: 2020_03_02_0000.abf (Thanks @KBenthall!)
The current step list is expected to be (in pA): [-200, -150, -100, -50, 0, 25, 50, 100, 150, 200, 250, 300, 350, 400, 500, 600]
I took a closer look at the ABF. For what it's worth, the command current is in the second channel so it could be accessed if desired.
abf = pyabf.ABF("2020_03_02_0000.abf")
for sweepNumber in abf.sweepList:
plt.subplot(311)
abf.setSweep(sweepNumber, channel=0)
plt.plot(abf.sweepX, abf.sweepY, color='b', lw=.5)
plt.subplot(313)
plt.plot(abf.sweepX, abf.sweepC, color='r', lw=.5)
plt.subplot(312)
abf.setSweep(sweepNumber, channel=1)
plt.plot(abf.sweepX, abf.sweepY, color='k', lw=.5)
This script produces the following image
which is comparable to clampfit
sweepC indeed looks wrong when plotted with pyABF, but it also looks wrong when "create stimulus waveform signal" is used in clampfit ๐ so it isn't quite as egregious of a bug as I initially thought!
I'll leave this ticket open until I get time to take a closer look at the header composition of the file to see if the user list of values can be retrieved anyway (in cases where perhaps they were not recorded in a second channel).
Ah, this is so great, thank you! I will try it out asap.
-- Katie Benthall Bateup Lab
Sounds good! If you hit any snags let me know.
I have the same issue. sweepC plots the wrong DAC values in my voltage clamp data (the values are all zeros which correspond to the stimulus waveform signal).
Unlike, KBenthall's 'List function' protocol, I voltage clamp the cell directly through my amplifier (Multiclamp 700B) not through my stimulus file, thus the values read zero.
I can see the correct DAC values in the header 'data' 2D array.
I can see the correct DAC values in the header 'data' 2D array.
Hi @IndigeNerd, thanks for posting this! Can you send me a sample ABF demonstrating this? It will make it easier for me to develop against. You can email it to me swharden@gmail.com if it's small enough, otherwise something like dropbox is the way to go.
Thanks!
I can see the correct DAC values in the header 'data' 2D array.
I'm a little confused by this. Can you post a screenshot of what this means (and also to show what the correct values for your ABF should be?) Thanks!
I have the same issue. sweepC plots the wrong DAC values in my voltage clamp data (the values are all zeros which correspond to the stimulus waveform signal).
Unlike, KBenthall's 'List function' protocol, I voltage clamp the cell directly through my amplifier (Multiclamp 700B) not through my stimulus file, thus the values read zero.
I can see the correct DAC values in the header 'data' 2D array.
The solution to this issue was similar to the one @KBenthall found. The command trace is in the second channel, so the solution was to do something like
abf.setSweep(sweepNumber=0, channel=1)
print(abf.sweepY)
multi-channel ABF tutorial:
https://swharden.com/pyabf/tutorial.php#multichannel
For what it's worth, here's how to access the values of the user list programmatically. Note that this just returns a short list of the values, not the command waveform.
def GetUserList(abf):
"""
Return the ABF user list as a list of values,
or return None if the ABF has no user list.
"""
assert isinstance(abf, pyabf.ABF)
if not hasattr(abf, '_stringsSection'):
return None
firstBlock = abf._stringsSection.strings[0]
firstBlockStrings = firstBlock.split(b'\x00')
userList = firstBlockStrings[-2].decode("utf-8")
userList = userList.split(",")
try:
return [float(x) for x in userList if x]
except:
return None
abfWithUserList = pyabf.ABF(PATH_DATA+"/2020_03_02_0000.abf")
abfWithoutUserList = pyabf.ABF(PATH_DATA+"/171116sh_0020.abf")
print(GetUserList(abfWithUserList))
print(GetUserList(abfWithoutUserList))
[-200.0, -150.0, -100.0, -50.0, 0.0, 25.0, 50.0, 100.0, 150.0, 200.0, 250.0, 300.0, 350.0, 400.0, 500.0, 600.0]
None
Interestingly, a few other ABFs in the data folder have a user list:
ABF | User List |
---|---|
171117_HFMixFRET.abf | [-100.0, 180.0, 160.0, 140.0, 120.0, 100.0, 80.0, 60.0, 40.0, 20.0, 0.0, -20.0, -60.0] |
19212027.abf | [-50.0, -55.0, -60.0, -65.0, -70.0, -75.0, -80.0, -85.0, -90.0, -95.0, -100.0, -105.0, -110.0, -115.0, -120.0] |
2020_03_02_0000.abf | [-200.0, -150.0, -100.0, -50.0, 0.0, 25.0, 50.0, 100.0, 150.0, 200.0, 250.0, 300.0, 350.0, 400.0, 500.0, 600.0] |
I might as well expose this as a class property to make it easier to access
pip install --upgrade pyabf
with pyABF 2.2.4 you can now:
print(abf.userList)
thanks again!