Degree Centrality and Clustering Coefficient in Adjacent matrix - python

Based on a dataset extracted from this link: Brain and Cosmic Web samples, I'm trying to do some Complex Network analysis.
The paper The Quantitative Comparison Between the Neuronal Network and the Cosmic Web, claims to have used this dataset, as well as its adjacent matrixes
"Mij, i.e., a matrix with rows/columns equal to the number of detected nodes, with value Mij = 1 if the nodes are separated by a distance ≤ llink , or Mij = 0 otherwise".
I then probed into the matrix, like so:
from astropy.io import fits
with fits.open('mind_dataset/matrix_CEREBELLUM_large.fits') as data:
matrix_cerebellum = pd.DataFrame(data[0].data)
which does not print a sparse matrix, but rather a matrix with distances from nodes expressed as pixels.
I've learned that the correspondence between 1 pixel and scale is:
neuronal_web_pixel = 0.32 # micrometers
And came up with a method in order to convert pixels to microns:
def pixels_to_scale(df, mind=False, cosmos=False):
one_pixel_equals_parsec = cosmic_web_pixel
one_pixel_equals_micron = neuronal_web_pixel
if mind:
df = df/one_pixel_equals_micron
if cosmos:
df = df/one_pixel_equals_parsec
return df
Then, another method to binaryze the matrix after the conversion:
def binarize_matrix(df, mind=False, cosmos=False):
if mind:
brain_Llink = 16.0 # microns
# distances less than 16 microns
brain_mask = (df<=brain_Llink)
# convert to 1
df = df.where(brain_mask, 1.0)
if cosmos:
cosmos_Llink = 1.2 # 1.2 mpc
brain_mask = (df<=cosmos_Llink)
df = df.where(brain_mask, 1.0)
return df
Finally, with:
matrix_cerebellum = pixels_to_scale(matrix_cerebellum, mind=True)
matrix_cerebellum = binarize_matrix(matrix_cerebellum, mind=True)
matrix_cerebellum.head(5) prints my sparse matrix of (mostly) 0.0s and 1.0s:
0 1 2 3 4 5 6 7 8 9 ... 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857
0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
3 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
4 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
5 rows × 1858 columns
Now I would like to calculate:
Degree Centrality of the network, given by the formula:
Cd(j) = Kj / n-1
Where kj is the number of (undirected) connections to/from each j-node and n is the total number of nodes in the entire network.
Clustering Coefficient, which quantifies the existence of infrastructure within the local vicinity of nodes, given by the formula:
C(j) = 2yi / Kj(Kj -1)
in which yj is the number of links between neighbooring nodes of the j-node.
For finding Degree Centrality, I have tried:
# find connections by adding matrix row values
matrix_cerebellum['K'] = matrix_cerebellum.sum(axis=1)
# applying formula
matrix_cerebellum['centrality'] = matrix_cerebellum['K']/matrix_cerebellum.shape[0]-1
Generates:
... K centrality
9.0 -0.995156
6.0 -0.996771
7.0 -0.996771
11.0 -0.996233
11.0 -0.994080
According to the paper, I should be finding:
"For the cerebellum slices we measured 〈k〉 ∼ 1.9 − 3.7",
For the average numbers of connections per node.
Also I'm finding negative centralities.
Does anyone know how to apply any of these formulas based on the dataframe above?

