Search code examples
pythonpandasmatplotlib

How to plot a line on the second axis over a HORIZONTAL (not VERTICAL) bar chart with Matplotlib?


I know how to plot a line on the second axis over a VERTICAL bar chart.

Now I want the bars HORIZONTAL and the line top-to-bottom, just like the whole chart rotates 90°.

If I simply replace bar with barh, the line is still left-to-right...

Can I do this with Matplotlib?

Here is a sample for VERTICAL bar chart:

import matplotlib.pyplot as plt
import pandas as pd

df = pd.DataFrame(
    {
        "A": [2, 4, 8],
        "B": [3, 5, 7],
        "C": [300, 100, 200],
    },
    index=["a", "b", "c"],
)
ax0 = df.plot(y=df.columns[:2], kind="bar", figsize=(10.24, 5.12))
ax1 = df["C"].plot(
    kind="line",
    secondary_y=True,
    color="g",
    marker=".",
)
plt.tight_layout()
plt.show()

Let me stress: I do see those questions related to VERTICAL bar charts. Now I'm asking about HORIZONTAL bar charts.

So this is not a duplicate question.


Solution

  • Pandas plotting doesn't readily support a secondary x axis. Instead, you can directly plot via matplotlib.

    (Note that df.plot(...) plots via pandas. Pandas plotting is a pandas specific interface towards matplotlib, and only supports a subset of matplotlib's functionality.)

    import matplotlib.pyplot as plt
    import pandas as pd
    
    df = pd.DataFrame(
        {
            "A": [2, 4, 8],
            "B": [3, 5, 7],
            "C": [300, 100, 200],
        },
        index=["a", "b", "c"],
    )
    ax0 = df.plot(y=df.columns[:2], kind="barh", figsize=(10.24, 5.12))
    ax1 = ax0.twiny()
    ax1.plot(df["C"],
             df.index,
             color="g",
             marker=".",
             )
    plt.tight_layout()
    plt.show()
    

    lineplot over pandas barh plot

    PS: To exchange the top and bottom axes, you can use:

    ax0 = df.plot(y=df.columns[:2], kind="barh", figsize=(10.24, 5.12))
    ax1 = ax0.twiny()
    ax1.plot(df["C"],
             df.index,
             color="g",
             marker=".",
             )
    # change the direction of the y-axis
    ax0.invert_yaxis()
    # set the x-axis for ax0 at the top
    ax0.tick_params(top=True, bottom=False, labeltop=True, labelbottom=False) # for the ticks and tick labels
    ax0.xaxis.set_label_position('top') # for the axis label
    # set the x-axis for ax1 at the bottom
    ax1.tick_params(top=False, bottom=True, labeltop=False, labelbottom=True)
    ax1.xaxis.set_label_position('bottom')
    

    exchanging top and bottom axes