yticks have unexpectedly variable font size - python

I am modifying the yticks font size of a figure but not all yticks are of the same font size. The last two yticks 0.8 and 0.6 are greater than the others.
def mm_to_inch(value):
return value/25.4
import matplotlib
matplotlib.use('Agg')
matplotlib.rcParams['font.sans-serif'] = "Arial"
matplotlib.rcParams['font.family'] = "sans-serif"
matplotlib.rcParams['figure.dpi'] = 300
import pandas as pd
import matplotlib.pyplot as plt
size = 112
fig, ax1 = plt.subplots(figsize=(mm_to_inch(size), mm_to_inch(size/2)))
df = pd.read_csv('testing_errors_prob.csv')
df = df.drop(['precision', 'recall', 'TP', 'FP', 'TN', 'FN'], axis=1)
df = df.sort_values(by=['accuracy'], ascending=False)
df = df.replace({'Gaussian Nb': 'Gaussian\nNb', 'Extra Trees': 'Extra\nTrees', 'Random Forest': 'Random\nForest',
'Decision Tree': 'Decision\nTree', 'Gradient Boost': 'Gradient\nBoost', 'Linear SVC': 'Linear\nSVC',
'Ada Boost': 'Ada\nBoost', 'Bernouli Nb': 'Bernouli\nNb'})
df.plot.bar(x='model', ax=ax1, color=['#093145', '#107896', '#829356'], width=0.8)
plt.tight_layout()
# plt.xticks(rotation=45, ha="right", fontsize=6)
# plt.yticks(fontsize=6)
plt.xticks(rotation=45, ha="right", fontsize=6)
plt.yticks(fontsize=6)
plt.legend(["Accuracy", "F1", "AUC ROC"], fontsize="xx-large", prop={'size': 5})
plt.subplots_adjust(left=0.07, right=0.9, top=0.9, bottom=0.2)
plt.xlabel('')
plt.savefig('results_updateddd_{}.png'.format(size))
plt.savefig('results_updateddd_{}.pdf'.format(size))
plt.close()
The figure looks like this:

I am still not sure why, but the bug was from plt.yticks(fontsize=6). I removed it and replaced it with
ax1.tick_params(axis='both', which='major', labelsize=6)

The main issue is that subplots_adjust causes new yticks to appear when it alters the subplot layout:
yticks(fontsize=6) only targets existing ticks, so it will not affect any future ticks that show up (e.g., new ticks that appear after subplots_adjust)
tick_params(labelsize=6) sets the tick size for the whole Axes, so even when new ticks show up, they automatically inherit this tick size just by being part of the Axes
So if you want to use the yticks(fontsize=6) method, make sure to call it last:
...
plt.subplots_adjust(left=0.07, right=0.9, top=0.9, bottom=0.2)
plt.yticks(fontsize=6)
...
fontsize -> subplots_adjust
subplots_adjust -> fontsize

Related

How do I animate the changes of a single data point's color using matplolib.animation?

