An overview of the toolbox functionality is shown in Figure 1. In the figure, boxes represent objects that can be created with the toolbox. Flow starts from the dataset object on the top left which represents the collection of raw data files. The raw data is loaded using information in the dataset to create intermediate objects that, for instance, contain data traces. These objects define electrophysiological measurements to be entered into the data matrix of the database object on the top right. The database object allows filtering and querying to refine its contents. From the database object, one can always go back to the dataset and find the raw data that results from a query. The arrows going to bottom objects and corresponding plots show the types of possible analyses that can be done on a database object. These analyses are typically for displaying statistical information. The red arrow is a speacial analysis for searching and matching rows between different databases. The match is done by taking a row from a database created with data from real neurons and finding best matching model neurons from a simulation database.
The objects in the figure are instances of classes that define their properties in the object-oriented framework. Each class comes with a hierarchy of of subclasses that specialize to specific functions. Subsequent sections describe each of these class hierarchies that make up the main components of the toolbox.
The database object is at the center of this toolbox (see Figure 1). It holds a data matrix with rows as observations and columns as attributes. The rows would normally correspond to results from individual data traces, or simply neurons. The columns hold values of separate measurements, statistical data, or parameter values.
A database object can be created from any of the classes in the hierarchy of Figure 2. The top-level database class is tests_db which contains a two-dimensional data matrix of real numbers and some metadata. The metadata consists of column labels (e.g., measure names), a dataset label, and data properties (e.g., time resolution). The subclasses are specialized for different tasks.
If the database object is created using a dataset object, this maintains a connection from the elements of the database (e.g., neurons) to the raw data. This allows raw data associated with database contents to be visualized during analysis. However, a database can be created from any data matrix given in the proper format.
Some specialized subclasses of tests_db are as follows:
The dataset object is responsible for creating the database objects (see Figure 1). It defines where the raw data is stored and what parameters are used to load and analyze it. It knows that raw data has parameters associated which individual raw data traces and how and which measures will be generated. This information is used to automatically generate a database from the dataset. It also allows reaching back the raw data from rows of an analyzed database.
Figure 3 shows the hierarchy for the dataset classes. The top-level dataset class is params_tests_dataset which is an incomplete class. That is, this class defines general utilities that can work for a variety of dataset subclasses, but one cannot make a object from the params_tests_dataset class directly. Instead, one of its subclasses must be chosen and used. Some of these specialized subclasses are as follows:
Since dataset and database objects are related and work together for some operations, it is convenient to have another object that bundles them together. There are several analysis routines that start from the database, retrieve raw data traces and other related information from the dataset and create a result. For instance, matching neurons from one database to another requires first comparing the measurements to find match candidates, and then comparing raw traces to visually represent the match quality.
The top-level dataset_db_bundle class in Figure 4 fulfills this purpose by bundling a dataset with the raw database, db, created from it, and with the reduced database, joined_db, that contains a one-row-per-neuron representation. Although being a virtual class that cannot be instantiated, it contains general methods and prototype methods that must be implemented in subclasses. This way, it provides guidelines for defining subclasses. Its two subclasses provide specialize methods for model and physiology databases, respectively.
Wrapper classes are designed to hold data and provide simple methods that operate on them. They can either hold raw data, or intermediate processed forms of data being byproducts of analysis routines. In the overall schema of Figure 1, the raw traces obtained from the dataset object are kept in data wrapper objects.
Figure 5 shows the hierarchy for the data wrapper classes. The most basic data wrapper class in this toolbox is the trace class, which holds raw voltage or current traces. The spikes object contains the spike times obtained by analyzing a trace object.
A data wrapper class does more than just holding the data. It defines a set of operations in terms of method functions that can work on the data held by the class. As a rule of thumb, if one needs to add some new functionality into the toolbox, it should be added as a method into a class holding the data on which to operate.
Some of the data wrapper classes are as follows:
Profile classes are designed to hold results of analysis and measurements on the data wrapper or database objects. The data and results are separated into different classes for added flexibility of saving data and results separately. Yet, the profiles normally keep a copy of the data wrapper object from which they obtained the measurements. The intention is to save the measurement results for possible visualization or later inspection, without having to repeat the analyses.
In Figure 6, the top-level results_profile class contains a simple MATLAB structure variable, results, that holds a set of name-value pairs. These are names of measurements and their corresponding values. Most of the subclasses are simplistic, and they exist only for organizational reasons. Some of them may implement specialized plotting methods that make use of the saved measurements. These subclasses can be briefly described as follows:
To integrate visualization into each class, common MATLAB plotting features are implemented in the supporting classes seen in Figure 7. These bring an object-oriented approach to plot generation in MATLAB. Plots can be generated as objects, saved, modified and included as subplots in larger plots.
The main plotting classes are plot_abstract, plot_superpose, and plot_stack. The most general plotting template class, and the top-level class in the hierarchy, is plot_abstract, which plots an axis using a single MATLAB command, like plot or bar. Multiple plot_abstract objects that use the same command can be superposed and still act as a single plot_abstract object. If they require different plotting commands (e.g., mixing plot and text labels), a plot_superpose object must be used that is composed of an array of plot_abstract objects. Multiple plot_abstract objects or any of the subclass objects can be composed together in a horizontal or vertical stack using the plot_stack class. Since plot_stack is itself a subclass of plot_abstract, it can be stacked as well. This allows creating virtually any complex structured figure using the three classes. Each of these classes have several properties that control the layout and details of placement and looks.
The rest of the classes in the hierarchy create typical types of plots for convenience:
These are miscellaneous classes that do not fit into any of the above categories:
For flexibility in passing optional arguments to methods, this toolbox adopted using property structures. A MATLAB structure, mostly called props, is passed to a method as the last argument:
>> myFunc('hello', props)
Most objects keep a property structure that define custom attributes passed at time of construction. These classes define a setProp method to modify properties after being created.
The simplistic implementation of object-oriented programming features in Matlab impose several strict limitations. MATLAB's powerful and flexible operator overloading feature helps overcome these limitations.
PANDORA Toolbox uses MATLAB operator overloading to facilitate manipulation of local and parent object fields. In MATLAB, object fields can only be accessed from the object's private methods. This means one cannot access the object fields using the dot operator. To give an example, the trace object has a dt field for time resolution. The following command fails:
??? Object fields can only be accessed within methods.
>> get(mytrace, 'dt')
ans = 1e-04
ans = 1e-4
ans = 1e-4
For debugging problems with methods, one can turn on the verbosity of information display during execution with:
>> warning on backtrace
To get the benefit of overloading, the top-level class must have the generic subsref and subsasgn methods. These methods can be copied from any of the other top-level classes. Any subclasses should have the generic get and set methods in place.