<jupyter_start><jupyter_text>Introduction Formally, a stream graph $S = (T,V,W,E)$ is defined by a set of time instants $T$, a finite set of nodes $V$, a set of temporal nodes $W \subseteq T \times V$, and a set of temporal links $E\subseteq T \times V \times V$.The set of time instants $T$ can be continuous or discrete. Likewise interactions (temporal links) between two nodes can be discrete $(b,b,u,v)$ or continuous $(b,e,u,v)$ (with $b,e \in T$ and $u,v \in V$). Stream graphs can be used to model any connected structure evolving through time. For instance, IP traffic between entities can be modelised as follow: whenever two IP adresses exchanges packets we record a temporal link between these two nodes corresponding to the duration of the exchange. First of all, we load an artificial example that will be used in the following steps of analysis and visualisation.<jupyter_code>path_directory = "examples/"
S = sg.read_stream_graph(path_nodes=path_directory + "example_nodes.sg",
                         path_links=path_directory + "example_links.sg")<jupyter_output><empty_output><jupyter_text>Basic visualisation<jupyter_code>fig = S.plot()<jupyter_output><empty_output><jupyter_text>We refer to this [Notebook](Drawing.ipynb) for more details on visualisation.  Stream Graph Object **Paradigme**: In ``Straph`` simple data structures should be represented by built-in python objects, resulting in a more comprehensive and intuitive code.After a comparative analysis we choose to use the following data structures for manipulating stream graphs. (As numerous algorithms and basic computations are not based on vectorials operations ``Numpy`` arrays were deemed to slow.) A ``StreamGraph`` object is constituted by five main attributes:- ```times```: the time window of the stream graph ($T$)- ```nodes```: the list of nodes present in the stream graph ($V$)- ```node_presence```: a list of list, each list corresponds to a node and contains its presence time ($W$)- ```links```: the list of links present in the stream graph ($E$)- ```node_presence```: a list of list, each list corresponds to a link and contains its presence time ($E$)<jupyter_code>S.times<jupyter_output><empty_output><jupyter_text>The stream graph spans from instant $0$ to $10$<jupyter_code>S.nodes<jupyter_output><empty_output><jupyter_text>``S`` contains $6$ nodes.Nodes are always represented by integers, their labels can be stored in the attribute ```node_to_label```.<jupyter_code>S.node_to_label
S.node_presence<jupyter_output><empty_output><jupyter_text>As we can see on the above figure, node $A$ with index $0$ is present from time $0$ to $5$, absent from $5$ to $7$ and present again from $7$ to $10$.<jupyter_code>S.links
S.link_presence<jupyter_output><empty_output><jupyter_text>The link $(4,5)$ (corresponding to nodes $E$ and $F$) with index $5$ is active from time $0$ to $3$ and again from $7$ to $10$. A short description of a stream graph scale can be obtained with ``.describe()``<jupyter_code>S.describe()<jupyter_output>Nb of Nodes :  6
Nb of segmented nodes :  11.0
Nb of links :  8
Nb of segmented links :  11.0
Nb of event times :  11<jupyter_text>We can add or remove nodes and links to/from a ``stream_graph`` object (we refer to this [notebook](Readers%20and%20Writers%20(Input-Output).ipynb) for further information).We can easily add a new node. Let's add $G$ present from $0$ to $3$ and from $8$ to $9$.<jupyter_code>S.add_node('G',[0,3,8,9])<jupyter_output><empty_output><jupyter_text>Likewise we can add a new link. If one of the extrimities is new, it will be added automatically for the duration of the link.Let's add a link $(G,H)$ from $1$ to $3$ and from $8$ to $9$.<jupyter_code>S.add_link(('G','H'),[1,3,8,9])
_ = S.plot()<jupyter_output><empty_output><jupyter_text>Let's remove these new interactions between nodes $G$ and $H$.<jupyter_code>S.remove_link(('G','H'))
_ = S.plot()<jupyter_output><empty_output><jupyter_text>*Note* : If we remove a node, all of its links will be automatically removed.<jupyter_code>S.remove_node('G')
S.remove_node('H')
S.plot()
plt.show()<jupyter_output><empty_output><jupyter_text>Manipulating a Stream Graph Object There are several manners to manipulate a ``StreamGraph`` object:   - Iterate on nodes    - Iterate on links   - Iterate on temporally ordered links   - Iterate on temporally ordered events<jupyter_code>for n,np in zip(S.nodes,S.node_presence):
    for b,e in zip(np[::2],np[1::2]): # Even index are arrivals and odd index departure
        print("Node ",S.node_to_label[n]," is present from ",b," to ",e)
