Example #1
0
    def aggregate_time_period(self,
                              period,
                              step_to_datetime=datetime.utcfromtimestamp):
        """
        Aggregate graph by time period (day, year, ...)

        Return a new dynamic graph without modifying the original one, aggregated such as all
        Yielded graphs are weighted (weight: number of apparition of edges during the period)

        :param period: either a string (minute,hour,day,week,month,year) or a function returning the timestamp truncated to the start of the desired period
        :param step_to_datetime: function to convert time step to a datetime object, default is utfromtimestamp
        :return: a DynGraph_SN object
        """
        to_return = tn.DynGraphSN()

        if isinstance(period, str):
            period_func = lambda x: self._date_threasholded(x, period)
        else:
            period_func = period

        for t, g in self.snapshots().items():
            t_date = step_to_datetime(t)

            new_t = period_func(t_date)

            #new_t = new_t.timestamp()
            new_t = int(new_t.replace(tzinfo=timezone.utc).timestamp())
            if not new_t in to_return.snapshots():
                to_return.add_snapshot(new_t, g)
            to_return._snapshots[new_t] = self._combine_weighted_graphs(
                [to_return.snapshots(new_t),
                 self.snapshots(t)])

        return to_return
Example #2
0
def read_snapshots(inputDir: str,
                   format=None,
                   frequency=1,
                   prefix="") -> DynGraphSN:
    """
    Read as one file per snapshot
    
    Read a dynamic graph as a directory containing one file per snapshot. 
    If the format is not provided, it is infered automatically from file extensions

    :param inputDir: directory where the files are located
    :param format: a string among edges(edgelist)|ncol|gefx|gml|pajek|graphML, by default, the extension of the files
    :return: a DynGraphSN object
    """

    anSnGraph = tn.DynGraphSN(frequency=frequency)
    files = os.listdir(inputDir)
    visibleFiles = [f for f in files if f[0] != "."]

    if format == None:
        format = _detectAutomaticallyFormat(visibleFiles[0])

    for f in visibleFiles:
        g = _read_network_file(inputDir + "/" + f, format)  # type:nx.Graph
        anSnGraph.add_snapshot(int(os.path.splitext(f)[0][len(prefix):]), g)

    return anSnGraph
Example #3
0
    def to_DynGraphSN(self,slices=None,discard_empty=True):
        """
        Convert to a snapshot representation.

        :param slices: can be one of

        - None, snapshot_affiliations are created such as a new snapshot is created at every node/edge change,
        - an integer, snapshot_affiliations are created using a sliding window
        - a list of periods, represented as pairs (start, end), each period yielding a snapshot
        :param discard_empty: if True, the returned dynamic network won't have empty snapshots

        :return: a dynamic graph represented as snapshot_affiliations, the weight of nodes/edges correspond to their presence time during the snapshot

        """
        dgSN = tn.DynGraphSN()
        if slices==None:
            freq = self.frequency()
            slices = freq
            #times = self.change_times()

        if isinstance(slices,int):
            duration = slices
            dgSN.frequency(slices)

            slices = []
            start = self._start
            end = start+duration
            slices.append((start, end))
            while(end<=self._end):
                start=end
                end = start+duration
                slices.append((start, end))

        for ts in slices:
            dgSN.add_snapshot(t=ts[0],graphSN=nx.Graph())

        sorted_times = [x[0] for x in slices]
        to_add = {}

        for n,interv in self.node_presence().items():
            intersection = interv._discretize(sorted_times)
            for t, duration in intersection.items():
                to_add.setdefault(t, []).append((n, {"weight": duration}))
        for t in to_add:
            dgSN.snapshots(t).add_nodes_from(to_add[t])

        to_add = {}
        for e,interv in self.interactions_intervals().items():
            elist=list(e)
            intersection = interv._discretize(sorted_times)
            for t,duration in intersection.items():
                to_add.setdefault(t,[]).append((elist[0],elist[1],{"weight":duration}))
        for t in to_add:
            dgSN.snapshots(t).add_edges_from(to_add[t])

        if discard_empty:
            dgSN.discard_empty_snapshots()

        return dgSN
Example #4
0
    def test_add_nodes(self):
        dg = dn.DynGraphSN()
        dg.add_node_presence(1, 1)
        dg.add_node_presence(2, 1)
        dg.add_nodes_presence_from(3, [1, 2, 3, 4])
        dg.remove_node_presence(2, 1)

        self.assertEqual(len(dg.snapshots()), 4)
        self.assertEqual(list(dg.snapshots()[1].nodes), [1, 3])
