Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subset dev #14

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Binary file added ASCAM instructions.pdf
Binary file not shown.
6 changes: 4 additions & 2 deletions README.md
Expand Up @@ -32,10 +32,12 @@ For launch options:
If you also issue `conda install python.app` in your new environment then you can have a well-behaved Mac GUI with the following command from the parent directory of ASCAM:
`pythonw /ASCAM/src/ascam.py`

20-03-01: Note, with the migration to Qt, some problems may be encountered on the Mac if you already have installations of Qt4+. A fresh environment (e.g. can help.
21-05-25: Update to Big Sur - Pyqtgraph and PyQt need Python 3.8, PySide2 5.15 and the command export QT_MAC_WANTS_LAYER=1 must be issued in the Terminal.

20-03-01: With the migration to Qt, some problems may be encountered on the Mac if you already have installations of Qt4+. A fresh environment can help.

21-05-25: Running under macOS Big Sur - Pyqtgraph and PyQt need Python 3.8, PySide2 5.15 and the command `export QT_MAC_WANTS_LAYER=1` must be issued in the Terminal.

## Running ASCAM
Note: Tables in axograph and matlab have named columns ASCAM uses these names to determine what data is dealing with. Therefore the column containing the recorded current should contain either "current", "trace" or "Ipatch", the name of the column holding the recorded piezo voltage should contain the string "piezo" and the name of the command voltage column should contain "command".

There is an example raw data file of an AMPA receptor single channel patch in the ASCAM/data folder. This recording was sampled at 40 kHz.
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Expand Up @@ -5,3 +5,4 @@ pandas==1.5.*
scipy==1.10.*
axographio==0.3.2
pyobjc-framework-Cocoa==6.2.2;sys_platform=='Darwin'
cython<=0.29
3 changes: 2 additions & 1 deletion src/core/filtering.py
@@ -1,3 +1,4 @@
#import logging
import numpy as np


Expand Down Expand Up @@ -190,7 +191,7 @@ def predict_backward(self, data, window_width):
else:
raise ValueError(
f"Mode {self.mode} is an unknown method for dealing\
with edges"
with edges"
)
return backward_prediction

Expand Down
29 changes: 17 additions & 12 deletions src/core/idealization.py
Expand Up @@ -104,20 +104,25 @@ def idealize_series(self):
self.idealize_episode(i)

def get_events(self, time_unit="s", trace_unit="A"):
if self.all_ep_inds != self.ind_idealized:
self.idealize_series()

# idealizing every trace, every time is killing us
# now we only get events from traces that were idealized
##if self.all_ep_inds != self.ind_idealized:
## self.idealize_series()
event_array = np.zeros((0, 5)).astype(object)
for episode in self.data.series:
# create a column containing the episode number
ep_events = Idealizer.extract_events(
self.idealization(episode.n_episode), self.time()
)
episode_number = episode.n_episode * np.ones(len(ep_events[:, 0]))
# glue that column to the event
ep_events = np.concatenate(
(episode_number[:, np.newaxis], ep_events), axis=1
)
event_array = np.concatenate((event_array, ep_events), axis=0)
if self.idealization(episode.n_episode) is not None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same as if self.idealization(episode.n_episode): so you don't have to use is not None at the end. Python treats None as well as empty list ([]) and empty string ('') as False in this context.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. Maybe I put an explanatory comment.

# create a column containing the episode number
ep_events = Idealizer.extract_events(
self.idealization(episode.n_episode), self.time()
)
episode_number = episode.n_episode * np.ones(len(ep_events[:, 0]))
# glue that column to the event
ep_events = np.concatenate(
(episode_number[:, np.newaxis], ep_events), axis=1
)
event_array = np.concatenate((event_array, ep_events), axis=0)

event_array[:, 1] *= CURRENT_UNIT_FACTORS[trace_unit]
event_array[:, 2:] *= TIME_UNIT_FACTORS[time_unit]
return event_array
Expand Down
96 changes: 64 additions & 32 deletions src/core/recording.py
Expand Up @@ -77,7 +77,8 @@ def from_file(
else:
raise ValueError(f"Cannot load from filetype {filetype}.")

recording.lists = {"All": (list(range(len(recording["raw_"]))), None)}

recording.subsets = {"All": (list(range(len(recording["raw_"]))), None)}

return recording

Expand All @@ -93,36 +94,42 @@ def __init__(self, filename="", sampling_rate=4e4):
# attributes for storing and managing the data
self["raw_"] = []
self.current_datakey = "raw_"

self.current_subsets = ["All"]
self.current_ep_ind = 0

# variables for user created lists of episodes
# `lists` stores the indices of the episodes in the list in the first
# element and the associated key as the second
# lists[name] = ([inds], key)
self.lists = dict()
# variables for user-created subsets of episodes
# `subsets` stores the indices of the episodes in the subset in the first
# element and the associated keyboard key as the second:
# subsets[name] = ([indices], key)
self.subsets = dict()

def select_episodes(self, datakey=None, lists=None):
def select_episodes(self, datakey=None, subsets=None):
if datakey is None:
datakey = self.current_datakey
if lists is None:
lists = ["All"]
if subsets is None:
subsets = self.current_subsets
indices = list()
for listname in lists:
indices.extend(self.lists[listname][0])
for subsetname in subsets:
indices.extend(self.subsets[subsetname][0])
indices = np.array(list(set(indices)))
return np.array(self[datakey])[indices]

def episodes_in_lists(self, names):
if isinstance(str, names):
names = [names]
def episodes_in_subsets(self, subset_names):
if isinstance(subset_names, str): #AP corrected here 291121
subset_names = [subset_names]
indices = list()
for listname in names:
indices.extend(self.lists[listname][0])
for subsetname in subset_names:
indices.extend(self.subsets[subsetname][0])
# remove duplicate indices
indices = np.array(list(set(indices)))
#print (indices)
debug_logger.debug(f"Selected episodes: {indices}")
return np.array(self.series)[indices]

if indices != []:
return np.array(self.series)[indices]
else:
return None

@property
def series(self):
return self[self.current_datakey]
Expand Down Expand Up @@ -303,7 +310,9 @@ def series_hist(
"""Create a histogram of all episodes in the presently selected series
"""
debug_logger.debug(f"series_hist")
# put all piezo traces and all current traces in lists

# put all piezo traces and all current traces in subsets

piezos = [episode.piezo for episode in self.series]
traces = [episode.trace for episode in self.series]
trace_list = []
Expand Down Expand Up @@ -411,7 +420,9 @@ def _load_from_pickle(recording):
def export_idealization(
self,
filepath,
lists_to_save,

subsets_to_save,

time_unit,
trace_unit,
amplitudes,
Expand All @@ -424,7 +435,7 @@ def export_idealization(
if not filepath.endswith(".csv"):
filepath += ".csv"

episodes = self.select_episodes(lists=lists_to_save)
episodes = self.select_episodes(subsets=subsets_to_save)

export_array = np.zeros(
shape=(len(episodes) + 1, episodes[0].idealization.size)
Expand All @@ -451,19 +462,22 @@ def export_matlab(
self,
filepath,
datakey,
lists_to_save,

subsets_to_save,

save_piezo,
save_command,
time_unit="s",
trace_unit="A",
piezo_unit="V",
command_unit="V",
):
"""Export all the episodes in the givens list(s) from the given series

"""Export all the episodes in the givens subset(s) from the given series
(only one) to a matlab file."""
debug_logger.debug(
f"export_matlab:\n"
f"saving the lists: {lists_to_save}\n"
f"saving the subsets: {subsets_to_save}\n"
f"of series {datakey}\n"
f"save piezo: {save_piezo}; "
"save command: {save_command}\n"
Expand All @@ -478,7 +492,9 @@ def export_matlab(
export_dict = dict()
export_dict["time"] = self["raw_"][0].time * TIME_UNIT_FACTORS[time_unit]
fill_length = len(str(len(self[datakey])))
episodes = self.select_episodes(datakey, lists_to_save)

episodes = self.select_episodes(datakey, subsets_to_save)

# # get the episodes we want to save
# indices = list()
# for listname in lists_to_save:
Expand All @@ -498,19 +514,27 @@ def export_matlab(
)
io.savemat(filepath, export_dict)

def export_axo(self, filepath, datakey, lists_to_save, save_piezo, save_command):

def export_axo(self, filepath, datakey, subsets_to_save, save_piezo, save_command):

"""Export data to an axograph file.

Argument:
filepath - location where the file is to be stored
datakey - series that should be exported
<<<<<<< HEAD
lists_to_save - the user-created lists of episodes that should be
=======
subsets_to_save - the user-created subsets of episodes that should be
>>>>>>> subsets
includes
save_piezo - if true piezo data will be exported
save_command - if true command voltage data will be exported"""
debug_logger.debug(
f"export_axo:\n"
f"saving the lists: {lists_to_save}\n"

f"saving the subsets: {subsets_to_save}\n"

f"of series {datakey}\n"
f"save piezo: {save_piezo}; save command: {save_command}\n"
f"saving to destination: {filepath}"
Expand All @@ -529,7 +553,8 @@ def export_axo(self, filepath, datakey, lists_to_save, save_piezo, save_command)
data_list = [self.episode().time]

# get the episodes we want to save
episodes = self.select_episodes(datakey, lists_to_save)

episodes = self.select_episodes(datakey, subsets_to_save)

for episode in episodes:
data_list.append(np.array(episode.trace))
Expand All @@ -544,7 +569,8 @@ def export_axo(self, filepath, datakey, lists_to_save, save_piezo, save_command)
file.write(filepath)

def create_first_activation_table(
self, datakey=None, time_unit="ms", lists_to_save=None, trace_unit="pA"

self, datakey=None, time_unit="ms", subsets_to_save=None, trace_unit="pA"
):
if datakey is None:
datakey = self.current_datakey
Expand All @@ -558,7 +584,9 @@ def create_first_activation_table(
episode.first_activation_amplitude
* CURRENT_UNIT_FACTORS[trace_unit],
)
for episode in self.select_episodes(datakey, lists_to_save)

for episode in self.select_episodes(datakey, subsets_to_save)

]
)
return export_array.astype(object)
Expand Down Expand Up @@ -587,12 +615,16 @@ def export_first_activation(
filepath,
datakey=None,
time_unit="ms",
lists_to_save=None,

subsets_to_save=None,

trace_unit="pA",
):
"""Export csv file of first activation times."""
export_array = self.create_first_activation_table(
datakey, time_unit, lists_to_save, trace_unit

datakey, time_unit, subsets_to_save, trace_unit

)
header = [
"Episode Number",
Expand Down
5 changes: 5 additions & 0 deletions src/core/savedata.py
@@ -1,6 +1,9 @@
from scipy import io
import os
import json
import pickle
from ..utils.tools import parse_filename



def save_metadata(data, filename):
Expand Down Expand Up @@ -96,8 +99,10 @@ def save_data(data, filename="", filetype="mat", save_piezo=True, save_command=T
save_piezo=save_piezo,
save_command=save_command,
)

# elif filetype == "pkl":
# return_status = save_pickle(data=data, filepath=filepath)

else:
print('Can only save as ".mat"!')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or as ".pkl" now :).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait, actually no. The save_pickle function this is referring to is still commented out. Nevermind!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed it again


Expand Down
29 changes: 28 additions & 1 deletion src/gui/analysis_widgets.py
Expand Up @@ -5,18 +5,25 @@
from PySide2 import QtCore
from PySide2.QtWidgets import (
QApplication,

QSizePolicy,
QComboBox,
QFileDialog,
QDialog,
QTableView,

QGridLayout,
QTabWidget,
QWidget,
QVBoxLayout,
QHBoxLayout,

QCheckBox,
QLineEdit,
QToolButton,
QTabBar,
QPushButton,
QLabel,
QLabel
)

from .io_widgets import ExportIdealizationDialog
Expand Down Expand Up @@ -54,6 +61,11 @@ def create_widgets(self):
self.calc_button = QPushButton("Calculate idealization")
self.calc_button.clicked.connect(self.calculate_click)
self.layout.addWidget(self.calc_button)

self.calc_subset_button = QPushButton("Calculate idealization for subset")
self.calc_subset_button.clicked.connect(self.calculate_subset_click)
self.layout.addWidget(self.calc_subset_button)


self.events_button = QPushButton("Show Table of Events")
self.events_button.clicked.connect(self.create_event_frame)
Expand Down Expand Up @@ -132,6 +144,21 @@ def idealization(self, n_episode=None):
def time(self, n_episode=None):
return self.current_tab.idealization_cache.time(n_episode)

def calculate_subset_click(self):
self.get_params()
### ask which subsets are checked
selected_subsets,_ = self.main.ep_frame.subset_frame.subsets_check()
if selected_subsets:
episodes = self.main.data.episodes_in_subsets(selected_subsets)
for ep in episodes:
print (f"Idealizing episode {ep.n_episode}")
self.main.data.current_ep_ind = ep.n_episode
self.idealize_episode()
self.main.plot_frame.update_episode()

else:
print (f"No subsets were selected, nothing to do.")

def calculate_click(self):
self.get_params()
self.idealize_episode()
Expand Down