Exemple #1
0
    def slice(self, start, end):
        """
        Keep only the selected period

        :param start:
        :param end:
        """

        to_return = tn.DynCommunitiesSN()
        interv = tn.Intervals((start, end))
        for t in list(self.snapshots.keys()):
            if t in interv:
                to_return.set_communities(self.snapshots[t], t)
        return to_return
Exemple #2
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
def seed_expansion(seed, granularity, t_quality, t_persistance, t_similarity,
                   QC, CSS, pre_computed_snapshots, C):
    # Find recursively snapshots in which this community still makes sense
    similars = []
    this_seed_nodes = seed[2]

    similars += _track_one_community(this_seed_nodes,
                                     seed[0],
                                     pre_computed_snapshots,
                                     score=QC,
                                     t_quality=t_quality,
                                     backward=True)
    similars += [(seed[0], seed[3])]
    similars += _track_one_community(this_seed_nodes,
                                     seed[0],
                                     pre_computed_snapshots,
                                     score=QC,
                                     t_quality=t_quality)

    #if the community is stable, i.e., makes sense more than t_persistance steps
    if len(similars) >= t_persistance:
        similars = [
            similars
        ]  # for genericity, we want to be able to deal with non-continuous intervals
        inter_presence = tn.Intervals([(sim[0][0], sim[-1][0] + granularity)
                                       for sim in similars])

        # check that a similar community has not already been found
        redundant = False
        for nodes, period, gran, score in C:

            # if the communities are similar and one of them has at least half of its duration included in the other
            if CSS(this_seed_nodes,
                   nodes) > t_similarity and inter_presence.intersection(
                       period).duration() > 0.5 * min(
                           [inter_presence.duration(),
                            period.duration()]):
                redundant = True
                break

        # If community not redundant, we compute its quality and add it to the list of communities
        if not redundant:
            sum_quality = 0
            for stable in similars:
                sum_quality += sum([1 - (x[1]) for x in stable])

            C.append(
                (this_seed_nodes, inter_presence, granularity, sum_quality))
Exemple #4
0
    def slice(self,start,end):
        """
        Keep only the selected period

        :param start:
        :param end:
        """

        to_return = tn.DynCommunitiesIG()
        interv = tn.Intervals((start,end))
        for c_id,c in self.communities().items():
            for n,the_inter in c.items():
                duration = interv.intersection(the_inter)
                if duration.duration()>0:
                    to_return.add_affiliation(n,c_id,duration)
        return to_return
Exemple #5
0
    def test_add_interactions_ig(self):
        dg = tn.DynGraphIG()
        dg.add_interaction(1,2,(4,5))

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

        dg.add_interaction(1, 2, (6,7))

        self.assertEqual(dg.edge_presence((1,2),as_intervals=True),tn.Intervals([(4,5),(6,7)]))


        dg.add_interactions_from({2, 3}, (6,7,9))

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

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

        dg.add_interactions_from([(1, 2),(1,3)], (10,12))
        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)])
    def to_DynCommunitiesIG(self, sn_duration, convertTimeToInteger=False):
        """
        Convert to SG snapshot_affiliations
        :param sn_duration: time of a snapshot, or None for automatic: each snapshot last until start of the next
        :param convertTimeToInteger: if True, snapshot_affiliations IDs will be forgottent and replaced by consecutive integers
        :return: DynamicCommunitiesIG
        """

        dynComTN= tn.DynCommunitiesIG()
        for i in range(len(self.snapshots)):
            if convertTimeToInteger:
                t=i
                tNext=i+1
            else:
                current_t = self.snapshots.peekitem(i)[0]

                if sn_duration != None:
                    tNext = current_t + sn_duration

                else:
                    if i < len(self.snapshots) - 1:
                        tNext = self.snapshots.peekitem(i + 1)[0]
                    else:
                        # computing the min duration to choose as duration of the last period
                        dates = list(self.snapshots.keys())
                        minDuration = min([dates[i + 1] - dates[i] for i in range(len(dates) - 1)])
                        tNext = current_t + minDuration


            for (cID,nodes) in self.snapshots.peekitem(i)[1].items(): #for each community for this timestep
                dynComTN.add_affiliation(nodes,cID,tn.Intervals((current_t,tNext)))


        #convert also events
        for (u,v,d) in self.events.edges(data=True):
            if d["type"]!="continue": #if snapshot_affiliations have different IDs
                dynComTN.events.add_event(u[1],v[1],d["time"][0],d["time"][1],d["type"])
        return dynComTN
