Hey guys! Let's dive into how to handle legends in Bokeh plots, specifically how to move that legend outside the plot area. It’s a common task when you want to create cleaner, more readable visualizations, and Bokeh provides some pretty neat ways to achieve this. So, buckle up, and let's get started!

    Why Put the Legend Outside?

    Before we get our hands dirty with code, let's quickly touch on why you might want to move the legend outside the plot area.

    • Readability: When your plot has a lot of data series, the legend can clutter the plot area, making it hard to see the actual data.
    • Aesthetics: Sometimes, a legend inside the plot just doesn't look good. Moving it outside can give your plot a cleaner, more professional look.
    • Space: If you have annotations or other visual elements in your plot, the legend might overlap with them. Moving it outside frees up space inside the plot.

    Basic Bokeh Plot

    First, let’s create a basic Bokeh plot. This will give us something to work with and show how the default legend placement looks.

    from bokeh.plotting import figure, show
    
    # Sample data
    x = [1, 2, 3, 4, 5]
    y1 = [6, 7, 2, 4, 5]
    y2 = [2, 3, 4, 5, 6]
    y3 = [4, 5, 5, 7, 2]
    
    # Create a figure
    p = figure(title="Basic Plot", x_axis_label="X", y_axis_label="Y")
    
    # Add renderers
    p.line(x, y1, legend_label="Line 1", line_color="blue", line_width=2)
    p.circle(x, y2, legend_label="Circle 1", fill_color="red", size=12)
    p.square(x, y3, legend_label="Square 1", fill_color="green", size=10)
    
    # Show the plot
    show(p)
    

    In this example, we're creating a simple plot with three different renderers: a line, circles, and squares. Each renderer has a legend_label assigned to it. By default, Bokeh places the legend inside the plot area. Now, let’s move that legend outside.

    Moving the Legend Outside

    Method 1: Using location and orientation

    One of the easiest ways to move the legend is by adjusting its location and orientation. The location attribute can take values like 'top_left', 'top_center', 'top_right', 'center_left', 'center', 'center_right', 'bottom_left', 'bottom_center', and 'bottom_right'. Let's try moving the legend to the top right corner outside the plot.

    from bokeh.plotting import figure, show
    from bokeh.models import Legend
    
    # Sample data
    x = [1, 2, 3, 4, 5]
    y1 = [6, 7, 2, 4, 5]
    y2 = [2, 3, 4, 5, 6]
    y3 = [4, 5, 5, 7, 2]
    
    # Create a figure
    p = figure(title="Plot with Legend Outside", x_axis_label="X", y_axis_label="Y")
    
    # Add renderers
    line = p.line(x, y1, legend_label="Line 1", line_color="blue", line_width=2)
    circle = p.circle(x, y2, legend_label="Circle 1", fill_color="red", size=12)
    square = p.square(x, y3, legend_label="Square 1", fill_color="green", size=10)
    
    # Move legend outside the plot
    p.legend.location = "top_right"
    
    # Show the plot
    show(p)
    

    This will move the legend to the top right corner, but you might notice it’s still inside the plot area. To move it completely outside, we need to use a different approach.

    Method 2: Using Legend and Layout

    The most robust way to place the legend outside the plot area is by creating a Legend object and adding it to the layout separately. This gives you more control over its placement.

    from bokeh.plotting import figure, show
    from bokeh.models import Legend
    from bokeh.layouts import column, row
    
    # Sample data
    x = [1, 2, 3, 4, 5]
    y1 = [6, 7, 2, 4, 5]
    y2 = [2, 3, 4, 5, 6]
    y3 = [4, 5, 5, 7, 2]
    
    # Create a figure
    p = figure(title="Plot with Legend Outside", x_axis_label="X", y_axis_label="Y",
               width=600, height=400) # Adjust width and height as needed
    
    # Add renderers
    line = p.line(x, y1, legend_label="Line 1", line_color="blue", line_width=2)
    circle = p.circle(x, y2, legend_label="Circle 1", fill_color="red", size=12)
    square = p.square(x, y3, legend_label="Square 1", fill_color="green", size=10)
    
    # Create a legend
    legend = Legend(items=[
        ("Line 1", [line]),
        ("Circle 1", [circle]),
        ("Square 1", [square]),
    ], location=(0, 0))
    
    # Remove the default legend
    p.legend.visible = False
    
    # Add the legend to the plot
    p.add_layout(legend, 'right')
    
    # Show the plot
    show(p)
    

    Let’s break down what’s happening here:

    1. Import Necessary Modules: We import Legend and column (or row) from bokeh.models and bokeh.layouts.
    2. Create Renderers: We create our plot and add the renderers as before.
    3. Create a Legend Object: We create a Legend object, specifying the items to be included. The items argument is a list of tuples, where each tuple contains the legend label and a list of renderers associated with that label.
    4. Hide the Default Legend: We set p.legend.visible = False to hide the default legend that Bokeh automatically creates.
    5. Add the Legend to the Layout: We use p.add_layout(legend, 'right') to add the legend to the right side of the plot. You can also use 'left', 'above', or 'below' to place it in other locations.

    This approach gives you much more control over the legend's placement. You can adjust the width and height of the plot to make space for the legend.

    Method 3: Using Column or Row Layouts

    Another flexible way to position the legend outside the plot is by using Bokeh's layout tools, specifically column and row. This method involves creating the legend as a separate plot element and then arranging it alongside the main plot.

    from bokeh.plotting import figure, show
    from bokeh.models import Legend
    from bokeh.layouts import column, row
    
    # Sample data
    x = [1, 2, 3, 4, 5]
    y1 = [6, 7, 2, 4, 5]
    y2 = [2, 3, 4, 5, 6]
    y3 = [4, 5, 5, 7, 2]
    
    # Create a figure
    p = figure(title="Plot with Legend Outside", x_axis_label="X", y_axis_label="Y",
               width=600, height=400)
    
    # Add renderers
    line = p.line(x, y1, legend_label="Line 1", line_color="blue", line_width=2)
    circle = p.circle(x, y2, legend_label="Circle 1", fill_color="red", size=12)
    square = p.square(x, y3, legend_label="Square 1", fill_color="green", size=10)
    
    # Create a legend
    legend = Legend(items=[
        ("Line 1", [line]),
        ("Circle 1", [circle]),
        ("Square 1", [square]),
    ], location=(0, 0))
    
    # Remove the default legend
    p.legend.visible = False
    
    # Create a layout with the plot and legend
    layout = row(p, legend)
    
    # Show the plot
    show(layout)
    

    In this example, we use row to place the plot and the legend side by side. If you want to place the legend below or above the plot, you can use column instead:

    layout = column(p, legend)
    

    This method provides a clean separation between the plot and the legend, making it easier to control their relative positions and sizes.

    Customizing the Legend

    No matter which method you choose to move the legend outside the plot, you can further customize its appearance. Here are a few common customizations:

    • Background Color: You can change the background color of the legend to make it stand out or blend in with the surrounding elements.
    • Border: You can add a border around the legend to visually separate it from the plot.
    • Text Style: You can change the font, size, and color of the legend text.

    Here’s an example of customizing the legend:

    from bokeh.plotting import figure, show
    from bokeh.models import Legend
    from bokeh.layouts import column, row
    
    # Sample data
    x = [1, 2, 3, 4, 5]
    y1 = [6, 7, 2, 4, 5]
    y2 = [2, 3, 4, 5, 6]
    y3 = [4, 5, 5, 7, 2]
    
    # Create a figure
    p = figure(title="Plot with Customized Legend", x_axis_label="X", y_axis_label="Y",
               width=600, height=400)
    
    # Add renderers
    line = p.line(x, y1, legend_label="Line 1", line_color="blue", line_width=2)
    circle = p.circle(x, y2, legend_label="Circle 1", fill_color="red", size=12)
    square = p.square(x, y3, legend_label="Square 1", fill_color="green", size=10)
    
    # Create a legend
    legend = Legend(items=[
        ("Line 1", [line]),
        ("Circle 1", [circle]),
        ("Square 1", [square]),
    ], location=(0, 0),
       background_fill_color="lightgrey",
       border_line_color="black",
       border_line_width=2,
       label_text_font="times",
       label_text_font_size="12pt",
       label_text_color="navy")
    
    # Remove the default legend
    p.legend.visible = False
    
    # Add the legend to the plot
    layout = row(p, legend)
    
    # Show the plot
    show(layout)
    

    In this example, we’re customizing the legend by setting the background color, border, font, font size, and text color. Feel free to play around with these settings to get the look you want.

    Adjusting Plot Size

    When moving the legend outside the plot, you might need to adjust the plot size to ensure everything fits nicely. You can do this by setting the width and height attributes of the figure object.

    p = figure(title="Plot with Legend Outside", x_axis_label="X", y_axis_label="Y",
               width=800, height=400)
    

    Experiment with different values to find the best fit for your plot and legend.

    Conclusion

    Alright, folks! That’s how you move a legend outside a Bokeh plot in Python. We covered a few different methods, from simple location adjustments to using Legend objects and layout tools. Each method has its advantages, so choose the one that best fits your needs. Remember to customize the legend and adjust the plot size to create a visually appealing and informative visualization. Happy plotting!