Example #5
0
    def test_remove_functions(self):
        dg = dn.DynGraphSN([nx.karate_club_graph(), nx.karate_club_graph()])
        dg.remove_interaction(1, 2, 1)

        g = nx.karate_club_graph()
        g.remove_edge(1, 2)
        self.assertEqual(set(g.edges), set(dg.snapshots(1).edges))

        dg = dn.DynGraphSN([nx.karate_club_graph(), nx.karate_club_graph()])
        g = nx.karate_club_graph()
        es = list(g.edges)[:3]
        print(es)
        dg.remove_interactions_from(es, 0)

        self.assertEqual(set(g.edges), set(dg.snapshots(1).edges))
        g.remove_edges_from(es)

        self.assertEqual(set(g.edges), set(dg.snapshots(0).edges))
Example #6
0
    def to_DynGraphSN(self, slices=None):
        """
        Convert to a snapshot representation.

        :param slices: can be one of

        - None, snapshot_affiliations are created such as a new snapshot is created at every node/edge change,
        - an integer, snapshot_affiliations are created using a sliding window
        - a list of periods, represented as pairs (start, end), each period yielding a snapshot

        :return: a dynamic graph represented as snapshot_affiliations, the weight of nodes/edges correspond to their presence time during the snapshot

        """
        dgSN = tn.DynGraphSN()
        if slices == None:
            times = self.change_times()
            slices = [(times[i], times[i + 1]) for i in range(len(times) - 1)]

        if isinstance(slices, int):
            duration = slices
            slices = []
            start = self._start
            end = start + duration
            while (end <= self._end):
                end = start + duration
                slices.append((start, end))
                start = end
                end = end + duration

        for ts in slices:
            dgSN.add_snapshot(t=ts[0], graphSN=nx.Graph())

        sorted_times = [x[0] for x in slices]
        #print(self.node_presence())
        to_add = {}

        for n, interv in self.node_presence().items():
            intersection = interv._discretize(sorted_times)
            for t, duration in intersection.items():
                to_add.setdefault(t, []).append((n, {"weight": duration}))
        for t in to_add:
            dgSN.snapshots(t).add_nodes_from(to_add[t])

        to_add = {}
        for e, interv in self.interactions().items():
            intersection = interv._discretize(sorted_times)
            for t, duration in intersection.items():
                to_add.setdefault(t, []).append((e[0], e[1], {
                    "weight": duration
                }))
        for t in to_add:
            dgSN.snapshots(t).add_edges_from(to_add[t])

        return dgSN
def generate_multi_temporal_scale(nb_steps=5000,
                                  nb_nodes=100,
                                  nb_com=10,
                                  noise=None,
                                  max_com_size=None,
                                  max_com_duration=None):
    """
    Generate dynamic graph with stable communities

    :param nb_steps: steps in the graph
    :param nb_nodes: total nb nodes
    :param nb_com: nb desired communities
    :param noise: random noise at each step, i.e. probability for any edge to exist at any step. default,1/(nb_nodes**2)
    :param max_com_size: max number of nodes. Default: nb_nodes/4
    :param max_com_duration: max community duration. Default: nb_steps/2
    :return:
    """

    to_return_graph = tn.DynGraphSN()
    to_return_com = tn.DynCommunitiesSN()

    if noise == None:
        noise = 1 / (nb_nodes * nb_nodes
                     )  #in average, 1 random interaction per timestep

    if max_com_duration == None:
        max_com_duration = nb_steps / 2

    if max_com_size == None:
        max_com_size = int(nb_nodes / 4)

    #initialise each step with a random graph with noise
    for i in range(nb_steps):
        to_return_graph.add_snapshot(i, nx.erdos_renyi_graph(nb_nodes, noise))

    #for each desired community
    for n in range(nb_com):
        #get a random size
        size = np.random.uniform(np.log(4), np.log(max_com_size))
        size = int(np.exp(size))

        #get a random duration
        duration = np.random.uniform(np.log(10), np.log(max_com_duration))
        duration = int(np.exp(duration))

        #We whoose the clique frequency so that all communities last long enough to be detectable
        cliques_frequency = 10 / duration

        _generate_a_community(to_return_graph, to_return_com, size, duration,
                              cliques_frequency)

    return (to_return_graph, to_return_com)
