User Tools

Site Tools


vruffet_cfarhat:vincent_ruffet_cedric_farhat

Visual Analytics Course

Bordeaux roadmap / Vincent Ruffet & Cédric Farhat's corner

Sujet : Afficher les communications téléphonques en cours à l'instant t

Source du jeu de données : http://hcil.cs.umd.edu/localphp/hcil/vast/archive/task.php?ts_id=121

Comment faire fonctionner le programme?

- Importer le fichier .csv du jeux de données CELL PHONES

- Charger le fichier python contenant le code

- Lancer l'application

Programme python : voir fin du wiki.

Sommaire

I - Introduction

II - Explications du code

III - Problèmes rencontrés

IV - Conclusion

Programme python

I - Introduction

Tout d'abord nous avons changé de jeux de données et donc de sujet car ce que nous avions choisi n'était pas accessible à notre niveau ou très difficile à mettre en place. En effet, nous avions imaginé charger plusieurs jeux de données contenant la liste des connexions entre deux terminaux d'une journée du logiciel p2p gnutella, afficher les graphes correspondant et montrer une animation décrivant l'évolution des connexions d'un jour à l'autre. Or le passage d'un jeu de données à l'autre est difficile à mettre en place et on s'est rendu compte que les entités d'un jeu de données n'avaient pas forcément de rapport avec les mêmes entités d'un autre jeu de données.

Nous avons donc décidé de changer de jeu de données et donc de sujet de projet. Notre nouveau jeu de données est une liste d'appels téléphoniques (http://hcil.cs.umd.edu/localphp/hcil/vast/archive/task.php?ts_id=121) effectués durant dix jours en juin 2006, et nous avons choisi le sujet de projet suivant: Afficher les communications en cours à l'instant t. Ce jeu de données a été utilisé dans le cadre du VAST challenge en 2008.

Le jeu de données est basé sur les communications entre 400 téléphones cellulaires sur dix jours en juin 2006. Chaque ligne de ce fichier, correspondant à une communication, est renseignée par 5 attributs : “From” et “To” correspondant respectivement à l'identifiant de l'appelant et de l'appelé, “Datetime” correspondant à la date et heure de l'appel, “Duration” étant la durée de l'appel et “Cell Tower” étant la localisation de l'appelant, mais nous n'utiliserons pas cet attribut dans notre sujet. Ce dernier attribut permet de situer sur une carte géographique, fournie avec le jeu de données, l'appelant.

Pour représenter ces nombreuses communications sous formes de graphe, les arêtes de celui-ci représenteront la liasons entre deux téléphones, eux-même représentés par les noeuds ainsi que les label pour identifier chaque téléphone. A l'aide de ces attributs et du graphe représentant les appels nous avons codé un programme affichant et effaçant - au fur et a mesure du temps - les éléments du graphe (une arête et les deux noeuds aux extrémités) En effet, au debut d'une communication, on affiche cette dernière et à la fin on l'efface, montrant ainsi le dynamisme des communications au cours du temps.

Nous allons voir dans la prochaine partie les détails du code.

II - Explications du code

Premièrement nous avons rendu invisible les éléments (noeuds, arrêtes, labels) du graphe final produit par le fichier .csv. Nous avons défini une fonction clearAll qui s'occupe de cette tâche. Elle change la couleur des arêtes, des bordures des arêtes, des noeuds, des bordures des noeuds et des labels (identifiants des téléphones) en blanc et les rend transparents.

