Search code examples
pythonpandasmatplotlibaxis-labelsxticks

Set custom xtick labels with pandas plot when using secondary_y


I want to show the x labels rotated 45 degrees. I tired the following code, but it still shows the x labels with 0 rotation.

How can the labels be rotated?

df_final = pd.DataFrame({'Count':[8601,188497,808,7081,684,15601,75,12325],
                         'Average': [0.128,0.131,0.144,.184,.134,.152,0.139,0.127]})
width = 0.8
df_final['Count'].plot(kind='bar', width = width)
df_final['Average'].plot(secondary_y=True)
ax = plt.gca()
ax.set_xticklabels(['One', 'Two', 'Three', 'Four', 'Five','Six','Seven','Eight'],rotation = 45)
plt.show()

Solution

    • pandas.DataFrame.plot returns matplotlib.axes.Axes. As such, assign the Axes of the first plot to a variable, ax.
      • ax = plt.gca() is not required, but would go directly before df_final['Average'].plot(...) if used.
    • The settings of the second plot take precedence, therefore the rotation should be set with rot=45 in the second plot call, or in set_xticklabels.
    • Also see Add label values to bar chart and line chart for adding labels.
    • Tested in python 3.11, pandas 1.5.3, matplotlib 3.7.0

    Sample Data and Setup

    import pandas as pd
    
    df_final = pd.DataFrame({'Count': [8601, 188497, 808, 7081, 684, 15601, 75, 12325],
                             'Average': [0.128, 0.131, 0.144, 0.184, 0.134, 0.152, 0.139, 0.127]})
    
    width = 0.8
    
    labels = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight']
    

    Option 1

    # assign the plot to ax
    ax = df_final['Count'].plot(kind='bar', width=width, figsize=(9, 6), legend=True)
    
    # place the secondary plot on ax
    df_final['Average'].plot(secondary_y=True, ax=ax, color='r', legend=True)  # optionally add rot=45 here
    
    # set the custom xtick labels and rotate
    _ = ax.set_xticklabels(labels, rotation=45)  # remove rotation=45 if using rot=45 in the previous line
    

    Option 2

    # set the index values to the labels, providing the order corresponds to the DataFrame
    df_final.index = labels
    
    # initial plot
    ax = df_final['Count'].plot(kind='bar', width=width, figsize=(9, 6), legend=True)
    
    # secondary plot and specify rotation
    df_final['Average'].plot(secondary_y=True, ax=ax, rot=45, color='r', legend=True)
    

    Option 3

    • Create the DataFrame with a custom index using the index parameter, and then plot like Option 2 without df_final.index = labels.
    df_final = pd.DataFrame({'Count': [8601, 188497, 808, 7081, 684, 15601, 75, 12325],
                             'Average': [0.128, 0.131, 0.144, 0.184, 0.134, 0.152, 0.139, 0.127]},
                            index=labels)
    

    enter image description here