Change the ticklabel orientation and legend position of plot - python

I am plotting a bar graph by reading data from a CSV using pandas in Python. I read a CSV into a DataFrame and plot them using matplotlib.
Here is how my CSV looks like:
SegmentName Sample1 Sample2 Sample3
Loop1 100 100 100
Loop2 100 100 100
res = DataFrame(pd.read_csv("results.csv", index_col="SegmentName"))
I plot and set the legend to be outside.
plt.figure()
ax = res.plot(kind='bar')
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.savefig("results.jpg")
However, the x-axis ticklabels are orientated vertically and hence I can't read the text. Also my legend outside is cut off.
Can I change the orientation of the ticklabels to be horizontal, and then adjust the entire figure so that the legend is visible?

Try using the 'rotation' keyword when you set the label. E.g.:
plt.xlabel('hi',rotation=90)
Or if you need to rotate the tick labels, try:
plt.xticks(rotation=90)
As for the positioning of the legend etc., it is probably worth taking a look at the tight layout guide

You should use the matplotlib API and call ax.set_xticklabels(res.index, rotation=0) like so:
index = Index(['loop1', 'loop2'], name='segment_name')
data = [[100] * 3, [100] * 3]
columns = ['sample1', 'sample2', 'sample3']
df = DataFrame(data, index=index, columns=columns)
fig, ax = subplots()
df.plot(ax=ax, kind='bar', legend=False)
ax.set_xticklabels(df.index, rotation=0)
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
fig.savefig('results.png', bbox_inches='tight')
to get the resulting plot:
Alternatively you can call fig.autofmt_xdate() for a nice tilted effect, which you can of course tinker with with the above (and more general) ax.set_xticklabels():
fig, ax = subplots()
df.plot(ax=ax, kind='bar', legend=False)
fig.autofmt_xdate()
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
fig.savefig('results-tilted.png', bbox_inches='tight')

For the rotation of the labels, you can simply tell pandas to rotate it for you by giving the number of degrees to the rot argument.
The legends being cut off is answered elsewhere as well, like here:
df = pd.DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])],
orient='index', columns=['one', 'two', 'three'])
ax = df.plot(kind='bar', rot=90)
lgd = ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
fig.savefig("results.jpg", bbox_extra_artists=(lgd,), bbox_inches='tight')

Related

Modifying subplots sizes

