Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib legend in increasing order

I have text files named as 5.txt, 10.txt, 15.txt, 20.txt but when I read the files with glob module and use fname variable in the legend I get disorganized legend data.

for fname in glob("*.txt"):

    potential, current_density = np.genfromtxt(fname, unpack=True)
    current_density = current_density*1e6
    ax = plt.gca()
    ax.get_yaxis().get_major_formatter().set_useOffset(False)
    plt.plot(potential,current_density, label=fname[0:-4])

plt.legend(loc=4,prop={'size':12}, 
           ncol=1, shadow=True, fancybox=True,
           title = "Scan rate (mV/s)")

enter image description here

How can I plot and give the corresponding label to the data with in increasing order?

like image 332
esilik Avatar asked Sep 05 '25 10:09

esilik


2 Answers

Just to provide yet another method, which does not require to change anything in the plotting part of the script:

handles, labels = plt.gca().get_legend_handles_labels()
handles, labels = zip(*[ (handles[i], labels[i]) for i in sorted(range(len(handles)), key=lambda k: list(map(int,labels))[k])] )
plt.legend(handles, labels, loc=4, ...)
like image 174
ImportanceOfBeingErnest Avatar answered Sep 08 '25 11:09

ImportanceOfBeingErnest


Method 1 (Recommended)

You will need to sort and display the legend yourself. plt.legend takes a list of lines and a list of strings as the first two optional positional arguments. You can maintain a list of the items you need, sort it into the order you want, and pass the portions you want over to legend.

ax = plt.gca()
legend_items = []
for fname in glob("*.txt"):
    potential, current_density = np.genfromtxt(fname, unpack=True)
    current_density *= 1e6
    line, = ax.plot(potential, current_density)
    name = fname[0:-4]
    legend_items.append((int(name), line, name))

legend_items.sort()
ax.get_yaxis().get_major_formatter().set_useOffset(False)
ax.legend([x[1] for x in legend_items], [x[2] for x in legend_items],
          loc=4, prop={'size':12},  ncol=1, shadow=True,
          fancybox=True, title = "Scan rate (mV/s)")

Major additions are marked in bold, while minor style changes that can probably be ignored are marked in italics.

Major additions include the accumulation of the items for the legend. I use tuples for each item because a list of tuples is automatically sorted by the first element first. The comma in line, = ax.plot... is necessary because it triggers argument unpacking on the list that plot returns. An alternative would be to do line = ax.plot(...)[0]. The file name is no longer added as an explicit label to the data.

Among the minor changes, I switched to using ax.plot and ax.legend instead of plt.plot and plt.legend. This is the object oriented part of Matplotlib's API and it makes things a little clearer. Also, you don't have to keep calling gca() to get the reference over and over this way. Also, set_useoffset only needs to be called only once, not inside the loop.

Method 2

Another way to approach the problem would be to pre-sort the file names before processing them, so that they appear in the correct order in your legend:

import os
file_list = os.listdir('.')
file_list = [x for x in file_list if x.endswith('.txt')]
file_list.sort(key=lambda x: int(x[0:-4]))
for fname in file_list:
    ...

You will have to do the name filtering yourself, but it is not especially difficult. The sorting key is just the number. Also, you will note that I got tired of doing the custom fancy formatting for this update :)

like image 41
Mad Physicist Avatar answered Sep 08 '25 11:09

Mad Physicist