a-r-j / graphein

Protein Graph Library
https://graphein.ai/
MIT License
1.03k stars 131 forks source link

asteroid_plot: show_edges #167

Closed avivko closed 2 years ago

avivko commented 2 years ago

Describe the bug Using show_edges=True as an argument raises an error because of the formatting of the colo(u)rs for the scatter plot.

To Reproduce For example:

from graphein.protein.visualisation import asteroid_plot
asteroid_plot(complex_graph, node_id="A:VAL:70", k=4, colour_nodes_by="shell", colour_edges_by="kind", show_edges=True)
Here's my error trace:
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [24], in <cell line: 2>()
      1 from graphein.protein.visualisation import asteroid_plot
----> 2 asteroid_plot(g_a, node_id="A:LYS:255", k=3, colour_nodes_by="shell",colour_edges_by="kind", show_edges=True)

File /glusterfs/dfs-gfs-dist/kormanav/miniconda3/envs/graphein-gpu/lib/python3.8/site-packages/graphein/protein/visualisation.py:733, in asteroid_plot(g, node_id, k, colour_nodes_by, colour_edges_by, edge_colour_map, show_labels, title, width, height, use_plotly, show_edges, node_size_multiplier)
    731         edge_y.append(y1)
    732         edge_y.append(None)
--> 733     edge_trace = go.Scatter(
    734         x=edge_x,
    735         y=edge_y,
    736         line=dict(width=1, color=edge_colors),
    737         hoverinfo="text",
    738         mode="lines",
    739         text=[
    740             " / ".join(list(edge_type))
    741             for edge_type in nx.get_edge_attributes(
    742                 subgraph, "kind"
    743             ).values()
    744         ],
    745     )
    747 node_x: List[str] = []
    748 node_y: List[str] = []

File ~/.local/lib/python3.8/site-packages/plotly/graph_objs/_scatter.py:3108, in Scatter.__init__(self, arg, cliponaxis, connectgaps, customdata, customdatasrc, dx, dy, error_x, error_y, fill, fillcolor, groupnorm, hoverinfo, hoverinfosrc, hoverlabel, hoveron, hovertemplate, hovertemplatesrc, hovertext, hovertextsrc, ids, idssrc, legendgroup, legendgrouptitle, legendrank, line, marker, meta, metasrc, mode, name, opacity, orientation, selected, selectedpoints, showlegend, stackgaps, stackgroup, stream, text, textfont, textposition, textpositionsrc, textsrc, texttemplate, texttemplatesrc, uid, uirevision, unselected, visible, x, x0, xaxis, xcalendar, xhoverformat, xperiod, xperiod0, xperiodalignment, xsrc, y, y0, yaxis, ycalendar, yhoverformat, yperiod, yperiod0, yperiodalignment, ysrc, **kwargs)
   3106 _v = line if line is not None else _v
   3107 if _v is not None:
-> 3108     self["line"] = _v
   3109 _v = arg.pop("marker", None)
   3110 _v = marker if marker is not None else _v

File ~/.local/lib/python3.8/site-packages/plotly/basedatatypes.py:4811, in BasePlotlyType.__setitem__(self, prop, value)
   4809 # ### Handle compound property ###
   4810 if isinstance(validator, CompoundValidator):
-> 4811     self._set_compound_prop(prop, value)
   4813 # ### Handle compound array property ###
   4814 elif isinstance(validator, (CompoundArrayValidator, BaseDataValidator)):

File ~/.local/lib/python3.8/site-packages/plotly/basedatatypes.py:5222, in BasePlotlyType._set_compound_prop(self, prop, val)
   5219 # Import value
   5220 # ------------
   5221 validator = self._get_validator(prop)
-> 5222 val = validator.validate_coerce(val, skip_invalid=self._skip_invalid)
   5224 # Save deep copies of current and new states
   5225 # ------------------------------------------
   5226 curr_val = self._compound_props.get(prop, None)

File ~/.local/lib/python3.8/site-packages/_plotly_utils/basevalidators.py:2467, in CompoundValidator.validate_coerce(self, v, skip_invalid, _validate)
   2464     v = self.data_class()
   2466 elif isinstance(v, dict):