This is not really a programming question, but I will try to answer it. The webpage with the data sources states that the adjacent matrix files for brain samples give distances between connected nodes expressed in pixels of the images used to reconstruct the networks. The paper then explains that to get the real adjacency matrix Mij (with 0 and 1 values only) the authors consider as connected nodes where the distance is at most 16 micrometers. I don't see the information on how many pixels in the image corresponds to one micrometer. This would be needed to compute the same matrix Mij that the authors used in their calculations.
Furthermore, the value〈k〉is not the degree centrality or the clustering coefficient (that depend on a node), but rather the average number of connections per node in the network, computed using the matrix Mij. The paper then compares the observed distributions of degree centralities and clustering coefficients in the brain and cosmic networks to the distribution one would see in a random network with the same number of nodes and the same value of〈k〉. The conclusion is that brain and cosmic networks are highly non-random.
Edits:
1. The conversion of 0.32 micrometers per pixel seems to be right. In the files with data on brain samples (both for cortex and cerebellum) the largest value is 50 pixels, which with this conversion corresponds to 16 micrometers. This suggests that the authors of the paper already thresholded the matrices, listing in them only distances not exceeding 16 micrometers. In view of this, to obtain the matrix Mij with 0 and 1 values only, one simply needs to replace all non-zero values with 1. An issue is that using the matrices obtained in this way one gets 〈k〉 = 9.22 for cerebellum and 〈k〉 = 7.13 for cortex, which is somewhat outside the ranges given in the paper. I don't know how to account for this discrepancy.
2. Negative centrality values are due to a mistake (missing parentheses) in the code. It should be:
matrix_cerebellum['centrality'] = matrix_cerebellum['K']/(matrix_cerebellum.shape[0] - 1)
3. Clustering coefficient and degree centrality of each node can be computed using tools provided by the networkx library:
from astropy.io import fits
import networkx as nx
# get the adjacency matrix for cortex
with fits.open('matrix_CORTEX_large.fits') as data:
M = data[0].data
M[M > 0] = 1
# create a graph object
G_cortex = nx.from_numpy_matrix(M)
# compute degree centrality of all nodes
centrality = nx.degree_centrality(G_cortex)
# compute clustering coefficient of all nodes
clustering = nx.clustering(G_cortex)

Related

How does this transparent extension come with a plot in lineplot?

The plot in documentation looks like this :
with code
sns.lineplot(x="timepoint", y="signal",
hue="region", style="event",
data=fmri)
and
mine comes out to be like this
for code :
sns.lineplot(
# data=fmri,
x=df["_C_UP"]["s"][:10],
y=df["_C_UP"]["px"][:10]
# hue="event"
);
How do I get the same effect for those lines ( that transparent color around it )
here is what my data looks like
#Energy s py pz px dxy dyz dz2 dxz dx2 tot
50 -17.98094 0.72320 0.31781 0.00000 0.31882 0.0 0.0 0.0 0.0 0.0 1.35982
51 -17.87394 0.29726 0.14415 0.00000 0.14491 0.0 0.0 0.0 0.0 0.0 0.58632
52 -17.76794 0.63694 0.02456 0.00000 0.02484 0.0 0.0 0.0 0.0 0.0 0.68634
53 -17.66194 1.78595 0.06032 0.00001 0.06139 0.0 0.0 0.0 0.0 0.0 1.90766
54 -17.55494 1.97809 0.09038 0.00001 0.09192 0.0 0.0 0.0 0.0 0.0 2.16040
In the fmri datasets, there are actually multiple observations for each time point and subgroup, for example, at timepoint == 14 :
fmri[fmri['timepoint']==14]
subject timepoint event region signal
1 s5 14 stim parietal -0.080883
57 s13 14 stim parietal -0.033713
58 s12 14 stim parietal -0.068297
59 s11 14 stim parietal -0.114469
60 s10 14 stim parietal -0.052288
61 s9 14 stim parietal -0.130267
So the line you see, is actually the mean of all these observations (stratified by group) and the ribbon is the 95% confidence interval of this mean. For example, you can turn this off by doing:
sns.lineplot(x="timepoint", y="signal",
hue="region", style="event",
data=fmri,ci=None)
So to get the exact plot, you need to have multiple observations or replicates. If you don't, and your intention is to just connect the points, you cannot get a confidence interval.
If you want to look at a trend line, one thing you can try is a polynomial smooth. And it makes sense to plot the data as points too.
Using an example from the same fmri dataset:
df = fmri[(fmri['subject']=="s5") & (fmri['event']== "stim") & (fmri['region'] == "frontal")]
sns.regplot(data=df,x = "timepoint",y = "signal",order=3)
Or use a loess smooth, which is more complicated (see this post about what is drawn below )
import matplotlib.pyplot as plt
from skmisc.loess import loess
lfit = loess(df['timepoint'],df['signal'])
lfit.fit()
pred = lfit.predict(df['timepoint'], stderror=True)
conf = pred.confidence()
fig, ax = plt.subplots()
sns.scatterplot(data=df,x = "timepoint",y = "signal",ax=ax)
sns.lineplot(x = df["timepoint"],y = pred.values,ax=ax,color="#A2D2FF")
ax.fill_between(df['timepoint'],conf.lower, conf.upper, alpha=0.1,color="#A2D2FF")
It depends on the data. The plot from the seaborn documentation that you show is based on a dataset where for every x value there are several y values (repeated measurements). The lines in the plot then indicate the means of those y values, and the shaded regions indicate the associated 95% confidence intervals.
In your data, there is only one y value for each x value, so there is no way to calculate a confidence interval.

