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

Paired brains #153

Open
wants to merge 58 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
42af7d1
Merge branch 'paired-brains' of github.com:WhitakerLab/scona into pai…
Islast Jul 11, 2019
5234b79
add split_groups function
Islast Jul 11, 2019
fe91cfd
add shuffleing to split_groups
Islast Jul 11, 2019
309951a
Add comments
KirstieJane Jul 11, 2019
360e7cf
copy dataframe
Islast Jul 11, 2019
e582376
Fix merge conflicts
KirstieJane Jul 11, 2019
4b1a44c
First commit of tutorial to compare groups
KirstieJane Jul 11, 2019
3091d30
Add comments
KirstieJane Jul 11, 2019
8d72ba5
implement patient grouping at correlation matrix level
Islast Oct 24, 2019
e54952b
unify parsers and install command line tools at setup
Islast Nov 11, 2019
960bc88
structure setup to install command line tools
Islast Nov 11, 2019
0cf0a26
unify parsers and install command line tools at setup
Islast Nov 11, 2019
1952f9e
structure setup to install command line tools
Islast Nov 11, 2019
7b4b8c5
Merge branch 'commandline' of github.com:Islast/scona into commandline
Islast Nov 12, 2019
d3f54eb
add split_groups function
Islast Jul 11, 2019
640983d
add shuffleing to split_groups
Islast Jul 11, 2019
8ddaaea
rebase onto master
Islast Nov 12, 2019
483c527
First commit of tutorial to compare groups
KirstieJane Jul 11, 2019
e27bbf4
implement patient grouping at correlation matrix level
Islast Oct 24, 2019
a731e9d
better syntax for boolean evaluation
Islast Nov 7, 2019
8385d93
Merge branch 'paired-brains' of github.com:Islast/scona into paired-b…
Islast Nov 12, 2019
92a392a
fix syntax error
Islast Nov 12, 2019
a51eff3
Merge branch 'commandline' into paired-brains
Islast Nov 20, 2019
8b251ea
variable renaming
Islast Nov 26, 2019
c2fb702
replace scona.py wrapper
Islast Nov 26, 2019
368c3df
writing out corrmat now optional
Islast Dec 7, 2019
292f8b7
regression test command line tool
Islast Dec 9, 2019
71ec9c9
add split_groups function
Islast Jul 11, 2019
6ffcb6d
add shuffleing to split_groups
Islast Jul 11, 2019
60a8992
Add comments
KirstieJane Jul 11, 2019
34cbad6
copy dataframe
Islast Jul 11, 2019
b1c582b
First commit of tutorial to compare groups
KirstieJane Jul 11, 2019
46e5d0e
Add comments
KirstieJane Jul 11, 2019
d234e07
implement patient grouping at correlation matrix level
Islast Oct 24, 2019
49e6d94
rebase onto master
Islast Nov 12, 2019
b22230e
better syntax for boolean evaluation
Islast Nov 7, 2019
f33389b
fix syntax error
Islast Nov 12, 2019
ae08cbb
Merge branch 'paired-brains' of github.com:Islast/scona into paired-b…
Islast Dec 9, 2019
73686f9
extend command line tools to groupwise matrix construction
Islast Dec 10, 2019
7db7293
extend groupwise analysis methods
Islast Dec 12, 2019
173019b
refactor groupwise analysis into command line tool
Islast Dec 12, 2019
fc19a24
delete obsolete write_fixtures file
Islast Dec 12, 2019
3bab9c7
improve flexibility of analysis pipelines
Islast Dec 12, 2019
78dc552
restore regression testing
Islast Dec 12, 2019
94d4ee1
Update documentation of analyses
Islast Dec 12, 2019
4c2200a
fix parser bugs
Islast Dec 12, 2019
6fe835f
fix issue #136
Islast Dec 12, 2019
e0a166c
implement nodal measures methods for GraphBundle
Islast Dec 13, 2019
1d0cf54
pad random graphs
Islast Dec 13, 2019
3ef382c
hopefully fix travis
Islast Dec 13, 2019
c1e9c0f
fix travis ci
Islast Dec 13, 2019
b8c1761
install scona in ci
Islast Dec 13, 2019
52bee1e
fix travis
Islast Dec 13, 2019
8c1bc8b
debug command line tool
Islast Dec 17, 2019
c71a857
fixture change following change in random graph padding
Islast Dec 17, 2019
245b85d
raise keyerror when fixtures don't exist
Islast Dec 18, 2019
7967cb8
clearer fixture identification
Islast Dec 18, 2019
2390496
specify routines for moving window analysis
Islast Jan 27, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ env:
secure: QvKbq/nKWPcVk5RrOD5rLUUioaM0iZ9PblW/ucd3zjPWdeJTKSG4BqFf+fWkLN+3aE9k+EeFsQ0voyKriKrSOHHOoHGIbROOdjM8+Uc06N8h1noJ/LLW1BBDbICtCMma70//QWYG+eqIGSBozp3tZ5QGbDZB5Nt5fNDSbO6rYJzfBF26jWAWjurB6E/6v+Dp3nbFRZ2Eju2/RG059w9Y5IK87n/GKUmiS3kRgLJaeSwvji8f6/29xVYsKhorw2GlQA0Es/rnXFHBF5tMqX1CrCjWGN2XH/MIRtrI7FxUz691LBXkGivOl8E6zjyKN/aTvjKe+PZHdF+VPCzjWMUklT2L5p4W6qbjxR+Uw/7ecfBeiTMkubzFig4kqpkz9skhfOiYCeEld+4dzl5mGFhpxwSXKLTQtVvwh7PGuuc22LWK4O5LyESv3jHHPyxEhB48GDc5illzalMqYw7+m/H1A8i4RSPx5xloNSMHmS1naNXq851kZmdLspqd67ECBNnLRN/AMfQIqltTlheFTYZk2hbD9g+fntjr1aboYIxyB1f33WSAJuyTBFagM/la/tVDUGM57WiV7dvLRXF1gXr4U7DzD7mUpflXmn75wCiuyNB4rWDv7LxEpgBR0iXJultzP0ewVPgAU31k8/rMapzfWwTX4z+kXgVNYJqe9XhBudY=
install:
- pip install -q networkx==$NETWORKX_VERSION
- pip install -q -r requirements.txt
- pip install sphinx sphinx sphinxcontrib-napoleon sphinx_rtd_theme
- pip install -r requirements.txt
- pip install .
script:
- python3 -m pytest -v --ignore=tests/regression_test.py
# temporarily ignore regression tests while we investigate why they are failing
# check cli installation
- scona -h
# run tests
- python3 -m pytest -v
# make docs
- cd docs
- make html
- touch .nojekyll
Expand Down
1 change: 1 addition & 0 deletions scona/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from scona.make_graphs import *
from scona.graph_measures import *
from scona.classes import *
from scona.analyses import *