-> 2467     v = self.data_class(v, skip_invalid=skip_invalid, _validate=_validate)
   2469 elif isinstance(v, self.data_class):
   2470     # Copy object
   2471     v = self.data_class(v)

File ~/.local/lib/python3.8/site-packages/plotly/graph_objs/scatter/_line.py:293, in Line.__init__(self, arg, color, dash, shape, simplify, smoothing, width, **kwargs)
    291 _v = color if color is not None else _v
    292 if _v is not None:
--> 293     self["color"] = _v
    294 _v = arg.pop("dash", None)
    295 _v = dash if dash is not None else _v

File ~/.local/lib/python3.8/site-packages/plotly/basedatatypes.py:4819, in BasePlotlyType.__setitem__(self, prop, value)
   4815         self._set_array_prop(prop, value)
   4817     # ### Handle simple property ###
   4818     else:
-> 4819         self._set_prop(prop, value)
   4820 else:
   4821     # Make sure properties dict is initialized
   4822     self._init_props()

File ~/.local/lib/python3.8/site-packages/plotly/basedatatypes.py:5163, in BasePlotlyType._set_prop(self, prop, val)
   5161         return
   5162     else:
-> 5163         raise err
   5165 # val is None
   5166 # -----------
   5167 if val is None:
   5168     # Check if we should send null update

File ~/.local/lib/python3.8/site-packages/plotly/basedatatypes.py:5158, in BasePlotlyType._set_prop(self, prop, val)
   5155 validator = self._get_validator(prop)
   5157 try:
-> 5158     val = validator.validate_coerce(val)
   5159 except ValueError as err:
   5160     if self._skip_invalid:

File ~/.local/lib/python3.8/site-packages/_plotly_utils/basevalidators.py:1382, in ColorValidator.validate_coerce(self, v, should_raise)
   1380     validated_v = self.vc_scalar(v)
   1381     if validated_v is None and should_raise:
-> 1382         self.raise_invalid_val(v)
   1384     v = validated_v
   1386 return v

File ~/.local/lib/python3.8/site-packages/_plotly_utils/basevalidators.py:289, in BaseValidator.raise_invalid_val(self, v, inds)
    286             for i in inds:
    287                 name += "[" + str(i) + "]"
--> 289         raise ValueError(
    290             """
    291     Invalid value of type {typ} received for the '{name}' property of {pname}
    292         Received value: {v}
    293 
    294 {valid_clr_desc}""".format(
    295                 name=name,
    296                 pname=self.parent_name,
    297                 typ=type_str(v),
    298                 v=repr(v),
    299                 valid_clr_desc=self.description(),
    300             )
    301         )