I am trying to change a color of the single data point (101th entry of df) in the scatter plot. How do I animate the changes using matplotlib.animation?
First, this is what original scatter plot is:
import matplotlib.animation as animat
fig ,ax = plt.subplots()
fig.set_dpi(100)
fig.set_size_inches(15, 8)
groups = df.groupby('Name')
for name, group in groups:
ax.plot(group.SepalWidth,
group.SepalLength,
marker='o',
linestyle='',
label=name)
ax.legend(fontsize=10) # legend position
plt.title('Scatter Plot of Sabotaged Iris Data', fontsize=20)
plt.annotate('Sabotaged Data', xy=(3.31,6.305), xytext=(3.5,6.5), arrowprops = dict(facecolor ='black', shrink = 0.04))
plt.xlabel('Sepal Width', fontsize=14)
plt.ylabel('Sepal Length', fontsize=14)
And now I am trying to change the colors as frame changes (maybe red, orange, yellow, pink, purple), but I can't think of how to fill up the update function:
def update(frame):
# ???
ani = animat.FuncAnimation(fig, update,
frames=360,
interval=0.01,
blit=True)
First off, you are mixing the old interface (such as plt.plot) with the newer object-oriented interface (which would use ax.plot). It's best to stick with one interface for code that is executed together.
You can't give an arbitrary color to an individual dot from a scatter plot. But you can create a new scatter plot with just one dot, and change its color inside the update function. If one constantly creates new elements inside the animation, the system's memory can get quite full. Therefore, usually a previously created element is updated.
Also note that FuncAnimation's interval is measured in milliseconds. Changes in less than 50 ms will be hard to notice (and probably the system can't regenerate that fast).
import matplotlib.animation as animat
import matplotlib.pyplot as plt
import seaborn as sns
df = sns.load_dataset('iris')
df.rename(columns={'species': 'Name', 'sepal_width': 'SepalWidth', 'sepal_length': 'SepalLength'}, inplace=True)
fig, ax = plt.subplots()
fig.set_dpi(100)
fig.set_size_inches(15, 8)
groups = df.groupby('Name')
for name, group in groups:
ax.plot(group.SepalWidth,
group.SepalLength,
marker='o',
linestyle='',
label=name)
ax.legend(fontsize=10) # legend position
ax.set_title('Scatter Plot of Sabotaged Iris Data', fontsize=20)
sabotaged = (3.31, 6.305)
ax.annotate('Sabotaged Data', xy=sabotaged, xytext=(3.5, 6.5), arrowprops=dict(facecolor='black', shrink=0.04))
ax.set_xlabel('Sepal Width', fontsize=14)
ax.set_ylabel('Sepal Length', fontsize=14)
scatter_dot = ax.scatter(*sabotaged, s=90, fc='turquoise', zorder=3)
# scatter_circle = ax.scatter(*sabotaged, s=100, fc='none', ec='red', zorder=3)
def update(frame):
colors = ['crimson', 'darkorange', 'lime', 'cornflowerblue', 'purple', 'fuchsia']
scatter_dot.set_color(colors[frame % len(colors)])
return scatter_dot,
ani = animat.FuncAnimation(fig, update,
frames=360,
interval=2000,
blit=True)
plt.show()

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()

Changing order of items in combined sns python graph

