User Tools

Site Tools


vincent_bocquel:solution_homework2

Projet de visualisation et d'analyse

Introduction

Dans ce rapport, nous allons vous exposer les différentes étapes de l'élaboration de notre projet. Nous reviendrons ainsi sur la méthodologie utilisée puis nous verrons les résultats obtenus et nous les analyserons. Enfin nous discuterons de nos conclusions et de la pertinence de celles-ci.

Jeu de données

Nous avons choisi pour ce projet d'utiliser le jeu de données accessible à cette adresse. Il se base sur des données récupérées sur reddit.com, au sein du subreddit Random Acts Of Pizza. Le principe de ce subreddit est simple, les utilisateurs demandent une pizza en tentant de convaincre via leur message et les autres utilisateurs peuvent alors décider d'offrir une pizza au demandeur.

Le jeu de données propose déjà une étude de celui-ci, tirant ses propres conclusions sur ce qui fait le succès d'une requête sur Random Acts Of Pizza.

Dans notre étude, nous proposons également de répondre à la question suivante : Quels facteurs influencent la réussite d'une demande de pizza gratuite ?

Méthodologie

Lors de notre analyse, nous avons choisi de nous intéresser aux critères suivants :

  • La longueur du message initial
  • Le nombre de vote de la demande
  • La popularité générale au sein de reddit du demandeur
  • L'activité du demandeur
  • Les types de sujet abordés au sein de la demande textuelle (argent, travail, etc.)

Nous avons utilisé les différents outils à notre disposition au sein de Tulip, tels que la visualisation direct du graphe ou les histogrammes. Nous avons également calculé différentes statistiques que nous jugions pertinentes.

Implémentation

Nous avons choisi d'importer nos données (JSON) au moyen de notre script Python (fourni en annexe). Nous générons donc les différents sommets en leur attribuant les propriétés qui leurs sont relatives au moyen d'une fonction generateGraph(). Nous avons également choisi de hiérarchiser notre graphe en sous graphes selon la réussite ou l'échec de la demande de pizza. Chacun de ces sous-graphes aura ainsi une couleur distincte de manière à faciliter la visualisation des sommets.

Parmis les critères d'étude choisis, la majeure partie s'appuie sur des données chiffrées que nous avons jugé pertinent de mettre en évidence à l'aide des histogrammes.

En revanche, les sujets abordés au sein de la requête ont nécessité un pré-traitement. Celui-ci consiste simplement à analyser le texte et à y repérer un certain nombre de mot-clés associés aux catégories préalablement établies. Nous notons que nous avons choisi d'utiliser les mots-clés fournis au sein du jeu de donnée pour effectuer cette détection.

Nous avons testé l'application de cette méthode avec différents nombres de mot-clé nécessaire pour être inclu dans une catégorie. Nous avons ainsi déterminé que trois mots-clés semblait être un bon compromis et permettait d'aboutir à des résultats intéressant.

Résultats

L'exécution de notre script permet d'obtenir une visualisation des différentes catégories de message (travail, famille, argent, désir, étude) avec une différenciation par couleur selon le succès de la requête.

Cette image présente donc le graphe telle qu'obtenu à l'issue de l'exécution du script et après avoir appliqué l'algorithme MMM Example No Twist Layout.

Nous constatons que le sujet qui revient le plus fréquemment dans les demandes est l'argent mais le taux de requête ayant été un succès n'est pas considérablement plus élevé. L'impact de l'utilisation de ce lexique sur l'obtention de la pizza n'est donc pas significatif.

Dans les images suivantes, nous mettons en évidence les sous-graphes de chacune des catégories choisies. Celles-ci permettent également d'appuyer notre premier constat.

Dans ces histogrammes, nous voyons que des critères tels que le nombre de vote négatif, positif, l'âge du compte ou encore la nombre de message sur reddit ne permettent pas d'idenfitier des facteurs directement liés au succès d'une demande. En revanche, la taille du message semble avoir un impact plus significatif comme nous le montre l'histogramme suivant :

Enfin, nous avons également calculé des moyennes pour les différentes informations que nous avons résumé au sein du tableau suivant.

Critère Pizza reçue Pizza non reçue
Taille des messages 499 374
Downvotes de la requête 2 2
Upvotes de la requête 8 5
Nombre de commentaires 5 2
Âge du demandeur* 280 244
Durée* entre le premier post sur raop et la requête 26 13
Nombre de commentaires du demandeur 128 112
Nombre de commentaires dans raop 1 0
Nombre de posts du demandeur 24 20
Nombre de subreddits 19 17
Upvotes - downvotes 1446 1071
Upvotes + downvotes 5119 3256

* Exprimé en jours

Ces résultats nous confirment ce que nous avions pu entrevoir via les histogrammes, à savoir que la taille du message a un impact sur la réussite de la demande. Cependant nous constatons de manière plus franche l'impact de certains autres facteurs tels que la popularité (générale à reddit et non au subreddit) du demandeur ainsi que le temps passé sur le subreddit RAOP avant que la personne n'effectue sa propre demande.

