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
Add Bayesian RSA to the package (reprsimil) #98
Changes from 61 commits
e4ab872
06c869a
45f5627
39441a9
762735b
00ed548
1465422
f77e4a2
e4ff88f
b0fc653
8b7232a
e84cd76
79da035
1f5775a
72993e0
df1212f
feffa12
33d1230
2e62141
cd92ca8
4a17697
e18827b
54b23cb
890dc15
54e8035
048b6c8
08eb888
f189941
787020d
99aa7c2
4b58738
d67008b
5350ddc
063a0c1
f77e228
99dcd7c
dc57582
b56a476
4dd6091
374cf64
19f76ff
c61db99
7d7254f
dc441a3
c524178
8d5128b
92de13a
607f371
d8d3a9f
2ff640e
19e5ce9
9c8b0da
cbdfd76
b174e7d
d927f19
f5cf7cf
59ada9c
9b1e30a
68a4600
b8fa72a
00403b8
3f5933f
6884290
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Copyright 2016 Mingbo Cai, Princeton Neuroscience Instititute, | ||
# Princeton University | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
"""A Bayesian method to perform Representational Similarity Analysis""" |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
# Copyright 2016 Intel Corporation | ||
# Copyright 2016 Intel Corporation, Princeton University | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
|
@@ -13,6 +13,9 @@ | |
# limitations under the License. | ||
import numpy as np | ||
import logging | ||
import re | ||
import warnings | ||
import os.path | ||
|
||
""" | ||
Some utility functions that can be used by different algorithms | ||
|
@@ -49,7 +52,6 @@ def from_sym_2_tri(symm): | |
"""convert a 2D symmetric matrix to an upper | ||
triangular matrix in 1D format | ||
|
||
|
||
Parameters | ||
---------- | ||
|
||
|
@@ -71,6 +73,7 @@ def from_sym_2_tri(symm): | |
|
||
def fast_inv(a): | ||
"""to invert a 2D matrix | ||
|
||
Parameters | ||
---------- | ||
|
||
|
@@ -100,3 +103,164 @@ def fast_inv(a): | |
except np.linalg.linalg.LinAlgError: | ||
logging.exception('Error from np.linalg.solve') | ||
raise | ||
|
||
|
||
def cov2corr(cov): | ||
"""Calculate the correlation matrix based on a | ||
covariance matrix | ||
|
||
Parameters | ||
---------- | ||
|
||
cov: 2D array | ||
|
||
Returns | ||
------- | ||
|
||
corr: 2D array | ||
correlation converted from the covarince matrix | ||
|
||
|
||
""" | ||
assert cov.ndim == 2, 'covariance matrix should be 2D array' | ||
inv_sd = 1 / np.sqrt(np.diag(cov)) | ||
corr = cov * inv_sd[None, :] * inv_sd[:, None] | ||
return corr | ||
|
||
|
||
class read_design: | ||
""" A class which has the ability of reading in design matrix in .1D file, | ||
generated by AFNI's 3dDeconvolve. | ||
|
||
Parameters | ||
---------- | ||
|
||
fname: string, the address of the file to read. | ||
|
||
include_orth: Boollean, whether to include "orthogonal" regressors in | ||
the design matrix which are usually head motion parameters. All | ||
the columns of design matrix are still going to be read in, but | ||
the attribute cols_used will reflect whether these orthogonal | ||
regressors are to be included for furhter analysis. | ||
|
||
include_pols: Boollean, whether to include polynomial regressors in | ||
the design matrix which are used to capture slow drift of signals. | ||
This will be reflected in the indices in the attribute cols_used. | ||
|
||
Attributes | ||
---------- | ||
|
||
design: 2d array. The design matrix read in from the csv file. | ||
|
||
n_col: number of total columns in the design matrix. | ||
|
||
column_types: 1d array. the types of each column in the design matrix. | ||
0 for orthogonal regressors (usually head motion parameters), | ||
-1 for polynomial basis (capturing slow drift of signals), | ||
values > 0 for stimulus conditions | ||
|
||
n_basis: scalar. The number of polynomial bases in the designn matrix. | ||
|
||
n_stim: scalar. The number of stimulus conditions. | ||
|
||
n_orth: scalar. The number of orthogoanal regressors (usually head | ||
motions) | ||
|
||
StimLabels: list. The names of each column in the design matrix. | ||
""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Arguments not documented here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also: worth checking if this and readAFNI below are duplicating nipype functionality. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think AFNI still does not provide easy python tool for reading relevant information from 1D files. Basically the support for python is very limited: https://afni.nimh.nih.gov/afni/community/board/read.php?1,82973,82973#msg-82973 |
||
def __init__(self, fname=None, include_orth=False, include_pols=False): | ||
if fname is None: | ||
# fname is the name of the file to read in the design matrix | ||
self.design = np.zeros([0, 0]) | ||
self.n_col = 0 | ||
# number of columns (conditions) in the design matrix | ||
self.column_types = np.ones(0) | ||
self.n_basis = 0 | ||
self.n_stim = 0 | ||
self.n_orth = 0 | ||
self.StimLabels = [] | ||
else: | ||
# isAFNI = re.match(r'.+[.](1D|1d|txt)$', fname) | ||
filename, ext = os.path.splitext(fname) | ||
# We assume all AFNI 1D files have extension of 1D or 1d or txt | ||
if ext in ['.1D', '.1d', '.txt']: | ||
self.readAFNI(fname=fname) | ||
|
||
self.include_orth = include_orth | ||
self.include_pols = include_pols | ||
self.cols_used = np.where(self.column_types == 1)[0] | ||
if self.include_orth: | ||
self.cols_used = np.sort( | ||
np.append(self.cols_used, np.where(self.column_types == 0)[0])) | ||
if self.include_pols: | ||
self.cols_used = np.sort(np.append( | ||
self.cols_used, np.where(self.column_types == -1)[0])) | ||
self.design_used = self.design[:, self.cols_used] | ||
if not self.include_pols: | ||
# baseline is not included, then we add a column of all 1's | ||
self.design_used = np.insert(self.design_used, 0, 1, axis=1) | ||
self.n_TR = np.size(self.design_used, axis=0) | ||
|
||
def readAFNI(self, fname): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Method names should be lowercase, with optional underscores for readability: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. Done |
||
# Read design file written by AFNI | ||
self.n_basis = 0 | ||
self.n_stim = 0 | ||
self.n_orth = 0 | ||
self.StimLabels = [] | ||
self.design = np.loadtxt(fname, ndmin=2) | ||
with open(fname) as f: | ||
all_text = f.read() | ||
|
||
find_n_column = re.compile( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Really basic error checking would be nice (just so that it doesn't fail silently if it doesn't find what it needs). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually I realized that there is a checking a few lines below, for whether the number of conditions read from the header matches the number of columns. |
||
r'^#[ ]+ni_type[ ]+=[ ]+"(?P<n_col>\d+)[*]', re.MULTILINE) | ||
n_col_found = find_n_column.search(all_text) | ||
if n_col_found: | ||
self.n_col = int(n_col_found.group('n_col')) | ||
if self.n_col != np.size(self.design, axis=1): | ||
warnings.warn( | ||
'The number of columns in the design matrix' | ||
+ 'does not match the header information') | ||
self.n_col = np.size(self.design, axis=1) | ||
else: | ||
self.n_col = np.size(self.design, axis=1) | ||
|
||
self.column_types = np.ones(self.n_col) | ||
# default that all columns are conditions of interest | ||
|
||
find_ColumnGroups = re.compile( | ||
r'^#[ ]+ColumnGroups[ ]+=[ ]+"(?P<CGtext>.+)"', re.MULTILINE) | ||
CG_found = find_ColumnGroups.search(all_text) | ||
if CG_found: | ||
CG_text = re.split(',', CG_found.group('CGtext')) | ||
curr_idx = 0 | ||
for CG in CG_text: | ||
split_by_at = re.split('@', CG) | ||
if len(split_by_at) == 2: | ||
# the first tells the number of columns in this condition | ||
# the second tells the condition type | ||
n_this_cond = int(split_by_at[0]) | ||
self.column_types[curr_idx:curr_idx + n_this_cond] = \ | ||
int(split_by_at[1]) | ||
curr_idx += n_this_cond | ||
elif len(split_by_at) == 1 and \ | ||
not re.search('..', split_by_at[0]): | ||
# Just a number, and not the type like '1..4' | ||
self.column_types[curr_idx] = int(split_by_at[0]) | ||
curr_idx += 1 | ||
else: # must be a single stimulus condition | ||
split_by_dots = re.split('\..', CG) | ||
n_this_cond = int(split_by_dots[1]) | ||
self.column_types[curr_idx:curr_idx + n_this_cond] = 1 | ||
curr_idx += n_this_cond | ||
self.n_basis = np.sum(self.column_types == -1) | ||
self.n_stim = np.sum(self.column_types > 0) | ||
self.n_orth = np.sum(self.column_types == 0) | ||
|
||
find_StimLabels = re.compile( | ||
r'^#[ ]+StimLabels[ ]+=[ ]+"(?P<SLtext>.+)"', re.MULTILINE) | ||
StimLabels_found = find_StimLabels.search(all_text) | ||
if StimLabels_found: | ||
self.StimLabels = \ | ||
re.split(r'[ ;]+', StimLabels_found.group('SLtext')) | ||
else: | ||
self.StimLabels = [] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
coverage | ||
flake8 | ||
flake8-print | ||
numdifftools | ||
pytest | ||
pytest-cython | ||
restructuredtext-lint | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Class names should be use the CapWords capitalization convention:
https://www.python.org/dev/peps/pep-0008/#class-names
While for the code that closely follows the formulas in the paper we can disregard this rule, please follow it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.