You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

dataset.py 16 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Created on Thu Mar 26 18:48:27 2020
  5. @author: ljia
  6. """
  7. import numpy as np
  8. import networkx as nx
  9. from gklearn.utils.graphfiles import loadDataset
  10. class Dataset(object):
  11. def __init__(self, filename=None, filename_y=None, extra_params=None):
  12. if filename is None:
  13. self.__graphs = None
  14. self.__target = None
  15. self.__node_labels = None
  16. self.__edge_labels = None
  17. self.__node_attrs = None
  18. self.__edge_attrs = None
  19. else:
  20. self.load_dataset(filename, filename_y=filename_y, extra_params=extra_params)
  21. self.__substructures = None
  22. self.__node_label_dim = None
  23. self.__edge_label_dim = None
  24. self.__directed = None
  25. self.__dataset_size = None
  26. self.__total_node_num = None
  27. self.__ave_node_num = None
  28. self.__min_node_num = None
  29. self.__max_node_num = None
  30. self.__total_edge_num = None
  31. self.__ave_edge_num = None
  32. self.__min_edge_num = None
  33. self.__max_edge_num = None
  34. self.__ave_node_degree = None
  35. self.__min_node_degree = None
  36. self.__max_node_degree = None
  37. self.__ave_fill_factor = None
  38. self.__min_fill_factor = None
  39. self.__max_fill_factor = None
  40. self.__node_label_nums = None
  41. self.__edge_label_nums = None
  42. self.__node_attr_dim = None
  43. self.__edge_attr_dim = None
  44. self.__class_number = None
  45. def load_dataset(self, filename, filename_y=None, extra_params=None):
  46. self.__graphs, self.__target = loadDataset(filename, filename_y=filename_y, extra_params=extra_params)
  47. self.set_labels_attrs()
  48. def load_predefined_dataset(self, ds_name):
  49. if ds_name == 'Letter-high': # node non-symb
  50. ds_file = '../../datasets/Letter-high/Letter-high_A.txt'
  51. self.__graphs, self.__target = loadDataset(ds_file)
  52. elif ds_name == 'Letter-med': # node non-symb
  53. ds_file = '../../datasets/Letter-high/Letter-med_A.txt'
  54. self.__graphs, self.__target = loadDataset(ds_file)
  55. elif ds_name == 'Letter-low': # node non-symb
  56. ds_file = '../../datasets/Letter-high/Letter-low_A.txt'
  57. self.__graphs, self.__target = loadDataset(ds_file)
  58. elif ds_name == 'Fingerprint':
  59. ds_file = '../../datasets/Fingerprint/Fingerprint_A.txt'
  60. self.__graphs, self.__target = loadDataset(ds_file)
  61. elif ds_name == 'SYNTHETIC':
  62. pass
  63. elif ds_name == 'SYNTHETICnew':
  64. ds_file = '../../datasets/SYNTHETICnew/SYNTHETICnew_A.txt'
  65. self.__graphs, self.__target = loadDataset(ds_file)
  66. elif ds_name == 'Synthie':
  67. pass
  68. elif ds_name == 'COIL-DEL':
  69. ds_file = '../../datasets/COIL-DEL/COIL-DEL_A.txt'
  70. self.__graphs, self.__target = loadDataset(ds_file)
  71. elif ds_name == 'COIL-RAG':
  72. pass
  73. elif ds_name == 'COLORS-3':
  74. pass
  75. elif ds_name == 'FRANKENSTEIN':
  76. pass
  77. self.set_labels_attrs()
  78. def set_labels_attrs(self, node_labels=None, node_attrs=None, edge_labels=None, edge_attrs=None):
  79. # @todo: remove labels which have only one possible values.
  80. if node_labels is None:
  81. self.__node_labels = self.__graphs[0].graph['node_labels']
  82. # # graphs are considered node unlabeled if all nodes have the same label.
  83. # infos.update({'node_labeled': is_nl if node_label_num > 1 else False})
  84. if node_attrs is None:
  85. self.__node_attrs = self.__graphs[0].graph['node_attrs']
  86. # for G in Gn:
  87. # for n in G.nodes(data=True):
  88. # if 'attributes' in n[1]:
  89. # return len(n[1]['attributes'])
  90. # return 0
  91. if edge_labels is None:
  92. self.__edge_labels = self.__graphs[0].graph['edge_labels']
  93. # # graphs are considered edge unlabeled if all edges have the same label.
  94. # infos.update({'edge_labeled': is_el if edge_label_num > 1 else False})
  95. if edge_attrs is None:
  96. self.__edge_attrs = self.__graphs[0].graph['edge_attrs']
  97. # for G in Gn:
  98. # if nx.number_of_edges(G) > 0:
  99. # for e in G.edges(data=True):
  100. # if 'attributes' in e[2]:
  101. # return len(e[2]['attributes'])
  102. # return 0
  103. def get_dataset_infos(self, keys=None):
  104. """Computes and returns the structure and property information of the graph dataset.
  105. Parameters
  106. ----------
  107. keys : list
  108. List of strings which indicate which informations will be returned. The
  109. possible choices includes:
  110. 'substructures': sub-structures graphs contains, including 'linear', 'non
  111. linear' and 'cyclic'.
  112. 'node_label_dim': whether vertices have symbolic labels.
  113. 'edge_label_dim': whether egdes have symbolic labels.
  114. 'directed': whether graphs in dataset are directed.
  115. 'dataset_size': number of graphs in dataset.
  116. 'total_node_num': total number of vertices of all graphs in dataset.
  117. 'ave_node_num': average number of vertices of graphs in dataset.
  118. 'min_node_num': minimum number of vertices of graphs in dataset.
  119. 'max_node_num': maximum number of vertices of graphs in dataset.
  120. 'total_edge_num': total number of edges of all graphs in dataset.
  121. 'ave_edge_num': average number of edges of graphs in dataset.
  122. 'min_edge_num': minimum number of edges of graphs in dataset.
  123. 'max_edge_num': maximum number of edges of graphs in dataset.
  124. 'ave_node_degree': average vertex degree of graphs in dataset.
  125. 'min_node_degree': minimum vertex degree of graphs in dataset.
  126. 'max_node_degree': maximum vertex degree of graphs in dataset.
  127. 'ave_fill_factor': average fill factor (number_of_edges /
  128. (number_of_nodes ** 2)) of graphs in dataset.
  129. 'min_fill_factor': minimum fill factor of graphs in dataset.
  130. 'max_fill_factor': maximum fill factor of graphs in dataset.
  131. 'node_label_nums': list of numbers of symbolic vertex labels of graphs in dataset.
  132. 'edge_label_nums': list number of symbolic edge labels of graphs in dataset.
  133. 'node_attr_dim': number of dimensions of non-symbolic vertex labels.
  134. Extracted from the 'attributes' attribute of graph nodes.
  135. 'edge_attr_dim': number of dimensions of non-symbolic edge labels.
  136. Extracted from the 'attributes' attribute of graph edges.
  137. 'class_number': number of classes. Only available for classification problems.
  138. All informations above will be returned if `keys` is not given.
  139. Return
  140. ------
  141. dict
  142. Information of the graph dataset keyed by `keys`.
  143. """
  144. infos = {}
  145. if keys == None:
  146. keys = [
  147. 'substructures',
  148. 'node_label_dim',
  149. 'edge_label_dim',
  150. 'directed',
  151. 'dataset_size',
  152. 'total_node_num',
  153. 'ave_node_num',
  154. 'min_node_num',
  155. 'max_node_num',
  156. 'total_edge_num',
  157. 'ave_edge_num',
  158. 'min_edge_num',
  159. 'max_edge_num',
  160. 'ave_node_degree',
  161. 'min_node_degree',
  162. 'max_node_degree',
  163. 'ave_fill_factor',
  164. 'min_fill_factor',
  165. 'max_fill_factor',
  166. 'node_label_nums',
  167. 'edge_label_nums',
  168. 'node_attr_dim',
  169. 'edge_attr_dim',
  170. 'class_number',
  171. ]
  172. # dataset size
  173. if 'dataset_size' in keys:
  174. if self.__dataset_size is None:
  175. self.__dataset_size = self.__get_dataset_size()
  176. infos['dataset_size'] = self.__dataset_size
  177. # graph node number
  178. if any(i in keys for i in ['total_node_num', 'ave_node_num', 'min_node_num', 'max_node_num']):
  179. all_node_nums = self.__get_all_node_nums()
  180. if 'total_node_num' in keys:
  181. if self.__total_node_num is None:
  182. self.__total_node_num = self.__get_total_node_num(all_node_nums)
  183. infos['total_node_num'] = self.__total_node_num
  184. if 'ave_node_num' in keys:
  185. if self.__ave_node_num is None:
  186. self.__ave_node_num = self.__get_ave_node_num(all_node_nums)
  187. infos['ave_node_num'] = self.__ave_node_num
  188. if 'min_node_num' in keys:
  189. if self.__min_node_num is None:
  190. self.__min_node_num = self.__get_min_node_num(all_node_nums)
  191. infos['min_node_num'] = self.__min_node_num
  192. if 'max_node_num' in keys:
  193. if self.__max_node_num is None:
  194. self.__max_node_num = self.__get_max_node_num(all_node_nums)
  195. infos['max_node_num'] = self.__max_node_num
  196. # graph edge number
  197. if any(i in keys for i in ['total_edge_num', 'ave_edge_num', 'min_edge_num', 'max_edge_num']):
  198. all_edge_nums = self.__get_all_edge_nums()
  199. if 'total_edge_num' in keys:
  200. if self.__total_edge_num is None:
  201. self.__total_edge_num = self.__get_total_edge_num(all_edge_nums)
  202. infos['total_edge_num'] = self.__total_edge_num
  203. if 'ave_edge_num' in keys:
  204. if self.__ave_edge_num is None:
  205. self.__ave_edge_num = self.__get_ave_edge_num(all_edge_nums)
  206. infos['ave_edge_num'] = self.__ave_edge_num
  207. if 'max_edge_num' in keys:
  208. if self.__max_edge_num is None:
  209. self.__max_edge_num = self.__get_max_edge_num(all_edge_nums)
  210. infos['max_edge_num'] = self.__max_edge_num
  211. if 'min_edge_num' in keys:
  212. if self.__min_edge_num is None:
  213. self.__min_edge_num = self.__get_min_edge_num(all_edge_nums)
  214. infos['min_edge_num'] = self.__min_edge_num
  215. # label number
  216. if 'node_label_dim' in keys:
  217. if self.__node_label_dim is None:
  218. self.__node_label_dim = self.__get_node_label_dim()
  219. infos['node_label_dim'] = self.__node_label_dim
  220. if 'node_label_nums' in keys:
  221. if self.__node_label_nums is None:
  222. self.__node_label_nums = {}
  223. for node_label in self.__node_labels:
  224. self.__node_label_nums[node_label] = self.get_node_label_num(node_label)
  225. infos['node_label_nums'] = self.__node_label_nums
  226. if 'edge_label_dim' in keys:
  227. if self.__edge_label_dim is None:
  228. self.__edge_label_dim = self.__get_edge_label_dim()
  229. infos['edge_label_dim'] = self.__edge_label_dim
  230. if 'edge_label_nums' in keys:
  231. if self.__edge_label_nums is None:
  232. self.__edge_label_nums = {}
  233. for edge_label in self.__edge_labels:
  234. self.__edge_label_nums[edge_label] = self.get_edge_label_num(edge_label)
  235. infos['edge_label_nums'] = self.__edge_label_nums
  236. if 'directed' in keys or 'substructures' in keys:
  237. if self.__directed is None:
  238. self.__directed = self.__is_directed()
  239. infos['directed'] = self.__directed
  240. # node degree
  241. if any(i in keys for i in ['ave_node_degree', 'max_node_degree', 'min_node_degree']):
  242. all_node_degrees = self.__get_all_node_degrees()
  243. if 'ave_node_degree' in keys:
  244. if self.__ave_node_degree is None:
  245. self.__ave_node_degree = self.__get_ave_node_degree(all_node_degrees)
  246. infos['ave_node_degree'] = self.__ave_node_degree
  247. if 'max_node_degree' in keys:
  248. if self.__max_node_degree is None:
  249. self.__max_node_degree = self.__get_max_node_degree(all_node_degrees)
  250. infos['max_node_degree'] = self.__max_node_degree
  251. if 'min_node_degree' in keys:
  252. if self.__min_node_degree is None:
  253. self.__min_node_degree = self.__get_min_node_degree(all_node_degrees)
  254. infos['min_node_degree'] = self.__min_node_degree
  255. # fill factor
  256. if any(i in keys for i in ['ave_fill_factor', 'max_fill_factor', 'min_fill_factor']):
  257. all_fill_factors = self.__get_all_fill_factors()
  258. if 'ave_fill_factor' in keys:
  259. if self.__ave_fill_factor is None:
  260. self.__ave_fill_factor = self.__get_ave_fill_factor(all_fill_factors)
  261. infos['ave_fill_factor'] = self.__ave_fill_factor
  262. if 'max_fill_factor' in keys:
  263. if self.__max_fill_factor is None:
  264. self.__max_fill_factor = self.__get_max_fill_factor(all_fill_factors)
  265. infos['max_fill_factor'] = self.__max_fill_factor
  266. if 'min_fill_factor' in keys:
  267. if self.__min_fill_factor is None:
  268. self.__min_fill_factor = self.__get_min_fill_factor(all_fill_factors)
  269. infos['min_fill_factor'] = self.__min_fill_factor
  270. if 'substructures' in keys:
  271. if self.__substructures is None:
  272. self.__substructures = self.__get_substructures()
  273. infos['substructures'] = self.__substructures
  274. if 'class_number' in keys:
  275. if self.__class_number is None:
  276. self.__class_number = self.__get_class_number()
  277. infos['class_number'] = self.__class_number
  278. if 'node_attr_dim' in keys:
  279. if self.__node_attr_dim is None:
  280. self.__node_attr_dim = self.__get_node_attr_dim()
  281. infos['node_attr_dim'] = self.__node_attr_dim
  282. if 'edge_attr_dim' in keys:
  283. if self.__edge_attr_dim is None:
  284. self.__edge_attr_dim = self.__get_edge_attr_dim()
  285. infos['edge_attr_dim'] = self.__edge_attr_dim
  286. return infos
  287. def print_graph_infos(self, infos):
  288. from collections import OrderedDict
  289. keys = list(infos.keys())
  290. print(OrderedDict(sorted(infos.items(), key=lambda i: keys.index(i[0]))))
  291. def cut_graphs(self, range_):
  292. self.__graphs = [self.__graphs[i] for i in range_]
  293. self.set_labels_attrs()
  294. def __get_dataset_size(self):
  295. return len(self.__graphs)
  296. def __get_all_node_nums(self):
  297. return [nx.number_of_nodes(G) for G in self.__graphs]
  298. def __get_total_node_nums(self, all_node_nums):
  299. return np.sum(all_node_nums)
  300. def __get_ave_node_num(self, all_node_nums):
  301. return np.mean(all_node_nums)
  302. def __get_min_node_num(self, all_node_nums):
  303. return np.amin(all_node_nums)
  304. def __get_max_node_num(self, all_node_nums):
  305. return np.amax(all_node_nums)
  306. def __get_all_edge_nums(self):
  307. return [nx.number_of_edges(G) for G in self.__graphs]
  308. def __get_total_edge_nums(self, all_edge_nums):
  309. return np.sum(all_edge_nums)
  310. def __get_ave_edge_num(self, all_edge_nums):
  311. return np.mean(all_edge_nums)
  312. def __get_min_edge_num(self, all_edge_nums):
  313. return np.amin(all_edge_nums)
  314. def __get_max_edge_num(self, all_edge_nums):
  315. return np.amax(all_edge_nums)
  316. def __get_node_label_dim(self):
  317. return len(self.__node_labels)
  318. def __get_node_label_num(self, node_label):
  319. nl = set()
  320. for G in self.__graphs:
  321. nl = nl | set(nx.get_node_attributes(G, node_label).values())
  322. return len(nl)
  323. def __get_edge_label_dim(self):
  324. return len(self.__edge_labels)
  325. def __get_edge_label_num(self, edge_label):
  326. el = set()
  327. for G in self.__graphs:
  328. el = el | set(nx.get_edge_attributes(G, edge_label).values())
  329. return len(el)
  330. def __is_directed(self):
  331. return nx.is_directed(self.__graphs[0])
  332. def __get_all_node_degrees(self):
  333. return [np.mean(list(dict(G.degree()).values())) for G in self.__graphs]
  334. def __get_ave_node_degree(self, all_node_degrees):
  335. return np.mean(all_node_degrees)
  336. def __get_max_node_degree(self, all_node_degrees):
  337. return np.amax(all_node_degrees)
  338. def __get_min_node_degree(self, all_node_degrees):
  339. return np.amin(all_node_degrees)
  340. def __get_all_fill_factors(self):
  341. """
  342. Get fill factor, the number of non-zero entries in the adjacency matrix.
  343. Returns
  344. -------
  345. list[float]
  346. List of fill factors for all graphs.
  347. """
  348. return [nx.number_of_edges(G) / (nx.number_of_nodes(G) ** 2) for G in self.__graphs]
  349. def __get_ave_fill_factor(self, all_fill_factors):
  350. return np.mean(all_fill_factors)
  351. def __get_max_fill_factor(self, all_fill_factors):
  352. return np.amax(all_fill_factors)
  353. def __get_min_fill_factor(self, all_fill_factors):
  354. return np.amin(all_fill_factors)
  355. def __get_substructures(self):
  356. subs = set()
  357. for G in self.__graphs:
  358. degrees = list(dict(G.degree()).values())
  359. if any(i == 2 for i in degrees):
  360. subs.add('linear')
  361. if np.amax(degrees) >= 3:
  362. subs.add('non linear')
  363. if 'linear' in subs and 'non linear' in subs:
  364. break
  365. if self.__directed:
  366. for G in self.__graphs:
  367. if len(list(nx.find_cycle(G))) > 0:
  368. subs.add('cyclic')
  369. break
  370. # else:
  371. # # @todo: this method does not work for big graph with large amount of edges like D&D, try a better way.
  372. # upper = np.amin([nx.number_of_edges(G) for G in Gn]) * 2 + 10
  373. # for G in Gn:
  374. # if (nx.number_of_edges(G) < upper):
  375. # cyc = list(nx.simple_cycles(G.to_directed()))
  376. # if any(len(i) > 2 for i in cyc):
  377. # subs.add('cyclic')
  378. # break
  379. # if 'cyclic' not in subs:
  380. # for G in Gn:
  381. # cyc = list(nx.simple_cycles(G.to_directed()))
  382. # if any(len(i) > 2 for i in cyc):
  383. # subs.add('cyclic')
  384. # break
  385. return subs
  386. def __get_class_num(self):
  387. return len(set(self.__target))
  388. def __get_node_attr_dim(self):
  389. return len(self.__node_attrs)
  390. def __get_edge_attr_dim(self):
  391. return len(self.__edge_attrs)
  392. @property
  393. def graphs(self):
  394. return self.__graphs
  395. @property
  396. def node_labels(self):
  397. return self.__node_labels
  398. @property
  399. def edge_labels(self):
  400. return self.__edge_labels
  401. @property
  402. def node_attrs(self):
  403. return self.__node_attrs
  404. @property
  405. def edge_attrs(self):
  406. return self.__edge_attrs

A Python package for graph kernels, graph edit distances and graph pre-image problem.