Après cela, nous avons essayé de prendre une approche différente. Nous avons essayé de catégoriser les différents messages en nous inspirant des travaux déjà effectués sur le sujet. Nous avons ainsi cinq catégories : désir, argent, travail, famille et étudiant. Nous avons ainsi obtenu les résultats suivants :

Pourcentage de personnes ayant obtenu leur pizza
Desire 26.16
Money 28.06
Family 33.76
Student 27.88
Job 35.06
Nombre de sujets évoqués Pourcentage de personnes ayant obtenu leur pizza
0 16.77
1 26.77
2 30.15
3 37.14
4 35.0
5 100.0

Les nombres de posts évoquant 4 et 5 sujets sont respectivement 20 et 2, ces nombres sont malheureusement trop faibles pour être pertinent. Mais nous pouvons néanmoins observer que plus le nombre de sujet abordé est élevé plus le taux de réussite est élevée, sans doute dû au fait que plus l'on évoque de sujets plus de personnes peuvent être touchées par ce que l'on dit. De plus, le fait de n'aborder aucun des sujets étudiés a un effet très négatif puisque les personnes qui font ça n'ont un taux de réussite que de 16.77%, 10 points derrière ceux qui se contentent de ne parler que d'un sujet.

Nous observons ainsi que les sujets qui semblent le plus à même d'inciter une autre personne à vous offrir une pizza sont la famille et le travail.

Conclusion

Dans ce projet, nous avons pu mettre en évidence certains des aspects permettant d'obtenir gratuitement une pizza sur le subreddit Random Acts Of Pizza. Cependant l'impact relatif à ces critères n'est pas suffisamment significatif pour permettre la rédaction d'une demande avec la quasi-certitude d'obtenir une pizza.

De plus, notre étude s'appuie partiellement sur l'analyse pseudo-lexicale de la demande or celle-ci n'est sans doute pas des plus précises. Malgré le fait que cette étude apporte quelques lumières sur l'obtention d'une pizza, elle reste beaucoup moins pertinence que l'analyse plus approfondie telle que celle décrite au sein de l'article How to Ask for a Favor: A Case Study on the Success of Altruistic Requests par Tim Althoff, Cristian Danescu-Niculescu-Mizil et Dan Jurafsky. En effet, celle-ci étudie l'aspect narratif du texte en prennant en compte des éléments tels que la politesse ou encore la réciprocité.

Bien que nous ayons pu extraire quelques informations à partir des données sur l'utilisateur ou du message (hormis son contenu), leur impact semble plus limité que celui du contenu.

Annexe

RAOP_Analysis.py
# Authors :
# 	Jérémy Turon
# 	Vincent Bocquel
 
from tulip import *
import codecs
import json
 
########################
### Global variables ###
########################
category = ["family","job","student","money","desire"]
 
# rootPath = os.getcwd()
# rootPath = "/net/cremi/vbocquel/Cours/S9_BioVisu/ProjetVisu/"
rootPath = "/net/cremi/jturon/BioInfosEtVisu/Visu/Projet/"
 
 
def main(graph): 
	viewBorderColor = graph.getColorProperty("viewBorderColor")
	viewBorderWidth = graph.getDoubleProperty("viewBorderWidth")
	viewColor = graph.getColorProperty("viewColor")
	viewFont = graph.getStringProperty("viewFont")
	viewFontSize = graph.getIntegerProperty("viewFontSize")
	viewLabel = graph.getStringProperty("viewLabel")
	viewLabelBorderColor = graph.getColorProperty("viewLabelBorderColor")
	viewLabelBorderWidth = graph.getDoubleProperty("viewLabelBorderWidth")
	viewLabelColor = graph.getColorProperty("viewLabelColor")
	viewLabelPosition = graph.getIntegerProperty("viewLabelPosition")
	viewLayout = graph.getLayoutProperty("viewLayout")
	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")
	# Preprocess data
	generateGraph(graph)
	colorNodes(graph)
	categorizeNodes(graph,2)
	subGraphsPerCategory(graph)
	messageLength(graph)
	stats(graph)
 
########################
 
def subGraphsPerCategory(graph):		
	subGraphs = { "family" : [],"job" : [],"student" : [],"money" : [],"desire" : []}
	viewLabel = graph.getStringProperty("viewLabel")
 
	for n in graph.getNodes():
		if(not viewLabel.getNodeValue(n) in ["family","job","student","money","desire"]):
			for m in graph.getInOutNodes(n):
				subGraphs[viewLabel.getNodeValue(m)].append(n)
		else:
			for i in category:
				subGraphs[i].append(n)
	for i in subGraphs.keys():
		graph.inducedSubGraph(subGraphs[i]).setName(i)
 
