Skip to content

Commit

Permalink
Add support for dependent variables in vis (#26)
Browse files Browse the repository at this point in the history
This should also fix styles.

I added some helpful checks for whether intermediate directories exist
when you try to write a graph to a path.
  • Loading branch information
audreyseo committed Aug 13, 2021
1 parent 389a5d9 commit b4041a3
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 25 deletions.
3 changes: 3 additions & 0 deletions examples/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.aux
*.pdf
*.synctex.gz
Binary file added examples/readme_dot_graph.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/readme_graph_tikz.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 18 additions & 12 deletions examples/readme_graph_tikz.tex
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@

\documentclass{standalone}
\usepackage{tikz}

\usetikzlibrary{graphs,graphdrawing,quotes}
\usegdlibrary{force,layered}
\begin{document}

\begin{tikzpicture}[cause/.style={draw=red, "causes", text=red, densely dotted},
associate/.style={draw=red, "assoc."},
\begin{tikzpicture}[causes/.style={draw=black, "cause", text=black},
associates/.style={draw=black, "assoc."},
min/.style={minimum size=2cm},
unit/.style={min,draw=black},
measure/.style={min,circle,draw=black},
dv/.style={draw=blue}]

has/.style={densely dotted},
nests/.style={dashed},
depvar/.style={draw=none,fill=grey!30}]

\graph[layered layout,sibling distance=3cm,level distance=3cm]{
member[unit, draw=red] -> [has] pounds lost[measure, fill=grey!30];
member[unit] -> [has] pounds lost[measure,depvar];
member -> [has] age[measure];
member -> [has] age*motivation[measure];
member -> [has] race group[measure];
pounds lost -> [associate] race group;
pounds lost -> [associate] age*motivation;
motivation[measure] -> [cause] pounds lost;
race group -> [associate] pounds lost;
age*motivation -> [associate] pounds lost;

pounds lost -> [associates] race group;
pounds lost -> [associates] Week[measure];
pounds lost -> [associates] age*motivation;
motivation[measure] -> [causes] pounds lost;
race group -> [associates] pounds lost;
Week -> [associates] pounds lost;
age*motivation -> [associates] pounds lost;

};
\end{tikzpicture}
\end{document}

\end{document}
15 changes: 8 additions & 7 deletions tests/test_graph_vis.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,22 @@ def test_exercise_group_simplified(self):
age = adult.numeric("age")
# Adults have one of four racial identities in this study.
race = adult.nominal("race group", cardinality=4)
# week = ts.SetUp("Week", order=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
week = ts.SetUp("Week", order=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])

motivation_level.causes(pounds_lost)
race.associates_with(pounds_lost)
# week.associates_with(pounds_lost)
week.associates_with(pounds_lost)

age.moderate(moderator=[motivation_level], on=pounds_lost)

design = ts.Design(dv=pounds_lost, ivs=[age, race])
gr = design.graph
gr.get_tikz_graph("test_exercise_group_simplified.tex")
gr.get_tikz_graph("examples/readme_graph_tikz.tex", dv=pounds_lost)
# gr._get_graph_tikz()
dot_gr = gr.get_dot_graph("readme_dot_graph.png")
dot_gr = gr.get_dot_graph("examples/readme_dot_graph.png", dv=pounds_lost)
# dot_gr.write_png("readme_dot_graph.png")

dot = gr._get_dot_graph()
for f in dot_formats:
dot.write("dot_example_{}".format(f), format=f)
gr.get_dot_graph("dot_example/dot_example.png", format="png")
# dot = gr._get_dot_graph()
# for f in dot_formats:
# dot.write("dot_example_{}".format(f), format=f)
48 changes: 44 additions & 4 deletions tisane/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
default_dot_edge_label,
)
import re
import os