Example #8
0
    def slice(self, start, end):
        """
        Keep only the selected period

        :param start:
        :param end:
        """

        to_return = tn.DynGraphSN()
        interv = tn.Intervals((start, end))
        for t in list(self._snapshots.keys()):
            if interv.contains_t(t):
                to_return.add_snapshot(t, self._snapshots[t])
        return to_return
Example #9
0
    def test_add_nodes_sn(self):
        dg = tn.DynGraphSN()
        dg.add_node_presence(1,1)
        dg.add_node_presence(2,1)
        dg.add_nodes_presence_from(3,[1,2,3,4,5,6])
        dg.add_nodes_presence_from([4,5],1)
        dg.add_nodes_presence_from([6, 7], [1,2])

        dg.remove_node_presence(2,1)
        dg.remove_node_presence(3, 2)

        self.assertEqual(set(dg.graph_at_time(1).nodes()),set([1,3,4,5,6,7]))
        self.assertEqual(set(dg.graph_at_time(2).nodes()),set([6,7]))
        self.assertEqual(set(dg.graph_at_time(3).nodes()),set([3]))
        self.assertEqual(set(dg.graph_at_time(4).nodes()),set([3]))
        self.assertEqual(set(dg.graph_at_time(6).nodes()),set([3]))
Example #10
0
    def to_DynGraphSN(self, slices=None):
        """
        Convert to a snapshot representation.

        :param slices: can be one of

        - None, snapshot_affiliations are created such as a new snapshot is created at every node/edge change,
        - an integer, snapshot_affiliations are created using a sliding window
        - a list of periods, represented as pairs (start, end), each period yielding a snapshot

        :return: a dynamic graph represented as snapshot_affiliations, the weight of nodes/edges correspond to their presence time during the snapshot

        """
        dgSN = tn.DynGraphSN()
        if slices == None:
            times = self.change_times()
            slices = [(times[i], times[i + 1]) for i in range(len(times) - 1)]

        if isinstance(slices, int):
            duration = slices
            slices = []
            start = self.start
            end = start + duration
            while (end <= self.end):
                end = start + duration
                slices.append((start, end))
                start = end
                end = end + duration
        for ts in slices:
            dgSN.add_snapshot(t=ts[0], graphSN=nx.Graph())

        for n, interv in self.node_presence().items():
            for ts in slices:
                presence = interv.intersection(Intervals([ts])).duration()
                if presence > 0:
                    dgSN.snapshots(ts[0]).add_node(n, weight=presence)

        for e, interv in self.interactions().items():
            for ts in slices:
                presence = interv.intersection(Intervals([ts])).duration()
                if presence > 0:
                    dgSN.snapshots(ts[0]).add_edge(e[0], e[1], weight=presence)

        return dgSN
Example #11
0
    def test_add_functions(self):
        dg = dn.DynGraphSN()
        dg.add_interaction(1, 2, 4)

        self.assertEqual(len(dg.snapshots()), 1)
        self.assertEqual(list(dg.snapshots()[4].edges), [(1, 2)])

        dg.add_interaction(1, 2, 6)

        self.assertEqual(dg.snapshots_timesteps(), [4, 6])

        dg.add_interactions_from((2, 3), [6, 7, 8])

        self.assertEqual(dg.snapshots_timesteps(), [4, 6, 7, 8])
        self.assertEqual(list(dg.snapshots()[6].edges), [(1, 2), (2, 3)])

        dg.add_interactions_from([(5, 6), (6, 7)], [9, 10])
        self.assertEqual(list(dg.snapshots()[9].edges), [(5, 6)])
        self.assertEqual(list(dg.snapshots()[10].edges), [(6, 7)])