for l,lp in zip(S.links,S.link_presence):
    for b,e in zip(lp[::2],lp[1::2]): # Even index are arrivals and odd index departure
        u,v = l
        print("Link ",(S.node_to_label[u],S.node_to_label[v])," is present from ",b," to ",e)
for e in S.ordered_links():
    if e[0] == 1:
        _, t0, t1, u, v = e
        print("Link arrival \t:",(t0,t1,S.node_to_label[u],S.node_to_label[v]))
    if e[0] == -1:
        _, t1, u, v = e
        print("Link departure \t:",(t1,S.node_to_label[u],S.node_to_label[v]))
for e in S.ordered_events():
    if e[0] == 2:
        _,t0,t1,u = e
        print("Node arrival \t:",(t0,t1,S.node_to_label[u]))
    elif e[0] == 1:
        _, t0, t1, u, v = e
        print("Link arrival \t:",(t0,t1,S.node_to_label[u],S.node_to_label[v]))
    elif e[0] == -1:
        _, t1, u, v = e
        print("Link departure \t:",(t1,S.node_to_label[u],S.node_to_label[v]))
    elif e[0] == -2:
        _,t1,u = e
        print("Node departure \t:",(t1,S.node_to_label[u]))<jupyter_output>Node arrival 	: (0.0, 5.0, 'A')
Node arrival 	: (0.0, 10.0, 'B')
Node arrival 	: (0.0, 1.0, 'C')
Node arrival 	: (0.0, 1.0, 'D')
Node arrival 	: (0.0, 10.0, 'E')
Node arrival 	: (0.0, 4.0, 'F')
Link arrival 	: (0.0, 4.0, 'A', 'B')
Link arrival 	: (0.0, 1.0, 'C', 'D')
Link arrival 	: (0.0, 4.0, 'E', 'F')
Link departure 	: (1.0, 'C', 'D')
Node departure 	: (1.0, 'C')
Node departure 	: (1.0, 'D')
Node arrival 	: (2.0, 4.0, 'D')
Link arrival 	: (2.0, 4.0, 'D', 'E')
Link arrival 	: (3.0, 4.0, 'D', 'F')
Node arrival 	: (4.0, 6.0, 'C')
Link arrival 	: (4.0, 5.0, 'B', 'C')
Link departure 	: (4.0, 'A', 'B')
Link departure 	: (4.0, 'D', 'E')
Link departure 	: (4.0, 'D', 'F')
Link departure 	: (4.0, 'E', 'F')
Node departure 	: (4.0, 'D')
Node departure 	: (4.0, 'F')
Link arrival 	: (5.0, 5.0, 'C', 'E')
Link departure 	: (5.0, 'B', 'C')
Link departure 	: (5.0, 'C', 'E')
Node departure 	: (5.0, 'A')
Node arrival 	: (6.0, 10.0, 'F')
Link arrival 	: (6.0, 10.0, 'E', 'F')
Node departure 	: (6.0, 'C')
[...]
{'fraction_non_alphanumeric': 0.1552324700893533, 'fraction_numerical': 0.028169014084507043, 'mean_word_length': 4.321514907332796, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '12', 'dir': 'jupyter-structured-clean-dedup', 'id': '434805', 'license': 'Apache-2.0', 'n_tokens_mistral': 2473, 'n_tokens_neox': 2408, 'n_words': 804, 'path': 'docs/_build/html/notebooks/Introduction.ipynb', 'repo_name': 'GiulioRossetti/Straph', 'size': '6603'}