Ensuite nous avons crée deux fonctions ayant pour but de remplir chacune un vecteur contenant des dates d'appel sous forme d'un timestamp. La première - initVecDateBegin - s'occupe de stocker les dates de début d'appel. La seconde - initVecDateEnd - stocke dans une liste des tuples contenant l'id de l'arête (de l'appel) et sa date de fin en timestamp, la date de fin étant la simple addition de la date de début et de la durée de l'appel. Une fois nos deux vecteurs remplis nous pouvons lancer notre fonction principale displayGraph qui s'occupe d'afficher ce que l'on veut.

La fonction displayGraph comporte une boucle qui va passer en revue chaque arête (appel) et effectuer une action pour chacune, soit l'afficher (si l'appel commence) soit la cacher (si l'appel finit). Afin de garder l'ordre chronologique des évènements on compare les timestamp du prochain élément de chaque liste. On affiche également dans la console la date et l'heure de l'appel ainsi que le nombre d'appels en cours. Finalement on actualise l'affichage du graphe avec la méthode updateVisualisation pour voir les différentes étapes de l'exécution du programme.

Pour ce qui est de l'affichage des arêtes et des noeuds on a crée deux fonctions showEdge et showNode qui changent la couleur des arêtes en noir et des noeuds en rouge, ainsi que de les rendre opaques. A l'inverse, les fonctions hideNode et hideEdge rendent transparents ces éléments et changent leur couleur en blanc.

Par nécessité nous avons également définit deux méthodes getNode et getEdge qui permettent de chercher respectivement un noeud et une arête dans le graphe à partir de leur identifiant.

Voici un exemple de l'affichage du graphe durant l'exécution du programme :

Et ici, la console à cet instant:

...
05/06/2006 17:15:00 : 20 communication(s) en cours
05/06/2006 17:16:00 : 21 communication(s) en cours
05/06/2006 17:16:00 : 22 communication(s) en cours
05/06/2006 17:17:00 : 23 communication(s) en cours
05/06/2006 17:17:34 : 22 communication(s) en cours
05/06/2006 17:18:26 : 21 communication(s) en cours
05/06/2006 17:18:40 : 20 communication(s) en cours
05/06/2006 17:19:00 : 21 communication(s) en cours
05/06/2006 17:19:00 : 22 communication(s) en cours
05/06/2006 17:19:00 : 23 communication(s) en cours
05/06/2006 17:19:19 : 22 communication(s) en cours

III - Problèmes rencontrés

Premier problème:

Afin de garder une proportionalité et une chronologie dans la durée des appels nous pensions mettre en place un timer qui commence au premier appel et s'incrémente à chaque tour d'horloge. Mais lors des tests cela s'est averré contraignant car l'exécution était beaucoup trop longue. Nous avons donc décider de supprimer le timer entrainant la perte de la proportionalité de la durées des appels tout en gardant la chronologie des évènements.

Deuxième problème:

Lorsqu'on supprime une arête (communication) on supprime également les deux noeuds (téléphones en communications) or un téléphone peut être en communication multiple. Il ne doit pas forcément être supprimé car si il est en communication multiple il doit rester afficher pour les autres appels en cours. Il faut donc faire un test sur les arêtes du noeud afin de déterminer si on doit effacer ou non celui-ci. Pour cela on a fait une fonction hasShowEdge pour répondre à ce problème qui vérifie que toutes les arêtes du noeud sont transparentes et de couleur blanche (non affichées). Si c'est le cas on laisse le noeud affiché, sinon on l'efface. Cependant, le test sur la couleur ne permet pas cette vérification et nous n'avons pas encore trouvé d'alternative.

IV - Conclusion

On remarque que le plus grand nombre de communications en cours simultanément se font de 8h à 11h et de 14h à 16h. Cela est cohérent car ça correspond aux heures de travail. A l'inverse, de façon évidente, durant la nuit (de 00h à 8h environ) il y a très peu d'appels, voir aucun par moments.

Ce projet a été intéressant et nous a permis de nous améliorer en python et de pratiquer Tulip. On pourrait améliorer le code en considérant l'attribut “Cell Tower” afin de visualiser la position géographique des téléphones durant les communications en utilisant comme fond la carte fournie dans le dossier téléchargé.

Programme Python :

Projet.py
# Powered by Python 2.7
 
# To cancel the modifications performed by the script
# on the current graph, click on the undo button.
 
# Some useful keyboards shortcuts : 
#   * Ctrl + D : comment selected lines.
#   * Ctrl + Shift + D  : uncomment selected lines.
#   * Ctrl + I : indent selected lines.
#   * Ctrl + Shift + I  : unindent selected lines.
#   * Ctrl + Return  : run script.
#   * Ctrl + F  : find selected text.
#   * Ctrl + R  : replace selected text.
#   * Ctrl + Space  : show auto-completion dialog.
 
from tulip import *
import time
import datetime
 
# the updateVisualization(centerViews = True) function can be called
# during script execution to update the opened views
 
# the pauseScript() function can be called to pause the script execution.
# To resume the script execution, you will have to click on the "Run script " button.
 
# the runGraphScript(scriptFile, graph) function can be called to launch another edited script on a tlp.Graph object.
# The scriptFile parameter defines the script name to call (in the form [a-zA-Z0-9_]+.py)
 
# the main(graph) function must be defined 
# to run the script on the current graph
 
def main(graph): 
	Cell_Tower =  graph.getIntegerProperty("Cell Tower")
	Datetime =  graph.getStringProperty("Datetime")
	Durationseconds =  graph.getIntegerProperty("Duration(seconds)")
	From =  graph.getIntegerProperty("From")
	Random_layout__viewLayout =  graph.getLayoutProperty("Random layout - (viewLayout)")
	To =  graph.getIntegerProperty("To")
	viewBorderColor =  graph.getColorProperty("viewBorderColor")
	viewBorderWidth =  graph.getDoubleProperty("viewBorderWidth")
	viewColor =  graph.getColorProperty("viewColor")
	viewFont =  graph.getStringProperty("viewFont")
	viewFontSize =  graph.getIntegerProperty("viewFontSize")
	viewLabel =  graph.getStringProperty("viewLabel")
	viewLabelColor =  graph.getColorProperty("viewLabelColor")
	viewLabelPosition =  graph.getIntegerProperty("viewLabelPosition")
	viewLabelRotation =  graph.getDoubleProperty("viewLabelRotation")
	viewLayout =  graph.getLayoutProperty("viewLayout")
	viewMetaGraph =  graph.getGraphProperty("viewMetaGraph")
	viewMetric =  graph.getDoubleProperty("viewMetric")
	viewRotation =  graph.getDoubleProperty("viewRotation")
	viewSelection =  graph.getBooleanProperty("viewSelection")
	viewShape =  graph.getIntegerProperty("viewShape")
	viewSize =  graph.getSizeProperty("viewSize")
	viewSrcAnchorShape =  graph.getIntegerProperty("viewSrcAnchorShape")
	viewSrcAnchorSize =  graph.getSizeProperty("viewSrcAnchorSize")
	viewTexture =  graph.getStringProperty("viewTexture")
	viewTgtAnchorShape =  graph.getIntegerProperty("viewTgtAnchorShape")
	viewTgtAnchorSize =  graph.getSizeProperty("viewTgtAnchorSize")
 
	# creation de deux listes
	vecDateBegin = list()	# liste des datetimes (timestamp) du debut des communications
	vecDateEnd = list()		# liste de tuples (id_communication, datetime (timestamp) de fin des communications)
 
	# effacement du graphe (noeuds, aretes et labels)
	clearAll(graph, viewColor, viewBorderColor, viewLabelColor)
	# initialisation des deux listes
	initVecDateBegin(graph, vecDateBegin, Datetime)
	initVecDateEnd(graph, vecDateEnd, vecDateBegin, Durationseconds)
	# affichage du graphe avec animation
	displayGraph(graph, vecDateBegin, vecDateEnd, viewColor, viewBorderColor, viewLabel, viewLabelColor, From, To)
 
 
 
# Effacer le graphe (noeuds, aretes, labels)
def clearAll(graph, viewColor, viewBorderColor, viewLabelColor):
	# effacement des aretes
	viewColor.setAllEdgeValue(tlp.Color(255,255,255,0))
	viewBorderColor.setAllEdgeValue(tlp.Color(255,255,255,0))
	# effacement des noeuds
	viewColor.setAllNodeValue(tlp.Color(255,255,255,0))
	viewBorderColor.setAllNodeValue(tlp.Color(255,255,255,0))
	# effacement des labels
	viewLabelColor.setAllNodeValue(tlp.Color(255,255,255,0))
 
 
 
# Afficher le graphe avec animation
def displayGraph(graph, vecDateBegin, vecDateEnd, viewColor, viewBorderColor, viewLabel, viewLabelColor, From, To):
	iVecDateBegin = 0	# indice de parcours de la liste vecDateBegin
	iVecDateEnd = 0	# indice de parcours de la liste vecDateEnd
	nbCommunication = 0				# compteur de communications en cours
 
	# boucle de deroulement de l'animation
	while (iVecDateBegin < len(vecDateBegin)) or (iVecDateEnd < len(vecDateEnd)):
 
		if (vecDateBegin[iVecDateBegin] <= vecDateEnd[iVecDateEnd][1]):
			# affichage de la communication (arete et noeuds)
			showEdge(graph, iVecDateBegin, viewColor, viewBorderColor, viewLabel, From, To)
			showNode(graph, iVecDateBegin, viewColor, viewBorderColor, viewLabel, viewLabelColor, From, To)
 
			# incrementation du compteur de communications en cours
			nbCommunication = nbCommunication + 1
			# affichage dans la console de l'heure du debut de la communication et du nombre de communications en cours
			print(datetime.datetime.fromtimestamp(int(vecDateBegin[iVecDateBegin])).strftime("%d/%m/%Y %H:%M:%S") + " : " + str(nbCommunication) + " communication(s) en cours")
			# incrementation de l'indice de parcours de la liste vecDateBegin
			iVecDateBegin = iVecDateBegin + 1
 
		else:
			# effacement de la communication (arrete et noeuds)
			hideEdge(graph, vecDateEnd[iVecDateEnd][0], viewColor, viewBorderColor, viewLabel, From, To)
			hideNode(graph, vecDateEnd[iVecDateEnd][0], viewColor, viewBorderColor, viewLabel, viewLabelColor, From, To)
 
			# decrementation du compteur de communications en cours
			nbCommunication = nbCommunication - 1
			# affichage dans la console de l'heure du debut de la communication et du nombre de communications en cours
			print(datetime.datetime.fromtimestamp(int(vecDateEnd[iVecDateEnd][1])).strftime("%d/%m/%Y %H:%M:%S") + " : " + str(nbCommunication) + " communication(s) en cours")
			# incrementation de l'indice de parcours de la liste vecDateBegin
			iVecDateEnd = iVecDateEnd + 1
 
		# actualisation de l'affichage du graphe
		updateVisualization(centerViews = True)
 
 
 
# Chercher un noeud du graphe a partir de son identifiant
def getNode(graph, i, viewLabel):
	for n in graph.getNodes():
		if i == int(viewLabel.getNodeValue(n)):
			return n	
 
 
 
# Chercher une arete du graphe a partir de son identifiant
def getEdge(graph, i):
	for e in graph.getEdges():
		if i == e.id:
			return e
 
 
 
# Initialisation de la liste vecDateBegin
def initVecDateBegin(graph, vecDateBegin, Datetime):
	# insertion de toutes les datetimes de debut des communications sous forme de timestamp (deja triees dans l'ordre chronologique)
	for e in graph.getEdges():
		vecDateBegin.insert(e.id, int(time.mktime(datetime.datetime.strptime(Datetime.getEdgeValue(e), "%Y%m%d %H%M").timetuple())))
 
 
 
# Initialisation de la liste vecDateEnd
def initVecDateEnd(graph, vecDateEnd, vecDateBegin, Durationseconds):
	# insertion de toutes les datetimes de fin des communications (datetime_debut + duree) sous forme de timestamp
	for e in graph.getEdges():
		vecDateEnd.insert(e.id, (e.id, vecDateBegin[e.id] + Durationseconds.getEdgeValue(e)))
	# tri de la liste dans l'ordre chronologique
	vecDateEnd.sort(key=lambda tup: tup[1])
 
 
 
# Afficher une arete du graphe
def showEdge(graph, e, viewColor, viewBorderColor, viewLabel, From, To):
	edge = getEdge(graph, e)
	viewColor.setEdgeValue(edge, tlp.Color(0, 0, 0, 100))
	viewBorderColor.setEdgeValue(edge, tlp.Color(0, 0, 0, 100))
 
 
 
# Afficher un noeud du graphe
def showNode(graph, e, viewColor, viewBorderColor, viewLabel, viewLabelColor, From, To):
	edge = getEdge(graph, e)
	viewColor.setNodeValue(getNode(graph, From.getEdgeValue(edge), viewLabel), tlp.Color(255, 0, 0, 100))
	viewBorderColor.setNodeValue(getNode(graph, From.getEdgeValue(edge), viewLabel), tlp.Color(255, 0, 0, 100))
	viewLabelColor.setNodeValue(getNode(graph, From.getEdgeValue(edge), viewLabel), tlp.Color(0, 0, 0, 100))
	viewColor.setNodeValue(getNode(graph, To.getEdgeValue(edge), viewLabel), tlp.Color(255, 0, 0, 100))
	viewBorderColor.setNodeValue(getNode(graph, To.getEdgeValue(edge), viewLabel), tlp.Color(255, 0, 0, 100))
	viewLabelColor.setNodeValue(getNode(graph, To.getEdgeValue(edge), viewLabel), tlp.Color(0, 0, 0, 100))
 
 
 
# Effacer une arete du graphe
def hideEdge(graph, e, viewColor, viewBorderColor, viewLabel, From, To):
	edge = getEdge(graph, e)
	viewColor.setEdgeValue(edge, tlp.Color(255, 255, 255, 0))
	viewBorderColor.setEdgeValue(edge, tlp.Color(255, 255, 255, 0))
 
 
 
# Effacer un noeud du graphe
def hideNode(graph, e, viewColor, viewBorderColor, viewLabel, viewLabelColor, From, To):
	edge = getEdge(graph, e)
	# verification que le noeud "from" de l'arete n'a pas de communication(s) en cours
#	if not hasShowEdge(graph, getNode(graph, From.getEdgeValue(edge), viewLabel), viewColor):
		# effacement du noeud "from" de l'arete
	viewColor.setNodeValue(getNode(graph, From.getEdgeValue(edge), viewLabel), tlp.Color(255, 255, 255, 0))
	viewBorderColor.setNodeValue(getNode(graph, From.getEdgeValue(edge), viewLabel), tlp.Color(255, 255, 255, 0))
	viewLabelColor.setNodeValue(getNode(graph, From.getEdgeValue(edge), viewLabel), tlp.Color(255, 255, 255, 0))
	# verification que le noeud "to" de l'arete n'a pas de communication(s) en cours
#	if not hasShowEdge(graph, getNode(graph, To.getEdgeValue(edge), viewLabel), viewColor):
		# effacement du noeud "to" de l'arete
	viewColor.setNodeValue(getNode(graph, To.getEdgeValue(edge), viewLabel), tlp.Color(255, 255, 255, 0))
	viewBorderColor.setNodeValue(getNode(graph, To.getEdgeValue(edge), viewLabel), tlp.Color(255, 255, 255, 0))
	viewLabelColor.setNodeValue(getNode(graph, To.getEdgeValue(edge), viewLabel), tlp.Color(255, 255, 255, 0))
 
 
 
# Verifier que le noeud n'a pas de communication(s) en cours
#def hasShowEdge(graph, node, viewColor):
#	for n in graph.getOutEdges(node):
#		if (viewColor.getEdgeValue(n) == tlp.Color(0, 0, 0, 100)):
#			return True
#	return False
/net/html/perso/melancon/Visual_Analytics_Course/data/pages/vruffet_cfarhat/vincent_ruffet_cedric_farhat.txt · Last modified: 2013/01/13 14:16 by cfarhat