from scona.wrappers import *

Expand Down
349 changes: 349 additions & 0 deletions scona/analyses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,349 @@
from scona.classes import BrainNetwork, GraphBundle
from scona.make_corr_matrices import corrmat_from_regionalmeasures

def network_analysis_from_matrix(
M,
cost,
n_rand,
name="critical_network",
seed=None,
parcellation=None,
centroids=None):
'''
Run the standard scona network analysis on an array M, interpreted as
a weighted graph.

This network analysis thresholds M at the desired cost to create a
binary network and calculates the network measures listed lower down.

For the purposes of comparison this analysis also generates a number
of random graphs via edge swapping (see :func:`networkx.double_edge_swap`)
and reports global measures and rich club measures

Parameters
----------
M : :class:`numpy.array` or :class:`pandas.DataFrame`
M will be treated as a weighted graph, where the
M[i][j] represents the weight of the edge connecting
node i to node j
cost : float
We construct a binary graph from M by restricting
to the ``cost*n/100`` highest weighted edges, where
``n`` is the number of edges in M.
n_rand : int
The analysis requires the generation of random graphs
to create a distribution to test M against. Use n_rand
to sepcify how many graphs you wish to create.
name : str, optional
This is an optional label for the graph M, to distinguish
it from the random graphs.
seed : int, optional
parcellation : list, optional
Anatomical names to assign to the nodes of your graph
centroids : list, optional
Anatomical locations to assign to the nodes of your
graph.

Returns
-------
:class:`scona.GraphBundle`, :class:`pandas.DataFrame`, :class:`pandas.DataFrame`, :class:`pandas.DataFrame`
* A dictionary of networks created during this analysis,
with M indexed by `name`
* A dataframe reporting the nodal measures for the
nodes of M
* A dataframe reporting the global measures of M and
all random graphs
* A dataframe reporting the rich club at every
degree of M and all random graphs

Network Measures
================
Nodal Measures
--------------
* "degree" : int
the number of incident edges
* "betweenness" : float
the betweenness centrality of each node, see :func:`networkx.betweenness_centrality`
* "closeness" : float
the closeness centrality of each node, see :func:`networkx.closeness_centrality`
* "clustering" : float
the clustering coefficient of each node, see :func:`networks.clustering`
* "module" : int
each node is assigned an integer-named module by the louvain
method of community detection, see https://python-louvain.readthedocs.io
* "participation_coefficient" : float
the participation coefficient of nodes of G with partition
defined by "module".
* "shortest_path_length" : float
the average shortest path length for each node in G.
"length" in this case means the number of edges, and does
not consider euclidean distance.
* "total_dist" : float
the total length of the incident edges
* "average_dist" : float
the average length of the incident edges
* "hemisphere" : str
L or R, as determined by the sign of the x coordinate
and assuming MNI space. The x coordinates are negative
in the left hemisphere and positive in the right.
* "interhem" : int
the number of adjacent interhemispheric edges
* "interhem_proportion" : float
the proportion of adjacent edges that are interhemispheric

Global Measures
---------------
* "average_clustering" : float
see :func:`networkx.average_clustering`
* "average_shortest_path_length" : float
see :func:`networkx.average_shortest_path_length`
* "assortativity" : float
see :func:`networkx.degree_assortativity_coefficient`
* "modularity" : float
modularity of network under partition defined by "module"
* "efficiency" : float
see :func:`networkx.global_efficiency`
* "small..."
small world coefficient of networks relative to the
network derived from M.
See :func:`graph_measures.small_world_coefficient`

Rich Club
---------
For some integer k, the rich club coefficient of degree k
measures the completeness of the subgraph on nodes with
degree >= k.
See :func:`networkx.rich_club_coefficient`
'''
# Initialise graph
weighted_network = BrainNetwork(
network=M,
parcellation=parcellation,
centroids=centroids)