Example #12
0
def from_pandas_interaction_list(interactions,
                                 format,
                                 frequency=1,
                                 source="n1",
                                 target="n2",
                                 time="time"):
    interactions = two_columns2unidrected_edge(interactions,
                                               source="n1",
                                               target="n2")
    if format == tn.DynGraphSN:
        all_times = set(interactions[time])
        all_graphs = {}
        for t in all_times:
            this_t = interactions[interactions["time"] == t]
            all_graphs[t] = nx.from_pandas_edgelist(this_t,
                                                    source=source,
                                                    target=target)

        return tn.DynGraphSN(all_graphs, frequency=frequency)

    if format == tn.DynGraphLS:
        #all_edges = set(interactions["e"])
        #print(len(all_edges))
        edges_time = {}
        for i, row in interactions.iterrows():
            edges_time.setdefault(row["e"], []).append(row[time])
        #for e in all_edges:
        #    edges_time[e]= list(interactions[interactions["e"]==e][time])
        to_return = tn.DynGraphLS(edges=edges_time, frequency=frequency)
        return to_return

    if format == tn.DynGraphIG:
        #all_edges = set(interactions["e"])
        edges_time = {}
        for i, row in interactions.iterrows():
            edges_time.setdefault(row["e"], []).append(row[time])
        for e, v in edges_time.items():
            edges_time[e] = tn.Intervals.from_time_list(v, frequency)
        #for e in all_edges:
        #    edges_time[e]= list(interactions[interactions["e"]==e][time])
        #    edges_time[e]=tn.Intervals.from_time_list(edges_time)
        to_return = tn.DynGraphIG(edges_time)
        return to_return
Example #13
0
    def test_add_interactions_sn(self):
        dg = tn.DynGraphSN()
        dg.add_interaction(1,2,4)

        self.assertEqual(set(dg.graph_at_time(4).edges()),set([(1,2)]))

        dg.add_interaction(1, 2, 6)

        self.assertEqual(dg.edge_presence((1,2)),[4,6])


        dg.add_interactions_from({2, 3}, [6,7,8])

        self.assertEqual(list(dg.graph_at_time(6).edges()),[(1,2),(2,3)])

        dg.add_interactions_from([(5, 6),(6,7)], 9)
        self.assertEqual(list(dg.graph_at_time(9).edges()),[(5,6),(6,7)])

        dg.add_interactions_from([(1, 2),(1,3)], [10,11])
        self.assertEqual(list(dg.graph_at_time(10).edges()),[(1,2),(1,3)])
        self.assertEqual(list(dg.graph_at_time(11).edges()),[(1,2),(1,3)])
Example #14
0
    def normalize_to_integers(self, nodes_start_at=1, time_start_at=1):
        """
        Transform time IDs and nodes to Integer


        :return: a new dynamic graph object, a dictionary of nodes {originalID:newID} and a dictionary of times {originalID:newID}
        """
        all_nodes = set()
        to_return = tn.DynGraphSN()
        for g in self.snapshots().values():
            all_nodes.update(set(g.nodes))
        nodes_dict = {v: (i + nodes_start_at) for i, v in enumerate(all_nodes)}

        for i, g in enumerate(self.snapshots().values()):
            to_return.add_snapshot(i + time_start_at,
                                   nx.relabel_nodes(g, nodes_dict))

        times = list(self.snapshots().keys())
        time_dict = {i + time_start_at: times[i] for i in range(len(times))}
        nodes_dict_inv = {v: k for k, v in nodes_dict.items()}

        return to_return, nodes_dict_inv, time_dict
Example #15
0
def _subset(graph, com, length):
    subgraph = tn.DynGraphSN(list(graph.snapshots().values())[:length])
    subcomsGT = tn.DynCommunitiesSN()
    for t in subgraph.snapshots_timesteps():
        subcomsGT.set_communities(t, com.snapshot_communities(t))
    return (subgraph, subcomsGT)
