Home > Net >  Legend in matplotlib shows first entry of a list only
Legend in matplotlib shows first entry of a list only

Time:11-19

I'm trying to display a custom legend for a bar graph, but it is only displaying the first legend in the legend list. How can I display all the values in the legend?

df.time_to_travel_grouping.value_counts().plot(kind="bar", 
                                               color = ["b","tab:green","tab:red","c","m","y","tab:blue","tab:orange"],
                                               xlabel="TTT", ylabel="Total Counts", 
                                               title="Fig4: Total Counts by Time to Travel Category (TTT)", figsize=(20,15))
plt.legend(["a","b","c","d","e","f","g","h"])
plt.subplots_adjust(bottom=0.15)
plt.subplots_adjust(left=0.15)

enter image description here

CodePudding user response:

Let's get the patches handles from the axes using ax.get_legend_handles_labels:

s = pd.Series(np.arange(100,50,-5), index=[*'abcdefghij'])
ax = s.plot(kind="bar", 
           color = ["b","tab:green","tab:red","c","m","y","tab:blue","tab:orange"],
           xlabel="TTT", ylabel="Total Counts", 
           title="Fig4: Total Counts by Time to Travel Category (TTT)", figsize=(20,15))


patches, _ = ax.get_legend_handles_labels()
labels = [*'abcdefghij']
ax.legend(*patches, labels, loc='best')

plt.subplots_adjust(bottom=0.15)
plt.subplots_adjust(left=0.15)

Output:

enter image description here

CodePudding user response:

To create an automatic legend, matplotlib stores labels for graphical elements. In the case of this bar plot, the complete 'container' pandas assigns one label to the complete 'container'.

You could remove the label of the container (assigning a label starting with _), and assign individual labels to the bars. The xtick labels can be used, as they are already in the desired order.

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

df = pd.DataFrame({'time_to_travel_grouping': np.random.choice([*'abcdefgh'], 200)})
ax = df.time_to_travel_grouping.value_counts().plot(kind="bar",
                                                    color=["b", "tab:green", "tab:red", "c", "m", "y", "tab:blue", "tab:orange"],
                                                    xlabel="TTT", ylabel="Total Counts",
                                                    title="Fig4: Total Counts by Time to Travel Category (TTT)",
                                                    figsize=(20, 15))

ax.containers[0].set_label('_nolegend')
for bar, tick_label in zip(ax.containers[0], ax.get_xticklabels()):
    bar.set_label(tick_label.get_text())
ax.legend()
plt.tight_layout()
plt.show()

pandas bar plot with individual bars in legend

With a little bit less internal manipulation, something similar can be obtained via seaborn:


import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as sns

df = pd.DataFrame({'time_to_travel_grouping': np.random.choice([*'abcdefgh'], 200)})

plt.figure(figsize=(20, 15))
ax = sns.countplot(data=df, x='time_to_travel_grouping', hue='time_to_travel_grouping',
                   palette=["b", "tab:green", "tab:red", "c", "m", "y", "tab:blue", "tab:orange"],
                   order=df.time_to_travel_grouping.value_counts().index,
                   dodge=False)
plt.setp(ax, xlabel="TTT", ylabel="Total Counts", title="Fig4: Total Counts by Time to Travel Category (TTT)")
plt.tight_layout()
plt.show()

sns.countplot

  • Related