Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
dimitrv committed Feb 22, 2017
0 parents commit ec156d3
Show file tree
Hide file tree
Showing 8 changed files with 533 additions and 0 deletions.
77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Tracking communities accross time:

It tracks network communities across consecutive time frames. In particular
it detects the evolutionary phenomenon a community can sustain from one time frame
to another:
- continuing
- shrinking
- growing
- split
- merge
- dissolving
- No_event (when none of the above events holds)




## File Structure & running

- It contain 6 .py (thon) files
config.py , event.py , hypergraph.py, inclusion.py , preprocessing.py , tracker.py
- To run from terminal:
python tracker.py <inputfile>.json
- output: a file named 'results.csv', the format is:
```window ID, community ID, window ID, community ID, event```
- In config.py there are 2 parameters Alpha,Beta. These are the thresholds for the event
identification. Should be between [0.1,1]

## Input data format

The file given as input to the GED algorithm must adhere to the following JSON template:

```sh
{
"windows":
[ //array of windows (i.e. timeframes)
{
"communities":
[ //array of communities in each window
[ //array of edges in each community
[ //array containing two node ids between which an edge exists (this is an edge of the community)
]
]
]
}
]
}
```
We assume that the data set has been split into time frames and that communities have
been discovered in each time frame.

An example input file is provided in the "input/example" directory to test GED, consisting of 20 windows.
input/example/community_edges.json