Example #16
0
def generate_multi_temporal_scale(nb_steps=5000,
                                  nb_nodes=100,
                                  nb_com=10,
                                  noise=None,
                                  max_com_size=None,
                                  max_com_duration=None):
    """
    Generate dynamic graph with stable communities

    This benchmark allows to generate temporal networks as described in
    `Detecting Stable Communities in Link Streams at Multiple Temporal Scales. Boudebza, S., Cazabet, R., Nouali, O., & Azouaou, F. (2019).`.

    To sum up the method, *stable* communities are generated (i.e., no node change).
    These communities exist for some periods, but have different *temporal scales*, i.e., some of them have a high frequency of edges (their edges appear at every step) while others have a lower frequency (i.e., each edge appear only every $t$ steps). To simplify, communities are complete cliques.(but for the low frequency ones, we might observe only a small fraction of their edges in every step)

    The basic parameters are the number of steps, number of nodes and number of communities.
    There are other parameters allowing to modify the random noise, the maximal size of communities and the maximal duration of communities,
    that are by default assigned with values scaled according to the other parameters.

    :param nb_steps: steps in the graph
    :param nb_nodes: total nb nodes
    :param nb_com: nb desired communities
    :param noise: random noise at each step, i.e. probability for any edge to exist at any step. default,1/(nb_nodes**2)
    :param max_com_size: max number of nodes. Default: nb_nodes/4
    :param max_com_duration: max community duration. Default: nb_steps/2
    :return:
    """

    to_return_graph = tn.DynGraphSN()
    to_return_com = tn.DynCommunitiesSN()

    if noise == None:
        noise = 1 / (nb_nodes * nb_nodes
                     )  #in average, 1 random interaction per timestep

    if max_com_duration == None:
        max_com_duration = nb_steps / 2

    if max_com_size == None:
        max_com_size = int(nb_nodes / 4)

    #initialise each step with a random graph with noise
    for i in range(nb_steps):
        to_return_graph.add_snapshot(i, nx.erdos_renyi_graph(nb_nodes, noise))

    #for each desired community
    for n in range(nb_com):
        #get a random size
        size = np.random.uniform(np.log(4), np.log(max_com_size))
        size = int(np.exp(size))

        #get a random duration
        duration = np.random.uniform(np.log(10), np.log(max_com_duration))
        duration = int(np.exp(duration))

        #We whoose the clique frequency so that all communities last long enough to be detectable
        cliques_frequency = 10 / duration

        _generate_a_community(to_return_graph, to_return_com, size, duration,
                              cliques_frequency)

    return (to_return_graph, to_return_com)
Example #17
0
    def to_DynGraphSN(self, slices=None, weighted=True):
        """
        Convert to a snapshot representation.

        :param slices: can be one of

        - None, snapshot_affiliations are created according to the frequency of the dynamic network (default one),
        - an integer, snapshot_affiliations are created using a sliding window
        - a list of periods, represented as pairs (start, end), each period yielding a snapshot

        :return: a dynamic graph represented as snapshot_affiliations, the weight of nodes/edges correspond to their presence time during the snapshot

        """
        dgSN = tn.DynGraphSN()
        if slices == None:
            slices = self.frequency()

        if isinstance(slices, int):
            duration = slices
            dgSN.frequency(slices)

            slices = []
            start = self._start
            end = start + duration
            slices.append((start, end))
            while (end <= self._end):
                start = end
                end = start + duration
                slices.append((start, end))

        all_times = sortedcontainers.SortedList(self.change_times())
        for ts in slices:
            #print(ts)
            if len(
                    list(
                        all_times.irange(ts[0], ts[1],
                                         inclusive=(True, False)))) > 0:
                dgSN.add_snapshot(t=ts[0],
                                  graphSN=self.cumulated_graph(
                                      ts, weighted=weighted))

                #dgSN.add_snapshot(t=ts[0],graphSN=nx.Graph())

        #sorted_times = [x[0] for x in slices]
        #print(self.node_presence())
        #to_add = {}

        # for n,interv in self.node_presence().items():
        #     intersection = interv._discretize(sorted_times)
        #     for t, duration in intersection.items():
        #         to_add.setdefault(t, []).append((n, {"weight": duration}))
        # for t in to_add:
        #     dgSN.snapshots(t).add_nodes_from(to_add[t])
        #
        # to_add = {}
        # for e,interv in self.interactions_intervals().items():
        #     intersection = interv._discretize(sorted_times)
        #     for t,duration in intersection.items():
        #         to_add.setdefault(t,[]).append((e[0],e[1],{"weight":duration}))
        # for t in to_add:
        #     dgSN.snapshots(t).add_edges_from(to_add[t])

        return dgSN