# Threshold graph
binary_network = weighted_network.threshold(cost)

# Calculate the modules, distance and hemispheric attributes
# and the nodal measures
binary_network.partition()
binary_network.calculate_spatial_measures()
binary_network.calculate_nodal_measures()

# Create setup for comparing binary_network against random graphs
# (note that this takes a bit of time because you're generating random
# graphs)
bundle = GraphBundle(graph_dict={name: binary_network})
bundle.create_random_graphs(name, n_rand, seed=seed)

# Add the small world coefficient to global measures
small_world = bundle.report_small_world(name)

for gname, network in bundle.items():
network.graph['global_measures'].update(
{"sw coeff against " + name: small_world[gname]})

return bundle, binary_network.report_nodal_measures(), bundle.report_global_measures(), bundle.report_rich_club()


def standard_analysis(
df,
names,
cost,
covars=None,
centroids=None,
method='pearson',
name="critical_network",
seed=None):
'''
Create a structural covariance analysis network from `df` and run
the standard scona network analysis on it.

To create the structural covariance network from `df`, scona
calculates the pairwise correlations of the columns in `names`
over the rows of `df`, correcting for covariance with the columns
of `covars`.
scona thresholds the resulting matrix at the desired cost to create
a binary network and calculates and returns a selection of network
measures; see :func:`network_analysis_from_matrix`.

For the purposes of comparison this analysis also generates a number
of random graphs via edge swapping (see :func:`networkx.double_edge_swap`)
and reports global measures and rich club measures

Parameters
----------
regional_measures : :class:`pandas.DataFrame`
a pandas DataFrame with individual brain scans as rows, and
columns including brain regions and covariates. The columns in
names and covars_list should be numeric.
names : list
a list of the brain regions whose correlation you want to assess
covars: list, optional
covars is a list of covariates (as DataFrame column headings)
to correct for before correlating brain regions.
method : string, optional
the method of correlation passed to :func:`pandas.DataFramecorr`
cost : float
We construct a binary graph from the correlation matrix by
restricting to the ``cost*n/100`` highest weighted edges, where
``n`` is the number of edges.
n_rand : int
The analysis requires the generation of random graphs
to create a distribution to test the against. Use n_rand
to specify how many graphs you wish to create.
name : str, optional
This is an optional label for the initial structural covariance
network, to distinguish it from the random graphs.
seed : int, optional
centroids : list, optional
Anatomical locations to assign to the nodes of your graph.

Returns
-------
:class:`scona.GraphBundle`, :class:`pandas.DataFrame`, :class:`pandas.DataFrame`, :class:`pandas.DataFrame`
* A dictionary of networks created during this analysis,
with the initial structural covariance network indexed by
`name`
* A dataframe reporting the nodal measures for the nodes of
the structural covariance network
* A dataframe reporting the global measures of all networks
* A dataframe reporting the rich club, at every degree, of
each network.
'''