def track_communities(dyn_graph,
                      t_granularity=1,
                      t_persistance=3,
                      t_quality=0.7,
                      t_similarity=0.3,
                      similarity=jaccard,
                      CD="louvain",
                      good_community=score_conductance,
                      weighted_aggregation=True,
                      Granularity=None,
                      start_time=None):
    """
    Proposed method to track communities
    :param dyn_graph:
    :param t_granularity: (\theta_\gamma) min temporal granularity/scale to analyze
    :param t_persistance:(\theta_p) minimum number of successive occurences for the community to be persistant
    :param t_quality: (\theta_q) threashold of community quality
    :param t_similarity: (\theta_q) threashold of similarity between communities
    :param similarity: (CSS)function that give a score of similarity between communities. Default: jaccard
    :param CD: (CD) community detection algorithm. A function returning a set of set of nodes. By default, louvain algorithm
    :param good_community: (QC)function to determine the quality of communities. Default: inverse of conductance
    :param weighted_aggregation: if true, the aggregation over time periods is done using weighted networks
    :param Granularity: (\Gamma) can be used to replace the default scales. List of int.
    :param start_time: the date at which to start the analysis. Can be useful, for instance, to start analysis at 00:00
    :return:
    """

    #set up the list of granularity/temporal scales to analyze
    if Granularity == None:
        Granularity = studied_scales(dyn_graph, t_granularity, t_persistance)

    if isinstance(Granularity, int):
        Granularity = [Granularity]

    if start_time == None:
        start_time = dyn_graph.snapshots_timesteps()[0]

    persistant_coms = []
    all_graphs = {}
    all_coms = {}

    #for each granularity level
    for current_granularity in Granularity:
        seeds = []

        #Aggregate the graph WITH or WITHOUT weights (on real data I checked, give better results without weights due to some very stronger weights between a small subset of nodes)
        s_graph_current_granularity = dyn_graph.aggregate_sliding_window(
            t_start=start_time,
            bin_size=current_granularity,
            weighted=weighted_aggregation)
        all_graphs[current_granularity] = s_graph_current_granularity

        #print("computing communities")

        dyn_coms = iterative_match(s_graph_current_granularity,
                                   CDalgo=CD)  #,CDalgo=infomap_communities)
        for t, g in s_graph_current_granularity.snapshots().items():
            interesting_connected_com = nx.connected_components(g)
            interesting_connected_com = [
                x for x in interesting_connected_com if len(x) >= 3
            ]
            for c in interesting_connected_com:
                dyn_coms.add_community(t, c)
        all_coms[current_granularity] = dyn_coms

        #print("computing quality for each com")

        for t, coms in all_coms[current_granularity].snapshots.items():
            current_graph = s_graph_current_granularity.snapshots(t)
            for cID, nodes in coms.items():
                quality = good_community(nodes, current_graph)
                seeds.append(
                    (t, cID, frozenset(nodes), quality, current_granularity)
                )  ##structure of items in coms_and_qualities:  (t,cID,frozenset(nodes),quality,granularity)

        seeds.sort(key=lambda x: x[3], reverse=True)

        #print("#nb seeds total",len(seeds))
        seeds = [c for c in seeds if c[3] >= t_quality]
        nb_good_seeds = len(seeds)
        #print("--",seeds)

        #filter out similar communities (same nodes in same period)
        for nodes, period, gran, score in persistant_coms:  #for each already save community
            # keep in the list of seeds those that are in a different period or that are sufficiently different, compared with the current one
            #seeds = [c for c in seeds if not period.contains_t(c[0]) or similarity(c[2],current_com[2])<t_no_overlap]
            seeds = [
                c for c in seeds if
                not seed_contained_in_persistent_com(nodes,
                                                     c[2],
                                                     c[0],
                                                     period,
                                                     similarity=similarity,
                                                     t_similarity=t_similarity)
            ]

        while len(seeds) > 0:

            current_com = seeds.pop(0)
            this_seed_nodes = current_com[2]
            #if interesting_node in this_seed_nodes:
            #   print("com tested ",this_seed_nodes)

            #Find recursively snapshots in which this community still makes sense
            similars = []

            similars += _track_one_community(this_seed_nodes,
                                             current_com[0],
                                             s_graph_current_granularity,
                                             score=good_community,
                                             t_quality=t_quality,
                                             backward=True)
            similars += [(current_com[0], current_com[3])]
            similars += _track_one_community(this_seed_nodes,
                                             current_com[0],
                                             s_graph_current_granularity,
                                             score=good_community,
                                             t_quality=t_quality)
            #if interesting_node in this_seed_nodes:
            #    print("similars found", similars)
            #If the duration of the com is long enough, keep the community
            if len(similars) >= t_persistance:
                similars = [
                    similars
                ]  #for genericity, we want to be able to deal with non-continuous intervals
                inter_presence = tn.Intervals([
                    (sim[0][0], sim[-1][0] + current_granularity)
                    for sim in similars
                ])

                #check that a similar community has not already been found
                redundant = False
                for nodes, period, gran, score in persistant_coms:

                    #if the communities are similar and one of them has at least half of its duration included in the other
                    if similarity(
                            this_seed_nodes, nodes
                    ) > t_similarity and inter_presence.intersection(
                            period).duration() > 0.5 * min(
                                [inter_presence.duration(),
                                 period.duration()]):
                        redundant = True
                        break

                if not redundant:
                    #persistant_coms[current_com]=(inter_presence,sum([x[1] for x in similars]))
                    sum_quality = 0
                    for stable in similars:
                        sum_quality += sum([1 - (x[1]) for x in stable])

                    persistant_coms.append((this_seed_nodes, inter_presence,
                                            current_granularity, sum_quality))

                    #keep in the list of seeds those that are in a different period or that are sufficiently different, compared with the current one
                    seeds = [
                        c for c in seeds
                        if not seed_contained_in_persistent_com(
                            this_seed_nodes, c[2], c[0], inter_presence,
                            similarity, t_similarity)
                    ]

        print("------- granularity (gamma): ", current_granularity, " | ",
              "# good seeds: ", nb_good_seeds,
              "# persistent communities found (total): ", len(persistant_coms))

    persistant_coms = sorted(persistant_coms, key=lambda x: x[3], reverse=True)
    return persistant_coms