Skip to content

Commit

Permalink
Feature/history datetime (#1420)
Browse files Browse the repository at this point in the history
* added history_date_tine and earliest/latest_date_time to node

* fmt

* Added functions onto nodes and edges

* Added third node in the wrong spot

* Added small fixes to docs and tidied up warnings

* fmt

* Fix for Lucas Review

* fmt
  • Loading branch information
miratepuffin authored Dec 20, 2023
1 parent cdd95ed commit c679a92
Show file tree
Hide file tree
Showing 25 changed files with 895 additions and 416 deletions.
8 changes: 8 additions & 0 deletions docs/source/reference/core/edge.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ Edge
:inherited-members:

.. autoclass:: raphtory.Edges
:autosummary:
:members:
:undoc-members:
:show-inheritance:
:private-members:
:inherited-members:

.. autoclass:: raphtory.MutableEdge
:autosummary:
:members:
:undoc-members:
Expand Down
25 changes: 25 additions & 0 deletions docs/source/reference/core/node.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Node
*****************
.. autoclass:: raphtory.Node
:autosummary:
:members:
:undoc-members:
:show-inheritance:
:private-members:
:inherited-members:

.. autoclass:: raphtory.Nodes
:autosummary:
:members:
:undoc-members:
:show-inheritance:
:private-members:
:inherited-members:

.. autoclass:: raphtory.MutableNode
:autosummary:
:members:
:undoc-members:
:show-inheritance:
:private-members:
:inherited-members:
195 changes: 117 additions & 78 deletions python/python/raphtory/plottingutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,63 @@
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox

motif_im_dir = Path(__file__).parents[1].absolute().as_posix()+"/motif-images/"
motif_im_dir = Path(__file__).parents[1].absolute().as_posix() + "/motif-images/"

def get_motif(xory:str,y:int):
path = motif_im_dir+xory+str(y)+".png"

def get_motif(xory: str, y: int):
path = motif_im_dir + xory + str(y) + ".png"
return plt.imread(path)


def get_motif_labels(motif_map):
return np.vectorize(human_format)(motif_map)


def offset_image(xory, coord, name, ax):
img = get_motif(xory, name)
im = OffsetImage(img,zoom=0.04)
im = OffsetImage(img, zoom=0.04)
im.image.axes = ax

if(xory=="x"):
ab = AnnotationBbox(im, (coord+0.5, 5.5), xybox=(0., -40.), frameon=False,
xycoords='data', boxcoords="offset points", box_alignment=(0.5,0.5), pad=0)
if xory == "x":
ab = AnnotationBbox(
im,
(coord + 0.5, 5.5),
xybox=(0.0, -40.0),
frameon=False,
xycoords="data",
boxcoords="offset points",
box_alignment=(0.5, 0.5),
pad=0,
)

else:
ab = AnnotationBbox(im, (0, coord), xybox=(0., -40.), frameon=False,
xycoords='data', boxcoords="offset points", box_alignment=(1.0,0.0), pad=0)
ab = AnnotationBbox(
im,
(0, coord),
xybox=(0.0, -40.0),
frameon=False,
xycoords="data",
boxcoords="offset points",
box_alignment=(1.0, 0.0),
pad=0,
)

ax.add_artist(ab)

ax.add_artist(ab)

def add_motifs_to_ax(ax):
for i in range(6):
offset_image("x",i,i,ax)
offset_image("y",i,i,ax)
offset_image("x", i, i, ax)
offset_image("y", i, i, ax)


def global_motif_heatplot(motifs, cmap="YlGnBu", **kwargs):
"""
Out-of-the-box plotting of global motif counts corresponding to the layout in Motifs in Temporal Networks (Paranjape et al)
Args:
motifs(list/np.array): 1 dimensional length-40 array of motifs, which should be the list of motifs returned from the `global_temporal_three_node_motifs` function in Raphtory.
**kwargs: arguments to
**kwargs: arguments to
Returns:
matplotlib.axes: ax item containing the heatmap with motif labels on the axes.
Expand All @@ -53,71 +74,85 @@ def global_motif_heatplot(motifs, cmap="YlGnBu", **kwargs):
motif_matrix = to_motif_matrix(motifs)
labels = get_motif_labels(motif_matrix)

ax = sns.heatmap(motif_matrix,square=True, cbar=True, cmap=cmap, annot=labels, annot_kws={"size":13}, fmt='', cbar_kws={"shrink": 1.0}, **kwargs)
ax = sns.heatmap(
motif_matrix,
square=True,
cbar=True,
cmap=cmap,
annot=labels,
annot_kws={"size": 13},
fmt="",
cbar_kws={"shrink": 1.0},
**kwargs,
)
add_motifs_to_ax(ax)
ax.tick_params(axis='x', which='major', pad=50)
ax.tick_params(axis='y', which='major', pad=50)
ax.tick_params(axis="x", which="major", pad=50)
ax.tick_params(axis="y", which="major", pad=50)
plt.setp(ax.get_xticklabels(), visible=False)
plt.setp(ax.get_yticklabels(), visible=False)
plt.tight_layout()
return ax


def to_motif_matrix(motifs, data_type=int):
"""
Converts a 40d vector of global motifs to a 2d grid of motifs corresponding to the layout in Motifs in Temporal Networks (Paranjape et al)
Args:
motifs(list/np.array): 1 dimensional length-40 array of motifs.
motifs(list/np.array): 1 dimensional length-40 array of motifs.
Returns:
numpy.array: 6x6 array of motifs whose ijth element is M_ij in Motifs in Temporal Networks (Paranjape et al).
"""
mapper = {0:(5,5),
1:(5,4),
2:(4,5),
3:(4,4),
4:(4,3),
5:(4,2),
6:(5,3),
7:(5,2),
8:(0,0),
9:(0,1),
10:(1,0),
11:(1,1),
12:(2,1),
13:(2,0),
14:(3,1),
15:(3,0),
16:(0,5),
17:(0,4),
18:(1,5),
19:(1,4),
20:(2,3),
21:(2,2),
22:(3,3),
23:(3,2),
24:(5,0),
25:(5,1),
26:(4,0),
27:(4,1),
28:(4,1),
29:(4,0),
30:(5,1),
31:(5,0),
32:(0,2),
33:(2,4),
34:(1,2),
35:(3,4),
36:(0,3),
37:(2,5),
38:(1,3),
39:(3,5)}

motif_2d = np.zeros((6,6),dtype=data_type)
mapper = {
0: (5, 5),
1: (5, 4),
2: (4, 5),
3: (4, 4),
4: (4, 3),
5: (4, 2),
6: (5, 3),
7: (5, 2),
8: (0, 0),
9: (0, 1),
10: (1, 0),
11: (1, 1),
12: (2, 1),
13: (2, 0),
14: (3, 1),
15: (3, 0),
16: (0, 5),
17: (0, 4),
18: (1, 5),
19: (1, 4),
20: (2, 3),
21: (2, 2),
22: (3, 3),
23: (3, 2),
24: (5, 0),
25: (5, 1),
26: (4, 0),
27: (4, 1),
28: (4, 1),
29: (4, 0),
30: (5, 1),
31: (5, 0),
32: (0, 2),
33: (2, 4),
34: (1, 2),
35: (3, 4),
36: (0, 3),
37: (2, 5),
38: (1, 3),
39: (3, 5),
}

motif_2d = np.zeros((6, 6), dtype=data_type)
for i in range(40):
motif_2d[mapper[i]]=motifs[i]
motif_2d[mapper[i]] = motifs[i]
return motif_2d


def human_format(num):
"""
Converts a number over 1000 to a string with 1 d.p and the corresponding letter. e.g. with input 24134, 24.1k as a string would be returned. This is used in the motif plots to make annotated heatmap cells more concise.
Expand All @@ -133,13 +168,15 @@ def human_format(num):
magnitude += 1
num /= 1000.0
# add more suffixes if you need them
return '%.1f%s' % (num, ['', 'K', 'M', 'B', 'T', 'P'][magnitude])
return "%.1f%s" % (num, ["", "K", "M", "B", "T", "P"][magnitude])


# Relating to distributions


def cdf(observations, normalised=True):
"""
Returns x coordinates and y coordinates for a cdf (cumulative density function) from a list of observations.
Returns x coordinates and y coordinates for a cdf (cumulative density function) from a list of observations.
Args:
observations(list): list of observations, should be numeric
Expand All @@ -149,19 +186,20 @@ def cdf(observations, normalised=True):
list(float): x coordinates for the cdf
list(float): y coordinates for the cdf
"""
data = np.array(listlike,dtype=object)
data = np.array(listlike, dtype=object)
N = len(listlike)

x = np.sort(data)
if (normalised):
y = np.arange(N)/float(N-1)
if normalised:
y = np.arange(N) / float(N - 1)
else:
y = np.arange(N)
return x, y


def ccdf(observations, normalised=True):
"""
Returns x coordinates and y coordinates for a ccdf (complementary cumulative density function) from a list of observations.
Returns x coordinates and y coordinates for a ccdf (complementary cumulative density function) from a list of observations.
Args:
observations(list): list of observations, should be numeric
Expand All @@ -171,16 +209,16 @@ def ccdf(observations, normalised=True):
list(float): x coordinates for the cdf
list(float): y coordinates for the cdf
"""
x, y = cdf(listlike,normalised)
x, y = cdf(listlike, normalised)
if normalised:
return x, 1.0-y
return x, 1.0 - y
else:
return x, len(listlike)-y
return x, len(listlike) - y


def lorenz(observations):
def lorenz(observations):
"""
Returns x coordinates and y coordinates for a Lorenz Curve from a list of observations.
Returns x coordinates and y coordinates for a Lorenz Curve from a list of observations.
Args:
observations(list): list of observations, should be numeric
Expand All @@ -191,9 +229,10 @@ def lorenz(observations):
"""
tmp_arr = np.array(sorted(listlike))
# print(tmp_arr[0])
x= np.arange(listlike.size)/(listlike.size -1)
x = np.arange(listlike.size) / (listlike.size - 1)
y = tmp_arr.cumsum() / tmp_arr.sum()
return x,y
return x, y


def ordinal_number(number):
"""
Expand All @@ -206,9 +245,9 @@ def ordinal_number(number):
str: ordinal for that number as string
"""
if 10 <= number % 100 <= 20:
suffix = 'th'
suffix = "th"
else:
suffixes = {1: 'st', 2: 'nd', 3: 'rd'}
suffix = suffixes.get(number % 10, 'th')
return str(number) + suffix
suffixes = {1: "st", 2: "nd", 3: "rd"}
suffix = suffixes.get(number % 10, "th")

return str(number) + suffix
3 changes: 2 additions & 1 deletion python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use pyo3::prelude::*;
use raphtory_core::python::{
graph::{
algorithm_result::AlgorithmResult,
edge::{PyDirection, PyEdge, PyEdges},
edge::{PyDirection, PyEdge, PyEdges, PyMutableEdge},
graph::PyGraph,
graph_with_deletions::PyGraphWithDeletions,
index::GraphIndex,
Expand Down Expand Up @@ -50,6 +50,7 @@ fn raphtory(py: Python<'_>, m: &PyModule) -> PyResult<()> {
PyMutableNode,
PyEdge,
PyEdges,
PyMutableEdge,
PyProperties,
PyConstProperties,
PyTemporalProperties,
Expand Down
7 changes: 4 additions & 3 deletions python/tests/test_algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,8 +473,9 @@ def test_temporal_SEIR():
seeded = [v for v in res.get_all_values() if v.infected == 0]
assert len(seeded) == 2

res = algorithms.temporal_SEIR(g, [1], 1.0, 0, rng_seed=1).sort_by_value(reverse=False)
res = algorithms.temporal_SEIR(g, [1], 1.0, 0, rng_seed=1).sort_by_value(
reverse=False
)
for i, (n, v) in enumerate(res):
assert n == g.node(i+1)
assert n == g.node(i + 1)
assert v.infected == i

Loading

0 comments on commit c679a92

Please sign in to comment.