M = corrmat_from_regionalmeasures(
df,
names,
covars=covars,
method=method)

return network_analysis_from_matrix(
M,
cost,
n_rand,
name=name,
seed=seed,
parcellation=names,
centroids=centroids)

def groupwise_analysis(
df,
names,
group_var,
cost,
covars=None,
method='pearson',
seed=None):
# run first for true group assignments
groupwise_bundle = GraphBundle.from_regional_measures(
df,
names,
groupby=group_var,
covars=covars,
method=method)
groupwise_bundle.threshold(cost)
global_frames = [groupwise_bundle.report_global_measures()]
grouping_type = ["unshuffled"]

padding = floor(log10(args.n_shuffle)) +1
# run analysis on random groupings
for i in range(args.n_shuffle):
gb = GraphBundle.from_regional_measures(
df,
names,
groupby=group_var,
covars=covars,
method=method,
shuffle=True,
seed=seed)
gb.threshold(cost)
global_frames.append(gb.report_global_measures())
grouping_type.append(str(i).zfill(padding))



gg_bundles = pd.concat(global_frames, keys=grouping_type)
gg_bundles.set_index(["group randomisation", "group"])

# split by grouping and write to file
#group_list = gg_bundles['group_var'].unique().tolist()
#for gv in group_list:
# gg_bundles.xs(gv, level="group").to_csv(gv+"")

for gv, gv_frame in gg_bundles.groupby(level="group"):
gv_frame.to_csv(gv+"")


def sliding_window_analysis(
df,
names,
cost,
window_var,
window_size,
window_overlap,
covars=None,
method='pearson',
seed=None):
'''
Create sliding window ...
see "Adolescent tuning of association cortex in human structural brain networks" by Frantisek Vasa et al.

Parameters
----------
regional_measures : :class:`pandas.DataFrame`
a pandas DataFrame with individual brain scans as rows, and
columns including brain regions and covariates. The columns in
names and covars_list should be numeric.
names : list
a list of the brain regions whose correlation you want to assess
window_var : str
the name of the column in regional_measures by which to rank
participants.
window_size : int or float
the size (number of subjects) of each sliding window.
A decimal value between 0 and 1 will be interpreted as a
proportion of the whole cohort. E.g if window_size is 0.1, and
the cohort is 100 subjects each window will contain 10 subjects.
window_overlap : int or float
the number of subjects in the overlap between two consecutive
windows. If window_overlap is a decimal between 0 and
1(not inclusive) then the intersection of two consecutive
windows will be window_overlap*(mean window size).
number_of_windows : int, optional
The number of windows you want to use for your analysis
odd_sized_bin : "last" or "first", optional
If it is not possible to construct equally sized windows,
choose either the last or the first window to have a
different size to the others. Default "last".
covars : list, optional
covars is a list of covariates (as DataFrame column headings)
to correct for before correlating brain regions.
method : string, optional
the method of correlation passed to :func:`pandas.DataFramecorr`
cost : float
We construct a binary graph from the correlation matrix by
restricting to the ``cost*n/100`` highest weighted edges, where
``n`` is the number of edges.
seed : int, optional
centroids : list, optional
Anatomical locations to assign to the nodes of your graph.

Returns
-------

'''

moving_window_bundle = GraphBundle.from_regional_measures(
df, names, covars=covars, method=method,
windowby=window_var, window_size=window_size)
moving_window_bundle.threshold(cost)


shuffle_list = []
for i in range(args.n_shuffle):
gb = GraphBundle.from_regional_measures(
df, names, covars=covars, method=method,
windowby=window_var, window_size=window_size,
shuffle=True, seed=seed)
gb.threshold(cost)
Loading