Example #18
0
def plot_longitudinal(dynamic_graph=None,communities=None, sn_duration=None,to_datetime=False, nodes=None,width=800,height=600,bokeh=False,auto_show=False):
    """
    A longitudinal view of nodes/snapshot_communities

    Plot snapshot_affiliations such as each node corresponds to a horizontal line and time corresponds to the horizontal axis

    :param dynamic_graph: DynGraphSN or DynGraphIG
    :param communities: dynamic snapshot_affiliations, DynCommunitiesSN or DynCommunitiesIG
    :param sn_duration: the duration of a snapshot, as int or timedelta. If none, inferred automatically as lasting until next snpashot
    :param to_datetime: one of True/False/function. If True, step IDs are converted to dates using datetime.utcfromtimestamp. If a function, should take a step ID and return a datetime object.
    :param nodes: If none, plot all nodes in lexicographic order. If a list of nodes, plot only those nodes, in that order
    :param width: width of the figure
    :param height: height of the figure
    """

    if dynamic_graph==None:
        if isinstance(communities,tn.DynCommunitiesSN):
            dynamic_graph = tn.DynGraphSN()
        else:
            dynamic_graph = tn.DynGraphIG()
    if to_datetime==True:
        to_datetime=datetime.utcfromtimestamp
        if sn_duration!=None and not isinstance(sn_duration,timedelta):
            sn_duration=timedelta(seconds=sn_duration)


    if isinstance(dynamic_graph,tn.DynGraphSN):
        if communities == None:
            communities = tn.DynCommunitiesSN()
        t = list(dynamic_graph.snapshots_timesteps())

        if communities != None and isinstance(communities,tn.DynCommunitiesSN):
            t = t + list(communities.snapshots.keys())
            t = sorted(list(set(t)))
        CDS = _sn_graph2CDS(dynamic_graph, communities, to_datetime=to_datetime,ts=t)
    else:
        if communities == None:
            communities = tn.DynCommunitiesIG()
        CDS = _ig_graph2CDS(dynamic_graph, communities, to_datetime=to_datetime)

    if isinstance(dynamic_graph,tn.DynGraphSN) and sn_duration!=None:
        CDS.data["duration"] = [sn_duration]*len(CDS.data["time"])



    if to_datetime!=False:
        CDS.data["duration_display"] = [x/1000 for x in CDS.data["duration"]]
    else:
        CDS.data["duration_display"]=CDS.data["duration"]

    #CDS.data["duration"] = [v+0.1 for v in CDS.data["duration"]]

    #should work for timedelta and integers
    CDS.data["time_shift"] = [CDS.data["time"][i] + CDS.data["duration"][i] / 2 for i in range(len(CDS.data["duration"]))]

    if nodes==None:
        nodes = sorted(list(set(CDS.data["node"])))
        nodes = [str(x) for x in nodes]

    #return _plot_longitudinal_bokeh(CDS,nodes,to_datetime,width,height,auto_show)
    if bokeh:
        return _plot_longitudinal_bokeh(CDS,nodes,to_datetime,width,height, auto_show)
    else:
        return _plot_longitudinal_pyplot(CDS,nodes,to_datetime,width,height)
Example #19
0
def plot_as_graph(dynamic_graph, communities=None, ts=None, width=800, height=600, slider=False, to_datetime=False,bokeh = False, auto_show=False, **kwargs):
    """
    Plot to see the static graph at each snapshot

    can be row of graphs or an interactive graph with a slider to change snapshot.
    In all cases, the position of nodes is the same in all snapshots.

    The position of nodes is determined using the networkx force directed layout, addition parameters of the function are passed
    to this functions (e.g., iterations=100, k=2...)

    :param dynamic_graph: DynGraphSN
    :param communities: dynamic snapshot_affiliations of the network (can be ignored)
    :param ts: time of snapshot(s) to display. single value or list. default None means all snapshots.
    :param slider: If None, a slider allows to interactively choose the step (work only in jupyter notebooks on a local machine)
    :param to_datetime: one of True/False/function. If True, step IDs are converted to dates using datetime.utcfromtimestamp. If a function, should take a step ID and return a datetime object.
    :param width: width of the figure
    :param height: height of the figure
    :return: bokeh layout containing slider and plot, or only plot if no slider.
    """


    if to_datetime==True:
        to_datetime=datetime.utcfromtimestamp


    # cleaning ts parameter
    if ts == None:
        if isinstance(dynamic_graph, tn.DynGraphIG):
            raise Exception(
                "If using IG graphs/communities, you must specified the desired t to plot")

        ts= list(dynamic_graph.snapshots_timesteps())
        if communities != None:
            ts = ts + list(communities.snapshots.keys())
            ts = sorted(list(set(ts)))

    if not isinstance(ts, list):
        ts = [ts]

    # Obtain snapshots for desired ts (graph and communities)
    if isinstance(dynamic_graph,tn.DynGraphIG):
        temp_graph_sn = tn.DynGraphSN()
        for t in ts:
            #print(t)
            temp_graph_sn.add_snapshot(t, dynamic_graph.graph_at_time(t))
        if communities!=None:
            temp_coms_sn = tn.DynCommunitiesSN()
            for t in ts:
                temp_coms_sn.set_communities(t,communities.communities(t))
            communities = temp_coms_sn

        dynamic_graph=temp_graph_sn

    #Obtain CDS for those snapshots
    CDS = _sn_graph2CDS(dynamic_graph, communities, to_datetime, ts=ts)


    unique_pos = _unique_positions(dynamic_graph,ts=ts,**kwargs)

    if bokeh:
        return _plot_as_graph_bokeh(ts,slider,dynamic_graph,CDS,unique_pos,width,height,to_datetime,auto_show)
    else:
        return _plot_as_graph_nx(ts,dynamic_graph,CDS,unique_pos,width,height,to_datetime)
