Repository: networkx/networkx
Pull request: Update `draw_<layout>` docstrings with usage examples
This PR adds to the existing `draw_<layout>` convenience functions to emphasize their intended use-case and highlight using the layout functions explicitly for more involved usages. I've done this primarily by adding a short line to the extended summary explaining what the convenience function wraps, as well as a `Notes` and `See Also` section that highlight other use-cases.

This also closes #5215 by updating the parameters and simplifying the argument parsing. Technically this is a change to the signature, but I don't think it needs any sort of warning as the behavior is equivalent.

This addresses most of https://github.com/networkx/networkx/discussions/5220#discussioncomment-1961800, though there is still the matter of adding convenience functions for the missing layouts, which I propose to do in a separate PR.

Problem statement:
Documentation for `draw_shell` does not explain all arguments
<!-- If you have a general question about NetworkX, please use the discussions tab to create a new discussion -->

<!--- Provide a general summary of the issue in the Title above -->



### Current Behavior
I was trying to debug code which assigns `nlist` in `draw_shell`. I didn't understand that until I read the source code.

The documentation for `draw_shell` says that the keyword arguments match `nx.draw_networkx`.  But `nlist` is not described there.  Instead it is described in `nx.shell_layout`.

### Expected Behavior
documentation that explains nlist.

### Steps to Reproduce
<!--- Provide a minimal example that reproduces the bug -->

### Environment
<!--- Please provide details about your local environment -->
Python version:
NetworkX version: 2.6.2


### Additional context
<!--- Add any other context about the problem here, screenshots, etc. -->

Hints from issue discussion:

Commit message: Fix links to layout functions (cross-module.

Patch to review:
diff --git a/networkx/drawing/nx_pylab.py b/networkx/drawing/nx_pylab.py
index 002958b719b..e0d063431e2 100644
--- a/networkx/drawing/nx_pylab.py
+++ b/networkx/drawing/nx_pylab.py
@@ -27,7 +27,6 @@
     random_layout,
     planar_layout,
 )