I need a plot graph with three different axes. So far, I adapted one script I have found on the web. Unfortunately, I have noticed that my data are not ordered properly. At first, I sort my dataframe by a column named 'a', but in the final figure, only this column seems to be sorted. I would like to order all of them. When I print dataframe after sorting everything seems to be fine.
I will really appreciate any help.
Here is my dataframe after I have sorted it based on column 'a' and here is final graph, where only area of catchments is sorted, but names of catchments, mean elevation and mean slope of catchments are not sorted properly.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns; sns.set()
from matplotlib.ticker import PercentFormatter
data1 = 'path to my .xlsx file'
df = pd.read_excel(data1, 'catchments_basic')# loading data
df_sorted = df.sort_values(by=['a'], ascending=False)
def make_patch_spines_invisible(ax):
ax.set_frame_on(True)
ax.patch.set_visible(False)
for sp in ax.spines.values():
sp.set_visible(False)
sns.set(style="white", rc={"lines.linewidth": 3})
fig, ax = plt.subplots(figsize=(15,10))
fig.subplots_adjust(right=0.75)
ax1 = ax.twinx()
ax2 = ax.twinx()
# Offset the right spine of par2. The ticks and label have already been
# placed on the right by twinx above.
ax2.spines["right"].set_position(("axes", 1.1))
# Having been created by twinx, par2 has its frame off, so the line of its
# detached spine is invisible. First, activate the frame but make the patch
# and spines invisible.
make_patch_spines_invisible(ax2)
# Second, show the right spine.
ax2.spines["right"].set_visible(True)
host = sns.barplot(x=df_sorted['Catchment'],
y=df_sorted["a"],
color='#004488',
label="area",
ax=ax)
par1 = sns.lineplot(x=df_sorted['Catchment'],
y=df_sorted["b"],
color='r',
marker="o",
label="mean elevation",
ax=ax1)
par2 = sns.lineplot(x=df_sorted['Catchment'],
y=df_sorted["c"],
color='g',
marker="o",
label="mean slope",
ax=ax2)
host.set_xlim(-1, 20)
host.set_ylim(0, 1000)
par1.set_ylim(0, 1000)
par2.set_ylim(0, 100)
host.set_xlabel("river catchment")
host.set_ylabel("area [$km^2$]")
par1.set_ylabel("mean elevation [m n. m.]")
par2.set_ylabel("mean slope [%]")
host.yaxis.label.set_color(color='#004488')
par1.yaxis.label.set_color(color='r')
par2.yaxis.label.set_color(color='g')
tkw = dict(size=4, width=1.5)
host.tick_params(axis='y', colors='#004488', **tkw)
host.tick_params(axis='x', colors='black', **tkw)
par1.tick_params(axis='y', colors='r', **tkw)
par2.tick_params(axis='y', colors='g', **tkw)
host.tick_params(axis='povodie', **tkw)
ax2.yaxis.set_major_formatter(PercentFormatter(decimals=0))
for tick in host.get_xticklabels():
tick.set_rotation(45)
host.set_title('Area, mean altitude and mean slope in selected river catchments', family='Arial', size=12, weight='bold')
host.grid(linestyle="dotted", color='black')
host.legend(loc='upper left')
par1.legend(loc='upper center')
par2.legend(loc='upper right')
save_results_to = 'path where I want to save figure
plt.tight_layout(pad=2)
plt.savefig(save_results_to + 'basic_characteristics_bar_line_combination.png', dpi = 300)
plt.show()
print ('done')
you should change sort parameter in sns.lineplot to False

Matplotlib won't show minor ticks when using subplots

I have several subplots and want to adjust the axis ticks settings by ax.tick_params. Everything works fine, however, the minor ticks are not shown. Here is a code example
import matplotlib.pyplot as plt
x = np.linspace(0,1,100)
y = x*x
f, (ax1,ax2) = plt.subplots(2, 1)
ax1.tick_params(axis="both", direction="in", which="both", right=False, top=True)
ax2.tick_params(axis="both", direction="in", which="both", right=True, top=False)
ax1.plot(x,y)
ax2.plot(x,-y)
plt.show()
I assumed that which=both would give me the minor ticks. However I need to add an additional
plt.minorticks_on()
which makes them visible but only in ax2.
How do I fix this?
With pyplot the danger is that you loose track of which one the current axes is that a command like plt.minorticks_on() operates on. Hence it would be beneficial to use the respective methods of the axes you're working with:
ax1.minorticks_on()
ax2.minorticks_on()
plt would work on the current axis which is ax2 in your case. One way is to first enable them using the way you did and then specify the number of minor ticks using AutoMinorLocator
from matplotlib.ticker import AutoMinorLocator
ax1.tick_params(axis="both", direction="in", which="both", right=False, top=True)
ax2.tick_params(axis="both", direction="in", which="both", right=True, top=False)
ax1.plot(x,y)
ax2.plot(x,-y)
for ax in [ax1, ax2]:
ax.xaxis.set_minor_locator(AutoMinorLocator(4))
ax.yaxis.set_minor_locator(AutoMinorLocator(4))

Python hide ticks but show tick labels

