Search code examples
pythonpandasplotplotly

Plot Bar Graph with different Parametes in X Axis


I have a DataFrame like below. It has Actual and Predicted columns. I want to compare Actual Vs Predicted in Bar plot in one on one. I have confidence value for Predicted column and default for Actual confidence is 1. So, I want to keep Each row in single bar group Actual and Predicted value will be X axis and corresponding Confidence score as y value.

I am unable to get the expected plot because X axis values are not aligned or grouped to same value in each row.

  Actual Predicted  Confidence
0      A         A        0.90
1      B         C        0.30
2      C         C        0.60
3      D         D        0.75

Expected Bar plot.

enter image description here

Any hint would be appreciable. Please let me know if further details required.

What I have tried so far.

df_actual = pd.DataFrame()
df_actual['Key']= df['Actual'].copy()
df_actual['Confidence'] = 1
df_actual['Identifier'] = 'Actual'

df_predicted=pd.DataFrame()
df_predicted = df[['Predicted', 'Confidence']]
df_predicted = df_predicted.rename(columns={'Predicted': 'Key'})
df_predicted['Identifier'] = 'Predicted'
df_combined = pd.concat([df_actual,df_predicted], ignore_index=True)
df_combined

fig = px.bar(df_combined, x="Key", y="Confidence", color='Identifier',
          barmode='group',  height=400)
fig.show()

enter image description here


Solution

  • I have found that adjusting the data first makes it easier to get the plot I want. I have used Seaborn, hope that is ok. Please see if this code works for you. I have considered that the df mentioned above is already available. I created df2 so that it aligns to what you had shown in the expected figure. Also, I used index as the X-axis column so that the order is maintained... Some adjustments to ensure xtick names align and the legend is outside as you wanted it.

    Code

    vals= []
    conf = []
    for x, y, z in zip(df.Actual, df.Predicted, df.Confidence):
        vals += [x, y]
        conf += [1, z]
    df2 = pd.DataFrame({'Values': vals, 'Confidence':conf}).reset_index()
    ax=sns.barplot(data = df2, x='index', y='Confidence', hue='Values',dodge=False)
    ax.set_xticklabels(['Actual', 'Predicted']*4)
    plt.legend(bbox_to_anchor=(1.0,1))
    plt.show()
    
    

    Plot

    enter image description here

    Update - grouping Actual and Predicted bars

    Hi @Mohammed - As we have already used up hue, I don't think there is a way to do this easily with Seaborn. You would need to use matplotlib and adjust the bar position, xtick positions, etc. Below is the code that will do this. You can change SET1 to another color map to change colors. I have also added a black outline as the same colored bars were blending into one another. Further, I had to rotate the xlables, as they were on top of one another. You can change it as per your requirements. Hope this helps...

    vals = df[['Actual','Predicted']].melt(value_name='texts')['texts']
    conf = [1]*4 + list(df.Confidence)
    ident = ['Actual', 'Predicted']*4
    df2 = pd.DataFrame({'Values': vals, 'Confidence':conf, 'Identifier':ident}).reset_index()
    
    uvals, uind = np.unique(df2["Values"], return_inverse=1)
    cmap = plt.cm.get_cmap("Set1")
    
    fig, ax=plt.subplots()
    l = len(df2)
    pos = np.arange(0,l) % (l//2) + (np.arange(0,l)//(l//2)-1)*0.4
    ax.bar(pos, df2["Confidence"], width=0.4, align="edge", ec="k",color=cmap(uind)  )  
    
    handles=[plt.Rectangle((0,0),1,1, color=cmap(i), ec="k") for i in range(len(uvals))]
    ax.legend(handles=handles, labels=list(uvals), prop ={'size':10}, loc=9, ncol=8) 
    
    pos=pos+0.2
    pos.sort()
    ax.set_xticks(pos)
    
    ax.set_xticklabels(df2["Identifier"][:l], rotation=45,ha='right', rotation_mode="anchor")
    ax.set_ylim(0, 1.2)
    plt.show()
    

    Output plot

    enter image description here