-import warnings
 
 __all__ = [
     "draw",
@@ -1192,7 +1191,11 @@ def draw_networkx_edge_labels(
 
 
 def draw_circular(G, **kwargs):
-    """Draw the graph G with a circular layout.
+    """Draw the graph `G` with a circular layout.
+
+    This is a convenience function equivalent to::
+
+        nx.draw(G, pos=nx.circular_layout(G), **kwargs)
 
     Parameters
     ----------
@@ -1200,15 +1203,33 @@ def draw_circular(G, **kwargs):
         A networkx graph
 
     kwargs : optional keywords
-        See networkx.draw_networkx() for a description of optional keywords,
-        with the exception of the pos parameter which is not used by this
-        function.
+        See `draw_networkx` for a description of optional keywords.
+
+    Notes
+    -----
+    The layout is computed each time this function is called. For
+    repeated drawing it is much more efficient to call
+    `~networkx.drawing.layout.circular_layout` directly and reuse the result::
+
+        >>> G = nx.complete_graph(5)
+        >>> pos = nx.circular_layout(G)
+        >>> nx.draw(G, pos=pos)  # Draw the original graph
+        >>> # Draw a subgraph, reusing the same node positions
+        >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
+
+    See Also
+    --------
+    :func:`~networkx.drawing.layout.circular_layout`
     """
     draw(G, circular_layout(G), **kwargs)
 
 
 def draw_kamada_kawai(G, **kwargs):
-    """Draw the graph G with a Kamada-Kawai force-directed layout.
+    """Draw the graph `G` with a Kamada-Kawai force-directed layout.
+
+    This is a convenience function equivalent to::
+
+        nx.draw(G, pos=nx.kamada_kawai_layout(G), **kwargs)
 
     Parameters
     ----------
@@ -1216,15 +1237,34 @@ def draw_kamada_kawai(G, **kwargs):
         A networkx graph
 
     kwargs : optional keywords
-        See networkx.draw_networkx() for a description of optional keywords,
-        with the exception of the pos parameter which is not used by this
-        function.
+        See `draw_networkx` for a description of optional keywords.
+
+    Notes
+    -----
+    The layout is computed each time this function is called.
+    For repeated drawing it is much more efficient to call
+    `~networkx.drawing.layout.kamada_kawai_layout` directly and reuse the
+    result::
+
+        >>> G = nx.complete_graph(5)
+        >>> pos = nx.kamada_kawai_layout(G)
+        >>> nx.draw(G, pos=pos)  # Draw the original graph
+        >>> # Draw a subgraph, reusing the same node positions
+        >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
+
+    See Also
+    --------
+    :func:`~networkx.drawing.layout.kamada_kawai_layout`
     """
     draw(G, kamada_kawai_layout(G), **kwargs)
 
 
 def draw_random(G, **kwargs):
-    """Draw the graph G with a random layout.
+    """Draw the graph `G` with a random layout.
+
+    This is a convenience function equivalent to::
+
+        nx.draw(G, pos=nx.random_layout(G), **kwargs)
 
     Parameters
     ----------
@@ -1232,20 +1272,36 @@ def draw_random(G, **kwargs):
         A networkx graph
 
     kwargs : optional keywords
-        See networkx.draw_networkx() for a description of optional keywords,
-        with the exception of the pos parameter which is not used by this
-        function.
+        See `draw_networkx` for a description of optional keywords.
+
+    Notes
+    -----
+    The layout is computed each time this function is called.
+    For repeated drawing it is much more efficient to call
+    `~networkx.drawing.layout.random_layout` directly and reuse the result::
+
+        >>> G = nx.complete_graph(5)
+        >>> pos = nx.random_layout(G)
+        >>> nx.draw(G, pos=pos)  # Draw the original graph
+        >>> # Draw a subgraph, reusing the same node positions
+        >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
+
+    See Also
+    --------
+    :func:`~networkx.drawing.layout.random_layout`
     """
     draw(G, random_layout(G), **kwargs)
 
 
 def draw_spectral(G, **kwargs):
-    """Draw the graph G with a spectral 2D layout.
+    """Draw the graph `G` with a spectral 2D layout.
+
+    This is a convenience function equivalent to::
 
-    Using the unnormalized Laplacian, the layout shows possible clusters of
-    nodes which are an approximation of the ratio cut. The positions are the
-    entries of the second and third eigenvectors corresponding to the
-    ascending eigenvalues starting from the second one.
+        nx.draw(G, pos=nx.spectral_layout(G), **kwargs)
+
+    For more information about how node positions are determined, see
+    `~networkx.drawing.layout.spectral_layout`.
 
     Parameters
     ----------
@@ -1253,15 +1309,33 @@ def draw_spectral(G, **kwargs):
         A networkx graph
 
     kwargs : optional keywords
-        See networkx.draw_networkx() for a description of optional keywords,
-        with the exception of the pos parameter which is not used by this
-        function.
+        See `draw_networkx` for a description of optional keywords.
+
+    Notes
+    -----
+    The layout is computed each time this function is called.
+    For repeated drawing it is much more efficient to call
+    `~networkx.drawing.layout.spectral_layout` directly and reuse the result::
+
+        >>> G = nx.complete_graph(5)
+        >>> pos = nx.spectral_layout(G)
+        >>> nx.draw(G, pos=pos)  # Draw the original graph
+        >>> # Draw a subgraph, reusing the same node positions
+        >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
+
+    See Also
+    --------
+    :func:`~networkx.drawing.layout.spectral_layout`
     """
     draw(G, spectral_layout(G), **kwargs)
 
 
 def draw_spring(G, **kwargs):
-    """Draw the graph G with a spring layout.
+    """Draw the graph `G` with a spring layout.
+
+    This is a convenience function equivalent to::
+
+        nx.draw(G, pos=nx.spring_layout(G), **kwargs)
 
     Parameters
     ----------
@@ -1269,34 +1343,76 @@ def draw_spring(G, **kwargs):
         A networkx graph
 
     kwargs : optional keywords
-        See networkx.draw_networkx() for a description of optional keywords,
-        with the exception of the pos parameter which is not used by this
-        function.
+        See `draw_networkx` for a description of optional keywords.
+
+    Notes
+    -----
+    `~networkx.drawing.layout.spring_layout` is also the default layout for
+    `draw`, so this function is equivalent to `draw`.
+
+    The layout is computed each time this function is called.
+    For repeated drawing it is much more efficient to call
+    `~networkx.drawing.layout.spring_layout` directly and reuse the result::
+
+        >>> G = nx.complete_graph(5)
+        >>> pos = nx.spring_layout(G)
+        >>> nx.draw(G, pos=pos)  # Draw the original graph
+        >>> # Draw a subgraph, reusing the same node positions
+        >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
+
+    See Also
+    --------
+    draw
+    :func:`~networkx.drawing.layout.spring_layout`
     """
     draw(G, spring_layout(G), **kwargs)
 
 
-def draw_shell(G, **kwargs):
-    """Draw networkx graph with shell layout.
+def draw_shell(G, nlist=None, **kwargs):
+    """Draw networkx graph `G` with shell layout.
+
+    This is a convenience function equivalent to::
+
+        nx.draw(G, pos=nx.shell_layout(G, nlist=nlist), **kwargs)
 
     Parameters
     ----------
     G : graph
         A networkx graph
 
+    nlist : list of list of nodes, optional
+        A list containing lists of nodes representing the shells.
+        Default is `None`, meaning all nodes are in a single shell.
+        See `~networkx.drawing.layout.shell_layout` for details.
+
     kwargs : optional keywords
-        See networkx.draw_networkx() for a description of optional keywords,
-        with the exception of the pos parameter which is not used by this
-        function.
+        See `draw_networkx` for a description of optional keywords.
+
+    Notes
+    -----
+    The layout is computed each time this function is called.
+    For repeated drawing it is much more efficient to call
+    `~networkx.drawing.layout.shell_layout` directly and reuse the result::
+
+        >>> G = nx.complete_graph(5)
+        >>> pos = nx.spring_layout(G)
+        >>> nx.draw(G, pos=pos)  # Draw the original graph
+        >>> # Draw a subgraph, reusing the same node positions
+        >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
+
+    See Also
+    --------
+    :func:`~networkx.drawing.layout.shell_layout`
     """
-    nlist = kwargs.get("nlist", None)
-    if nlist is not None:
-        del kwargs["nlist"]
     draw(G, shell_layout(G, nlist=nlist), **kwargs)
 
 
 def draw_planar(G, **kwargs):
-    """Draw a planar networkx graph with planar layout.
+    """Draw a planar networkx graph `G` with planar layout.
+
+    This is a convenience function equivalent to::
+
+        nx.draw(G, pos=nx.planar_layout(G), **kwargs)
 
     Parameters
     ----------
@@ -1304,9 +1420,28 @@ def draw_planar(G, **kwargs):
         A planar networkx graph
 
     kwargs : optional keywords
-        See networkx.draw_networkx() for a description of optional keywords,
-        with the exception of the pos parameter which is not used by this
-        function.
+        See `draw_networkx` for a description of optional keywords.
+
+    Raises
+    ------
+    NetworkXException
+        When `G` is not planar
+
+    Notes
+    -----
+    The layout is computed each time this function is called.
+    For repeated drawing it is much more efficient to call
+    `~networkx.drawing.layout.planar_layout` directly and reuse the result::
+
+        >>> G = nx.path_graph(5)
+        >>> pos = nx.planar_layout(G)
+        >>> nx.draw(G, pos=pos)  # Draw the original graph
+        >>> # Draw a subgraph, reusing the same node positions
+        >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
+
+    See Also
+    --------
+    :func:`~networkx.drawing.layout.planar_layout`
     """
     draw(G, planar_layout(G), **kwargs)
 


Code review comments:
diff_hunk: @@ -1192,121 +1191,257 @@ def draw_networkx_edge_labels(
 
 
 def draw_circular(G, **kwargs):
-    """Draw the graph G with a circular layout.
+    """Draw the graph `G` with a circular layout.
+
+    This is a convenience function equivalent to::
+
+        nx.draw(G, pos=nx.circular_layout(G), **kwargs)
 
     Parameters
     ----------
     G : graph
         A networkx graph
 
     kwargs : optional keywords
-        See networkx.draw_networkx() for a description of optional keywords,
-        with the exception of the pos parameter which is not used by this
-        function.
+        See `draw_networkx` for a description of optional keywords.
+
+    Notes
+    -----
+    The layout is computed each time this function is called. For
+    repeated drawing it is much more efficient to call
+    `~networkx.drawing.layout.circular_layout` directly and reuse the result::
+
+        >>> G = nx.complete_graph(5)
+        >>> pos = nx.circular_layout(G)
+        >>> nx.draw(G, pos=pos)  # Draw the original graph
+        >>> # Draw a subgraph, reusing the same node positions
+        >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
+
+    See Also
+    --------
+    :func:`~networkx.drawing.layout.circular_layout`
     """
     draw(G, circular_layout(G), **kwargs)
 
 
 def draw_kamada_kawai(G, **kwargs):
-    """Draw the graph G with a Kamada-Kawai force-directed layout.
+    """Draw the graph `G` with a Kamada-Kawai force-directed layout.
+
+    This is a convenience function equivalent to::
+
+        nx.draw(G, pos=nx.kamada_kawai_layout(G), **kwargs)
 
     Parameters
     ----------
     G : graph
         A networkx graph
 
     kwargs : optional keywords
-        See networkx.draw_networkx() for a description of optional keywords,
-        with the exception of the pos parameter which is not used by this
-        function.
+        See `draw_networkx` for a description of optional keywords.
+
+    Notes
+    -----
+    The layout is computed each time this function is called.
+    For repeated drawing it is much more efficient to call
+    `~networkx.drawing.layout.kamada_kawai_layout` directly and reuse the
+    result::
+
+        >>> G = nx.complete_graph(5)
+        >>> pos = nx.kamada_kawai_layout(G)
+        >>> nx.draw(G, pos=pos)  # Draw the original graph
+        >>> # Draw a subgraph, reusing the same node positions
+        >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
+
+    See Also
+    --------
+    :func:`~networkx.drawing.layout.kamada_kawai_layout`
     """
     draw(G, kamada_kawai_layout(G), **kwargs)
 
 
 def draw_random(G, **kwargs):
-    """Draw the graph G with a random layout.
+    """Draw the graph `G` with a random layout.
+
+    This is a convenience function equivalent to::
+
+        nx.draw(G, pos=nx.random_layout(G), **kwargs)
 
     Parameters
     ----------
     G : graph
         A networkx graph
 
     kwargs : optional keywords
-        See networkx.draw_networkx() for a description of optional keywords,
-        with the exception of the pos parameter which is not used by this
-        function.
+        See `draw_networkx` for a description of optional keywords.
+
+    Notes
+    -----
+    The layout is computed each time this function is called.
+    For repeated drawing it is much more efficient to call
+    `~networkx.drawing.layout.random_layout` directly and reuse the result::
+
+        >>> G = nx.complete_graph(5)
+        >>> pos = nx.random_layout(G)
+        >>> nx.draw(G, pos=pos)  # Draw the original graph
+        >>> # Draw a subgraph, reusing the same node positions
+        >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
+
+    See Also
+    --------
+    :func:`~networkx.drawing.layout.random_layout`
     """
     draw(G, random_layout(G), **kwargs)
 
 
 def draw_spectral(G, **kwargs):
-    """Draw the graph G with a spectral 2D layout.
+    """Draw the graph `G` with a spectral 2D layout.
+
+    This is a convenience function equivalent to::
 
-    Using the unnormalized Laplacian, the layout shows possible clusters of
-    nodes which are an approximation of the ratio cut. The positions are the
-    entries of the second and third eigenvectors corresponding to the
-    ascending eigenvalues starting from the second one.
+        nx.draw(G, pos=nx.spectral_layout(G), **kwargs)
+
+    For more information about how node positions are determined, see
+    `~networkx.drawing.layout.spectral_layout`.
 
     Parameters
     ----------
     G : graph
         A networkx graph
 
     kwargs : optional keywords
-        See networkx.draw_networkx() for a description of optional keywords,
-        with the exception of the pos parameter which is not used by this
-        function.
+        See `draw_networkx` for a description of optional keywords.
+
+    Notes
+    -----
+    The layout is computed each time this function is called.
+    For repeated drawing it is much more efficient to call
+    `~networkx.drawing.layout.spectral_layout` directly and reuse the result::
+
+        >>> G = nx.complete_graph(5)
+        >>> pos = nx.spectral_layout(G)
+        >>> nx.draw(G, pos=pos)  # Draw the original graph
+        >>> # Draw a subgraph, reusing the same node positions
+        >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
+
+    See Also
+    --------
+    :func:`~networkx.drawing.layout.spectral_layout`
     """
     draw(G, spectral_layout(G), **kwargs)
 
 
 def draw_spring(G, **kwargs):
-    """Draw the graph G with a spring layout.
+    """Draw the graph `G` with a spring layout.
+
+    This is a convenience function equivalent to::
+
+        nx.draw(G, pos=nx.spring_layout(G), **kwargs)
 
     Parameters
     ----------
     G : graph
         A networkx graph
 
     kwargs : optional keywords
-        See networkx.draw_networkx() for a description of optional keywords,
-        with the exception of the pos parameter which is not used by this
-        function.
+        See `draw_networkx` for a description of optional keywords.
+
+    Notes
+    -----
+    `~networkx.drawing.layout.spring_layout` is also the default layout for
+    `draw`, so this function is equivalent to `draw`.
+
+    The layout is computed each time this function is called.
+    For repeated drawing it is much more efficient to call
+    `~networkx.drawing.layout.spring_layout` directly and reuse the result::
+
+        >>> G = nx.complete_graph(5)
+        >>> pos = nx.spring_layout(G)
+        >>> nx.draw(G, pos=pos)  # Draw the original graph
+        >>> # Draw a subgraph, reusing the same node positions
+        >>> nx.draw(G.subgraph([0, 1, 2]), pos=pos, node_color="red")
+
+    See Also
+    --------
+    draw
+    :func:`~networkx.drawing.layout.spring_layout`
     """
     draw(G, spring_layout(G), **kwargs)
 
 
-def draw_shell(G, **kwargs):
-    """Draw networkx graph with shell layout.
+def draw_shell(G, nlist=None, **kwargs):
+    """Draw networkx graph `G` with shell layout.
+
+    This is a convenience function equivalent to::
+
+        nx.draw(G, pos=nx.shell_layout(G, nlist=nlist), **kwargs)
 
     Parameters
     ----------
     G : graph
         A networkx graph
 
+    nlist : list of list of nodes, optional
+        A list containing lists of nodes representing the shells.
+        Default is `None`, meaning all nodes are in a single shell.
+        See `~networkx.drawing.layout.shell_layout` for details.
+
     kwargs : optional keywords
-        See networkx.draw_networkx() for a description of optional keywords,
-        with the exception of the pos parameter which is not used by this
-        function.
+        See `draw_networkx` for a description of optional keywords.
+
+    Notes
+    -----
+    The layout is computed each time this function is called.
+    For repeated drawing it is much more efficient to call
+    `~networkx.drawing.layout.shell_layout` directly and reuse the result::
+
+        >>> G = nx.complete_graph(5)
+        >>> pos = nx.spring_layout(G)
path: networkx/drawing/nx_pylab.py
text: @user1:

```suggestion
        >>> pos = nx.shell_layout(G)
```