Pandas to bipartite graph

I have already added nodes into my graph but i cant seem to understand the way to add the edges to it. The edges correspond to any value of 1 in my pivot tabel. The table is of the following form:
movie_id 1 2 3 4 5 ... 500
user_id ...
501 1.0 0.0 1.0 0.0 0.0 ... 0.0
502 1.0 0.0 0.0 0.0 0.0 ... 0.0
503 0.0 0.0 0.0 0.0 0.0 ... 1.0
504 0.0 0.0 0.0 1.0 0.0 ... 0.0
. ...
.
1200
This is the code i have used for my nodes:
B = nx.Graph()
B.add_nodes_from(user_rating_pivoted.index, bipartite=0)
B.add_nodes_from(user_rating_pivoted.columns, bipartite=1)
And i imagine the edges should be formed in a similar way :
add_edges_from(...) for idx, row in user_rating_pivoted.iterrows())
Let's add prefixes to those indices and columns, and use them as nodes to more easily associate the connections:
print(df)
movie_1 movie_2 movie_3 movie_4 movie_5 movie_6
user_1 1.0 1.0 1.0 1.0 0.0 0.0
user_2 1.0 0.0 0.0 0.0 0.0 0.0
user_3 0.0 1.0 0.0 0.0 0.0 1.0
user_4 1.0 0.0 1.0 0.0 1.0 0.0
In order to get the edges (and keep the node names) we could use pandas to transform a little the dataframe. We can get a MultiIndex using stack, and then indexing on the values that are 1.Then we can use add_edges_from to add all the edge data:
B = nx.Graph()
B.add_nodes_from(df.index, bipartite=0)
B.add_nodes_from(df.columns, bipartite=1)
s = df.stack()
B.add_edges_from(s[s==1].index)
We can use bipartite_layout for a nice layout of the bipartite graph:
top = nx.bipartite.sets(B)[0]
pos = nx.bipartite_layout(B, top)
nx.draw(B, pos=pos,
node_color='lightgreen',
node_size=2500,
with_labels=True)
Note that it is likely that these highly sparse matrices lead to disconnected graphs though, i.e graphs in which not all nodes are connected to some other node, and attempting to obtain both sets will raise an error as specified here.
AmbiguousSolution – Raised if the input bipartite graph is disconnected and no container with all nodes in one bipartite set is provided. When determining the nodes in each bipartite set more than one valid solution is possible if the input graph is disconnected.
In such case you can just plot as a regular graph with:
rcParams['figure.figsize'] = 10 ,8
nx.draw(B,
node_color='lightgreen',
node_size=2000,
with_labels=True)

k-means returns nan values?

I recently came across a k-means tutorial that looks a bit different than what I remember the algorithm to be, but it should still do the same after all it's k-means. So, I went and gave it a try with some data, here's how the code looks:
# Assignment Stage:
def assignment(data, centroids):
for i in centroids.keys():
#sqrt((x1-x2)^2+(y1-y2)^2 + etc)
data['distance_from_{}'.format(i)]= (
np.sqrt((data['soloRatio']-centroids[i][0])**2
+(data['secStatus']-centroids[i][1])**2
+(data['shipsDestroyed']-centroids[i][2])**2
+(data['combatShipsLost']-centroids[i][3])**2
+(data['miningShipsLost']-centroids[i][4])**2
+(data['exploShipsLost']-centroids[i][5])**2
+(data['otherShipsLost']-centroids[i][6])**2
))
print(data['distance_from_{}'.format(i)])
centroid_distance_cols = ['distance_from_{}'.format(i) for i in centroids.keys()]
data['closest'] = data.loc[:, centroid_distance_cols].idxmin(axis=1)
data['closest'] = data['closest'].astype(str).str.replace('\D+', '')
return data
data = assignment(data, centroids)
and:
#Update stage:
import copy
old_centroids = copy.deepcopy(centroids)
def update(k):
for i in centroids.keys():
centroids[i][0]=np.mean(data[data['closest']==i]['soloRatio'])
centroids[i][1]=np.mean(data[data['closest']==i]['secStatus'])
centroids[i][2]=np.mean(data[data['closest']==i]['shipsDestroyed'])
centroids[i][3]=np.mean(data[data['closest']==i]['combatShipsLost'])
centroids[i][4]=np.mean(data[data['closest']==i]['miningShipsLost'])
centroids[i][5]=np.mean(data[data['closest']==i]['exploShipsLost'])
centroids[i][6]=np.mean(data[data['closest']==i]['otherShipsLost'])
return k
#TODO: add graphical representation?
while True:
closest_centroids = data['closest'].copy(deep=True)
centroids = update(centroids)
data = assignment(data,centroids)
if(closest_centroids.equals(data['closest'])):
break
When I run the initial assignment stage, it returns the distances, however when I run the update stage, all distance values become NaN, and I just dont know why or at which point exactly this happens... Maybe I made I mistake I can't spot?
Here's an excerpt of the data im working with:
Unnamed: 0 characterID combatShipsLost exploShipsLost miningShipsLost \
0 0 90000654.0 8.0 4.0 5.0
1 1 90001581.0 97.0 5.0 1.0
2 2 90001595.0 61.0 0.0 0.0
3 3 90002023.0 22.0 1.0 0.0
4 4 90002030.0 74.0 0.0 1.0
otherShipsLost secStatus shipsDestroyed soloRatio
0 0.0 5.003100 1.0 10.0
1 0.0 2.817807 6251.0 6.0
2 0.0 -2.015310 752.0 0.0
3 4.0 5.002769 43.0 5.0
4 1.0 3.090204 301.0 7.0

