def ratePartition(self): """ Metoda do oceny podziału sieci. Porównuje podział wykonany z wzrocowym i wyznacza procentową skuteczność podziału. @return: """ result = 0 maximal_result = 0 for index, node in enumerate(self.graph.nodes()): for groupNo in self.partition: if node in groupNo: nodeComputedGroup = self.partition.index(groupNo) for groupNo in handPartition: if node in groupNo: nodeOriginalGroup = handPartition.index(groupNo) for secondNode in self.graph.nodes()[index+1:]: for groupNo in self.partition: if secondNode in groupNo: secondNodeComputedGroup = self.partition.index(groupNo) for groupNo in handPartition: if secondNode in groupNo: secondNodeOriginalGroup = handPartition.index(groupNo) if nodeOriginalGroup == secondNodeOriginalGroup: maximal_result += 1 if (nodeOriginalGroup == secondNodeOriginalGroup) and (nodeComputedGroup == secondNodeComputedGroup): result += 1 ret = float(result)/maximal_result * 100.0 logger.info("# Jakość grupowania FB: " + str(ret) + "%") return ret
def karateClub(): """ Funkcja uruchamia różne algorytmy grupowania na sieci klubu karate @return: W systemie plików zapisywane są obrazy PNG z wynikami grupowań """ karateG = karate_graph.karate_graph() cq = sna.Cliquer(karateG) logger.info("# Karate Graph... ") partition = cq.blondelAlgorithm() cq.prettyPlotter(l=partition, filename="karate_blondel.png") partition = cq.newmanAlgorithm() cq.prettyPlotter(l=partition, filename="karate_newman.png") partition = cq.causetNewmanAlgorithm() cq.prettyPlotter(l=partition, filename="karate_causet_newman.png") partition = cq.MCLAlgorithm(2.0) cq.prettyPlotter(l=partition, filename="karate_MCL.png") logger.info("# Karate Graph wykonano")
def rateQuality(self, hardGroupsNo = 0): ''' Wyznacza wskaźniki jakości działania metody autorskiej @type hardGroupsNo: number @param hardGroupsNo: liczba kolejnych (od najmniej licznej) grup, których członkowie będą uznani za podjerzanych @rtype: tuple @return: Krotka matchRate i popRate ''' if hardGroupsNo: self.selectGroups = [hardGroupsNo] for i in self.selectGroups: # pobierz pierwszych i grup matchCount = 0 # suspects = self.cq.smartGetFristNGroups(self.result, i) suspects = self.cq.getSuspectedGroups(self.result, i) logger.debug("# Podejrzani: ") logger.debug("# " + str(suspects)) for v in self.rings: if int(v) in suspects: matchCount += 1 matchRate = matchCount/len(self.rings) * 100.0 popRate = len(suspects)/self.vertexNo * 100.0 suspectsRate = matchCount/len(suspects) * 100.0 logger.info("%d ; %f ; %f" % (i, matchRate, popRate)) # liczba grup, procent wykrytych, procent podejrzewanje populacji return (matchRate, popRate, suspectsRate)
def nativeCliquer(self): """ Wykorzystuje metodę znajdowania klik z biblioteki NetworkX. Metoda ta nie wykorzystuje algorytmów grupowania, wyznacza jedynie k-kliki. Nie jest to właściwe podejście do problemu. """ cliques = list(nx.find_cliques(self.graph)) logger.info("Kliki wyznaczone:") import pprint pprint.pprint(cliques)
def __init__(self, testingDataPath = "/tmp/testing-ring.txt"): """ Konstruktor @param testingDataPath: ścieżka do pliku z testowymi danymi o głosowaniu @type testingDataPath: text """ logger.info("################### WYZNACZANIE KLIK ZLOSLIWYCH GLOSUJACYCH ######################") self.testingDataPath = testingDataPath self.dm = sna.DataMaker(self.testingDataPath) self.gm = sna.GraphMaker(self.testingDataPath)
def loadGraph(self, filename = 'fb.gpickle'): """ Ładuje sieć do pamięci z pliku typu python pickle. @param filename: nazwa pliku """ self.graph = nx.read_gpickle(filename) # anonimizacja grafu, każdemu węzłowi przypisywany jest numer i = 1 for node in self.graph: self.labels[node] = i i += 1 logger.info("########################## FACEBOOK EXPERIMENT ########################") logger.debug("Sieć załadowana, krawędzi: %d, węzłów: %d" % (self.graph.number_of_edges(), self.graph.number_of_nodes()))
def partitionGraph(self, filename = "fb_partition.png", method = 'blondelAlgorithm'): from thesis.sna import Cliquer cq = Cliquer(self.graph) function = getattr(cq, method) self.partition = function() # write down partition for cluster in self.partition: members = "" for member in cluster: members += str(self.labels[member]) + " " logger.info(str(self.partition.index(cluster)) + ", " + members) # partition done, making colors # making graph nodeList = self.partition colorList = [0] * self.graph.number_of_nodes() for nbunch in nodeList: for n in nbunch: colorList[self.graph.nodes().index(n)] = nodeList.index(nbunch) import matplotlib.pyplot as plt # stupid hack becouse of pygraphviz utf-8 malfunction graph_copy = nx.Graph(); graph_copy.add_nodes_from(self.graph.nodes()) graph_copy.add_edges_from(self.graph.edges()) # end stupid hack pos=nx.pygraphviz_layout(graph_copy,prog='neato') for i in pos: pos[i] = (pos[i][0]*3, pos[i][1]*3) plt.figure(figsize=(20,20)) plt.axis('off') nx.draw_networkx_edges(self.graph,pos,alpha=0.2) nx.draw_networkx_nodes(self.graph, pos, size = 4, node_color=colorList, with_labels=False) nx.draw_networkx_labels(self.graph, pos, font_size = 10, labels = self.labels) import config filename = config.PLOT_OUT_DIR+filename plt.savefig(filename)
def plotTuple(self, tuple, caption = "This is experiment", xlabel = "This is x label", ylabel = "This is y label", file_title = "chart", xaxis = [], step = 1): """ Metoda rysuje wykres ilustrujący eksperyment @param tuple: krotka z wynikami @param caption: tytuł wykresu @param xlabel: tytuł osi poziomej @param ylabel: tytuł osi pionowej @param file_title: nazwa pliku @return: W systemie plików pojawia się plik w formacie eps będący zadanym wykresem """ if not xaxis: from numpy import arange xaxis = arange(0,len(tuple[0]),1) logger.info("############# Rysowanie wykresu ################") logger.info("# " + str(tuple)) import Gnuplot g = Gnuplot.Gnuplot() suspects = Gnuplot.Data(xaxis, tuple[0], title ='procent wykrytych zlosliwych glosujacych', with_="points lt 1 lw 6 lc 1") population = Gnuplot.Data(xaxis, tuple[1], title='podejrzany procent populacji', with_="points lt 4 lw 6 lc 3") suspectsR = Gnuplot.Data(xaxis, tuple[2], title='procent zlosliwych glosujacych wsrod podejrzanych', with_="points lt 2 lw 6 lc 4") near100 = Gnuplot.Data(xaxis, tuple[3], title='procent wykryc powyzej 90%', with_="points lt 3 lw 6 lc 6") g.title(caption) g.xlabel(xlabel) g.ylabel(ylabel) g('set xtics ' + repr(step)) g('set grid') g('set size 1.3,1.3') maxs = [] maxs.append(max(tuple[0])) maxs.append(max(tuple[1])) maxs.append(max(tuple[2])) maxs.append(max(tuple[3])) max_y = max(maxs) g('set yrange [ 0 : ' + repr(max_y+10) + ' ]') g.plot(suspects, population, suspectsR, near100) #g.plot(suspects, population, suspectsR) import config file_title = config.PLOT_OUT_DIR+file_title g.hardcopy(file_title + '.eps', eps=True)
def fbExperiment(): """ Uruchomienie eksperymentów na danych z platformy facebook. Zakłada, że sieć znajduje się w pliku fb.gpickle. """ fb = Facebooker() fb.loadGraph() fb.partitionGraph() fb.ratePartition() fb.computeMeasures() fb.loadMeasures() fb.graphMeasure('clustering', True) fb.graphMeasure('betweeness', True) fb.graphMeasure('degree', True) fb.graphMeasure('closeness', True) fb.graphMeasure('eigenvector', True) from thesis import logger logger.info("# Średni wsp. gronowania: " + str(fb.centrality['avg_clustering'])) logger.info("# Średnia najkrótsza ścieżka: " + str(fb.centrality['avg_shortest_path'] ))
def iterateParam(self, minValue = 10, maxValue = 25, step = 5, param = "target_size", hardGroupsNo = 2, runsNo = 100): ''' Służy do przeprowadzania eksperytmentu polegającego na ocenie jakości działania metody przy zmieniającym się jednym parametrze generatora. Podawana jest nazwa parametru, wartości minimalna i maksymalna oraz krok, z jakim zmieniania jest wartość. @param minValue: wartość startowa parametru @param maxValue: wartośc końcowa @param step: krok @param param: nazwa parametru @param hardGroupsNo: liczba grup @param runsNo: liczba uruchomień @rtype: tuple @return Krotka zwierające krotkę z wynikami oraz listę argumentów, w celu rysowania wykresów ''' oldDictValue = self.paramsDict[param] self.paramsDict[param] = minValue matchRates = [] popRates = [] suspectRates = [] near100Rates = [] xaxis = [] while self.paramsDict[param] <= maxValue: logger.info("# Będzie zmieniany parametr %s : teraz wynosi %d:" % (param, self.paramsDict[param])) tuple = self.compute(hardGroupsNo = hardGroupsNo, runsNo = runsNo) xaxis.append(self.paramsDict[param]) self.paramsDict[param] += step if hardGroupsNo: matchRates.append(tuple[0]) popRates.append(tuple[1]) suspectRates.append(tuple[2]) near100Rates.append(tuple[3]) self.paramsDict[param] = oldDictValue if hardGroupsNo: return ((matchRates, popRates, suspectRates, near100Rates), xaxis)
def makeGraph(self): """ Tworzy sieć według autorskiej metody wczytując dane o głosowaniu z pliku. @rtype: networkx.Graph @return: Wczytana sieć. """ if self.file_type == FileType.VOTING_RING: i = 0 voters = [] with open(self.path) as file: for line in file: if not i % 2 == 0: # voters line voters = [int(v) for v in line.split()] # voting for same item increases edge weight between all of voters by 1 self.graph.add_nodes_from(voters) self.addEdges(voters) i += 1 logger.info("# Sieć załadowana, węzłów: " + repr(self.graph.number_of_nodes()) + ", krawędzi: " + repr(self.graph.number_of_edges())) return self.graph
def graphMeasure(self, measure = 'degree', anonymized_log = True): """ Metoda twrzy rysunek grafu reprezentującego sieć kolorując węzły odpowiednio do charakteryzującej je miary centralnośći. @param measure: nazwa miary centralności, której wartość bęðzie wykorzystana przy kolorowaniu węzłów @type anonymized_log: boolean @param anonymized_log: włączenie anonimizacji danych zapisywanch w pliku dziennika (numery zamiast pełnego imienia i nazwiska) """ measureValues = self.centrality[measure] filename = measure + ".png" # print sorted node list on INFO nodesDict = dict() for node, data in self.graph.nodes_iter(data=True): nodesDict[node] = measureValues[node] sortedNodes = sorted(nodesDict, key=nodesDict.get, reverse = True) logger.info("******########*******" + measure + "******########*******") for node in sortedNodes: if not anonymized_log: logger.info(self.graph.node[node]['name'] +" (" + str(self.labels[node]) + "): " + str(nodesDict[node])) # z nazwiskami osob else: logger.info(str(self.labels[node]) + ": " + str(nodesDict[node])) # same numery # set my value to 0 for better colors if self.graph.node[node]['name'] == 'Marcin Mincer': measureValues[node] = 0 # stupid hack becouse of pygraphviz utf-8 malfunction graph_copy = nx.Graph(); graph_copy.add_nodes_from(self.graph.nodes()) graph_copy.add_edges_from(self.graph.edges()) # end stupid hack pos=nx.pygraphviz_layout(graph_copy,prog='neato') for i in pos: pos[i] = (pos[i][0]*3, pos[i][1]*3) import matplotlib.pyplot as plt plt.figure(figsize=(20,20)) plt.axis('off') nx.draw_networkx_edges(self.graph,pos,alpha=0.2) nx.draw_networkx_nodes(self.graph, pos, size = 4, with_labels=False, node_color=measureValues.values(), cmap=plt.cm.get_cmap('Spectral')) nx.draw_networkx_labels(self.graph, pos, font_size = 10, labels = self.labels) plt.colorbar(orientation="horizontal", fraction = 0.04, pad = 0.01, aspect = 16) import config filename = config.PLOT_OUT_DIR+filename plt.savefig(filename)
def sixtyOne(sliceLevels = [3]): """ Funkcja uruchamia autorską metodą na danych ze strony thesixtyone.com @param sliceLevels: lista poziomów odcięcia, z jakimi ma być uruchomiony algorytm @return: Obraz PNG w systemie plików, lista podejrzanych w pliku dziennika """ import config gm = sna.GraphMaker(config.SIXTYONE_SOURCE_FILE) gm.makeGraph() for sliceLevel in sliceLevels: cq = sna.Cliquer(gm.graph) nodesNo = cq.graph.number_of_nodes() cq.sliceGraph(sliceLevel) partition = cq.blondelAlgorithm() suspects = cq.smartGetFristNGroups(partition, 2) suspects_no = len(suspects) logger.info("############### EKSPERYMENT THESIXTYONE ##################") logger.info("# O przynależność do kliki jest podejrzanch %d użytkowników, co stanowi %f procent całej populacji." % (suspects_no, (suspects_no / nodesNo ) * 100.0) ) logger.info("# Identyfikatory podjerzanych użytkowników: ") logger.info(suspects)
def compute(self, hardGroupsNo = 0, runsNo = 1): ''' Metoda wykonuje określoną w L{runsNo} liczbę uruchomień algorytmu, zwaraca średnią arytmetyczną wskaźników jakości. @type hardGroupsNo: number @param hardGroupsNo: liczba grup (począwszy od najmniej licznej), których członkowie są uważania za podjerzanych @rtype: tuple @return: Zwraca krotkę (matchRate, popRate, suspectsRate, near100rate), która zwiera średnią arytmetyczną z wartości wyznaczonych przez metodę L{rateQuality}. ''' logger.info("# Parametry generatora") logger.info("# " + str(self.paramsDict)) logger.info("# Liczba prób: %d" % runsNo ) logger.info("# Poziom odcięcia: %d" % self.paramsDict['slice_level']) matchRate = 0.0 matchRateList = [] popRate = 0.0 suspectsRate = 0.0 near100Rate = 0.0 for run in xrange(runsNo): self.dm.generate(**self.paramsDict) self.graph = self.gm.makeGraph() self.rings = sum(self.dm.voting_rings, []) # H-H-HHACKISH spłaszczenie listy list self.vertexNo = len(self.graph) self.cq = sna.Cliquer(self.graph) self.cq.sliceGraph(self.paramsDict['slice_level']) self.result = self.cq.blondelAlgorithm() tuple = self.rateQuality(hardGroupsNo) matchRate += tuple[0] matchRateList.append(tuple[0]) popRate += tuple[1] suspectsRate += tuple[2] # czwarty wskaźnik jakości if tuple[0] > 90: near100Rate += 1 logger.debug(near100Rate) matchRateAvg = float(matchRate/float(runsNo)) # obliczenie wariancji matchRate matchRateList = map(lambda rate: (rate - matchRateAvg)**2, matchRateList) matchRateVariance = 1.0/runsNo * sum(matchRateList) # wyznaczanie średnich arytmetycznych tuple = (float(matchRate/float(runsNo)), float(popRate / float(runsNo)), float(suspectsRate / float(runsNo)), float(near100Rate / float(runsNo))*100.0) logger.info("# Po %d uruchomieniach (procent wykrytych; procent populacji podjerzewany; procent faktycznych wśród podejrzewanych; procent podejść, gdzie wykryto powyżej 90 procent" % runsNo) logger.info(tuple) logger.info("# Dla parametrów generatora: ") logger.info(self.paramsDict) logger.info("# Wariancja porcentu wykrytych") logger.info(matchRateVariance) logger.info("# ------------------- KONIEC --------------------") return tuple