Example #1
0
def main(args):
    usage = '''
PathLinker.py [options] NETWORK NODE_TYPES
REQUIRED arguments:
    NETWORK - A tab-delimited file with one directed interaction per
        line. Each line should have at least 2 columns: tail, head. Edges
        are directed from tail to head. This file can have a third column
        specifying the edge weight, which is required unless the --PageRank
        option is used (see --PageRank help for a note on these weights).
        To run PathLinker on an unweighted graph, set all edge weights
        to 1 in the input network.

    NODE_TYPES - A tab-delimited file denoting nodes as receptors or TRs. The first
        column is the node name, the second is the node type, either 'source'
        (or 'receptor') or 'target' (or 'tr' or 'tf'). Nodes which are neither receptors nor TRs may
        be omitted from this file or may be given a type which is neither 'source'
        nor 'target'.
    
'''
    parser = OptionParser(usage=usage)

    # General Options
    parser.add_option('-o', '--output', type='string', default='out_', metavar='STR',\
        help='A string to prepend to all output files. (default="out")')

    parser.add_option('', '--write-paths', action='store_true', default=False,\
        help='If given, also output a list of paths found by KSP in addition to the ranked edges.')
    
    parser.add_option('', '--no-log-transform', action='store_true', default=False,\
        help='Normally input edge weights are log-transformed. This option disables that step.')

    parser.add_option('', '--largest-connected-component', action='store_true', default=False,\
        help='Run PathLinker on only the largest weakly connected component of the graph. May provide performance speedup.')

    # Random Walk Group
    group = OptionGroup(parser, 'Random Walk Options')

    group.add_option('', '--PageRank', action='store_true', default=False,\
        help='Run the PageRank algorithm to generate edge visitation flux values, which are then used as weights for KSP. A weight column in the network file is not needed if this option is given, as the PageRank visitation fluxes are used for edge weights in KSP. If a weight column is given, these weights are interpreted as a weighted PageRank graph.')

    group.add_option('-q', '--q-param', action='store', type='float', default=0.5,\
        help='The value of q indicates the probability that the random walker teleports back to a source node during the random walk process. (default=0.5)')

    group.add_option('-e', '--epsilon', action='store', type='float', default=0.0001,\
            help='A small value used to test for convergence of the iterative implementation of PageRank. (default=0.0001)')

    group.add_option('', '--max-iters', action='store', type='int', default=500,\
        help='Maximum number of iterations to run the PageRank algorithm. (default=500)')

    parser.add_option_group(group)

    # k shortest paths Group
    group = OptionGroup(parser, 'k Shortest Paths Options')

    group.add_option('-k', '--k-param', type='int', default=100,\
        help='The number of shortest paths to find. (default=100)')

    group.add_option('','--allow-mult-targets', action='store_true', default=False,\
                     help='By default, PathLinker will remove outgoing edges from targets to ensure that there is only one target on each path.  If --allow-mult-targets is specified, these edges are not removed.')

    group.add_option('','--allow-mult-sources', action='store_true', default=False,\
                     help='By default, PathLinker will remove incoming edges to sources to ensure that there is only one source on each path.  If --allow-mult-sources is specified, these edges are not removed.')

    parser.add_option_group(group)


    # parse the command line arguments
    (opts, args) = parser.parse_args()

    # get the required arguments
    num_req_args = 2
    if len(args)!=num_req_args:
        parser.print_help()
        sys.exit('\nERROR: PathLinker.py requires %d positional arguments, %d given.' %(num_req_args, len(args)))
    
    NETWORK_FILE = args[0]
    NODE_VALUES_FILE = args[1]

    # Validate options
    if(opts.PageRank and opts.no_log_transform):
        sys.exit('\nERROR: Options --PageRank and --no-log-transform should not be used together. PageRank weights are probabilities, and must be log-transformed to have an additive interpretation.')

    ## Read the network from file
    net = nx.DiGraph()

    # Read the network file
    print('\nReading the network from %s' %(NETWORK_FILE))
    infile = open(NETWORK_FILE, 'r')
    for line in infile:
        items = [x.strip() for x in line.rstrip().split('\t')]

        # Skip empty lines or those beginning with '#' comments
        if line=='':
            continue
        if line[0]=='#':
            continue

        id1 = items[0]
        id2 = items[1]

        # Ignore self-edges
        if id1==id2:
            continue

        # Possibly use an edge weight
        eWeight = 1
        if(len(items) > 2):
            eWeight = float(items[2])
        elif(not opts.PageRank):
            print("\nERROR: All edges must have a weight, unless --PageRank is used. Edge (%s --> %s) does not have a weight entry."%(id1, id2))
            exit(-1)

        # Assign the weight. Note in the PageRank case, "weight" is
        # interpreted as running PageRank and edgeflux on a weighted
        # graph. 
        net.add_edge(id1, id2, ksp_weight=eWeight, weight=eWeight)


    # Print info about the network
    print(nx.info(net))

    # Operate on only the largest connected component
    if(opts.largest_connected_component):

        conn_comps = nx.weakly_connected_component_subgraphs(net)
        
        # This is the only portion of the program which prevents
        # compatibility between Python 2 & 3. In 2, this object is a
        # generator, but in 3 it is a list. Just check the type and
        # handle accordingly to provide cross-compatibility.
        if(isinstance(conn_comps, types.GeneratorType)):
            net = next(conn_comps)
        elif(isinstance(conn_comps, list)):
            net = conn_comps[0]
        else:
            print("Compatibility error between NetworkX and Python versions. Connected components object from NetworkX does not have acceptable type.")
            exit(-1)
        

        print("\n Using only the largest weakly connected component:\n"+nx.info(net))

    # Read the sources and targets on which to run PageRank and KSP
    sources = set()
    targets = set()

    # Read the receptors and TRs file
    print("Reading sources and targets from " + NODE_VALUES_FILE)
    for line in open(NODE_VALUES_FILE, 'r').readlines():
        items = [x.strip() for x in line.rstrip().split('\t')]

        # Skip empty lines and lines beginning with '#' comments
        if line=='':
            continue
        if line[0]=='#':
            continue

        if items[1] in ['source', 'receptor']:
            sources.add(items[0])
        elif items[1] in ['target', 'tr', 'tf']:
            targets.add(items[0])

    print('\nRead %d sources and %d targets' % (len(sources), len(targets)))

    # Remove sources and targets that don't appear in the network, and do some sanity checks on sets
    sources = set([s for s in sources if s in net])
    targets = set([t for t in targets if t in net])
    print('\tAfter removing sources and targets that are not in the network: %d sources and %d targets.' %(len(sources), len(targets)))
    if len(sources)==0:
        sys.exit('ERROR: No sources are in the network.')
    if len(targets)==0:
        sys.exit('ERROR: No targets are in the network.')
    if len(sources.intersection(targets))>0:
        sys.exit('ERROR: %d proteins are listed as both a source and target.' %(len(sources.intersection(targets))))


    # Run PageRank on the network
    # (if opts.PageRank == false, the weights were read from a file above)
    if(opts.PageRank):

        PR_PARAMS = {'q' : opts.q_param,\
                     'eps' : opts.epsilon,\
                     'maxIters' : opts.max_iters}

        print('\nRunning PageRank on net.(q=%f)' %(opts.q_param))

        # The initial weights are entirely on the source nodes, so this 
        # corresponds to a random walk that teleports back to the sources.
        weights = dict.fromkeys(sources, 1.0)
        prFinal = pagerank(net, weights, **PR_PARAMS)
        
        # Write node visitation probabilities
        # (function imported from PageRank)
        writePageRankWeights(prFinal,filename='%s-node-pagerank.txt' % (opts.output))

        # Weight the edges by the flux from the nodes
        calculateFluxEdgeWeights(net, prFinal)

        # Write edge fluxes
        printEdgeFluxes('%s-edge-fluxes.txt' % (opts.output), net)
    
    ## Prepare the network to run KSP

    # Remove improper edges from the sources and targets. This portion
    # must be performed before the log transformation, so that the
    # renormalization within accounts for the probability lost to the
    # removed edges.  These transformations are executed by default;
    # to prevent them, use the opts.allow_mult_sources or opts.allow_mult_targets
    # arguments.
    if not opts.allow_mult_sources:
        modifyGraphForKSP_removeEdgesToSources(net, sources)
    if not opts.allow_mult_targets:
        modifyGraphForKSP_removeEdgesFromTargets(net, targets)

    # Transform the edge weights with a log transformation
    if(not opts.no_log_transform):
        logTransformEdgeWeights(net)

    # Add a super source and super sink. Performed after the
    # transformations so that the edges can be given an additive
    # weight of 0 and thus not affect the resulting path cost.
    modifyGraphForKSP_addSuperSourceSink(net, sources, targets, weightForArtificialEdges = 0)
    
    ## Run the pathfinding algorithm
    print('\nComputing the k=%d shortest simple paths.' %(opts.k_param))
    paths = ksp.k_shortest_paths_yen(net, 'source', 'sink', opts.k_param, weight='ksp_weight')

    if len(paths)==0:
        sys.exit('\tERROR: Targets are not reachable from the sources.')

    ## Use the results of KSP to rank edges

    # Prepare the k shortest paths for output to flat files
    pathgraph = nx.DiGraph()
    for k,path in enumerate(paths, 1):

        # Process the edges in this path
        edges = []
        for i in range(len(path)-1):
            t = path[i][0]
            h = path[i+1][0]

            # Skip edges that have already been seen in an earlier path
            if pathgraph.has_edge(t, h):
                continue

            # Skip edges that include our artificial supersource or
            # supersink
            if t=='source' or h=='sink':
                continue

            # This is a new edge. Add it to the list and note which k it
            # appeared in.
            else:
                edges.append( (t,h,{'ksp_id':k, 'ksp_weight':net.edge[t][h]['ksp_weight']}) )

        # Add all new, good edges from this path to the network
        pathgraph.add_edges_from(edges)

        # Each node is ranked by the first time it appears in a path.
        # Identify these by check for any nodes in the graph which do
        # not have 'ksp_id' attribute, meaning they were just added
        # from this path.
        for n in pathgraph.nodes():
            if 'ksp_id' not in pathgraph.node[n]:
                pathgraph.node[n]['ksp_id'] = k


    ## Write out the results to file    

    # Write a list of all edges encountered, ranked by the path they
    # first appeared in.
    kspGraphOutfile = '%sk_%d-ranked-edges.txt' %(opts.output, opts.k_param)
    printKSPGraph(kspGraphOutfile, pathgraph)
    print('\nKSP results are in "%s"' %(kspGraphOutfile))

    # Write a list of all paths found by the ksp algorithm, if
    # requested.
    if(opts.write_paths):
        kspOutfile = '%sk_%d-paths.txt' %(opts.output, opts.k_param)
        printKSPPaths(kspOutfile, paths)
        print('KSP paths are in "%s"' %(kspOutfile))

    print('\nFinished!')
