i2mint/meshed

Give easier control over digraph generation

Closed this issue · 1 comments

Compare the two graphs in the image below;

image

Here we see two things that could be nice to offer options to:

  • Use circle for input nodes
  • skip intermediate nodes (instead, edges would only be from func node to func node

Below is some code that might help:

from inspect import signature, Parameter

def args_funcnames(funcs):
    for func in funcs:
        sig = signature(func)
        for param in sig.parameters.values():
            arg_name = ""  # initialize
            if param.kind == Parameter.VAR_POSITIONAL:
                arg_name += '*' 
            elif param.kind == Parameter.VAR_KEYWORD:
                arg_name += '**'
            arg_name += param.name  # append name of param
            yield arg_name, func.__name__

        
from graphviz import Digraph

def funcs_to_digraph(funcs, graph=None):
    graph = graph or Digraph()
    graph.edges(list(args_funcnames(funcs)))
    graph.body.extend([
        ", ".join(func.__name__ for func in funcs) + " [shape=box]"
    ])
    return graph


def profit(revenue, expense):
    return revenue - expense

def revenue(price, sold):
    return price * sold

def expense(cost, sold):
    return cost * sold

def sold(price, elasticity, base=1e6):
    return  base * price ** (1 - elasticity)

funcs = (profit, revenue, expense, sold)
funcs_to_digraph(funcs)

commit 4cdf0de gives us the ability to do this.

Consider this example:

def cost(impressions, cost_per_impression):
    return impressions * cost_per_impression

def clicks(impressions, click_per_impression):
    return impressions * click_per_impression

def sales(clicks, sales_per_click):
    return clicks * sales_per_click

def revenue(sales, revenue_per_sale):
    return sales * revenue_per_sale

def profit(revenue, cost):
    return revenue - cost

from meshed import DAG

funnel_dag = DAG([cost, clicks, sales, revenue, profit])

funnel_dag.dot_digraph()

image

You can make rendering changes like this:

funnel_dag.dot_digraph(
    vnode_shape='ellipse', 
    func_display=False,
    end_lines=['impressions [label=IMP shape=box]', 'clicks [style=filled, fillcolor=grey]']
)

image