Source code for grakel.datasets.base

"""The base file for loading default datasets."""
# Python 2/3 cross-compatibility import
from __future__ import print_function

import os
import shutil
import zipfile
import ssl
try:
    # Python 2
    from urllib2 import HTTPError
    from urllib2 import urlopen
except ImportError:
    # Python 3+
    from urllib.error import HTTPError
    from urllib.request import urlopen

import numpy as np

from shutil import copyfileobj

from collections import Counter

from sklearn.utils import Bunch

from grakel.graph import Graph

global datasets_metadata, symmetric_dataset

dataset_metadata = {
    "AIDS": {"nl": True, "el": True, "na": True, "ea": False,
             "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
             "morris/graphkerneldatasets/AIDS.zip"},
    "BZR": {"nl": True, "el": False, "na": True, "ea": False,
            "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
            "morris/graphkerneldatasets/BZR.zip"},
    "BZR_MD": {"nl": True, "el": True, "na": False, "ea": True,
               "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
               "morris/graphkerneldatasets/BZR_MD.zip"},
    "COIL-DEL": {"nl": False, "el": True, "na": True, "ea": False,
                 "link": "https://ls11-www.cs.uni-dortmund.de/people/morris/" +
                 "graphkerneldatasets/COIL-DEL.zip"},
    "COIL-RAG": {"nl": False, "el": False, "na": True, "ea": True,
                 "link": "https://ls11-www.cs.uni-dortmund.de/people/morris/" +
                 "graphkerneldatasets/COIL-RAG.zip"},
    "COLLAB": {"nl": False, "el": False, "na": False, "ea": False,
               "link": "https://ls11-www.cs.uni-dortmund.de/people/morris/" +
               "graphkerneldatasets/COLLAB.zip"},
    "COX2": {"nl": True, "el": False, "na": True, "ea": False,
             "link": "https://ls11-www.cs.uni-dortmund.de/people/morris/" +
             "graphkerneldatasets/COX2.zip"},
    "COX2_MD": {"nl": True, "el": True, "na": False, "ea": True,
                "link": "https://ls11-www.cs.uni-dortmund.de/people/morris/" +
                "graphkerneldatasets/COX2_MD.zip"},
    "DHFR": {"nl": True, "el": False, "na": True, "ea": False,
             "link": "https://ls11-www.cs.uni-dortmund.de/people/morris/" +
             "graphkerneldatasets/DHFR.zip"},
    "DHFR_MD": {"nl": True, "el": True, "na": False, "ea": True,
                "link": "https://ls11-www.cs.uni-dortmund.de/people/morris/" +
                "graphkerneldatasets/DHFR_MD.zip"},
    "ER_MD": {"nl": True, "el": True, "na": False, "ea": True,
              "link": "https://ls11-www.cs.uni-dortmund.de/people/morris/" +
              "graphkerneldatasets/ER_MD.zip"},
    "DD": {"nl": True, "el": False, "na": False, "ea": False,
           "link": "https://ls11-www.cs.uni-dortmund.de/people/morris/" +
           "graphkerneldatasets/DD.zip"},
    "ENZYMES": {"nl": True, "el": False, "na": True, "ea": False,
                "link": "https://ls11-www.cs.uni-dortmund.de/people/morris/" +
                "graphkerneldatasets/ENZYMES.zip"},
    "Cuneiform": {"nl": True, "el": True, "na": True, "ea": True,
                  "link": "https://ls11-www.cs.uni-dortmund.de/people/morris/"
                          "graphkerneldatasets/Cuneiform.zip"},
    "FINGERPRINT": {"nl": False, "el": False, "na": True, "ea": True,
                    "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                    "morris/graphkerneldatasets/Fingerprint.zip"},
    "FIRSTMM_DB": {"nl": True, "el": False, "na": True, "ea": True,
                   "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                   "morris/graphkerneldatasets/FIRSTMM_DB.zip"},
    "FRANKENSTEIN": {"nl": False, "el": False, "na": True, "ea": False,
                     "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
                     "ris/graphkerneldatasets/FRANKENSTEIN.zip"},
    "IMDB-BINARY": {"nl": False, "el": False, "na": False, "ea": False,
                    "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
                    "ris/graphkerneldatasets/IMDB-BINARY.zip"},
    "IMDB-MULTI": {"nl": False, "el": False, "na": False, "ea": False,
                   "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                   "morris/graphkerneldatasets/IMDB-MULTI.zip"},
    "Letter-high": {"nl": False, "el": False, "na": True, "ea": False,
                    "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
                    "ris/graphkerneldatasets/Letter-high.zip"},
    "Letter-low": {"nl": False, "el": False, "na": True, "ea": False,
                   "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
                   "ris/graphkerneldatasets/Letter-low.zip"},
    "Letter-med": {"nl": False, "el": False, "na": True, "ea": False,
                   "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
                   "ris/graphkerneldatasets/Letter-med.zip"},
    "Mutagenicity": {"nl": True, "el": True, "na": False, "ea": False,
                     "link": "https://ls11-www.cs.uni-dortmund.de/peo" +
                     "ple/morris/graphkerneldatasets/Mutagenicity.zip"},
    "MSRC_9": {"nl": True, "el": False, "na": False, "ea": False,
               "link": "https://ls11-www.cs.uni-dortmund.de/people/morris/" +
               "graphkerneldatasets/MSRC_9.zip"},
    "MSRC_21": {"nl": True, "el": False, "na": False, "ea": False,
                "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
                "ris/graphkerneldatasets/MSRC_21.zip"},
    "MSRC_21C": {"nl": True, "el": False, "na": False, "ea": False,
                 "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
                 "ris/graphkerneldatasets/MSRC_21C.zip"},
    "MUTAG": {"nl": True, "el": True, "na": False, "ea": False,
              "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
              "ris/graphkerneldatasets/MUTAG.zip"},
    "NCI1": {"nl": True, "el": False, "na": False, "ea": False,
             "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
             "ris/graphkerneldatasets/NCI1.zip"},
    "NCI109": {"nl": True, "el": False, "na": False, "ea": False,
               "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
               "ris/graphkerneldatasets/NCI109.zip"},
    "PTC_FM": {"nl": True, "el": True, "na": False, "ea": False,
               "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
               "ris/graphkerneldatasets/PTC_FM.zip"},
    "PTC_FR": {"nl": True, "el": True, "na": False, "ea": False,
               "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
               "ris/graphkerneldatasets/PTC_FR.zip"},
    "PTC_MM": {"nl": True, "el": True, "na": False, "ea": False,
               "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
               "ris/graphkerneldatasets/PTC_MM.zip"},
    "PTC_MR": {"nl": True, "el": True, "na": False, "ea": False,
               "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
               "ris/graphkerneldatasets/PTC_MR.zip"},
    "PROTEINS": {"nl": True, "el": False, "na": True, "ea": False,
                 "link": "https://ls11-www.cs.uni-dortmund.de/people/mor" +
                 "ris/graphkerneldatasets/PROTEINS.zip"},
    "PROTEINS_full": {"nl": True, "el": False, "na": True, "ea": False,
                      "link": "https://ls11-www.cs.uni-dortmund.de/people" +
                      "/morris/graphkerneldatasets/PROTEINS_full.zip"},
    "REDDIT-BINARY": {"nl": False, "el": False, "na": False, "ea": False,
                      "link": "https://ls11-www.cs.uni-dortmund.de/people" +
                      "/morris/graphkerneldatasets/REDDIT-BINARY.zip"},
    "REDDIT-MULTI-5K": {"nl": False, "el": False, "na": False, "ea": False,
                        "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                        "morris/graphkerneldatasets/REDDIT-MULTI-5K.zip"},
    "REDDIT-MULTI-12K": {"nl": False, "el": False, "na": False, "ea": False,
                         "link": "https://ls11-www.cs.uni-dortmund.de/peop" +
                         "le/morris/graphkerneldatasets/REDDIT-MULTI-12K.zip"},
    "SYNTHETIC": {"nl": False, "el": False, "na": True, "ea": False,
                  "link": "https://ls11-www.cs.uni-dortmund.de/people" +
                  "/morris/graphkerneldatasets/SYNTHETIC.zip"},
    "SYNTHETICnew": {"nl": False, "el": False, "na": True, "ea": False,
                     "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                     "morris/graphkerneldatasets/SYNTHETICnew.zip"},
    "Synthie": {"nl": False, "el": False, "na": True, "ea": False,
                "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                "morris/graphkerneldatasets/Synthie.zip"},
    "Tox21_AHR": {"nl": True, "el": True, "na": False, "ea": False,
                  "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                  "morris/graphkerneldatasets/Tox21_AHR.zip"},
    "Tox21_AR": {"nl": True, "el": True, "na": False, "ea": False,
                 "link": "https://ls11-www.cs.uni-dortmund.de/people/morris/" +
                 "graphkerneldatasets/COX2_MD.zip"},
    "Tox21_AR-LBD": {"nl": True, "el": True, "na": False, "ea": False,
                     "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                     "morris/graphkerneldatasets/Tox21_AR-LBD.zip"},
    "Tox21_ARE": {"nl": True, "el": True, "na": False, "ea": False,
                  "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                  "morris/graphkerneldatasets/Tox21_ARE.zip"},
    "Tox21_aromatase": {"nl": True, "el": True, "na": False, "ea": False,
                        "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                        "morris/graphkerneldatasets/Tox21_aromatase.zip"},
    "Tox21_ATAD5": {"nl": True, "el": True, "na": False, "ea": False,
                    "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                    "morris/graphkerneldatasets/Tox21_ATAD5.zip"},
    "Tox21_ER": {"nl": True, "el": True, "na": False, "ea": False,
                 "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                 "morris/graphkerneldatasets/Tox21_ER.zip"},
    "Tox21_ER_LBD": {"nl": True, "el": True, "na": False, "ea": False,
                     "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                     "morris/graphkerneldatasets/Tox21_ER_LBD.zipp"},
    "Tox21_HSE": {"nl": True, "el": True, "na": False, "ea": False,
                  "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                  "morris/graphkerneldatasets/Tox21_HSE.zip"},
    "Tox21_MMP": {"nl": True, "el": True, "na": False, "ea": False,
                  "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                  "morris/graphkerneldatasets/Tox21_MMP.zip"},
    "Tox21_p53": {"nl": True, "el": True, "na": False, "ea": False,
                  "link": "https://ls11-www.cs.uni-dortmund.de/people/" +
                  "morris/graphkerneldatasets/Tox21_p53.zip"},
    "Tox21_PPAR-gamma": {"nl": True, "el": True, "na": False, "ea": False,
                         "link": "https://ls11-www.cs.uni-dortmund.de/peop" +
                         "le/morris/graphkerneldatasets/Tox21_PPAR-gamma.zip"}
}

symmetric_dataset = False


def read_data(
        name,
        with_classes=True,
        prefer_attr_nodes=False,
        prefer_attr_edges=False,
        produce_labels_nodes=False,
        as_graphs=False,
        is_symmetric=symmetric_dataset):
    """Create a dataset iterable for GraphKernel.

    Parameters
    ----------
    name : str
        The dataset name.

    with_classes : bool, default=False
        Return an iterable of class labels based on the enumeration.

    produce_labels_nodes : bool, default=False
        Produce labels for nodes if not found.
        Currently this means labeling its node by its degree inside the Graph.
        This operation is applied only if node labels are non existent.

    prefer_attr_nodes : bool, default=False
        If a dataset has both *node* labels and *node* attributes
        set as labels for the graph object for *nodes* the attributes.

    prefer_attr_edges : bool, default=False
        If a dataset has both *edge* labels and *edge* attributes
        set as labels for the graph object for *edge* the attributes.

    as_graphs : bool, default=False
        Return data as a list of Graph Objects.

    is_symmetric : bool, default=False
        Defines if the graph data describe a symmetric graph.

    Returns
    -------
    Gs : iterable
        An iterable of graphs consisting of a dictionary, node
        labels and edge labels for each graph.

    classes : np.array, case_of_appearance=with_classes==True
        An one dimensional array of graph classes aligned with the lines
        of the `Gs` iterable. Useful for classification.

    """
    indicator_path = "./"+str(name)+"/"+str(name)+"_graph_indicator.txt"
    edges_path = "./" + str(name) + "/" + str(name) + "_A.txt"
    node_labels_path = "./" + str(name) + "/" + str(name) + "_node_labels.txt"
    node_attributes_path = "./"+str(name)+"/"+str(name)+"_node_attributes.txt"
    edge_labels_path = "./" + str(name) + "/" + str(name) + "_edge_labels.txt"
    edge_attributes_path = \
        "./" + str(name) + "/" + str(name) + "_edge_attributes.txt"
    graph_classes_path = \
        "./" + str(name) + "/" + str(name) + "_graph_labels.txt"

    # node graph correspondence
    ngc = dict()
    # edge line correspondence
    elc = dict()
    # dictionary that keeps sets of edges
    Graphs = dict()
    # dictionary of labels for nodes
    node_labels = dict()
    # dictionary of labels for edges
    edge_labels = dict()

    # Associate graphs nodes with indexes
    with open(indicator_path, "r") as f:
        for (i, line) in enumerate(f, 1):
            ngc[i] = int(line[:-1])
            if int(line[:-1]) not in Graphs:
                Graphs[int(line[:-1])] = set()
            if int(line[:-1]) not in node_labels:
                node_labels[int(line[:-1])] = dict()
            if int(line[:-1]) not in edge_labels:
                edge_labels[int(line[:-1])] = dict()

    # Extract graph edges
    with open(edges_path, "r") as f:
        for (i, line) in enumerate(f, 1):
            edge = line[:-1].replace(' ', '').split(",")
            elc[i] = (int(edge[0]), int(edge[1]))
            Graphs[ngc[int(edge[0])]].add((int(edge[0]), int(edge[1])))
            if is_symmetric:
                Graphs[ngc[int(edge[1])]].add((int(edge[1]), int(edge[0])))

    # Extract node attributes
    if (prefer_attr_nodes and
        dataset_metadata[name].get(
                "na",
                os.path.exists(node_attributes_path)
                )):
        with open(node_attributes_path, "r") as f:
            for (i, line) in enumerate(f, 1):
                node_labels[ngc[i]][i] = \
                    [float(num) for num in
                     line[:-1].replace(' ', '').split(",")]
    # Extract node labels
    elif dataset_metadata[name].get(
            "nl",
            os.path.exists(node_labels_path)
            ):
        with open(node_labels_path, "r") as f:
            for (i, line) in enumerate(f, 1):
                node_labels[ngc[i]][i] = int(line[:-1])
    elif produce_labels_nodes:
        for i in range(1, len(Graphs)+1):
            node_labels[i] = dict(Counter(s for (s, d) in Graphs[i] if s != d))

    # Extract edge attributes
    if (prefer_attr_edges and
        dataset_metadata[name].get(
            "ea",
            os.path.exists(edge_attributes_path)
            )):
        with open(edge_attributes_path, "r") as f:
            for (i, line) in enumerate(f, 1):
                attrs = [float(num)
                         for num in line[:-1].replace(' ', '').split(",")]
                edge_labels[ngc[elc[i][0]]][elc[i]] = attrs
                if is_symmetric:
                    edge_labels[ngc[elc[i][1]]][(elc[i][1], elc[i][0])] = attrs

    # Extract edge labels
    elif dataset_metadata[name].get(
            "el",
            os.path.exists(edge_labels_path)
            ):
        with open(edge_labels_path, "r") as f:
            for (i, line) in enumerate(f, 1):
                edge_labels[ngc[elc[i][0]]][elc[i]] = int(line[:-1])
                if is_symmetric:
                    edge_labels[ngc[elc[i][1]]][(elc[i][1], elc[i][0])] = \
                        int(line[:-1])

    Gs = list()
    if as_graphs:
        for i in range(1, len(Graphs)+1):
            Gs.append(Graph(Graphs[i], node_labels[i], edge_labels[i]))
    else:
        for i in range(1, len(Graphs)+1):
            Gs.append([Graphs[i], node_labels[i], edge_labels[i]])

    if with_classes:
        classes = []
        with open(graph_classes_path, "r") as f:
            for line in f:
                classes.append(int(line[:-1]))

        classes = np.array(classes, dtype=np.int)
        return Bunch(data=Gs, target=classes)
    else:
        return Bunch(data=Gs)


def _download_zip(url, output_name):
    """Download a file from a requested url and store locally.

    Parameters
    ----------
    url : str
        The url from where the file will be downloaded.

    output_name : str
        The name of the file in the local directory.

    Returns
    -------
    None.

    """
    ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
    filename = output_name + ".zip"
    try:
        data_url = urlopen(url, context=ctx)
    except HTTPError as e:
        if e.code == 404:
            e.msg = "Dataset '%s' not found on mldata.org." % output_name
        raise

    # Store Zip File
    try:
        with open(filename, 'w+b') as zip_file:
            copyfileobj(data_url, zip_file)
    except Exception:
        os.remove(filename)
        raise
    data_url.close()


[docs]def fetch_dataset( name, verbose=True, data_home=None, download_if_missing=True, with_classes=True, produce_labels_nodes=False, prefer_attr_nodes=False, prefer_attr_edges=False, as_graphs=False): """Load a dataset from a huge collection of benchmark datasets :cite:`KKMMN2016`. For more info visit: :xref:`gd` Parameters ---------- name : str The name of the dataset (as found in :xref:`gd`). verbose : bool, default=True Print messages, throughout execution. data_home : string, default=None Specify another download and cache folder for the datasets. By default all grakel data is stored in '~/grakel_data' subfolders. download_if_missing : boolean, default=True If False, raise a IOError if the data is not locally available instead of trying to download the data from the source site. with_classes : bool, default=False Return an iterable of class labels based on the enumeration. produce_labels_nodes : bool, default=False Produce labels for nodes if not found. Currently this means labeling its node by its degree inside the Graph. This operation is applied only if node labels are non existent. prefer_attr_nodes : bool, default=False If a dataset has both *node* labels and *node* attributes set as labels for the graph object for *nodes* the attributes. prefer_attr_edges : bool, default=False If a dataset has both *edge* labels and *edge* attributes set as labels for the graph object for *edge* the attributes. as_graphs : bool, default=False Return data as a list of Graph Objects. Returns ------- graphs : iterable Returns an iterable of the produced *valid-graph-format* and labels for each node. classes : list Returns a list of all the classes corresponding to each graph by order of input. """ name = str(name) if name in dataset_metadata: if data_home is None: data_home = os.path.join(os.path.expanduser("~"), 'grakel_data') exists = os.path.isdir(data_home) missing = not os.path.exists(os.path.join(data_home, name + ".zip")) cwd = os.getcwd() if missing: if download_if_missing: if not exists: if verbose: print("Initializing folder at", str(data_home)) os.makedirs(data_home) os.chdir(data_home) if verbose: print("Downloading dataset for", name + "..") _download_zip(dataset_metadata[name]["link"], name) else: raise IOError('Dataset ' + name + ' was not found on ' + str(data_home)) else: # move to the general data directory os.chdir(data_home) with zipfile.ZipFile(str(name) + '.zip', "r") as zip_ref: if verbose: print("Extracting dataset ", str(name) + "..") zip_ref.extractall() if verbose: print("Parsing dataset ", str(name) + "..") data = read_data(name, with_classes=with_classes, prefer_attr_nodes=prefer_attr_nodes, prefer_attr_edges=prefer_attr_edges, produce_labels_nodes=produce_labels_nodes, is_symmetric=symmetric_dataset, as_graphs=as_graphs) if verbose: print("Parse was succesful..") if verbose: print("Deleting unzipped dataset files..") shutil.rmtree(str(name)) if verbose: print("Going back to the original directory..") os.chdir(cwd) return data else: raise ValueError('Dataset: "'+str(name)+'" is currently unsupported.' + '\nSupported datasets come from ' 'https://ls11-www.cs.tu-dortmund.de/staff/morris/' + 'graphkerneldatasets. If your dataset name appears' + ' them send us a pm, to explain you either why we ' + 'don\'t support it, or to update our dataset ' + 'database.')
[docs]def get_dataset_info(dataset_name, default=None): """Return the info concerning the existence of a certain dataset. Parameters ---------- dataset_name : str The name of the dataset. default : Object, default=None The default return value if the dataset is not found. Returns ------- dictionary_get : default or dictionary_entry The info of a dataset as a dictionary with fields: - **nl** : A boolean flag indicating if the dataset has node labels. - **el** : A boolean flag indicating if the dataset has edge labels. - **na** : A boolean flag indicating if the dataset has node attributes. - **ea** : A boolean flag indicating if the dataset has edge attributes. - **link** : A str corresponding to the download link of the dataset. """ return dataset_metadata.get(dataset_name, default)