ValueError: 
    Invalid value of type 'builtins.list' received for the 'color' property of scatter.line
        Received value: [(0.69284, 0.165141, 0.564522, 1.0), (0.050383, 0.029803, 0.527975, 1.0), (0.050383, 0.029803, 0.527975, 1.0), (0.050383, 0.029803, 0.527975, 1.0), (0.050383, 0.029803, 0.527975, 1.0), (0.050383, 0.029803, 0.527975, 1.0), (0.69284, 0.165141, 0.564522, 1.0), (0.417642, 0.000564, 0.65839, 1.0), (0.417642, 0.000564, 0.65839, 1.0), (0.050383, 0.029803, 0.527975, 1.0), (0.050383, 0.029803, 0.527975, 1.0), (0.69284, 0.165141, 0.564522, 1.0), (0.050383, 0.029803, 0.527975, 1.0), (0.050383, 0.029803, 0.527975, 1.0), (0.881443, 0.392529, 0.383229, 1.0)]

    The 'color' property is a color and may be specified as:
      - A hex string (e.g. '#ff0000')
      - An rgb/rgba string (e.g. 'rgb(255,0,0)')
      - An hsl/hsla string (e.g. 'hsl(0,100%,50%)')
      - An hsv/hsva string (e.g. 'hsv(0,100%,100%)')
      - A named CSS color:
            aliceblue, antiquewhite, aqua, aquamarine, azure,
            beige, bisque, black, blanchedalmond, blue,
            blueviolet, brown, burlywood, cadetblue,
            chartreuse, chocolate, coral, cornflowerblue,
            cornsilk, crimson, cyan, darkblue, darkcyan,
            darkgoldenrod, darkgray, darkgrey, darkgreen,
            darkkhaki, darkmagenta, darkolivegreen, darkorange,
            darkorchid, darkred, darksalmon, darkseagreen,
            darkslateblue, darkslategray, darkslategrey,
            darkturquoise, darkviolet, deeppink, deepskyblue,
            dimgray, dimgrey, dodgerblue, firebrick,
            floralwhite, forestgreen, fuchsia, gainsboro,
            ghostwhite, gold, goldenrod, gray, grey, green,
            greenyellow, honeydew, hotpink, indianred, indigo,
            ivory, khaki, lavender, lavenderblush, lawngreen,
            lemonchiffon, lightblue, lightcoral, lightcyan,
            lightgoldenrodyellow, lightgray, lightgrey,
            lightgreen, lightpink, lightsalmon, lightseagreen,
            lightskyblue, lightslategray, lightslategrey,
            lightsteelblue, lightyellow, lime, limegreen,
            linen, magenta, maroon, mediumaquamarine,
            mediumblue, mediumorchid, mediumpurple,
            mediumseagreen, mediumslateblue, mediumspringgreen,
            mediumturquoise, mediumvioletred, midnightblue,
            mintcream, mistyrose, moccasin, navajowhite, navy,
            oldlace, olive, olivedrab, orange, orangered,
            orchid, palegoldenrod, palegreen, paleturquoise,
            palevioletred, papayawhip, peachpuff, peru, pink,
            plum, powderblue, purple, red, rosybrown,
            royalblue, rebeccapurple, saddlebrown, salmon,
            sandybrown, seagreen, seashell, sienna, silver,
            skyblue, slateblue, slategray, slategrey, snow,
            springgreen, steelblue, tan, teal, thistle, tomato,
            turquoise, violet, wheat, white, whitesmoke,
            yellow, yellowgreen

Expected behavior Having edges in the plot would be nice :)

Desktop (please complete the following information):

a-r-j commented 2 years ago

Hey @avivko I'll check this out ASAP. Do you have a PDB code for the example?

avivko commented 2 years ago

No hurry @a-r-j :)

I used the PDB ID "6REW". This is a more detailed rundown of how I got the error:


from graphein.protein.subgraphs import extract_subgraph_from_chains
from graphein.protein.config import ProteinGraphConfig, DSSPConfig
from graphein.protein.features.nodes.amino_acid import expasy_protein_scale, meiler_embedding
from graphein.protein.features.nodes import asa, rsa

conf_functions = {"edge_construction_functions": [add_peptide_bonds,
                                                  add_aromatic_interactions,
                                                  add_hydrogen_bond_interactions,
                                                  add_disulfide_interactions,
                                                  add_ionic_interactions,
                                                  add_aromatic_sulphur_interactions,
                                                  add_cation_pi_interactions],
                  "graph_metadata_functions": [asa, rsa],                                        # Add ASA and RSA features.
                  "node_metadata_functions": [meiler_embedding,partial(expasy_protein_scale, add_separate=True)], # Add expasy features (partial: each feature is added under a separate key)
                  "dssp_config":DSSPConfig(),                                                    # Add DSSP config in order to compute ASA and RSA.
                  "pdb_dir": '/vol/tmp/kormanav/pdb_dir'
                 }

config = ProteinGraphConfig(**conf_functions)
g = construct_graph(config=config, pdb_code="6rew")
g_a = extract_subgraph_from_chains(g, ["A"])

from graphein.protein.visualisation import asteroid_plot
asteroid_plot(g_a, node_id="A:LYS:255", k=3, colour_nodes_by="shell",colour_edges_by="kind", show_edges=True)
a-r-j commented 2 years ago

Ah, I dug into this.

The problem is that plotly doesn't support multiple colours for a single line trace. I've worked around this before here by making a new trace for each line segment. If you wanted to have a go at making the change I'd be super happy to support.

Alternatively, you can do what the function is doing under the hood and use the k-hop subgraph function and plot that with the normal plotting function