diff --git a/gklearn/utils/dataset.py b/gklearn/utils/dataset.py index 8e225d6..3d6fa2c 100644 --- a/gklearn/utils/dataset.py +++ b/gklearn/utils/dataset.py @@ -12,13 +12,13 @@ import os class Dataset(object): - - import warnings - warnings.simplefilter('always', DeprecationWarning) - warnings.warn('This class has been moved to "gklearn.dataset" module. The class "gklearn.utils.dataset.Dataset" has not been maintained since Nov 12th, 2020 (version 0.2.1) and will be removed since version 0.4.0.', DeprecationWarning) - - + + def __init__(self, filename=None, filename_targets=None, **kwargs): + import warnings + warnings.simplefilter('always', DeprecationWarning) + warnings.warn('This class has been moved to "gklearn.dataset" module. The class "gklearn.utils.dataset.Dataset" has not been maintained since Nov 12th, 2020 (version 0.2.1) and will be removed since version 0.4.0.', DeprecationWarning) + if filename is None: self._graphs = None self._targets = None @@ -28,7 +28,7 @@ class Dataset(object): self._edge_attrs = None else: self.load_dataset(filename, filename_targets=filename_targets, **kwargs) - + self._substructures = None self._node_label_dim = None self._edge_label_dim = None @@ -53,8 +53,8 @@ class Dataset(object): self._node_attr_dim = None self._edge_attr_dim = None self._class_number = None - - + + def load_dataset(self, filename, filename_targets=None, **kwargs): self._graphs, self._targets, label_names = load_dataset(filename, filename_targets=filename_targets, **kwargs) self._node_labels = label_names['node_labels'] @@ -62,15 +62,15 @@ class Dataset(object): self._edge_labels = label_names['edge_labels'] self._edge_attrs = label_names['edge_attrs'] self.clean_labels() - - + + def load_graphs(self, graphs, targets=None): # this has to be followed by set_labels(). self._graphs = graphs self._targets = targets # self.set_labels_attrs() # @todo - - + + def load_predefined_dataset(self, ds_name): current_path = os.path.dirname(os.path.realpath(__file__)) + '/' if ds_name == 'Acyclic': @@ -130,7 +130,7 @@ class Dataset(object): self._graphs, self._targets, label_names = load_dataset(ds_file) elif ds_name == 'NCI109': ds_file = current_path + '../../datasets/NCI109/NCI109_A.txt' - self._graphs, self._targets, label_names = load_dataset(ds_file) + self._graphs, self._targets, label_names = load_dataset(ds_file) elif ds_name == 'PAH': ds_file = current_path + '../../datasets/PAH/dataset.ds' self._graphs, self._targets, label_names = load_dataset(ds_file) @@ -143,13 +143,13 @@ class Dataset(object): pass else: raise Exception('The dataset name "', ds_name, '" is not pre-defined.') - + self._node_labels = label_names['node_labels'] self._node_attrs = label_names['node_attrs'] self._edge_labels = label_names['edge_labels'] self._edge_attrs = label_names['edge_attrs'] self.clean_labels() - + def set_labels(self, node_labels=[], node_attrs=[], edge_labels=[], edge_attrs=[]): self._node_labels = node_labels @@ -157,7 +157,7 @@ class Dataset(object): self._edge_labels = edge_labels self._edge_attrs = edge_attrs - + def set_labels_attrs(self, node_labels=None, node_attrs=None, edge_labels=None, edge_attrs=None): # @todo: remove labels which have only one possible values. if node_labels is None: @@ -183,86 +183,86 @@ class Dataset(object): # if 'attributes' in e[2]: # return len(e[2]['attributes']) # return 0 - - + + def get_dataset_infos(self, keys=None, params=None): """Computes and returns the structure and property information of the graph dataset. - + Parameters ---------- keys : list, optional A list of strings which indicate which informations will be returned. The possible choices includes: - - 'substructures': sub-structures graphs contains, including 'linear', 'non + + 'substructures': sub-structures graphs contains, including 'linear', 'non linear' and 'cyclic'. - + 'node_label_dim': whether vertices have symbolic labels. - + 'edge_label_dim': whether egdes have symbolic labels. - + 'directed': whether graphs in dataset are directed. - + 'dataset_size': number of graphs in dataset. - + 'total_node_num': total number of vertices of all graphs in dataset. - + 'ave_node_num': average number of vertices of graphs in dataset. - + 'min_node_num': minimum number of vertices of graphs in dataset. - + 'max_node_num': maximum number of vertices of graphs in dataset. - + 'total_edge_num': total number of edges of all graphs in dataset. - + 'ave_edge_num': average number of edges of graphs in dataset. - + 'min_edge_num': minimum number of edges of graphs in dataset. - + 'max_edge_num': maximum number of edges of graphs in dataset. - + 'ave_node_degree': average vertex degree of graphs in dataset. - + 'min_node_degree': minimum vertex degree of graphs in dataset. - + 'max_node_degree': maximum vertex degree of graphs in dataset. - - 'ave_fill_factor': average fill factor (number_of_edges / + + 'ave_fill_factor': average fill factor (number_of_edges / (number_of_nodes ** 2)) of graphs in dataset. - + 'min_fill_factor': minimum fill factor of graphs in dataset. - + 'max_fill_factor': maximum fill factor of graphs in dataset. - + 'node_label_nums': list of numbers of symbolic vertex labels of graphs in dataset. - + 'edge_label_nums': list number of symbolic edge labels of graphs in dataset. - - 'node_attr_dim': number of dimensions of non-symbolic vertex labels. + + 'node_attr_dim': number of dimensions of non-symbolic vertex labels. Extracted from the 'attributes' attribute of graph nodes. - - 'edge_attr_dim': number of dimensions of non-symbolic edge labels. + + 'edge_attr_dim': number of dimensions of non-symbolic edge labels. Extracted from the 'attributes' attribute of graph edges. - + 'class_number': number of classes. Only available for classification problems. - + 'all_degree_entropy': the entropy of degree distribution of each graph. - + 'ave_degree_entropy': the average entropy of degree distribution of all graphs. - + All informations above will be returned if `keys` is not given. - + params: dict of dict, optional - A dictinary which contains extra parameters for each possible + A dictinary which contains extra parameters for each possible element in ``keys``. - + Return ------ dict Information of the graph dataset keyed by `keys`. """ infos = {} - + if keys == None: keys = [ 'substructures', @@ -292,13 +292,13 @@ class Dataset(object): 'all_degree_entropy', 'ave_degree_entropy' ] - + # dataset size if 'dataset_size' in keys: if self._dataset_size is None: self._dataset_size = self._get_dataset_size() infos['dataset_size'] = self._dataset_size - + # graph node number if any(i in keys for i in ['total_node_num', 'ave_node_num', 'min_node_num', 'max_node_num']): all_node_nums = self._get_all_node_nums() @@ -307,22 +307,22 @@ class Dataset(object): if self._total_node_num is None: self._total_node_num = self._get_total_node_num(all_node_nums) infos['total_node_num'] = self._total_node_num - + if 'ave_node_num' in keys: if self._ave_node_num is None: self._ave_node_num = self._get_ave_node_num(all_node_nums) infos['ave_node_num'] = self._ave_node_num - + if 'min_node_num' in keys: if self._min_node_num is None: self._min_node_num = self._get_min_node_num(all_node_nums) infos['min_node_num'] = self._min_node_num - + if 'max_node_num' in keys: if self._max_node_num is None: self._max_node_num = self._get_max_node_num(all_node_nums) infos['max_node_num'] = self._max_node_num - + # graph edge number if any(i in keys for i in ['total_edge_num', 'ave_edge_num', 'min_edge_num', 'max_edge_num']): all_edge_nums = self._get_all_edge_nums() @@ -331,12 +331,12 @@ class Dataset(object): if self._total_edge_num is None: self._total_edge_num = self._get_total_edge_num(all_edge_nums) infos['total_edge_num'] = self._total_edge_num - + if 'ave_edge_num' in keys: if self._ave_edge_num is None: self._ave_edge_num = self._get_ave_edge_num(all_edge_nums) infos['ave_edge_num'] = self._ave_edge_num - + if 'max_edge_num' in keys: if self._max_edge_num is None: self._max_edge_num = self._get_max_edge_num(all_edge_nums) @@ -346,120 +346,120 @@ class Dataset(object): if self._min_edge_num is None: self._min_edge_num = self._get_min_edge_num(all_edge_nums) infos['min_edge_num'] = self._min_edge_num - + # label number if 'node_label_dim' in keys: if self._node_label_dim is None: self._node_label_dim = self._get_node_label_dim() - infos['node_label_dim'] = self._node_label_dim - + infos['node_label_dim'] = self._node_label_dim + if 'node_label_nums' in keys: if self._node_label_nums is None: self._node_label_nums = {} for node_label in self._node_labels: self._node_label_nums[node_label] = self._get_node_label_num(node_label) infos['node_label_nums'] = self._node_label_nums - + if 'edge_label_dim' in keys: if self._edge_label_dim is None: self._edge_label_dim = self._get_edge_label_dim() - infos['edge_label_dim'] = self._edge_label_dim - + infos['edge_label_dim'] = self._edge_label_dim + if 'edge_label_nums' in keys: if self._edge_label_nums is None: self._edge_label_nums = {} for edge_label in self._edge_labels: self._edge_label_nums[edge_label] = self._get_edge_label_num(edge_label) infos['edge_label_nums'] = self._edge_label_nums - + if 'directed' in keys or 'substructures' in keys: if self._directed is None: self._directed = self._is_directed() infos['directed'] = self._directed - + # node degree if any(i in keys for i in ['ave_node_degree', 'max_node_degree', 'min_node_degree']): all_node_degrees = self._get_all_node_degrees() - + if 'ave_node_degree' in keys: if self._ave_node_degree is None: self._ave_node_degree = self._get_ave_node_degree(all_node_degrees) infos['ave_node_degree'] = self._ave_node_degree - + if 'max_node_degree' in keys: if self._max_node_degree is None: self._max_node_degree = self._get_max_node_degree(all_node_degrees) infos['max_node_degree'] = self._max_node_degree - + if 'min_node_degree' in keys: if self._min_node_degree is None: self._min_node_degree = self._get_min_node_degree(all_node_degrees) infos['min_node_degree'] = self._min_node_degree - + # fill factor if any(i in keys for i in ['ave_fill_factor', 'max_fill_factor', 'min_fill_factor']): all_fill_factors = self._get_all_fill_factors() - + if 'ave_fill_factor' in keys: if self._ave_fill_factor is None: self._ave_fill_factor = self._get_ave_fill_factor(all_fill_factors) infos['ave_fill_factor'] = self._ave_fill_factor - + if 'max_fill_factor' in keys: if self._max_fill_factor is None: self._max_fill_factor = self._get_max_fill_factor(all_fill_factors) infos['max_fill_factor'] = self._max_fill_factor - + if 'min_fill_factor' in keys: if self._min_fill_factor is None: self._min_fill_factor = self._get_min_fill_factor(all_fill_factors) infos['min_fill_factor'] = self._min_fill_factor - + if 'substructures' in keys: if self._substructures is None: self._substructures = self._get_substructures() infos['substructures'] = self._substructures - + if 'class_number' in keys: if self._class_number is None: self._class_number = self._get_class_number() infos['class_number'] = self._class_number - + if 'node_attr_dim' in keys: if self._node_attr_dim is None: self._node_attr_dim = self._get_node_attr_dim() infos['node_attr_dim'] = self._node_attr_dim - + if 'edge_attr_dim' in keys: if self._edge_attr_dim is None: self._edge_attr_dim = self._get_edge_attr_dim() infos['edge_attr_dim'] = self._edge_attr_dim - + # entropy of degree distribution. - + if 'all_degree_entropy' in keys: if params is not None and ('all_degree_entropy' in params) and ('base' in params['all_degree_entropy']): base = params['all_degree_entropy']['base'] else: base = None infos['all_degree_entropy'] = self._compute_all_degree_entropy(base=base) - + if 'ave_degree_entropy' in keys: if params is not None and ('ave_degree_entropy' in params) and ('base' in params['ave_degree_entropy']): base = params['ave_degree_entropy']['base'] else: base = None infos['ave_degree_entropy'] = np.mean(self._compute_all_degree_entropy(base=base)) - + return infos - - + + def print_graph_infos(self, infos): from collections import OrderedDict keys = list(infos.keys()) print(OrderedDict(sorted(infos.items(), key=lambda i: keys.index(i[0])))) - - + + def remove_labels(self, node_labels=[], edge_labels=[], node_attrs=[], edge_attrs=[]): node_labels = [item for item in node_labels if item in self._node_labels] edge_labels = [item for item in edge_labels if item in self._edge_labels] @@ -485,8 +485,8 @@ class Dataset(object): self._node_attrs = [na for na in self._node_attrs if na not in node_attrs] if len(edge_attrs) > 0: self._edge_attrs = [ea for ea in self._edge_attrs if ea not in edge_attrs] - - + + def clean_labels(self): labels = [] for name in self._node_labels: @@ -543,8 +543,8 @@ class Dataset(object): for ed in G.edges(): del G.edges[ed][name] self._edge_attrs = labels - - + + def cut_graphs(self, range_): self._graphs = [self._graphs[i] for i in range_] if self._targets is not None: @@ -561,8 +561,8 @@ class Dataset(object): self._graphs = [p[1] for p in trimed_pairs] self._targets = [self._targets[i] for i in idx] self.clean_labels() - - + + def copy(self): dataset = Dataset() graphs = [g.copy() for g in self._graphs] if self._graphs is not None else None @@ -575,8 +575,8 @@ class Dataset(object): dataset.set_labels(node_labels=node_labels, node_attrs=node_attrs, edge_labels=edge_labels, edge_attrs=edge_attrs) # @todo: clean_labels and add other class members? return dataset - - + + def get_all_node_labels(self): node_labels = [] for g in self._graphs: @@ -585,8 +585,8 @@ class Dataset(object): if nl not in node_labels: node_labels.append(nl) return node_labels - - + + def get_all_edge_labels(self): edge_labels = [] for g in self._graphs: @@ -595,94 +595,94 @@ class Dataset(object): if el not in edge_labels: edge_labels.append(el) return edge_labels - - + + def _get_dataset_size(self): return len(self._graphs) - - + + def _get_all_node_nums(self): return [nx.number_of_nodes(G) for G in self._graphs] - - + + def _get_total_node_nums(self, all_node_nums): return np.sum(all_node_nums) - - + + def _get_ave_node_num(self, all_node_nums): return np.mean(all_node_nums) - - + + def _get_min_node_num(self, all_node_nums): return np.amin(all_node_nums) - - + + def _get_max_node_num(self, all_node_nums): return np.amax(all_node_nums) - - + + def _get_all_edge_nums(self): return [nx.number_of_edges(G) for G in self._graphs] - - + + def _get_total_edge_nums(self, all_edge_nums): return np.sum(all_edge_nums) - - + + def _get_ave_edge_num(self, all_edge_nums): return np.mean(all_edge_nums) - - + + def _get_min_edge_num(self, all_edge_nums): return np.amin(all_edge_nums) - - + + def _get_max_edge_num(self, all_edge_nums): return np.amax(all_edge_nums) - - + + def _get_node_label_dim(self): return len(self._node_labels) - - + + def _get_node_label_num(self, node_label): nl = set() for G in self._graphs: nl = nl | set(nx.get_node_attributes(G, node_label).values()) return len(nl) - - + + def _get_edge_label_dim(self): return len(self._edge_labels) - - + + def _get_edge_label_num(self, edge_label): el = set() for G in self._graphs: el = el | set(nx.get_edge_attributes(G, edge_label).values()) return len(el) - - + + def _is_directed(self): return nx.is_directed(self._graphs[0]) - - + + def _get_all_node_degrees(self): return [np.mean(list(dict(G.degree()).values())) for G in self._graphs] - - + + def _get_ave_node_degree(self, all_node_degrees): return np.mean(all_node_degrees) - - + + def _get_max_node_degree(self, all_node_degrees): return np.amax(all_node_degrees) - - + + def _get_min_node_degree(self, all_node_degrees): return np.amin(all_node_degrees) - - + + def _get_all_fill_factors(self): """Get fill factor, the number of non-zero entries in the adjacency matrix. @@ -692,20 +692,20 @@ class Dataset(object): List of fill factors for all graphs. """ return [nx.number_of_edges(G) / (nx.number_of_nodes(G) ** 2) for G in self._graphs] - + def _get_ave_fill_factor(self, all_fill_factors): return np.mean(all_fill_factors) - - + + def _get_max_fill_factor(self, all_fill_factors): return np.amax(all_fill_factors) - - + + def _get_min_fill_factor(self, all_fill_factors): return np.amin(all_fill_factors) - - + + def _get_substructures(self): subs = set() for G in self._graphs: @@ -737,22 +737,22 @@ class Dataset(object): # if any(len(i) > 2 for i in cyc): # subs.add('cyclic') # break - + return subs - - + + def _get_class_num(self): return len(set(self._targets)) - - + + def _get_node_attr_dim(self): return len(self._node_attrs) - - + + def _get_edge_attr_dim(self): return len(self._edge_attrs) - + def _compute_all_degree_entropy(self, base=None): """Compute the entropy of degree distribution of each graph. @@ -767,15 +767,15 @@ class Dataset(object): The calculated entropy. """ from gklearn.utils.stats import entropy - + degree_entropy = [] for g in self._graphs: degrees = list(dict(g.degree()).values()) en = entropy(degrees, base=base) degree_entropy.append(en) return degree_entropy - - + + @property def graphs(self): return self._graphs @@ -784,8 +784,8 @@ class Dataset(object): @property def targets(self): return self._targets - - + + @property def node_labels(self): return self._node_labels @@ -794,25 +794,25 @@ class Dataset(object): @property def edge_labels(self): return self._edge_labels - - + + @property def node_attrs(self): return self._node_attrs - - + + @property def edge_attrs(self): return self._edge_attrs - - + + def split_dataset_by_target(dataset): import warnings warnings.simplefilter('always', DeprecationWarning) warnings.warn('This function has been moved to "gklearn.dataset" module. The function "gklearn.utils.dataset.split_dataset_by_target" has not been maintained since Nov 12th, 2020 (version 0.2.1) and will be removed since version 0.4.0.', DeprecationWarning) - + from gklearn.preimage.utils import get_same_item_indices - + graphs = dataset.graphs targets = dataset.targets datasets = [] diff --git a/gklearn/utils/graphdataset.py b/gklearn/utils/graphdataset.py index 4c64fd0..4dd7881 100644 --- a/gklearn/utils/graphdataset.py +++ b/gklearn/utils/graphdataset.py @@ -5,345 +5,345 @@ This file is for old version of graphkit-learn. def get_dataset_attributes(Gn, - target=None, - attr_names=[], - node_label=None, - edge_label=None): - """Returns the structure and property information of the graph dataset Gn. - - Parameters - ---------- - Gn : List of NetworkX graph - List of graphs whose information will be returned. - - target : list - The list of classification targets corresponding to Gn. Only works for - classification problems. - - attr_names : list - List of strings which indicate which informations will be returned. The - possible choices includes: - - 'substructures': sub-structures Gn contains, including 'linear', 'non + target=None, + attr_names=[], + node_label=None, + edge_label=None): + """Returns the structure and property information of the graph dataset Gn. + + Parameters + ---------- + Gn : List of NetworkX graph + List of graphs whose information will be returned. + + target : list + The list of classification targets corresponding to Gn. Only works for + classification problems. + + attr_names : list + List of strings which indicate which informations will be returned. The + possible choices includes: + + 'substructures': sub-structures Gn contains, including 'linear', 'non linear' and 'cyclic'. - 'node_labeled': whether vertices have symbolic labels. + 'node_labeled': whether vertices have symbolic labels. - 'edge_labeled': whether egdes have symbolic labels. + 'edge_labeled': whether egdes have symbolic labels. - 'is_directed': whether graphs in Gn are directed. + 'is_directed': whether graphs in Gn are directed. - 'dataset_size': number of graphs in Gn. + 'dataset_size': number of graphs in Gn. - 'ave_node_num': average number of vertices of graphs in Gn. + 'ave_node_num': average number of vertices of graphs in Gn. - 'min_node_num': minimum number of vertices of graphs in Gn. + 'min_node_num': minimum number of vertices of graphs in Gn. - 'max_node_num': maximum number of vertices of graphs in Gn. + 'max_node_num': maximum number of vertices of graphs in Gn. - 'ave_edge_num': average number of edges of graphs in Gn. + 'ave_edge_num': average number of edges of graphs in Gn. - 'min_edge_num': minimum number of edges of graphs in Gn. + 'min_edge_num': minimum number of edges of graphs in Gn. - 'max_edge_num': maximum number of edges of graphs in Gn. + 'max_edge_num': maximum number of edges of graphs in Gn. - 'ave_node_degree': average vertex degree of graphs in Gn. + 'ave_node_degree': average vertex degree of graphs in Gn. - 'min_node_degree': minimum vertex degree of graphs in Gn. + 'min_node_degree': minimum vertex degree of graphs in Gn. - 'max_node_degree': maximum vertex degree of graphs in Gn. + 'max_node_degree': maximum vertex degree of graphs in Gn. - 'ave_fill_factor': average fill factor (number_of_edges / + 'ave_fill_factor': average fill factor (number_of_edges / (number_of_nodes ** 2)) of graphs in Gn. - 'min_fill_factor': minimum fill factor of graphs in Gn. + 'min_fill_factor': minimum fill factor of graphs in Gn. - 'max_fill_factor': maximum fill factor of graphs in Gn. + 'max_fill_factor': maximum fill factor of graphs in Gn. - 'node_label_num': number of symbolic vertex labels. + 'node_label_num': number of symbolic vertex labels. - 'edge_label_num': number of symbolic edge labels. + 'edge_label_num': number of symbolic edge labels. - 'node_attr_dim': number of dimensions of non-symbolic vertex labels. + 'node_attr_dim': number of dimensions of non-symbolic vertex labels. Extracted from the 'attributes' attribute of graph nodes. - 'edge_attr_dim': number of dimensions of non-symbolic edge labels. + 'edge_attr_dim': number of dimensions of non-symbolic edge labels. Extracted from the 'attributes' attribute of graph edges. - 'class_number': number of classes. Only available for classification problems. + 'class_number': number of classes. Only available for classification problems. - node_label : string - Node attribute used as label. The default node label is atom. Mandatory - when 'node_labeled' or 'node_label_num' is required. + node_label : string + Node attribute used as label. The default node label is atom. Mandatory + when 'node_labeled' or 'node_label_num' is required. - edge_label : string - Edge attribute used as label. The default edge label is bond_type. - Mandatory when 'edge_labeled' or 'edge_label_num' is required. - - Return - ------ - attrs : dict - Value for each property. - """ - import networkx as nx - import numpy as np - - attrs = {} - - def get_dataset_size(Gn): - return len(Gn) - - def get_all_node_num(Gn): - return [nx.number_of_nodes(G) for G in Gn] - - def get_ave_node_num(all_node_num): - return np.mean(all_node_num) - - def get_min_node_num(all_node_num): - return np.amin(all_node_num) - - def get_max_node_num(all_node_num): - return np.amax(all_node_num) - - def get_all_edge_num(Gn): - return [nx.number_of_edges(G) for G in Gn] - - def get_ave_edge_num(all_edge_num): - return np.mean(all_edge_num) - - def get_min_edge_num(all_edge_num): - return np.amin(all_edge_num) - - def get_max_edge_num(all_edge_num): - return np.amax(all_edge_num) - - def is_node_labeled(Gn): - return False if node_label is None else True - - def get_node_label_num(Gn): - nl = set() - for G in Gn: - nl = nl | set(nx.get_node_attributes(G, node_label).values()) - return len(nl) - - def is_edge_labeled(Gn): - return False if edge_label is None else True - - def get_edge_label_num(Gn): - el = set() - for G in Gn: - el = el | set(nx.get_edge_attributes(G, edge_label).values()) - return len(el) - - def is_directed(Gn): - return nx.is_directed(Gn[0]) - - def get_ave_node_degree(Gn): - return np.mean([np.mean(list(dict(G.degree()).values())) for G in Gn]) - - def get_max_node_degree(Gn): - return np.amax([np.mean(list(dict(G.degree()).values())) for G in Gn]) - - def get_min_node_degree(Gn): - return np.amin([np.mean(list(dict(G.degree()).values())) for G in Gn]) - - # get fill factor, the number of non-zero entries in the adjacency matrix. - def get_ave_fill_factor(Gn): - return np.mean([nx.number_of_edges(G) / (nx.number_of_nodes(G) - * nx.number_of_nodes(G)) for G in Gn]) - - def get_max_fill_factor(Gn): - return np.amax([nx.number_of_edges(G) / (nx.number_of_nodes(G) - * nx.number_of_nodes(G)) for G in Gn]) - - def get_min_fill_factor(Gn): - return np.amin([nx.number_of_edges(G) / (nx.number_of_nodes(G) - * nx.number_of_nodes(G)) for G in Gn]) - - def get_substructures(Gn): - subs = set() - for G in Gn: - degrees = list(dict(G.degree()).values()) - if any(i == 2 for i in degrees): - subs.add('linear') - if np.amax(degrees) >= 3: - subs.add('non linear') - if 'linear' in subs and 'non linear' in subs: - break - - if is_directed(Gn): - for G in Gn: - if len(list(nx.find_cycle(G))) > 0: - subs.add('cyclic') - break - # else: - # # @todo: this method does not work for big graph with large amount of edges like D&D, try a better way. - # upper = np.amin([nx.number_of_edges(G) for G in Gn]) * 2 + 10 - # for G in Gn: - # if (nx.number_of_edges(G) < upper): - # cyc = list(nx.simple_cycles(G.to_directed())) - # if any(len(i) > 2 for i in cyc): - # subs.add('cyclic') - # break - # if 'cyclic' not in subs: - # for G in Gn: - # cyc = list(nx.simple_cycles(G.to_directed())) - # if any(len(i) > 2 for i in cyc): - # subs.add('cyclic') - # break - - return subs - - def get_class_num(target): - return len(set(target)) - - def get_node_attr_dim(Gn): - for G in Gn: - for n in G.nodes(data=True): - if 'attributes' in n[1]: - return len(n[1]['attributes']) - return 0 - - def get_edge_attr_dim(Gn): - for G in Gn: - if nx.number_of_edges(G) > 0: - for e in G.edges(data=True): - if 'attributes' in e[2]: - return len(e[2]['attributes']) - return 0 - - if attr_names == []: - attr_names = [ - 'substructures', - 'node_labeled', - 'edge_labeled', - 'is_directed', - 'dataset_size', - 'ave_node_num', - 'min_node_num', - 'max_node_num', - 'ave_edge_num', - 'min_edge_num', - 'max_edge_num', - 'ave_node_degree', - 'min_node_degree', - 'max_node_degree', - 'ave_fill_factor', - 'min_fill_factor', - 'max_fill_factor', - 'node_label_num', - 'edge_label_num', - 'node_attr_dim', - 'edge_attr_dim', - 'class_number', - ] - - # dataset size - if 'dataset_size' in attr_names: - - attrs.update({'dataset_size': get_dataset_size(Gn)}) - - # graph node number - if any(i in attr_names - for i in ['ave_node_num', 'min_node_num', 'max_node_num']): - - all_node_num = get_all_node_num(Gn) - - if 'ave_node_num' in attr_names: - - attrs.update({'ave_node_num': get_ave_node_num(all_node_num)}) - - if 'min_node_num' in attr_names: - - attrs.update({'min_node_num': get_min_node_num(all_node_num)}) - - if 'max_node_num' in attr_names: - - attrs.update({'max_node_num': get_max_node_num(all_node_num)}) - - # graph edge number - if any(i in attr_names for i in - ['ave_edge_num', 'min_edge_num', 'max_edge_num']): - - all_edge_num = get_all_edge_num(Gn) + edge_label : string + Edge attribute used as label. The default edge label is bond_type. + Mandatory when 'edge_labeled' or 'edge_label_num' is required. - if 'ave_edge_num' in attr_names: + Return + ------ + attrs : dict + Value for each property. + """ + import networkx as nx + import numpy as np + + attrs = {} + + def get_dataset_size(Gn): + return len(Gn) + + def get_all_node_num(Gn): + return [nx.number_of_nodes(G) for G in Gn] + + def get_ave_node_num(all_node_num): + return np.mean(all_node_num) + + def get_min_node_num(all_node_num): + return np.amin(all_node_num) + + def get_max_node_num(all_node_num): + return np.amax(all_node_num) + + def get_all_edge_num(Gn): + return [nx.number_of_edges(G) for G in Gn] + + def get_ave_edge_num(all_edge_num): + return np.mean(all_edge_num) + + def get_min_edge_num(all_edge_num): + return np.amin(all_edge_num) + + def get_max_edge_num(all_edge_num): + return np.amax(all_edge_num) + + def is_node_labeled(Gn): + return False if node_label is None else True + + def get_node_label_num(Gn): + nl = set() + for G in Gn: + nl = nl | set(nx.get_node_attributes(G, node_label).values()) + return len(nl) + + def is_edge_labeled(Gn): + return False if edge_label is None else True + + def get_edge_label_num(Gn): + el = set() + for G in Gn: + el = el | set(nx.get_edge_attributes(G, edge_label).values()) + return len(el) + + def is_directed(Gn): + return nx.is_directed(Gn[0]) + + def get_ave_node_degree(Gn): + return np.mean([np.mean(list(dict(G.degree()).values())) for G in Gn]) + + def get_max_node_degree(Gn): + return np.amax([np.mean(list(dict(G.degree()).values())) for G in Gn]) + + def get_min_node_degree(Gn): + return np.amin([np.mean(list(dict(G.degree()).values())) for G in Gn]) + + # get fill factor, the number of non-zero entries in the adjacency matrix. + def get_ave_fill_factor(Gn): + return np.mean([nx.number_of_edges(G) / (nx.number_of_nodes(G) + * nx.number_of_nodes(G)) for G in Gn]) + + def get_max_fill_factor(Gn): + return np.amax([nx.number_of_edges(G) / (nx.number_of_nodes(G) + * nx.number_of_nodes(G)) for G in Gn]) + + def get_min_fill_factor(Gn): + return np.amin([nx.number_of_edges(G) / (nx.number_of_nodes(G) + * nx.number_of_nodes(G)) for G in Gn]) + + def get_substructures(Gn): + subs = set() + for G in Gn: + degrees = list(dict(G.degree()).values()) + if any(i == 2 for i in degrees): + subs.add('linear') + if np.amax(degrees) >= 3: + subs.add('non linear') + if 'linear' in subs and 'non linear' in subs: + break + + if is_directed(Gn): + for G in Gn: + if len(list(nx.find_cycle(G))) > 0: + subs.add('cyclic') + break +# else: +# # @todo: this method does not work for big graph with large amount of edges like D&D, try a better way. +# upper = np.amin([nx.number_of_edges(G) for G in Gn]) * 2 + 10 +# for G in Gn: +# if (nx.number_of_edges(G) < upper): +# cyc = list(nx.simple_cycles(G.to_directed())) +# if any(len(i) > 2 for i in cyc): +# subs.add('cyclic') +# break +# if 'cyclic' not in subs: +# for G in Gn: +# cyc = list(nx.simple_cycles(G.to_directed())) +# if any(len(i) > 2 for i in cyc): +# subs.add('cyclic') +# break + + return subs + + def get_class_num(target): + return len(set(target)) + + def get_node_attr_dim(Gn): + for G in Gn: + for n in G.nodes(data=True): + if 'attributes' in n[1]: + return len(n[1]['attributes']) + return 0 + + def get_edge_attr_dim(Gn): + for G in Gn: + if nx.number_of_edges(G) > 0: + for e in G.edges(data=True): + if 'attributes' in e[2]: + return len(e[2]['attributes']) + return 0 + + if attr_names == []: + attr_names = [ + 'substructures', + 'node_labeled', + 'edge_labeled', + 'is_directed', + 'dataset_size', + 'ave_node_num', + 'min_node_num', + 'max_node_num', + 'ave_edge_num', + 'min_edge_num', + 'max_edge_num', + 'ave_node_degree', + 'min_node_degree', + 'max_node_degree', + 'ave_fill_factor', + 'min_fill_factor', + 'max_fill_factor', + 'node_label_num', + 'edge_label_num', + 'node_attr_dim', + 'edge_attr_dim', + 'class_number', + ] + + # dataset size + if 'dataset_size' in attr_names: - attrs.update({'ave_edge_num': get_ave_edge_num(all_edge_num)}) + attrs.update({'dataset_size': get_dataset_size(Gn)}) - if 'max_edge_num' in attr_names: + # graph node number + if any(i in attr_names + for i in ['ave_node_num', 'min_node_num', 'max_node_num']): - attrs.update({'max_edge_num': get_max_edge_num(all_edge_num)}) + all_node_num = get_all_node_num(Gn) + + if 'ave_node_num' in attr_names: - if 'min_edge_num' in attr_names: + attrs.update({'ave_node_num': get_ave_node_num(all_node_num)}) + + if 'min_node_num' in attr_names: - attrs.update({'min_edge_num': get_min_edge_num(all_edge_num)}) + attrs.update({'min_node_num': get_min_node_num(all_node_num)}) + + if 'max_node_num' in attr_names: - # label number - if any(i in attr_names for i in ['node_labeled', 'node_label_num']): - is_nl = is_node_labeled(Gn) - node_label_num = get_node_label_num(Gn) + attrs.update({'max_node_num': get_max_node_num(all_node_num)}) + + # graph edge number + if any(i in attr_names for i in + ['ave_edge_num', 'min_edge_num', 'max_edge_num']): - if 'node_labeled' in attr_names: - # graphs are considered node unlabeled if all nodes have the same label. - attrs.update({'node_labeled': is_nl if node_label_num > 1 else False}) + all_edge_num = get_all_edge_num(Gn) - if 'node_label_num' in attr_names: - attrs.update({'node_label_num': node_label_num}) + if 'ave_edge_num' in attr_names: - if any(i in attr_names for i in ['edge_labeled', 'edge_label_num']): - is_el = is_edge_labeled(Gn) - edge_label_num = get_edge_label_num(Gn) + attrs.update({'ave_edge_num': get_ave_edge_num(all_edge_num)}) - if 'edge_labeled' in attr_names: - # graphs are considered edge unlabeled if all edges have the same label. - attrs.update({'edge_labeled': is_el if edge_label_num > 1 else False}) + if 'max_edge_num' in attr_names: - if 'edge_label_num' in attr_names: - attrs.update({'edge_label_num': edge_label_num}) + attrs.update({'max_edge_num': get_max_edge_num(all_edge_num)}) - if 'is_directed' in attr_names: - attrs.update({'is_directed': is_directed(Gn)}) + if 'min_edge_num' in attr_names: - if 'ave_node_degree' in attr_names: - attrs.update({'ave_node_degree': get_ave_node_degree(Gn)}) + attrs.update({'min_edge_num': get_min_edge_num(all_edge_num)}) - if 'max_node_degree' in attr_names: - attrs.update({'max_node_degree': get_max_node_degree(Gn)}) + # label number + if any(i in attr_names for i in ['node_labeled', 'node_label_num']): + is_nl = is_node_labeled(Gn) + node_label_num = get_node_label_num(Gn) - if 'min_node_degree' in attr_names: - attrs.update({'min_node_degree': get_min_node_degree(Gn)}) - - if 'ave_fill_factor' in attr_names: - attrs.update({'ave_fill_factor': get_ave_fill_factor(Gn)}) + if 'node_labeled' in attr_names: + # graphs are considered node unlabeled if all nodes have the same label. + attrs.update({'node_labeled': is_nl if node_label_num > 1 else False}) - if 'max_fill_factor' in attr_names: - attrs.update({'max_fill_factor': get_max_fill_factor(Gn)}) + if 'node_label_num' in attr_names: + attrs.update({'node_label_num': node_label_num}) - if 'min_fill_factor' in attr_names: - attrs.update({'min_fill_factor': get_min_fill_factor(Gn)}) + if any(i in attr_names for i in ['edge_labeled', 'edge_label_num']): + is_el = is_edge_labeled(Gn) + edge_label_num = get_edge_label_num(Gn) - if 'substructures' in attr_names: - attrs.update({'substructures': get_substructures(Gn)}) + if 'edge_labeled' in attr_names: + # graphs are considered edge unlabeled if all edges have the same label. + attrs.update({'edge_labeled': is_el if edge_label_num > 1 else False}) - if 'class_number' in attr_names: - attrs.update({'class_number': get_class_num(target)}) + if 'edge_label_num' in attr_names: + attrs.update({'edge_label_num': edge_label_num}) - if 'node_attr_dim' in attr_names: - attrs['node_attr_dim'] = get_node_attr_dim(Gn) + if 'is_directed' in attr_names: + attrs.update({'is_directed': is_directed(Gn)}) - if 'edge_attr_dim' in attr_names: - attrs['edge_attr_dim'] = get_edge_attr_dim(Gn) + if 'ave_node_degree' in attr_names: + attrs.update({'ave_node_degree': get_ave_node_degree(Gn)}) - from collections import OrderedDict - return OrderedDict( - sorted(attrs.items(), key=lambda i: attr_names.index(i[0]))) + if 'max_node_degree' in attr_names: + attrs.update({'max_node_degree': get_max_node_degree(Gn)}) + + if 'min_node_degree' in attr_names: + attrs.update({'min_node_degree': get_min_node_degree(Gn)}) + + if 'ave_fill_factor' in attr_names: + attrs.update({'ave_fill_factor': get_ave_fill_factor(Gn)}) + + if 'max_fill_factor' in attr_names: + attrs.update({'max_fill_factor': get_max_fill_factor(Gn)}) + + if 'min_fill_factor' in attr_names: + attrs.update({'min_fill_factor': get_min_fill_factor(Gn)}) + + if 'substructures' in attr_names: + attrs.update({'substructures': get_substructures(Gn)}) + + if 'class_number' in attr_names: + attrs.update({'class_number': get_class_num(target)}) + + if 'node_attr_dim' in attr_names: + attrs['node_attr_dim'] = get_node_attr_dim(Gn) + + if 'edge_attr_dim' in attr_names: + attrs['edge_attr_dim'] = get_edge_attr_dim(Gn) + + from collections import OrderedDict + return OrderedDict( + sorted(attrs.items(), key=lambda i: attr_names.index(i[0]))) def load_predefined_dataset(ds_name): import os from gklearn.utils.graphfiles import loadDataset - + current_path = os.path.dirname(os.path.realpath(__file__)) + '/' if ds_name == 'Acyclic': ds_file = current_path + '../../datasets/Acyclic/dataset_bps.ds' @@ -415,5 +415,5 @@ def load_predefined_dataset(ds_name): pass else: raise Exception('The dataset name "', ds_name, '" is not pre-defined.') - + return graphs, targets \ No newline at end of file diff --git a/gklearn/utils/kernels.py b/gklearn/utils/kernels.py index 49ce04b..5bd7e4d 100644 --- a/gklearn/utils/kernels.py +++ b/gklearn/utils/kernels.py @@ -18,8 +18,8 @@ def deltakernel(x, y): References ---------- - [1] H. Kashima, K. Tsuda, and A. Inokuchi. Marginalized kernels between - labeled graphs. In Proceedings of the 20th International Conference on + [1] H. Kashima, K. Tsuda, and A. Inokuchi. Marginalized kernels between + labeled graphs. In Proceedings of the 20th International Conference on Machine Learning, Washington, DC, United States, 2003. """ return x == y #(1 if condition else 0) @@ -68,7 +68,7 @@ def polynomialkernel(x, y, d=1, c=0): x, y : array d : integer, default 1 - + c : float, default 0 Returns @@ -89,7 +89,7 @@ def linearkernel(x, y): x, y : array d : integer, default 1 - + c : float, default 0 Returns