PathFinder is a toolbox for finding common patterns in a set of related datasets (matrices).
As an example, say we have multiple datasets arranged into
Each dataset is a matrix
Through this notation, it can be seen that within any given domain, all datasets share the row dimension, and within any given modality, all the datasets share the column dimension.
We further assume that we don't necessarily have access to all modalities in all domains. We are missing some of the
Our objective is to find a set of low-rank matrix decompositions
Such decompositions mean that we want to find common subspaces within each domain
The above decompositions are ill-defined unless we add additional constraints on the matrices
As we said earlier, some of the modalities might be missing in some of the domains/species. We define a mask
Now we can write the overall loss function as:
Below are some extensions to the framework we set above:
1) Datasets as graphs.
The datasets may not necessarily be organised into a neat table of domains-by-modalities. A more general organisation is to have a list of datasets and two lists of lookup tables that link the datasets to the set of left/right matrices in a decomposition.
The below diagram shows how a data table relates to a data graph:
Mathematically, we can re-write our matrix decompositions as:
where
2) SVD-style decomposition.
The decompositions
where
3) ICA.
When estimating the decomposition
Detailed examples can be found in the project Notebooks. Below are short example.
We start by organising our data into a table:
data_dict = {
'Human' : {'FMRI' : X1, 'DMRI': X2, 'GeneExp': X3},
'Monkey' :,{'FMRI' : None, 'DMRI': X4, 'GeneExp': None},
'Mouse' : {'FMRI' : None, 'DMRI': X5, 'GeneExp': X6},
}In this example, we have 3 domains and 3 modalities. Some of the combinations of modalities and domains are missing (set to None).
To run the PathFinder, we create a matrix decomposition object and the run the fit() method. To do a L2-regularision, we use the Ridge class from sklearn. We can use method_kwargs to pass arguments to the Ridge object.
from pathfinder import decomp
from sklearn.linear_model import Ridge
algo = decomp.JointOuterDecomp(n_components=5,
method=Ridge, method_kwargs={'alpha':1e2})
algo.fit(data_dict)You can look at the loss function over iterations with:
import matplotlib.pyplot as plt
_ = plt.plot(algo._loss)And you can look at the predictions with:
data_pred = algo.predict(as_dict=True)We can extend the table/dict organisation to a graph by using lookups. Below is an example using the data dictionary from the example above.
data_list = [X1,X2,X3,X4,X5,X6]
alpha = [0, 0, 0, 1, 2, 2]
beta = [0, 1, 2, 1, 1, 2]We can visualise the graph as a table (when possible):
from pathfinder import utils
utils.Lookup_to_DataTable(data_list, alpha, beta,
row_names=['Human', 'Monkey', 'Mouse'],
col_names=['FMRI','DMRI','GeneExp'])Here we replace the two-matrix decomposition with a three-matrix decomposition including orthonormal (unitary) left/right matrices and a diagonal matrix in the middle.
from pathfinder import decomp
algo = decomp.JointSVD(n_components=5)
algo.fit(data_dict)When using a data list instead of a dict, we need to provide the lookup tables to the fitting function:
algo.fit(data_list, alpha, beta)Here we choose the option of running ICA after the decomposition.
from pathfinder import decomp
algo = decomp.JointSVD(n_components=5, do_ica='both')
algo.fit(data_dict)
