How to change axis values to italic format (Python, Matplotlib) - python

Currently I'm working on a university bioinformatics project. I have plot with an axis where values should be in italic format (Proteobacteria, Bacteroidata, etc should be in italic), but I can't find any solution how to change format ONLY for one axis values. I have found that using plt.rcParams.update() function can help, but it changes all plot/graph to italic format, but as I said I only need to change only one axis.
My code:
color_map = ['#dfdfdf' for i in range(len(order_count))]
color_map[0] = '#e81518'
fig, ax = plt.subplots(1,1, figsize=(13,7))
ax.barh(phyl_count['Phylum'], phyl_count['Phyl_Perc'], linewidth=0.6, color = color_map)
plt.gca().invert_yaxis()
ax.grid(axis='x', alpha=0.4)
plt.xlabel('Percent')
plt.ylabel('Phylum')
for index,data in enumerate(phyl_count['Phyl_Perc']):
plt.text(x=data+0.1, y=index+0.20,s=f'{data}%')
plt.show()
And I get this graph, how can I change Proteobacteria, Bacteriodata, etc to italic?

You can obtain a list of the labels by writing
labels = ax.get_yticklabels()
This gives you a list of the labels, where each label is an instance of Matplotlibs Text. This has a set_style-method, where one of the options is to set the text 'italic' so you can do
for lbl in labels:
lbl.set_style('italic')

Related

Combine two matplotlib markers into one in legend

To have a custom marker, I made two scatter plots with same data points but different markers. Thus by plotting one marker on top of the other I get the look of a new custom marker. Now I want to use it in legend. Is there a way I can use two markers one on top of the other in legend and show them as a single marker.
Edit:
The question is not regarding how to share the same label for two different markers, but how to plot one marker on top of other in the legend
Using a tuple of markers answers the question;
from numpy.random import randn
m=np.random.uniform(size=10)
x=np.arange(0,10,1)
y=x**2
fig, ax = plt.subplots(1,1)
blue_dot = ax.scatter(x[:5],y[:5], s=m*100, color='b')
red_dot = ax.scatter(x[5:],y[5:], s=200*m, color='r')
black_cross = ax.scatter(x[5:],y[5:], s=400*m, marker='+', color='k')
lgnd = ax.legend([blue_dot, (red_dot, black_cross)], ["Blue Circle", "Red Circle and Black Cross"])
Now I want to change the size of the markers in the legend so that all the markers of equal size. For that, I have tried adding this to above code.
lgnd.legendHandles[0]._sizes = [200]
lgnd.legendHandles[1]._sizes = [200] # this is affecting the size of red_dot only
How do I change the size of black_cross as well in the legend?

Seaborn Heatmap: underline text in a cell

I am making some data analysis in Python, and I am using Seaborn for visualization.
Seaborn works very nice for creating heatmaps.
I am trying to underline the maximum values for each column in my heatmap.
I was able to correctly highlight the text in the maximum cells by making them italic and bold. Still, I found no way to underline it.
This is an example of my code:
data_matrix = < extract my data and put them into a matrix >
max_in_each_column = np.max(data_matrix, axis=0)
sns.heatmap(data_matrix,
mask=data_matrix == max_in_each_column,
linewidth=0.5,
annot=True,
xticklabels=my_x_tick_labels,
yticklabels=my_y_tick_labels,
cmap="coolwarm_r")
sns.heatmap(data_matrix,
mask=data_matrix != max_in_each_column,
annot_kws={"style": "italic", "weight": "bold"},
linewidth=0.5,
annot=True,
xticklabels=my_x_tick_labels,
yticklabels=my_y_tick_labels,
cbar=False,
cmap="coolwarm_r")
This is my current result:
Of course I have tried using argumentannot_kws={"style": "underlined"}, but apparently in Seaborn the "style" key only supports values "normal", "italic" or "oblique".
Is there a workaround to this?
Yes, you can workaround your problem using tex commands within your texts. The basic idea is that you use the annot key of seaborn.heatmap to assign an array of strings as text labels. These contain your data values + some tex prefixes/suffixes to allow tex making them bold/emphasized (italic)/underlined or whatsoever.
An example (with random numbers):
# random data
data_matrix = np.round(np.random.rand(10, 10), decimals=2)
max_in_each_column = np.max(data_matrix, axis=0)
# Activating tex in all labels globally
plt.rc('text', usetex=True)
# Adjust font specs as desired (here: closest similarity to seaborn standard)
plt.rc('font', **{'size': 14.0})
plt.rc('text.latex', preamble=r'\usepackage{lmodern}')
# remains unchanged
sns.heatmap(data_matrix,
mask=data_matrix == max_in_each_column,
linewidth=0.5,
annot=True,
cmap="coolwarm_r")
# changes here
sns.heatmap(data_matrix,
mask=data_matrix != max_in_each_column,
linewidth=0.5,
# Use annot key with np.array as value containing strings of data + latex
# prefixes/suffices making the bold/italic/underline formatting
annot=np.array([r'\textbf{\emph{\underline{' + str(data) + '}}}'
for data in data_matrix.ravel()]).reshape(
np.shape(data_matrix)),
# fmt key must be empty, formatting error otherwise
fmt='',
cbar=False,
cmap="coolwarm_r")
plt.show()
Further explanation the annotation array:
# For all matrix_elements in your 2D data array (2D requires the .ravel() and .reshape()
# stuff at the end) construct in sum a 2D data array consisting of strings
# \textbf{\emph{\underline{<matrix_element>}}}. Each string will be represented by tex as
# a bold, italic and underlined representation of the matrix_element
np.array([r'\textbf{\emph{\underline{' + str(data) + '}}}'
for data in data_matrix.ravel()]).reshape(np.shape(data_matrix))
The resulting plot is basically what you wanted:

Matplotlib center alignment for pie chart labels

I have produced a very simple pie chart in Python using Matplotlib and I am wanting to edit the alignment of my labels. I have used \n within my labels to split the line as the labels are too long for one line. But as you can see from the picture called 'pie chart image', it's a mix of weird alignments at the moment. I would really like to have it center alignment.
For other chart/graph types in Matplotlib there is an argument called align where you can set it to center, however, plt.pie(...) does not seem to have this attribute.
Here is my code:
import matplotlib.pyplot as plt
k = [7,15]
labels = 'Strongly and Mostly \n Agree', 'Strongly/Mostly Disagree \n and In the Middle'
plt.pie(k, labels= labels)
plt.show()
Any ideas?
You can pass a dictionary of text properties to plt.pie via the textprops argument. For example:
plt.pie(k, labels=labels, textprops={'weight': 'bold'})
However, if you try to specify the horizontalalignment property, you'll get an error saying that you provided that parameter twice. Obviously you didn't, but matplotlib passed both it's hard-coded value and your value to some internal function.
But that's probably a good thing. The way I see it, there's not so much a mix of alignments, but a consistent alignment of the text against the pie.
Back to your question
pie returns both the patches and the labels for each wedge. So you can loop through the labels after your initial call to pie to modify their alignment. That looks like this:
k = [7, 15]
labels = 'Strongly and Mostly\nAgree', 'Strongly/Mostly Disagree\nand In the Middle'
fig, ax = plt.subplots()
ax.set_aspect('equal')
wedges, labels = ax.pie(k, labels=labels, textprops={'weight': 'bold'})
for label in labels:
label.set_horizontalalignment('center')
As you can see, the labels now overlap with the wedges, diminishing legibility.
The labels also have a set_position method (i.e., label.set_position((x, y))), but recomputing the positions for N labels in a pie chart sounds like a Bad Time to me.

Matplotlib - axvspan vs subplots