I can remove the ticks with
ax.set_xticks([])
ax.set_yticks([])
but this removes the labels as well. Any way I can plot the tick labels but not the ticks and the spine
You can set the tick length to 0 using tick_params (http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.tick_params):
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot([1],[1])
ax.tick_params(axis=u'both', which=u'both',length=0)
plt.show()
Thanks for your answers #julien-spronck and #cmidi.
As a note, I had to use both methods to make it work:
import numpy as np
import matplotlib.pyplot as plt
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(11, 3))
data = np.random.random((4, 4))
ax1.imshow(data)
ax1.set(title='Bad', ylabel='$A_y$')
# plt.setp(ax1.get_xticklabels(), visible=False)
# plt.setp(ax1.get_yticklabels(), visible=False)
ax1.tick_params(axis='both', which='both', length=0)
ax2.imshow(data)
ax2.set(title='Somewhat OK', ylabel='$B_y$')
plt.setp(ax2.get_xticklabels(), visible=False)
plt.setp(ax2.get_yticklabels(), visible=False)
# ax2.tick_params(axis='both', which='both', length=0)
ax3.imshow(data)
ax3.set(title='Nice', ylabel='$C_y$')
plt.setp(ax3.get_xticklabels(), visible=False)
plt.setp(ax3.get_yticklabels(), visible=False)
ax3.tick_params(axis='both', which='both', length=0)
plt.show()
While attending a coursera course on Python, this was a question.
Below is the given solution, which I think is more readable and intuitive.
ax.tick_params(top=False,
bottom=False,
left=False,
right=False,
labelleft=True,
labelbottom=True)
This worked for me:
plt.tick_params(axis='both', labelsize=0, length = 0)
matplotlib.pyplot.setp(*args, **kwargs) is used to set properties of an artist object. You can use this in addition to get_xticklabels() to make it invisible.
something on the lines of the following
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(2,1,1)
ax.set_xlabel("X-Label",fontsize=10,color='red')
plt.setp(ax.get_xticklabels(),visible=False)
Below is the reference page
http://matplotlib.org/api/pyplot_api.html
You can set the yaxis and xaxis set_ticks_position properties so they just show on the left and bottom sides, respectively.
ax.yaxis.set_ticks_position('left')
ax.xaxis.set_ticks_position('bottom')
Furthermore, you can hide the spines as well by setting the set_visible property of the specific spine to False.
axes[i].spines['right'].set_visible(False)
axes[i].spines['top'].set_visible(False)
This Worked out pretty well for me! try it out
import matplotlib.pyplot as plt
import numpy as np
plt.figure()
languages =['Python', 'SQL', 'Java', 'C++', 'JavaScript']
pos = np.arange(len(languages))
popularity = [56, 39, 34, 34, 29]
plt.bar(pos, popularity, align='center')
plt.xticks(pos, languages)
plt.ylabel('% Popularity')
plt.title('Top 5 Languages for Math & Data \nby % popularity on Stack Overflow',
alpha=0.8)
# remove all the ticks (both axes),
plt.tick_params(top='off', bottom='off', left='off', right='off', labelleft='off',
labelbottom='on')
plt.show()
Currently came across the same issue, solved as follows on version 3.3.3:
# My matplotlib ver: 3.3.3
ax.tick_params(tick1On=False) # "for left and bottom ticks"
ax.tick_params(tick2On=False) # "for right and top ticks, which are off by default"
Example:
fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4, 5], [1, 2, 3, 4, 5])
ax.tick_params(tick1On=False)
plt.show()
Output:
Assuming that you want to remove some ticks on the Y axes and only show the yticks that correspond to the ticks that have values higher than 0 you can do the following:
from import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# yticks and yticks labels
yTicks = list(range(26))
yTicks = [yTick if yTick % 5 == 0 else 0 for yTick in yTicks]
yTickLabels = [str(yTick) if yTick % 5 == 0 else '' for yTick in yTicks]
Then you set up your axis object's Y axes as follow:
ax.yaxis.grid(True)
ax.set_yticks(yTicks)
ax.set_yticklabels(yTickLabels, fontsize=6)
fig.savefig('temp.png')
plt.close()
And you'll get a plot like this:

Categories

Resources