Example #20
0
def plot_as_graph(dynamic_graph,
                  communities=None,
                  ts=None,
                  slider=False,
                  to_datetime=False,
                  width=800,
                  height=600,
                  auto_show=False):
    """
    Plot to see the static graph at each snapshot

    can be row of graphs or an interactive graph with a slider to change snapshot.
    In all cases, the position of nodes is the same in all snapshots

    :param dynamic_graph: DynGraphSN
    :param communities: dynamic snapshot_affiliations of the network (can be ignored)
    :param ts: time of snapshot(s) to display. single value or list. default None means all snapshots.
    :param slider: If None, a slider allows to interactively choose the step (work only in jupyter notebooks on a local machine)
    :param to_datetime: one of True/False/function. If True, step IDs are converted to dates using datetime.utcfromtimestamp. If a function, should take a step ID and return a datetime object.
    :param width: width of the figure
    :param height: height of the figure
    :param auto_show: if True, the plot is directly displayed in a jupyter notebook. In any other setting, should be False, and the graph should be displayed as any bokeh plot, depending on the setting.
    :return: bokeh layout containing slider and plot, or only plot if no slider.
    """

    if to_datetime == True:
        to_datetime = datetime.utcfromtimestamp

    if ts == None:
        if isinstance(dynamic_graph, tn.DynGraphIG):
            raise Exception(
                "If using IG graphs/communities, you must specified the desired t to plot"
            )

        ts = list(dynamic_graph.snapshots_timesteps())
        if communities != None:
            ts = ts + list(communities.snapshots.keys())
            ts = sorted(list(set(ts)))

    if not isinstance(ts, list):
        ts = [ts]

    if isinstance(dynamic_graph, tn.DynGraphIG):
        temp_graph_sn = tn.DynGraphSN()
        for t in ts:
            #print(t)
            temp_graph_sn.add_snapshot(t, dynamic_graph.graph_at_time(t))
        if communities != None:
            temp_coms_sn = tn.DynCommunitiesSN()
            for t in ts:
                temp_coms_sn.set_communities(t, communities.communities(t))
        dynamic_graph = temp_graph_sn
        communities = temp_coms_sn
    CDS = _sn_graph2CDS(dynamic_graph, communities, to_datetime, ts=ts)

    unique_pos = _unique_positions(dynamic_graph, ts=ts)

    if not slider:
        list_of_figures = []
        for current_t in ts:
            (a_figure,
             a_graph_plot) = _init_net_properties(dynamic_graph, CDS,
                                                  unique_pos, current_t, width,
                                                  height, to_datetime)
            list_of_figures.append(a_figure)
        layout = bokeh.layouts.row(list_of_figures)

    else:
        init_t = ts[0]
        #slider_Step = min([allTimes[i+1]-allTimes[i] for i in range(0,len(allTimes)-1)])
        slider_Step = 1
        slider_object = bokeh.models.Slider(
            start=0, end=len(ts), value=init_t, step=1,
            title="Plotted_step")  #,callback_policy="mouseup")

        (a_figure,
         a_graph_plot) = _init_net_properties(dynamic_graph, CDS, unique_pos,
                                              init_t, width, height,
                                              to_datetime)

        def update_graph(a, oldt, newt):
            _update_net(ts[newt], a_graph_plot, dynamic_graph)

        slider_object.on_change('value', update_graph)

        layout = bokeh.layouts.column(slider_object, a_figure)

    if auto_show:

        def modify_doc(doc):
            doc.add_root(layout)

        bokeh.io.output_notebook()
        bokeh.io.show(modify_doc)
    else:
        return layout