Give easier control over digraph generation
Closed this issue · 1 comments
thorwhalen commented
Compare the two graphs in the image below;
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)
thorwhalen commented
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()
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]']
)