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.

graphdataset.py 11 kB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. """ Obtain all kinds of attributes of a graph dataset.
  2. """
  3. def get_dataset_attributes(Gn,
  4. target=None,
  5. attr_names=[],
  6. node_label=None,
  7. edge_label=None):
  8. """Returns the structure and property information of the graph dataset Gn.
  9. Parameters
  10. ----------
  11. Gn : List of NetworkX graph
  12. List of graphs whose information will be returned.
  13. target : list
  14. The list of classification targets corresponding to Gn. Only works for
  15. classification problems.
  16. attr_names : list
  17. List of strings which indicate which informations will be returned. The
  18. possible choices includes:
  19. 'substructures': sub-structures Gn contains, including 'linear', 'non
  20. linear' and 'cyclic'.
  21. 'node_labeled': whether vertices have symbolic labels.
  22. 'edge_labeled': whether egdes have symbolic labels.
  23. 'is_directed': whether graphs in Gn are directed.
  24. 'dataset_size': number of graphs in Gn.
  25. 'ave_node_num': average number of vertices of graphs in Gn.
  26. 'min_node_num': minimum number of vertices of graphs in Gn.
  27. 'max_node_num': maximum number of vertices of graphs in Gn.
  28. 'ave_edge_num': average number of edges of graphs in Gn.
  29. 'min_edge_num': minimum number of edges of graphs in Gn.
  30. 'max_edge_num': maximum number of edges of graphs in Gn.
  31. 'ave_node_degree': average vertex degree of graphs in Gn.
  32. 'min_node_degree': minimum vertex degree of graphs in Gn.
  33. 'max_node_degree': maximum vertex degree of graphs in Gn.
  34. 'ave_fill_factor': average fill factor (number_of_edges /
  35. (number_of_nodes ** 2)) of graphs in Gn.
  36. 'min_fill_factor': minimum fill factor of graphs in Gn.
  37. 'max_fill_factor': maximum fill factor of graphs in Gn.
  38. 'node_label_num': number of symbolic vertex labels.
  39. 'edge_label_num': number of symbolic edge labels.
  40. 'node_attr_dim': number of dimensions of non-symbolic vertex labels.
  41. Extracted from the 'attributes' attribute of graph nodes.
  42. 'edge_attr_dim': number of dimensions of non-symbolic edge labels.
  43. Extracted from the 'attributes' attribute of graph edges.
  44. 'class_number': number of classes. Only available for classification problems.
  45. node_label : string
  46. Node attribute used as label. The default node label is atom. Mandatory
  47. when 'node_labeled' or 'node_label_num' is required.
  48. edge_label : string
  49. Edge attribute used as label. The default edge label is bond_type.
  50. Mandatory when 'edge_labeled' or 'edge_label_num' is required.
  51. Return
  52. ------
  53. attrs : dict
  54. Value for each property.
  55. """
  56. import networkx as nx
  57. import numpy as np
  58. attrs = {}
  59. def get_dataset_size(Gn):
  60. return len(Gn)
  61. def get_all_node_num(Gn):
  62. return [nx.number_of_nodes(G) for G in Gn]
  63. def get_ave_node_num(all_node_num):
  64. return np.mean(all_node_num)
  65. def get_min_node_num(all_node_num):
  66. return np.amin(all_node_num)
  67. def get_max_node_num(all_node_num):
  68. return np.amax(all_node_num)
  69. def get_all_edge_num(Gn):
  70. return [nx.number_of_edges(G) for G in Gn]
  71. def get_ave_edge_num(all_edge_num):
  72. return np.mean(all_edge_num)
  73. def get_min_edge_num(all_edge_num):
  74. return np.amin(all_edge_num)
  75. def get_max_edge_num(all_edge_num):
  76. return np.amax(all_edge_num)
  77. def is_node_labeled(Gn):
  78. return False if node_label is None else True
  79. def get_node_label_num(Gn):
  80. nl = set()
  81. for G in Gn:
  82. nl = nl | set(nx.get_node_attributes(G, node_label).values())
  83. return len(nl)
  84. def is_edge_labeled(Gn):
  85. return False if edge_label is None else True
  86. def get_edge_label_num(Gn):
  87. el = set()
  88. for G in Gn:
  89. el = el | set(nx.get_edge_attributes(G, edge_label).values())
  90. return len(el)
  91. def is_directed(Gn):
  92. return nx.is_directed(Gn[0])
  93. def get_ave_node_degree(Gn):
  94. return np.mean([np.mean(list(dict(G.degree()).values())) for G in Gn])
  95. def get_max_node_degree(Gn):
  96. return np.amax([np.mean(list(dict(G.degree()).values())) for G in Gn])
  97. def get_min_node_degree(Gn):
  98. return np.amin([np.mean(list(dict(G.degree()).values())) for G in Gn])
  99. # get fill factor, the number of non-zero entries in the adjacency matrix.
  100. def get_ave_fill_factor(Gn):
  101. return np.mean([nx.number_of_edges(G) / (nx.number_of_nodes(G)
  102. * nx.number_of_nodes(G)) for G in Gn])
  103. def get_max_fill_factor(Gn):
  104. return np.amax([nx.number_of_edges(G) / (nx.number_of_nodes(G)
  105. * nx.number_of_nodes(G)) for G in Gn])
  106. def get_min_fill_factor(Gn):
  107. return np.amin([nx.number_of_edges(G) / (nx.number_of_nodes(G)
  108. * nx.number_of_nodes(G)) for G in Gn])
  109. def get_substructures(Gn):
  110. subs = set()
  111. for G in Gn:
  112. degrees = list(dict(G.degree()).values())
  113. if any(i == 2 for i in degrees):
  114. subs.add('linear')
  115. if np.amax(degrees) >= 3:
  116. subs.add('non linear')
  117. if 'linear' in subs and 'non linear' in subs:
  118. break
  119. if is_directed(Gn):
  120. for G in Gn:
  121. if len(list(nx.find_cycle(G))) > 0:
  122. subs.add('cyclic')
  123. break
  124. # else:
  125. # # @todo: this method does not work for big graph with large amount of edges like D&D, try a better way.
  126. # upper = np.amin([nx.number_of_edges(G) for G in Gn]) * 2 + 10
  127. # for G in Gn:
  128. # if (nx.number_of_edges(G) < upper):
  129. # cyc = list(nx.simple_cycles(G.to_directed()))
  130. # if any(len(i) > 2 for i in cyc):
  131. # subs.add('cyclic')
  132. # break
  133. # if 'cyclic' not in subs:
  134. # for G in Gn:
  135. # cyc = list(nx.simple_cycles(G.to_directed()))
  136. # if any(len(i) > 2 for i in cyc):
  137. # subs.add('cyclic')
  138. # break
  139. return subs
  140. def get_class_num(target):
  141. return len(set(target))
  142. def get_node_attr_dim(Gn):
  143. for G in Gn:
  144. for n in G.nodes(data=True):
  145. if 'attributes' in n[1]:
  146. return len(n[1]['attributes'])
  147. return 0
  148. def get_edge_attr_dim(Gn):
  149. for G in Gn:
  150. if nx.number_of_edges(G) > 0:
  151. for e in G.edges(data=True):
  152. if 'attributes' in e[2]:
  153. return len(e[2]['attributes'])
  154. return 0
  155. if attr_names == []:
  156. attr_names = [
  157. 'substructures',
  158. 'node_labeled',
  159. 'edge_labeled',
  160. 'is_directed',
  161. 'dataset_size',
  162. 'ave_node_num',
  163. 'min_node_num',
  164. 'max_node_num',
  165. 'ave_edge_num',
  166. 'min_edge_num',
  167. 'max_edge_num',
  168. 'ave_node_degree',
  169. 'min_node_degree',
  170. 'max_node_degree',
  171. 'ave_fill_factor',
  172. 'min_fill_factor',
  173. 'max_fill_factor',
  174. 'node_label_num',
  175. 'edge_label_num',
  176. 'node_attr_dim',
  177. 'edge_attr_dim',
  178. 'class_number',
  179. ]
  180. # dataset size
  181. if 'dataset_size' in attr_names:
  182. attrs.update({'dataset_size': get_dataset_size(Gn)})
  183. # graph node number
  184. if any(i in attr_names
  185. for i in ['ave_node_num', 'min_node_num', 'max_node_num']):
  186. all_node_num = get_all_node_num(Gn)
  187. if 'ave_node_num' in attr_names:
  188. attrs.update({'ave_node_num': get_ave_node_num(all_node_num)})
  189. if 'min_node_num' in attr_names:
  190. attrs.update({'min_node_num': get_min_node_num(all_node_num)})
  191. if 'max_node_num' in attr_names:
  192. attrs.update({'max_node_num': get_max_node_num(all_node_num)})
  193. # graph edge number
  194. if any(i in attr_names for i in
  195. ['ave_edge_num', 'min_edge_num', 'max_edge_num']):
  196. all_edge_num = get_all_edge_num(Gn)
  197. if 'ave_edge_num' in attr_names:
  198. attrs.update({'ave_edge_num': get_ave_edge_num(all_edge_num)})
  199. if 'max_edge_num' in attr_names:
  200. attrs.update({'max_edge_num': get_max_edge_num(all_edge_num)})
  201. if 'min_edge_num' in attr_names:
  202. attrs.update({'min_edge_num': get_min_edge_num(all_edge_num)})
  203. # label number
  204. if any(i in attr_names for i in ['node_labeled', 'node_label_num']):
  205. is_nl = is_node_labeled(Gn)
  206. node_label_num = get_node_label_num(Gn)
  207. if 'node_labeled' in attr_names:
  208. # graphs are considered node unlabeled if all nodes have the same label.
  209. attrs.update({'node_labeled': is_nl if node_label_num > 1 else False})
  210. if 'node_label_num' in attr_names:
  211. attrs.update({'node_label_num': node_label_num})
  212. if any(i in attr_names for i in ['edge_labeled', 'edge_label_num']):
  213. is_el = is_edge_labeled(Gn)
  214. edge_label_num = get_edge_label_num(Gn)
  215. if 'edge_labeled' in attr_names:
  216. # graphs are considered edge unlabeled if all edges have the same label.
  217. attrs.update({'edge_labeled': is_el if edge_label_num > 1 else False})
  218. if 'edge_label_num' in attr_names:
  219. attrs.update({'edge_label_num': edge_label_num})
  220. if 'is_directed' in attr_names:
  221. attrs.update({'is_directed': is_directed(Gn)})
  222. if 'ave_node_degree' in attr_names:
  223. attrs.update({'ave_node_degree': get_ave_node_degree(Gn)})
  224. if 'max_node_degree' in attr_names:
  225. attrs.update({'max_node_degree': get_max_node_degree(Gn)})
  226. if 'min_node_degree' in attr_names:
  227. attrs.update({'min_node_degree': get_min_node_degree(Gn)})
  228. if 'ave_fill_factor' in attr_names:
  229. attrs.update({'ave_fill_factor': get_ave_fill_factor(Gn)})
  230. if 'max_fill_factor' in attr_names:
  231. attrs.update({'max_fill_factor': get_max_fill_factor(Gn)})
  232. if 'min_fill_factor' in attr_names:
  233. attrs.update({'min_fill_factor': get_min_fill_factor(Gn)})
  234. if 'substructures' in attr_names:
  235. attrs.update({'substructures': get_substructures(Gn)})
  236. if 'class_number' in attr_names:
  237. attrs.update({'class_number': get_class_num(target)})
  238. if 'node_attr_dim' in attr_names:
  239. attrs['node_attr_dim'] = get_node_attr_dim(Gn)
  240. if 'edge_attr_dim' in attr_names:
  241. attrs['edge_attr_dim'] = get_edge_attr_dim(Gn)
  242. from collections import OrderedDict
  243. return OrderedDict(
  244. sorted(attrs.items(), key=lambda i: attr_names.index(i[0])))

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