"""
Class for expressing how variables (i.e., proxies) relate to one another at a
Expand Down Expand Up @@ -172,6 +173,9 @@ def get_causes_associates_tikz_graph(
def get_tikz_graph(
self, path="graph.tex", edge_filter=lambda x: True, dv: AbstractVariable = None
):
path_dir = os.path.dirname(path)
if path_dir and not os.path.exists(path_dir):
os.makedirs(path_dir)
with open(path, "w") as f:
f.write(self._get_tikz_graph(edge_filter=edge_filter, dv=dv))
pass
Expand Down Expand Up @@ -208,7 +212,7 @@ def sanitize_characters(string):
"unit"
if isinstance(var, Unit) and not isinstance(var, Measure)
else "measure"
) + ("dv" if var == dv else "")
) + (",depvar" if var == dv else "")
nodes_code = ""
# for n in nodes:
# # TODO: get the type of the node
Expand Down Expand Up @@ -241,6 +245,7 @@ def get_causes_associates_dot_graph(
style=default_dot_edge_style,
color=default_dot_edge_color,
label=default_dot_edge_label,
dv: AbstractVariable = None,
):
"""Write a DOT graph representation to a file, containing only the edges with types "causes" or "associates"
Expand Down Expand Up @@ -269,6 +274,10 @@ def get_causes_associates_dot_graph(
)
and edge_filter(edge_data),
add_extension=add_extension,
style=style,
color=color,
label=label,
dv=dv,
)

def get_dot_graph(
Expand All @@ -280,6 +289,7 @@ def get_dot_graph(
style=default_dot_edge_style,
color=default_dot_edge_color,
label=default_dot_edge_label,
dv: AbstractVariable = None,
):
"""Write the DOT graph representation to a file.
Expand All @@ -301,7 +311,7 @@ def get_dot_graph(
"""
# TODO: Add style, color, and label parameter descriptions
graph = self._get_dot_graph(
edge_filter=edge_filter, style=style, color=color, label=label
edge_filter=edge_filter, style=style, color=color, label=label, dv=dv
)
assert (
format in dot_formats
Expand All @@ -316,7 +326,15 @@ def get_dot_graph(
):
first_extension = dot_formats_extensions[format]["extensions"][0]
path = path + "." + first_extension

pass
pass
pass
path_dir = os.path.dirname(path)
if path_dir and not os.path.exists(path_dir):
# print("Path directory: {}".format(path_dir))
# Create the directory, if it doesn't exist
os.makedirs(path_dir)
pass
graph.write(path, format=format)

def _get_dot_graph(
Expand All @@ -325,8 +343,9 @@ def _get_dot_graph(
style=default_dot_edge_style,
color=default_dot_edge_color,
label=default_dot_edge_label,
dv: AbstractVariable=None,
):
"""Internal method to obtain a DOT graph object representing this graph.
""" Internal method to obtain a DOT graph object representing this graph.
Parameters
----------
Expand All @@ -350,6 +369,27 @@ def _get_dot_graph(
# TODO: fix style parameter description
graph = pydot.Dot("graph_vis", graph_type="digraph")
edges = list(self._graph.edges(data=True))
nodes = []
for (n0, n1, _) in edges:
if n0 not in nodes:
nodes.append(n0)
pass
if n1 not in nodes:
nodes.append(n1)
pass
pass
for n0 in nodes:
var0 = self.get_variable(n0)
shape = "box" if isinstance(var0, Unit) and not isinstance(var0, Measure) else "ellipse"
nodestyle=""
fillcolor="white"
if var0 == dv:
nodestyle = "filled"
fillcolor = "#AAAAAA"
pass
graph.add_node(pydot.Node(n0, label=n0, style=nodestyle, fillcolor=fillcolor, shape=shape))
pass


for (n0, n1, edge_data) in edges:
if edge_filter(edge_data):
Expand Down
6 changes: 4 additions & 2 deletions tisane/graph_vis_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
unit/.style={min,draw=black},
measure/.style={min,circle,draw=black},
has/.style={densely dotted},
nests/.style={dashed}]
nests/.style={dashed},
depvar/.style={draw=none,fill=grey!30}]
"""

graph = """ \\graph[layered layout,sibling distance={},level distance={}]{}
Expand Down Expand Up @@ -192,7 +193,8 @@
"associates": "",
"causes": "",
"nests": "dashed",
"default": "dotted",
"has": "dotted",
"default": "solid",
}
default_dot_edge_color = {"default": "black"}
default_dot_edge_label = {"associates": "assoc.", "causes": "cause", "default": ""}
Expand Down

0 comments on commit b4041a3

Please sign in to comment.