prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Add a function to easily display a matotlib histogram in rio-insp
  See #455 for background 
  
  Might be useful to surface this both in `rio insp` and as `rasterio.show_histogram()`.
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `rasterio/tool.py`
  Content:
  ```
  1 
  2 import code
  3 import collections
  4 import logging
  5 
  6 try:
  7     import matplotlib.pyplot as plt
  8 except ImportError:
  9     plt = None
  10 
  11 import numpy
  12 
  13 import rasterio
  14 
  15 
  16 logger = logging.getLogger('rasterio')
  17 
  18 Stats = collections.namedtuple('Stats', ['min', 'max', 'mean'])
  19 
  20 # Collect dictionary of functions for use in the interpreter in main()
  21 funcs = locals()
  22 
  23 
  24 def show(source, cmap='gray'):
  25     """Show a raster using matplotlib.
  26 
  27     The raster may be either an ndarray or a (dataset, bidx)
  28     tuple.
  29     """
  30     if isinstance(source, tuple):
  31         arr = source[0].read(source[1])
  32     else:
  33         arr = source
  34     if plt is not None:
  35         plt.imshow(arr, cmap=cmap)
  36         plt.show()
  37     else:
  38         raise ImportError("matplotlib could not be imported")
  39 
  40 
  41 def stats(source):
  42     """Return a tuple with raster min, max, and mean.
  43     """
  44     if isinstance(source, tuple):
  45         arr = source[0].read(source[1])
  46     else:
  47         arr = source
  48     return Stats(numpy.min(arr), numpy.max(arr), numpy.mean(arr))
  49 
  50 
  51 def main(banner, dataset, alt_interpreter=None):
  52     """ Main entry point for use with python interpreter """
  53     local = dict(funcs, src=dataset, np=numpy, rio=rasterio, plt=plt)
  54     if not alt_interpreter:
  55         code.interact(banner, local=local)
  56     elif alt_interpreter == 'ipython':
  57         import IPython
  58         IPython.InteractiveShell.banner1 = banner
  59         IPython.start_ipython(argv=[], user_ns=local)
  60     else:
  61         raise ValueError("Unsupported interpreter '%s'" % alt_interpreter)
  62 
  63     return 0
  64
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/rasterio/tool.py b/rasterio/tool.py
  --- a/rasterio/tool.py
  +++ b/rasterio/tool.py
  @@ -11,6 +11,7 @@
   import numpy
   
   import rasterio
  +from rasterio.five import zip_longest
   
   
   logger = logging.getLogger('rasterio')
  @@ -48,6 +49,76 @@
       return Stats(numpy.min(arr), numpy.max(arr), numpy.mean(arr))
   
   
  +def show_hist(source, bins=10, masked=True, title='Histogram'):
  +
  +    """
  +    Easily display a histogram with matplotlib.
  +
  +    Parameters
  +    ----------
  +    bins : int, optional
  +        Compute histogram across N bins.
  +    data : np.array or rasterio.Band or tuple(dataset, bidx)
  +        Input data to display.  The first three arrays in multi-dimensional
  +        arrays are plotted as red, green, and blue.
  +    masked : bool, optional
  +        When working with a `rasterio.Band()` object, specifies if the data
  +        should be masked on read.
  +    title : str, optional
  +        Title for the figure.
  +    """
  +
  +    if plt is None:
  +        raise ImportError("Could not import matplotlib")
  +
  +    if isinstance(source, (tuple, rasterio.Band)):
  +        arr = source[0].read(source[1], masked=masked)
  +    else:
  +        arr = source
  +
  +    # The histogram is computed individually for each 'band' in the array
  +    # so we need the overall min/max to constrain the plot
  +    rng = arr.min(), arr.max()
  +
  +    if len(arr.shape) is 2:
  +        arr = [arr]
  +        colors = ['gold']
  +    else:
  +        colors = ('red', 'green', 'blue', 'violet', 'gold', 'saddlebrown')
  +
  +    # If a rasterio.Band() is given make sure the proper index is displayed
  +    # in the legend.
  +    if isinstance(source, (tuple, rasterio.Band)):
  +        labels = [str(source[1])]
  +    else:
  +        labels = (str(i + 1) for i in range(len(arr)))
  +
  +    # This loop should add a single plot each band in the input array,
  +    # regardless of if the number of bands exceeds the number of colors.
  +    # The colors slicing ensures that the number of iterations always
  +    # matches the number of bands.
  +    # The goal is to provide a curated set of colors for working with
  +    # smaller datasets and let matplotlib define additional colors when
  +    # working with larger datasets.
  +    for bnd, color, label in zip_longest(arr, colors[:len(arr)], labels):
  +
  +        plt.hist(
  +            bnd.flatten(),
  +            bins=bins,
  +            alpha=0.5,
  +            color=color,
  +            label=label,
  +            range=rng
  +        )
  +
  +    plt.legend(loc="upper right")
  +    plt.title(title, fontweight='bold')
  +    plt.grid(True)
  +    plt.xlabel('DN')
  +    plt.ylabel('Frequency')
  +    plt.show()
  +
  +
   def main(banner, dataset, alt_interpreter=None):
       """ Main entry point for use with python interpreter """
       local = dict(funcs, src=dataset, np=numpy, rio=rasterio, plt=plt)
  
verification_info: {"golden_diff": "diff --git a/rasterio/tool.py b/rasterio/tool.py\n--- a/rasterio/tool.py\n+++ b/rasterio/tool.py\n@@ -11,6 +11,7 @@\n import numpy\n \n import rasterio\n+from rasterio.five import zip_longest\n \n \n logger = logging.getLogger('rasterio')\n@@ -48,6 +49,76 @@\n     return Stats(numpy.min(arr), numpy.max(arr), numpy.mean(arr))\n \n \n+def show_hist(source, bins=10, masked=True, title='Histogram'):\n+\n+    \"\"\"\n+    Easily display a histogram with matplotlib.\n+\n+    Parameters\n+    ----------\n+    bins : int, optional\n+        Compute histogram across N bins.\n+    data : np.array or rasterio.Band or tuple(dataset, bidx)\n+        Input data to display.  The first three arrays in multi-dimensional\n+        arrays are plotted as red, green, and blue.\n+    masked : bool, optional\n+        When working with a `rasterio.Band()` object, specifies if the data\n+        should be masked on read.\n+    title : str, optional\n+        Title for the figure.\n+    \"\"\"\n+\n+    if plt is None:\n+        raise ImportError(\"Could not import matplotlib\")\n+\n+    if isinstance(source, (tuple, rasterio.Band)):\n+        arr = source[0].read(source[1], masked=masked)\n+    else:\n+        arr = source\n+\n+    # The histogram is computed individually for each 'band' in the array\n+    # so we need the overall min/max to constrain the plot\n+    rng = arr.min(), arr.max()\n+\n+    if len(arr.shape) is 2:\n+        arr = [arr]\n+        colors = ['gold']\n+    else:\n+        colors = ('red', 'green', 'blue', 'violet', 'gold', 'saddlebrown')\n+\n+    # If a rasterio.Band() is given make sure the proper index is displayed\n+    # in the legend.\n+    if isinstance(source, (tuple, rasterio.Band)):\n+        labels = [str(source[1])]\n+    else:\n+        labels = (str(i + 1) for i in range(len(arr)))\n+\n+    # This loop should add a single plot each band in the input array,\n+    # regardless of if the number of bands exceeds the number of colors.\n+    # The colors slicing ensures that the number of iterations always\n+    # matches the number of bands.\n+    # The goal is to provide a curated set of colors for working with\n+    # smaller datasets and let matplotlib define additional colors when\n+    # working with larger datasets.\n+    for bnd, color, label in zip_longest(arr, colors[:len(arr)], labels):\n+\n+        plt.hist(\n+            bnd.flatten(),\n+            bins=bins,\n+            alpha=0.5,\n+            color=color,\n+            label=label,\n+            range=rng\n+        )\n+\n+    plt.legend(loc=\"upper right\")\n+    plt.title(title, fontweight='bold')\n+    plt.grid(True)\n+    plt.xlabel('DN')\n+    plt.ylabel('Frequency')\n+    plt.show()\n+\n+\n def main(banner, dataset, alt_interpreter=None):\n     \"\"\" Main entry point for use with python interpreter \"\"\"\n     local = dict(funcs, src=dataset, np=numpy, rio=rasterio, plt=plt)\n", "issue": "Add a function to easily display a matotlib histogram in rio-insp\nSee #455 for background \n\nMight be useful to surface this both in `rio insp` and as `rasterio.show_histogram()`.\n\n", "before_files": [{"content": "\nimport code\nimport collections\nimport logging\n\ntry:\n    import matplotlib.pyplot as plt\nexcept ImportError:\n    plt = None\n\nimport numpy\n\nimport rasterio\n\n\nlogger = logging.getLogger('rasterio')\n\nStats = collections.namedtuple('Stats', ['min', 'max', 'mean'])\n\n# Collect dictionary of functions for use in the interpreter in main()\nfuncs = locals()\n\n\ndef show(source, cmap='gray'):\n    \"\"\"Show a raster using matplotlib.\n\n    The raster may be either an ndarray or a (dataset, bidx)\n    tuple.\n    \"\"\"\n    if isinstance(source, tuple):\n        arr = source[0].read(source[1])\n    else:\n        arr = source\n    if plt is not None:\n        plt.imshow(arr, cmap=cmap)\n        plt.show()\n    else:\n        raise ImportError(\"matplotlib could not be imported\")\n\n\ndef stats(source):\n    \"\"\"Return a tuple with raster min, max, and mean.\n    \"\"\"\n    if isinstance(source, tuple):\n        arr = source[0].read(source[1])\n    else:\n        arr = source\n    return Stats(numpy.min(arr), numpy.max(arr), numpy.mean(arr))\n\n\ndef main(banner, dataset, alt_interpreter=None):\n    \"\"\" Main entry point for use with python interpreter \"\"\"\n    local = dict(funcs, src=dataset, np=numpy, rio=rasterio, plt=plt)\n    if not alt_interpreter:\n        code.interact(banner, local=local)\n    elif alt_interpreter == 'ipython':\n        import IPython\n        IPython.InteractiveShell.banner1 = banner\n        IPython.start_ipython(argv=[], user_ns=local)\n    else:\n        raise ValueError(\"Unsupported interpreter '%s'\" % alt_interpreter)\n\n    return 0\n", "path": "rasterio/tool.py"}], "after_files": [{"content": "\nimport code\nimport collections\nimport logging\n\ntry:\n    import matplotlib.pyplot as plt\nexcept ImportError:\n    plt = None\n\nimport numpy\n\nimport rasterio\nfrom rasterio.five import zip_longest\n\n\nlogger = logging.getLogger('rasterio')\n\nStats = collections.namedtuple('Stats', ['min', 'max', 'mean'])\n\n# Collect dictionary of functions for use in the interpreter in main()\nfuncs = locals()\n\n\ndef show(source, cmap='gray'):\n    \"\"\"Show a raster using matplotlib.\n\n    The raster may be either an ndarray or a (dataset, bidx)\n    tuple.\n    \"\"\"\n    if isinstance(source, tuple):\n        arr = source[0].read(source[1])\n    else:\n        arr = source\n    if plt is not None:\n        plt.imshow(arr, cmap=cmap)\n        plt.show()\n    else:\n        raise ImportError(\"matplotlib could not be imported\")\n\n\ndef stats(source):\n    \"\"\"Return a tuple with raster min, max, and mean.\n    \"\"\"\n    if isinstance(source, tuple):\n        arr = source[0].read(source[1])\n    else:\n        arr = source\n    return Stats(numpy.min(arr), numpy.max(arr), numpy.mean(arr))\n\n\ndef show_hist(source, bins=10, masked=True, title='Histogram'):\n\n    \"\"\"\n    Easily display a histogram with matplotlib.\n\n    Parameters\n    ----------\n    bins : int, optional\n        Compute histogram across N bins.\n    data : np.array or rasterio.Band or tuple(dataset, bidx)\n        Input data to display.  The first three arrays in multi-dimensional\n        arrays are plotted as red, green, and blue.\n    masked : bool, optional\n        When working with a `rasterio.Band()` object, specifies if the data\n        should be masked on read.\n    title : str, optional\n        Title for the figure.\n    \"\"\"\n\n    if plt is None:\n        raise ImportError(\"Could not import matplotlib\")\n\n    if isinstance(source, (tuple, rasterio.Band)):\n        arr = source[0].read(source[1], masked=masked)\n    else:\n        arr = source\n\n    # The histogram is computed individually for each 'band' in the array\n    # so we need the overall min/max to constrain the plot\n    rng = arr.min(), arr.max()\n\n    if len(arr.shape) is 2:\n        arr = [arr]\n        colors = ['gold']\n    else:\n        colors = ('red', 'green', 'blue', 'violet', 'gold', 'saddlebrown')\n\n    # If a rasterio.Band() is given make sure the proper index is displayed\n    # in the legend.\n    if isinstance(source, (tuple, rasterio.Band)):\n        labels = [str(source[1])]\n    else:\n        labels = (str(i + 1) for i in range(len(arr)))\n\n    # This loop should add a single plot each band in the input array,\n    # regardless of if the number of bands exceeds the number of colors.\n    # The colors slicing ensures that the number of iterations always\n    # matches the number of bands.\n    # The goal is to provide a curated set of colors for working with\n    # smaller datasets and let matplotlib define additional colors when\n    # working with larger datasets.\n    for bnd, color, label in zip_longest(arr, colors[:len(arr)], labels):\n\n        plt.hist(\n            bnd.flatten(),\n            bins=bins,\n            alpha=0.5,\n            color=color,\n            label=label,\n            range=rng\n        )\n\n    plt.legend(loc=\"upper right\")\n    plt.title(title, fontweight='bold')\n    plt.grid(True)\n    plt.xlabel('DN')\n    plt.ylabel('Frequency')\n    plt.show()\n\n\ndef main(banner, dataset, alt_interpreter=None):\n    \"\"\" Main entry point for use with python interpreter \"\"\"\n    local = dict(funcs, src=dataset, np=numpy, rio=rasterio, plt=plt)\n    if not alt_interpreter:\n        code.interact(banner, local=local)\n    elif alt_interpreter == 'ipython':\n        import IPython\n        IPython.InteractiveShell.banner1 = banner\n        IPython.start_ipython(argv=[], user_ns=local)\n    else:\n        raise ValueError(\"Unsupported interpreter '%s'\" % alt_interpreter)\n\n    return 0\n", "path": "rasterio/tool.py"}]}