I'm writing a pythonic script for a coastal engineering application which should output, amongst other things, a figure with two subplots.
The problem is that I would like to shade a section of both subplots using plt.axvspan() but for some reason it only shades one of them.
Please find below an excerpt of the section of the code where I set up the plots as well as the figure that it's currently outputting (link after code).
Thanks for your help, and sorry if this is a rookie question (but it just happens that I am indeed a rookie in Python... and programming in general) but I couldn't find an answer for this anywhere else.
Feel free to add any comments to the code.
# PLOTTING
# now we generate a figure with the bathymetry vs required m50 and another figure with bathy vs Hs
#1. Generate plots
fig = plt.figure() # Generate Figure
ax = fig.add_subplot(211) # add the first plot to the figure.
depth = ax.plot(results[:,0],results[:,1]*-1,label="Depth [mDMD]") #plot the first set of data onto the first set of axis.
ax2 = ax.twinx() # generate a secondary vertical axis with the same horizontal axis as the first
m50 = ax2.plot(results[:,0],results[:,6],"r",label="M50 [kg]") # plot the second set of data onto the second vertical axis
ax3 = fig.add_subplot(212) # generate the second subplot
hs = ax3.plot(results[:,0],results[:,2],"g",label="Hs(m)")
#Now we want to find where breaking starts to occur so we shade it on the plot.
xBreakingDistance = results[numpy.argmax(breakingIndex),0]
# and now we plot a box from the origin to the depth of breaking.
plt.axvspan(0,xBreakingDistance,facecolor="b",alpha=0.1) # this box is called a span in matplotlib (also works for axhspan)
# and then we write BREAKING ZONE in the box we just created
yLimits = ax.get_ylim() # first we get the range of y being plotted
yMiddle = (float(yLimits[1])-float(yLimits[0])) / 2 + yLimits[0] # then we calculate the middle value in y (to center the text)
xMiddle = xBreakingDistance / 2 # and then the middle value in x (to center the text)
#now we write BREAKING ZONE in the center of the box.
ax.text(xMiddle,yMiddle,"BREAKING ZONE",fontweight="bold",rotation=90,verticalalignment="center",horizontalalignment="center")
#FIGURE FORMATTING
ax.set_xlabel("Distance [m]") # define x label
ax.set_ylabel("Depth [mDMD]") # define y label on the first vertical axis (ax)
ax2.set_ylabel("M50 [kg]") # define y label on the second vertical axis (ax2)
ax.grid() # show grid
ax3.set_xlabel("Distance[m]") #define x label
ax3.set_ylabel("Hs[m]") # define y label
ax3.grid()
plt.tight_layout() # minimize subplot labels overlapping
# generating a label on a plot with 2 vertical axis is not very intuitive. Normally we would just write ax.label(loc=0)
combined_plots = depth+m50 #first we need to combine the plots in a vector
combined_labels = [i.get_label() for i in combined_plots] # and then we combine the labels
ax.legend(combined_plots,combined_labels,loc=0) # and finally we plot the combined_labels of the combined_plots
plt.savefig("Required M50(kg) along the trench.png",dpi=1000)
plt.close(fig)
Output Figure:
By just calling plt.axvspan, you are telling matplotlib to create the axvspan on the currently active axes (i.e. in this case, the last one you created, ax3)
You need to plot the axvspan on both of the axes you would like for it to appear on. In this case, ax and ax3.
So, you could do:
ax.axvspan(0,xBreakingDistance,facecolor="b",alpha=0.1)
ax3.axvspan(0,xBreakingDistance,facecolor="b",alpha=0.1)
or in one line:
[this_ax.axvspan(0,xBreakingDistance,facecolor="b",alpha=0.1) for this_ax in [ax,ax3]]
It's difficult to analyze your code and not being able to reproduce it. I advise you to build a minimal example. In any case notice that you are calling "plt.axvspan(" which is general call to the library.
You need to specifically state that you want this in both "ax" and "ax2" (i think).
Also if you need more control consider using Patches (I don't know axvspan):
import matplotlib.pyplot as plt
import matplotlib.patches as patches
fig1 = plt.figure()
ax1 = fig1.add_subplot(111, aspect='equal')
ax1.add_patch(
patches.Rectangle(
(0.1, 0.1), # (x,y)
0.5, # width
0.5, # height
)
)
fig1.savefig('rect1.png', dpi=90, bbox_inches='tight')
See that call to "ax1" in the example? Just make something similar to yours. Or just add axvspan to each of your plots.

Changing color of single characters in matplotlib plot labels

Using matplotlib in python 3.4:
I would like to be able to set the color of single characters in axis labels.
For example, the x-axis labels for a bar plot might be ['100','110','101','111',...], and I would like the first value to be red, and the others black.
Is this possible, is there some way I could format the text strings so that they would be read out in this way? Perhaps there is some handle that can be grabbed at set_xticklabels and modified?
or, is there some library other than matplotlib that could do it?
example code (to give an idea of my idiom):
rlabsC = ['100','110','101','111']
xs = [1,2,3,4]
ys = [0,.5,.25,.25]
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.bar(xs,ys)
ax.set_xticks([a+.5 for a in xs])
ax.set_xticklabels(rlabsC, fontsize=16, rotation='vertical')
thanks!
It's going to involve a little work, I think. The problem is that individual Text objects have a single color. A workaround is to split your labels into multiple text objects.
First we write the last two characters of the label. To write the first character we need to know how far below the axis to draw -- this is accomplished using transformers and the example found here.
rlabsC = ['100','110','101','111']
xs = [1,2,3,4]
ys = [0,.5,.25,.25]
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.bar(xs,ys)
ax.set_xticks([a+.5 for a in xs])
plt.tick_params('x', labelbottom='off')
text_kwargs = dict(rotation='vertical', fontsize=16, va='top', ha='center')
offset = -0.02
for x, label in zip(ax.xaxis.get_ticklocs(), rlabsC):
first, rest = label[0], label[1:]
# plot the second and third numbers
text = ax.text(x, offset, rest, **text_kwargs)
# determine how far below the axis to place the first number
text.draw(ax.figure.canvas.get_renderer())
ex = text.get_window_extent()
tr = transforms.offset_copy(text._transform, y=-ex.height, units='dots')
# plot the first number
ax.text(x, offset, first, transform=tr, color='red', **text_kwargs)

Categories

Resources