How to use incremental PCA on dask dataframe?

I am using a dask dataframe which can not be loaded directly into the memory because of the size of it. I want to perform dimentionality reduction of top of using incremental PCA.
My dataframe is sparse in nature, so the question is can I perform it and if yes then how to do so.
image_features_df.head(3)
feat1 feat2 feat3 ... feat25087 feat25088 fid selling_price
0 0.0 0.0 0.0 ... 0.0 0.0 2 269.00
4 0.3 0.1 0.0 ... 0.0 0.8 26 1720.00
6 0.8 0.0 0.0 ... 0.0 0.1 50 18145.25
The above is a view of my dataframe. I want the output to have 95% cumulative varience. How to do so?
My dataframe has 100,000 rows and 25088 columns so please tell a solution which is memory efficient.
Have a look at the PCA implementation in dask-ML, https://ml.dask.org/modules/generated/dask_ml.decomposition.PCA.html,
this might already work for your case, as it uses the tsqr algorithm (https://arxiv.org/abs/1301.1071)

Retrieve TF-IDF Values for Textual Documents in CSV File

I have a CSV file with two columns (no header), held in a variable known as 'dataset':
Year Document Text
0 ['1991'] ['FACTSHEET ', 'WHAT ', 'IS ', 'AIDS', 'AIDS '...
1 ['1991'] ['HIV ', 'IT', "'S ", 'YOUR ', 'CHOICE', 'Ever...
2 ['1991'] ['ACET ', 'AIDS ', 'CARE ', 'EDUCATION ', 'AND...
I'm attempting to construct a Bag of Words model using Scikit-learn and gather the weightings using TF-IDF. However, I'm having difficulty with obtaining actual results, as the output of the code below returns 2480 rows (correct) * 346862 columns (corrected by #Jarad). I would appreciate someone helping me decipher these results, and point me in the right direction as to their formatting (to provide clarity) or correction (to provide validity) so that I can progress towards the later stages of a Bag of Words model implementation.
Python Code:
from sklearn.feature_extraction.text import TfidfVectorizer
v = TfidfVectorizer()
x = v.fit_transform(dataset.iloc[:,1])
df1 = pd.DataFrame(x.toarray(), columns=v.get_feature_names())
print(df1)
Output:
00 000 0000 00000 00000000 00000001 0000001 00001
0 0.000000 0.011453 0.000000 0.0 0.0 0.0 0.0 0.0
1 0.000000 0.022032 0.000000 0.0 0.0 0.0 0.0 0.0
2 0.006352 0.009717 0.000000 0.0 0.0 0.0 0.0 0.0
3 0.001422 0.015949 0.000000 0.0 0.0 0.0 0.0 0.0
4 0.000000 0.002377 0.000000 0.0 0.0 0.0 0.0 0.0
Should I tokenize the document prior to storing it in the CSV file? I decided against it due to the fact that I would hope to analyse sentence structure at a later stage as well.

Categories