Example #2
0
def main(args):
    usage = '''
PathLinker.py [options] NETWORK NODE_TYPES
REQUIRED arguments:
    NETWORK - A tab-delimited file with one directed interaction per
        line. Each line should have at least 2 columns: tail, head. Edges
        are directed from tail to head. This file can have a third column
        specifying the edge weight, which is required unless the --PageRank
        option is used (see --PageRank help for a note on these weights).
        To run PathLinker on an unweighted graph, set all edge weights
        to 1 in the input network.

    NODE_TYPES - A tab-delimited file denoting nodes as receptors or TRs. The first
        column is the node name, the second is the node type, either 'source'
        (or 'receptor') or 'target' (or 'tr' or 'tf'). Nodes which are neither receptors nor TRs may
        be omitted from this file or may be given a type which is neither 'source'
        nor 'target'.
    
'''
    parser = OptionParser(usage=usage)

    # General Options
    parser.add_option('-o', '--output', type='string', default='out_', metavar='STR',\
        help='A string to prepend to all output files. (default="out")')

    parser.add_option('', '--write-paths', action='store_true', default=False,\
        help='If given, also output a list of paths found by KSP in addition to the ranked edges.')

    parser.add_option('', '--no-log-transform', action='store_true', default=False,\
        help='Normally input edge weights are log-transformed. This option disables that step.')

    parser.add_option('', '--largest-connected-component', action='store_true', default=False,\
        help='Run PathLinker on only the largest weakly connected component of the graph. May provide performance speedup.')

    # Random Walk Group
    group = OptionGroup(parser, 'Random Walk Options')

    group.add_option('', '--PageRank', action='store_true', default=False,\
        help='Run the PageRank algorithm to generate edge visitation flux values, which are then used as weights for KSP. A weight column in the network file is not needed if this option is given, as the PageRank visitation fluxes are used for edge weights in KSP. If a weight column is given, these weights are interpreted as a weighted PageRank graph.')

    group.add_option('-q', '--q-param', action='store', type='float', default=0.5,\
        help='The value of q indicates the probability that the random walker teleports back to a source node during the random walk process. (default=0.5)')

    group.add_option('-e', '--epsilon', action='store', type='float', default=0.0001,\
            help='A small value used to test for convergence of the iterative implementation of PageRank. (default=0.0001)')

    group.add_option('', '--max-iters', action='store', type='int', default=500,\
        help='Maximum number of iterations to run the PageRank algorithm. (default=500)')

    parser.add_option_group(group)

    # k shortest paths Group
    group = OptionGroup(parser, 'k Shortest Paths Options')

    group.add_option('-k', '--k-param', type='int', default=100,\
        help='The number of shortest paths to find. (default=100)')

    group.add_option('','--allow-mult-targets', action='store_true', default=False,\
                     help='By default, PathLinker will remove outgoing edges from targets to ensure that there is only one target on each path.  If --allow-mult-targets is specified, these edges are not removed.')

    group.add_option('','--allow-mult-sources', action='store_true', default=False,\
                     help='By default, PathLinker will remove incoming edges to sources to ensure that there is only one source on each path.  If --allow-mult-sources is specified, these edges are not removed.')

    parser.add_option_group(group)


    # parse the command line arguments
    (opts, args) = parser.parse_args()

    # get the required arguments
    num_req_args = 2
    if len(args)!=num_req_args:
        parser.print_help()
        sys.exit('\nERROR: PathLinker.py requires %d positional arguments, %d given.' %(num_req_args, len(args)))

    NETWORK_FILE = args[0]
    NODE_VALUES_FILE = args[1]

    # Validate options
    if(opts.PageRank and opts.no_log_transform):
        sys.exit('\nERROR: Options --PageRank and --no-log-transform should not be used together. PageRank weights are probabilities, and must be log-transformed to have an additive interpretation.')

    pathlinker = PathLinker(opts.no_log_transform, opts.largest_connected_component, opts.PageRank, opts.q_param, opts.epsilon, opts.max_iters, opts.k_param, opts.allow_mult_targets, opts.allow_mult_sources)

    try:
        # Read the network file
        print('\nReading the network from %s' %(NETWORK_FILE))
        # Read the network from file
        net = pathlinker.read_network_file(open(NETWORK_FILE, 'r'))

        # Print info about the network
        print(nx.info(net))

        # Read the sources and targets on which to run PageRank and KSP
        print("Reading sources and targets from " + NODE_VALUES_FILE)
        sources, targets = pathlinker.read_nodes_values_file(open(NODE_VALUES_FILE, 'r'))

        # prFinal will be None if --PageRank option is not used
        # paths are k shortest paths found using ksp pathfinding algorithm
        # pathgraph is used to prepare the k shortest paths for output to flat files
        prFinal, paths, pathgraph = pathlinker.execute()

    except PathLinkerError as e:
        sys.exit(e.value)

    if opts.PageRank:
        # Write node visitation probabilities
        # (function imported from PageRank)
        writePageRankWeights(prFinal, filename='%s-node-pagerank.txt' % (opts.output))
        # Write edge fluxes
        printEdgeFluxes('%s-edge-fluxes.txt' % (opts.output), net)

    ## Write out the results to file

    # Write a list of all edges encountered, ranked by the path they
    # first appeared in.
    kspGraphOutfile = '%sk_%d-ranked-edges.txt' %(opts.output, opts.k_param)
    printKSPGraph(kspGraphOutfile, pathgraph)
    print('\nKSP results are in "%s"' %(kspGraphOutfile))

    # Write a list of all paths found by the ksp algorithm, if
    # requested.
    if(opts.write_paths):
        kspOutfile = '%sk_%d-paths.txt' %(opts.output, opts.k_param)
        printKSPPaths(kspOutfile, paths)
        print('KSP paths are in "%s"' %(kspOutfile))

    print('\nFinished!')