```sh
<------- community ----------> <------- community ---------->
time-step:1 --> {"windows":[{"communities":[ [[id1,id2],[id2,id3],[id3,id1]], [[id4,id5],[id5,id6],[id6,id7]] ]
time-step:2 --> {"windows":[{"communities":[ [[id8,id9],[id9,id10],[id10,id1]], [[id11, id12],[id12,id13],[id13,id11]] ]
```
## Output-data-format
- The output file generated by GED has the following format:
<window id for current timestep>,<community id for current timestep>,<window id for next timestep>,<community id for next timestep>,<event>
- E.g. ```0,40,1,42,continuing``` means that community 40 in window 0 has evolved (corresponds) to community 42 in the next window (window 1) and the associated event with this evolution is continue.
- Windows are numbered consecutively starting from 0. The same holds for the communities in each window.
## Reference
@inproceedings{brodka2011tracking,
title={Tracking group evolution in social networks},
author={Br{\'o}dka, Piotr and Saganowski, Stanis{\l}aw and Kazienko, Przemys{\l}aw},
booktitle={International Conference on Social Informatics},
pages={316--319},
year={2011},
organization={Springer}
}
107 changes: 107 additions & 0 deletions Tracker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
import json
import networkx as nx
import preprocessing,config
import sys
import time
from inclusion import *
from event import Event
from hypergraph import Hypergraph

trueEvents = {'merging':0,'splitting':0,'growing':0,'shrinking':0,
'continuing':0,'dissolving':0,'forming':0,'No_event':0}

class Tracker():

def __init__(self, graphs):
self.graphs = graphs
self.communities = []
self.results = []
self.inclusions = {}
self.Dcandidates = {}
self.Fcandidates = {}
self.hypergraphs = []

def compare_communities(self,):
for index, interval in enumerate(self.graphs):
if index < len(self.graphs) - 1:
self.inclusions = {}
window_id = 'TF%s -> TF%s' % (index,index+1)
Dhypergraph = nx.DiGraph(window=window_id)
print 'Initialize inclusions dict start...'
Dhypergraph = self.initialize_inclusions(index,Dhypergraph)
print 'Initialize inclusions dict finish...'

for ic,community_t in enumerate(interval):

for ic2, community_t1 in enumerate(self.graphs[index + 1]):

inclusion = self.inclusions[community_t.graph['cid']][community_t1.graph['cid']]['inclusion']
inversed = self.inclusions[community_t.graph['cid']][community_t1.graph['cid']]['inversed_inclusion']

event = Event(community_t,community_t1,inclusion,inversed,self.inclusions)
result = event.classify()
if result in ['growing','shrinking','continuing']:
Dhypergraph.add_edge(community_t,community_t1,event_type=result)
self.results.append({ 'network_t': community_t.graph['cid'],
'network_t1': community_t1.graph['cid'],
'resulted_event': result})

hypergraph = Hypergraph(Dhypergraph)
self.hypergraphs.append(hypergraph)


def initialize_inclusions(self,index,Dhypergraph):
self.Dcandidates = {}
self.Fcandidates = {}
pastTFcommunities = self.graphs[index]
futureTFcommunities = self.graphs[index+1]
for ic,community_t in enumerate(pastTFcommunities):
Dhypergraph.add_node(community_t)
key1 = community_t.graph['cid']
self.Dcandidates[key1] = community_t
self.inclusions[key1]={}
for ic2, community_t1 in enumerate(futureTFcommunities):
key2 = community_t1.graph['cid']
if ic==0:
Dhypergraph.add_node(community_t1)
self.Fcandidates[key2] = community_t1
self.inclusions[key2] = {}
inclusions = CentralityInclusion(community_t, community_t1)
inclusion, inversed = inclusions.compute_inclusion()
if inclusion >0.1 or inversed > 0.1:
if key1 in self.Dcandidates:
del self.Dcandidates[key1]
if key2 in self.Fcandidates:
del self.Fcandidates[key2]
self.inclusions[key1].update({key2:{'inclusion':inclusion,'inversed_inclusion':inversed}})

for key in self.Dcandidates.keys():
Dhypergraph.add_edge(self.Dcandidates[key],futureTFcommunities[-1],event_type='dissolving')
for key in self.Fcandidates.keys():
Dhypergraph.add_edge(pastTFcommunities[-1],self.Fcandidates[key],event_type='forming')
return Dhypergraph

def analyze_results(self,):
events_names = ['merging', 'splitting', 'growing',
'shrinking', 'continuing', 'dissolving',
'forming', 'no_event']
events = [e['resulted_event'] for e in self.results]
for name in events_names:
print name, events.count(name)

if __name__=='__main__':
if len(sys.argv) != 2:
print 'Usage: Tracker.py <inputfile.json>'
print 'Exiting with code 1'
exit(1)
start_time = time.time()
graphs = preprocessing.getGraphs(sys.argv[1])
tracker = Tracker(graphs)
tracker.compare_communities()
with open('tmpfiles/ged_results.csv','w')as f:
for hypergraph in tracker.hypergraphs:
hypergraph.calculateEvents(f)
print "--- %s seconds ---" %(time.time() - start_time)


2 changes: 2 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALPHA = 0.9
BETA = 0.9
128 changes: 128 additions & 0 deletions event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# -*- coding: utf-8 -*-
import config

class Event():

def __init__(self, g1, g2, inclusion, inversed_inclusion, inclusions):
self.alpha = config.ALPHA
self.beta = config.BETA
self.g1 = g1
self.g2 = g2
self.g1_size = len(g1)
self.g2_size = len(g2)
self.inclusion = inclusion
self.inversed_inclusion = inversed_inclusion
self.inclusions = inclusions
self.events = {1:'merging',
2:'splitting',
3:'growing',
4:'shrinking',
5:'continuing',
6:'dissolving',
7:'forming',
0:'no_event'}

def is_merging(self,):
if ((self.inclusion >= self.alpha) and
(self.inversed_inclusion < self.beta) and
(self.g1_size <= self.g2_size) and
(self.check_matchings(self.g1, 'network_t0') == 'more')):
return True

def is_splitting(self,):
if ((self.inclusion < self.alpha) and
(self.inversed_inclusion >= self.beta) and
(self.g1_size >= self.g2_size) and
(self.check_matchings(self.g2, 'network_t1') == 'more')):
return True

def is_growing(self,):
if ((self.inclusion >= self.alpha) and
(self.inversed_inclusion >= self.beta) and
(self.g1_size < self.g2_size)):
#~ print "Growing from",self.g1.graph,"to",self.g2.graph
return True

def is_shrinking(self,):
if ((self.inclusion >= self.alpha) and
(self.inversed_inclusion >= self.beta) and
(self.g1_size > self.g2_size)):
#~ print "Shrinking",self.g1.graph,"to",self.g2.graph
return True

def is_continuing(self,):
if ((self.inclusion >= self.alpha) and
(self.inversed_inclusion >= self.beta) and
(self.g1_size == self.g2_size)):
#~ print "Continue",self.g1.graph,"to",self.g2.graph
return True

def is_dissolving(self,):
g1_id = self.g1.graph['cid']
g2_id = self.g2.graph['cid'].split('_')[0]
coms = [group for group in self.inclusions if (g1_id == group.split('_')[0]+'_'+group.split('_')[1] and
g2_id in group)]

for tupel in coms:
if (self.inclusions[tupel]['inclusion'] > 0.1 and
self.inclusions[tupel]['inversed_inclusion'] > 0.1):
return False
comm = [c.split('_')[3] for c in coms]
comm.sort(key=lambda x: int(filter(str.isdigit,x)))

if not (self.g2.graph['cid'].split('_')[1] == comm[-1]):
return False
#~ print "Dissolving: past",self.g1.graph,"future",self.g2.graph
return True

def is_forming(self,):
g2_id = self.g2.graph['cid']
g1_id = self.g1.graph['cid'].split('_')[0]
coms = [group for group in self.inclusions if (g1_id in group and
g2_id == group.split('_')[2]+'_'+group.split('_')[3])]

for tupel in coms:
if (self.inclusions[tupel]['inclusion'] > 0.1 and
self.inclusions[tupel]['inversed_inclusion'] > 0.1):
return False
comm = [c.split('_')[1] for c in coms] #keep the all community ids of past TF
comm.sort(key=lambda x: int(filter(str.isdigit,x)))

if not (self.g1.graph['cid'].split('_')[1] == comm[-1]): #to avoid multiple formation of the same community
return False
#~ print "Forming: past",self.g1.graph,"future",self.g2.graph
return True

def check_matchings(self, network, window):
matches = 0
events = [m for m in self.results if m[window]==network.graph['cid']]
for e in events:
if not e['resulted_event'] == 'no_event':
matches += 1
if matches == 1:
return 'one'
elif matches > 1:
return 'more'
else:
return 'none'

def check(self,):
#~ if self.is_merging():
#~ return 1
#~ elif self.is_splitting():
#~ return 2
if self.is_growing():
return 3
elif self.is_shrinking():
return 4
elif self.is_continuing():
return 5
#~ elif self.is_dissolving():
#~ return 6
#~ elif self.is_forming():
#~ return 7
else:
return 0

def classify(self,):
return self.events[self.check()]
Loading

0 comments on commit ec156d3

Please sign in to comment.