def stats(graph):
	received = graph.getBooleanProperty("requester_received_pizza")
	viewLabel = graph.getStringProperty("viewLabel")
 
	for n in graph.getNodes():
		if( viewLabel.getNodeValue(n) in ["family","job","student","money","desire"]):
			cpt = 0.
			cptMax = 0.
			for m in graph.getInOutNodes(n):
				cptMax += 1.
				if( received.getNodeValue(m) ):
					cpt += 1.
			print viewLabel.getNodeValue(n) + " : " + str(cpt) + "/" + str(cptMax) + "=>" + str((cpt/cptMax)*100.)
 
	print '\n'
 
	res = [[0.,0.],[0.,0.], [0.,0.], [0.,0.], [0.,0.], [0.,0.]]
 
	res4 = { "family" : 0,"job" : 0,"student" : 0,"money" : 0,"desire" : 0}
	res5 = { "family" : 0,"job" : 0,"student" : 0,"money" : 0,"desire" : 0}
 
	for n in graph.getNodes():
		if( not viewLabel.getNodeValue(n) in ["family","job","student","money","desire"]):
			cpt = 0
			for m in graph.getInOutNodes(n):
				cpt += 1
			res[cpt][1] += 1.
			if( received.getNodeValue(n) ):
				res[cpt][0] += 1.
			if(cpt == 4):
				for m in graph.getInOutNodes(n):
					res4[viewLabel.getNodeValue(m)]+=1
			if(cpt == 5):
				for m in graph.getInOutNodes(n):
					res5[viewLabel.getNodeValue(m)]+=1
 
	for i in range(0,6):
		print str(res[i][0]) + '/' + str(res[i][1]) + ' => ' + str( 100. * (res[i][0]/ res[i][1]))
 
	print '\n'	
 
	print res4
	print res5
 
def categorizeNodes(graph, s):
	viewLabel = graph.getStringProperty("viewLabel")
	basePath = os.path.join(rootPath, 'pizza_request_dataset', 'narratives')
 
	narratives = {
		"money":{"keywords":[],"node":None},
		"job":{"keywords":[],"node":None},
		"family":{"keywords":[],"node":None},
		"student":{"keywords":[],"node":None},
		"desire":{"keywords":[],"node":None}
	}
 
	for k in narratives:
		narratives[k]["node"] = graph.addNode()
		viewLabel.setNodeStringValue(narratives[k]["node"], k)
		with open(basePath+ '/' + k+'.txt','r') as f:
			narratives[k]["keywords"] = f.read().split('\r\n')
 
	for n in graph.getNodes():
		request = graph.getStringProperty("request_text").getNodeValue(n)
		for k in narratives:
			cpt = 0
			for keyWord in narratives[k]["keywords"]:
				cpt += request.count(keyWord)
			if(cpt > s):
				graph.addEdge(n,narratives[k]["node"])
 
def colorNodes(graph):
	gotPizza = graph.getSubGraph("gotPizza")
	didntGotPizza = graph.getSubGraph("didntGotPizza")
	viewColor = graph.getColorProperty("viewColor")
	for n in gotPizza.getNodes():
		viewColor.setNodeValue(n,tlp.Color(0,255,0,255))
	for n in didntGotPizza.getNodes():
		viewColor.setNodeValue(n,tlp.Color(0,0,255,255))
 
def generateGraph(graph):
	graph.setName("RAOP")	
	path = os.path.join('pizza_request_dataset','pizza_request_dataset.json')
	dataset = read_dataset(path)
 
	# Build graph and add properties
	for r in dataset:
		newNode = graph.addNode()
		for key in dataset[0].keys():
			valueType = type(dataset[0][key])
			if valueType is int:
				graph.getIntegerProperty(key).setNodeValue(newNode,r[key])
			elif valueType is unicode:
				text = "" if r[key] is None else r[key]
				graph.getStringProperty(key).setNodeValue(newNode,text)
			elif valueType is float:
				graph.getDoubleProperty(key).setNodeValue(newNode,r[key])
			elif valueType is bool:
				graph.getBooleanProperty(key).setNodeValue(newNode,r[key])
			elif valueType is list:
				graph.getStringVectorProperty(key).setNodeValue(newNode,r[key])
 
	# Generate subgraphes depending on the success of the request
	GP = []
	DGP = []
	gotPizzaProperty = graph.getBooleanProperty('requester_received_pizza')
	for n in graph.getNodes():
		if gotPizzaProperty.getNodeValue(n):
			GP.append(n)
		else:
			DGP.append(n)
	gotPizza = graph.inducedSubGraph(GP)
	gotPizza.setName("gotPizza")	
	didntGotPizza = graph.inducedSubGraph(DGP)
	didntGotPizza.setName("didntGotPizza")
 
def read_dataset(path):
	with codecs.open(path, 'r', 'utf-8') as myFile:
		content = myFile.read()
	dataset = json.loads(content)
	return dataset
 
def hasSubredditInCommon(graph, m, n):
	subreddit = graph.getStringVectorProperty("requester_subreddits_at_request")
	intersection = set(subreddit.getNodeValue(m)).intersection(set(subreddit.getNodeValue(n)))
	return len(intersection) > 0
 
def messageLength(graph):
	messageLength = graph.getIntegerProperty("messageLength")
	post = graph.getStringProperty("request_text_edit_aware")
 
	for n in graph.getNodes():
		messageLength.setNodeValue(n,len(post.getNodeValue(n)))
/net/html/perso/melancon/Visual_Analytics_Course/data/pages/vincent_bocquel/solution_homework2.txt · Last modified: 2015/01/19 16:32 by bocquel