I have been trying to find some answers, but most of them don't include a table, or they solve the problem generally and I get in trouble trying to find a workaround with the table I created as I managed to put the table through an empty axis. But now decreasing the right-axis size (as the table gets accommodated to the axis size) and increasing the left two-axis size is becoming a daunting task.
I have this code:
fig = plt.figure(figsize=(18,5))
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(223)
ax3 = fig.add_subplot(122)
ax3.axis('off')
data = pd.DataFrame({'metrics': ['MSLE train', 'msle_test', 'asdsad'],
'values': [0.43, 0.52, 0.54]})
ax3.table(cellText=data.values, colLabels=data.columns, loc='center')
fig.suptitle(f'Train MSLE: {msle_train}, Test MSLE: {msle_test}')
ax1 = y_data.plot(label='Original data', ax=ax1, c='blue')
ax1 = y_pred_train.plot(ax=ax1, c='orange')
ax1 = y_pred_test.plot(ax=ax1, c='orange', linestyle='--')
ax1.legend()
ax2 = error_train.plot(label='Train error', ax=ax2)
ax2 = error_test.plot(label='Test error', ax=ax2, linestyle='--')
ax2.legend()
plt.show()
That returns this plot:
I'm looking to increase the horizontal size of the two left plots, something near the red mark:
Any suggestions?
You can use gridspec.
It even works with a vertical centered right hand side and a table:
import matplotlib.pyplot as plt
from matplotlib import gridspec
import pandas as pd
data = pd.DataFrame({'metrics': ['MSLE train', 'msle_test', 'asdsad'],
'values': [0.43, 0.52, 0.54]})
fig = plt.figure(figsize=(18,5))
gs = gridspec.GridSpec(4, 2, width_ratios=[3,1])
ax1 = fig.add_subplot(gs[0:2,:-1])
ax1.set_title('ax1')
ax2 = fig.add_subplot(gs[2:4,:-1])
ax2.set_title('ax2')
ax3 = fig.add_subplot(gs[1:3,1])
ax3.set_axis_off()
ax3.table(cellText=data.values, colLabels=data.columns, loc='center')
fig.tight_layout()
plt.show()
Notes:
Horizontal alignment is set with the ratio of width_ratios=[3,1]
fig.tight_layout() is helpfull to automatically align the spacing between the plots.
Vertical centering is achieved with a little workaround by having initially a larger vertical grid than required (no. of vertical plots) and distributing the plots and table accordingly (see e.g. gs[2:4).
The titles were just added for visual orientation.
ax3.set_axis_off() is required to suppress the plot frame at the table position - without it you'll get:

How can I fix legend color issue in Bar graph in Python?

I have a problem about defining many color in legend part of bar graph.
After I've done some essential process, I draw a figure by using the code shown below.
ax = df.plot(kind='bar', stacked=True,figsize=(13,10))
plt.title('Title List', fontsize=20)
leg = ax.legend(loc='center right', bbox_to_anchor=(1.3, 0.5), ncol=1)
plt.tight_layout()
plt.savefig('images/image1.png', bbox_inches = "tight")
plt.show()
When I run the code, some colors are the same.
How can I define unique colors in legend part?
Here is the screenshot
My answer:
After I defining colormap as rainbow, All defined colors in legend parts became unique.
Change to code
ax = df.plot(kind='bar', stacked=True,figsize=(13,10))
to
ax = df.plot(kind='bar', stacked=True,figsize=(13,10), colormap='rainbow')

Matplotlib: changing datetime ticks makes plot disappear

im having serious trouble modifying how and which x-axis labels are presented in my plot.
I have a datetime index and want to reduce the number of xticks been shown and remove the year from it. Should be simple, right?! But, for some reason, the plot disappears after i set major formatter and locator. Here is a working example:
import datetime
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import pandas as pd
teste = pd.DataFrame(index=pd.date_range('2019-01-01','2019-12-31',freq='2D'),columns=['A','B','C'])
teste['A']=.4
teste['B']=.5
teste['C']=.1
for col in teste.columns:
variation = np.random.rand(len(teste))
teste[col]+=variation/10.0
teste['total']=teste.sum(axis=1)
for col in teste.columns:
teste[col]/=teste['total']
ax = plt.figure(figsize=(24,10)).add_axes([0,0,1,1])
teste.drop('total',axis=1).plot(kind='bar',stacked='True',ax=ax,width=1,colormap='coolwarm')
ax.tick_params(labelsize=14)
ax.set_xlabel('')
ax.set_title('Teste',fontsize=28)
ax.set_ylabel('Share (%)',fontsize=22)
ax.tick_params(axis='both',labelsize=20)
ax.legend(bbox_to_anchor=(1.05, 1),fontsize=22, loc='upper left', borderaxespad=0.)
As you can see, the xticks are unreadable. But when i try to format:
ax = plt.figure(figsize=(24,10)).add_axes([0,0,1,1])
teste.drop('total',axis=1).plot(kind='bar',stacked='True',ax=ax,width=1,colormap='RdBu')
ax.xaxis_date()
ax.xaxis.set_major_locator(mdates.DayLocator(interval=10))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%d/%m"))
ax.xaxis.set_minor_formatter(mdates.DateFormatter("%d/%m"))
ax.set_xlim(teste.index[0],teste.index[-1])
ax.margins(0)
ax.tick_params(labelsize=14)
ax.set_xlabel('')
ax.set_title('Teste',fontsize=28)
ax.set_ylabel('Share (%)',fontsize=22)
ax.tick_params(axis='both',labelsize=20)
ax.legend(bbox_to_anchor=(1.05, 1),fontsize=22, loc='upper left', borderaxespad=0.)
The plot vanishes. What am i doing wrong? I`ve tried everything. plt.MaxNLocator(N=10) also doesn't work. It spreads the first N points all over the axis, completely disregarding where it actually should be.
Any help would be greatly appreciated.
Thanks in advance,
Edit: #Trenton McKinney:
Removing ax.set_xlim(teste.index[0],teste.index[-1]) makes the plot appear but without the xticks.
I used the method shown on the Matplotlib website: Stacked Bar Graph
With a bar plot, every bar has a location [0, ..., n]
ind selects the locs to label
dates are the names of the selected ticks
ax = plt.figure(figsize=(24,10)).add_axes([0,0,1,1])
teste.drop('total',axis=1).plot(kind='bar',stacked='True',ax=ax,width=1,colormap='RdBu')
# locations of tick marks to label
ind = np.arange(0, len(teste.index)+1, 10)
# label for ticks
dates = teste.index.date[0::10] # %y-%m-%d format
# dates = teste.index.strftime('%d/%m')[0::10] # %d/%m format
# set the xticks
plt.xticks(ind, dates)
# only used to show locs and labels if you're having trouble
# locs, labels = plt.xticks()
# label_t = [x.get_text() for x in labels]
# formatting
ax.margins(0)
ax.tick_params(labelsize=14)
ax.set_xlabel('')
ax.set_title('Teste',fontsize=28)
ax.set_ylabel('Share (%)',fontsize=22)
ax.tick_params(axis='both',labelsize=20)
ax.legend(bbox_to_anchor=(1.05, 1),fontsize=22, loc='upper left', borderaxespad=0.)
plt.show()
Optionally
fig, ax = plt.subplots(figsize=(20, 8))
p1 = ax.bar(teste.index, teste.A)
p2 = ax.bar(teste.index, teste.B, bottom=teste.A)
p3 = ax.bar(teste.index, teste.C, bottom=teste.A+teste.B)
ax.xaxis_date()
ax.xaxis.set_major_locator(mdates.DayLocator(interval=10))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%d/%m"))
ax.set_xlim(teste.index[0],teste.index[-1])
plt.xticks(rotation=45, ha='right') # or (rotation=90, ha='center')
plt.show()

How to add plot labels of different axes to the same legend in Python?

I am trying to plot two curves on two y-axes as shown in figure. The red plot (pressure) the primary axis and green (needle lift) the secondary axis. And I am trying to add the plot labels to the same legend. But I cannot add them to the same legend. It overlaps as shown in figure, Raw placed above Needle lift.
The code I used:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import ticker as mtick
data = np.genfromtxt("secondary_axis.dat", skiprows = 2, delimiter = ',')
time = data[:, 0]
pressure = data[:, 1] * 0.006894759086775369
pressure_charge = data[0, 0]
needle_lift = data[:, 2]
figure = plt.figure(figsize=(5.15, 5.15))
figure.clf()
plot = plt.subplot(111)
plot.plot(time, pressure, label = r'\textit{Raw}')
plot.set_xlabel(r'\textit{X}', labelpad=6)
plot.set_ylabel(r'\textit{Y}', labelpad=6)
primary_ticks = len(plot.yaxis.get_major_ticks())
ax2 = plot.twinx()
ax2.plot(time, needle_lift, label = r'\textit{Needle lift}', color='#4DAF4A')
plot.set_zorder(ax2.get_zorder()+2)
plot.patch.set_visible(False)
ax2.grid(False)
ax2.set_ylabel(r'\textit{Z}', labelpad=6)
ax2.yaxis.set_major_locator(mtick.LinearLocator(primary_ticks))
plot.legend(loc = 'center left', bbox_to_anchor = (1.2, 0.5))
ax2.legend(loc = 'center left', bbox_to_anchor = (1.2, 0.5))
plt.show()
The data is available here
How to add plot labels of different axes to the same legend? I want them to be ordered as you when multiple lines are plot on the primary axis as given below:
The problem is that you create two legends. You get nicer results with only one. For that you need to store the line artists:
l1, = plot.plot(time, pressure, label=r'\textit{Raw}')
# ...
l2, = ax2.plot(time, needle_lift, label=r'\textit{Needle lift}', color='#4DAF4A')
And then you can use them to create the legend, by supplying the artists and the desired labels (you could also provide the strings here directly):
plt.legend((l1, l2), (l1.get_label(), l2.get_label()), loc='center left',
bbox_to_anchor=(1.2, 0.5))
Result:

How to shrink plot on x-axis in matplotlib?

I've seen a couple examples but the plots are constructed differently and I don't see how to make the syntax work. Here's my code:
pdf_file = PdfPages(sys.argv[1].split('.')[0] + "_graphs.pdf")
for i in range(0, len(list_of_data)):
biorep = int(list_of_figure_key[i].split('.')[1])
construct = int(list_of_figure_key[i].split('.')[0].split('_')[1])
plot(time, list_of_data[i], color=color_dict[construct], linestyle=linestyle_dict[biorep], label=list_of_figure_key[i] )
xlabel('time (hours)', fontsize=9)
ylabel(ReadType, fontsize=9)
xlim(min(time),max(time))
legend(fontsize=8, loc='center left', bbox_to_anchor=(1, .5))
pdf_file.savefig()
It produces a beautiful figure but the legend is much too long and goes off the edge of the page. I'd like to shrink the plot on the x-axis so the legend will fit as a 2-column legend.
Figure can be seen here: http://i.imgur.com/mvgzIhj.jpg
Thanks in advance!
You can make a two-column legend using the ncol legend attribute. You can shrink the width of the plot by drawing the axis on the plot and fixing its size:
from matplotlib import pyplot as plt
fig = plt.figure() # initialize figure
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # add axis
To make this work with your code, something like this should work:
# import pyplot
from matplotlib import pyplot as plt
# set up filename to save it
pdf_file = PdfPages(sys.argv[1].split('.')[0] + "_graphs.pdf")
# set up axis object
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
# plot your data
for i in range(0, len(list_of_data)):
biorep = int(list_of_figure_key[i].split('.')[1])
construct = int(list_of_figure_key[i].split('.')[0].split('_')[1])
ax.plot(time, list_of_data[i], color=color_dict[construct],
linestyle=linestyle_dict[biorep], label=list_of_figure_key[i] )
# modify axis limits and legend
ax.set_xlabel('time (hours)', fontsize=9)
ax.set_ylabel(ReadType, fontsize=9)
ax.set_xlim(min(time),max(time))
ax.legend(fontsize=8, loc='upper left', bbox_to_anchor=(1, .5), ncol=2)
# save final figure
plt.savefig(pdf_file)
In your code, you were remaking the legend, the limits and the legend at each iteration of the for-loop, as well as saving and then overwriting the pdf image. This isn't necessary -- you can just do it once at the end.
For more legend tips, this post is handy. This one is also helpful.

Categories

Resources