're-sort' / adapt ticks of matshow matrix plot - python

I tried hard, but I'm stuck with matplotlib here. Please overlook, that the mpl docs are a bit confusing to me . My question concerns the following:
I draw a symmetrical n*n matrix D with matshow function. That works.
I want to do the same thing, just with different order of (the n) items in D
D = [:,neworder]
D = [neworder,:]
Now, how do I make the ticks reproduce this neworder, preferably using additionally MaxNLocator?
As far as I understand...
set_xticklabels assigns labels to the ticks by order, independently of where the ticks are set?!
set_xticks (mpl docs: 'Set the x ticks with list of ticks') here I'm really not sure what it does. Can somebody explain it precisely? I don't know, whether these functions are helpful in my case at all. Maybe even things are different between using a common xy plot and matshow.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca()
D = np.arange(100).reshape(10,10)
neworder = np.arange(10)
np.random.shuffle(neworder)
D = D[:,neworder]
D = D[neworder, :]
# modify ticks somehow...
ax.matshow(D)
plt.show()
Referring to Paul's answer, think I tried smth like this. Using the neworder to define positions and using it for the labels, I added plt.xticks(neworder, neworder) as tick-modifier. For example with neworder = [9 8 4 7 2 6 3 0 1 5] I get is this
The order of the labels is correct, but the ticks are not. The labels should be independently show the correct element independently of where the ticks are set. So where is the mistake?

I think what you want to do is set the labels on the new plot to show the rearranged order of the values. Is that right? If so, you want to keep the tick locations the same, but change the labels:
plt.xticks(np.arange(0,10), neworder)
plt.yticks(np.arange(0,10), neworder)
Edit: Note that these commands must be issued after matshow. This seems to be a quirk of matshow (plot does not show this behaviour, for example). Perhaps it's related to this line from the plt.matshow documentation:
Because of how :func:matshow tries to set the figure aspect ratio to be the
one of the array, if you provide the number of an already
existing figure, strange things may happen.
Perhaps the safest way to go is to issue plt.matshow(D) without first creating a figure, then use plt.xticks and plt.yticks to make adjustments.
Your question also asks about the set_ticks and related axis methods. The same thing can be accomplished using those tools, again after issuing matshow:
ax = plt.gca()
ax.xaxis.set_ticks(np.arange(0,10)) # turn on all tick locations
ax.xaxis.set_ticklabels(neworder) # use neworder for labels
Edit2: The next part of your question is related to setting a max number of ticks. 20 would require a new example. For our example I'll set the max no. of ticks at 2:
ax = plt.gca()
ax.xaxis.set_major_locator(plt.MaxNLocator(nbins=3)) # one less tick than 'bin'
tl = ax.xaxis.get_ticklocs() # get current tick locations
tl[1:-1] = [neworder[idx] for idx in tl[1:-1]] # find what the labels should be at those locs
ax.xaxis.set_ticklabels(tl) # set the labels
plt.draw()

You are on the right track. The plt.xticks command is what you need.
You can specify the xtick locations and the label at each position with the following command.
labelPositions = arange(len(D))
newLabels = ['z','y','x','w','v','u','t','s','q','r']
plt.xticks(labelPositions,newLabels)
You could also specify an arbitrary order for labelPositions, as they will be assigned based on the values in the vector.
labelPositions = [0,9,1,8,2,7,3,6,4,5]
newLabels = ['z','y','x','w','v','u','t','s','q','r']
plt.xticks(labelPositions,newLabels)

Related

How to print multiple plots together in python?

I am trying to print about 42 plots in 7 rows, 6 columns, but the printed output in jupyter notebook, shows all the plots one under the other. I want them in (7,6) format for comparison. I am using matplotlib.subplot2grid() function.
Note: I do not get any error, and my code works, however the plots are one under the other, vs being in a grid/ matrix form.
Here is my code:
def draw_umap(n_neighbors=15, min_dist=0.1, n_components=2, metric='euclidean', title=''):
fit = umap.UMAP(
n_neighbors=n_neighbors,
min_dist=min_dist,
n_components=n_components,
metric=metric
)
u = fit.fit_transform(df);
plots = []
plt.figure(0)
fig = plt.figure()
fig.set_figheight(10)
fig.set_figwidth(10)
for i in range(7):
for j in range(6):
plt.subplot2grid((7,6), (i,j), rowspan=7, colspan=6)
plt.scatter(u[:,0], u[:,1], c= df.iloc[:,0])
plt.title(title, fontsize=8)
n=range(7)
d=range(6)
for n in n_neighbors:
for d in dist:
draw_umap(n_neighbors=n, min_dist=d, title="n_neighbors={}".format(n) + " min_dist={}".format(d))
I did refer to this post to get the plots in a grid and followed the code.
I also referred to this post, and modified my code for size of the fig.
Is there a better way to do this using Seaborn?
What am I missing here? Please help!
Both questions that you have linked contain solutions that seem more complicated than necessary. Note that subplot2grid is useful only if you want to create subplots of varying sizes which I understand is not your case. Also note that according to the docs Using GridSpec, as demonstrated in GridSpec demo is generally preferred, and I would also recommend this function only if you want to create subplots of varying sizes.
The simple way to create a grid of equal-sized subplots is to use plt.subplots which returns an array of Axes through which you can loop to plot your data as shown in this answer. That solution should work fine in your case seeing as you are plotting 42 plots in a grid of 7 by 6. But the problem is that in many cases you may find yourself not needing all the Axes of the grid, so you will end up with some empty frames in your figure.
Therefore, I suggest using a more general solution that works in any situation by first creating an empty figure and then adding each Axes with fig.add_subplot as shown in the following example:
import numpy as np # v 1.19.2
import matplotlib.pyplot as plt # v 3.3.4
# Create sample dataset
rng = np.random.default_rng(seed=1) # random number generator
nvars = 8
nobs = 50
xs = rng.uniform(size=(nvars, nobs))
ys = rng.normal(size=(nvars, nobs))
# Create figure with appropriate space between subplots
fig = plt.figure(figsize=(10, 8))
fig.subplots_adjust(hspace=0.4, wspace=0.3)
# Plot data by looping through arrays of variables and list of colors
colors = plt.get_cmap('tab10').colors
for idx, x, y, color in zip(range(len(xs)), xs, ys, colors):
ax = fig.add_subplot(3, 3, idx+1)
ax.scatter(x, y, color=color)
This could be done in seaborn as well, but I would need to see what your dataset looks like to provide a solution relevant to your case.
You can find a more elaborate example of this approach in the second solution in this answer.

Managing ticks for multiple lines on the same subplot in Matplotlib

I want to plot multiple lines in the same plot, like in the picture below:
The problem with the picture is that if the Y values of the graphs aren't similar the y ticks get jumbled, it's unclear which tick is related to the first graph and which one isn't.
Is there a way for me to colour the ticks of each graph differently (preferably to the colour of the graph)? or maybe separate it into different columns?
Also, I wouldn't mind using more than one subplot, as long as the graphs' space overlaps.
The code I use to create the new lines:
def generate_graph():
colors = "rgbmcmyk"
subplot_recent.clear()
lines_drawn = []
mat_figure.legends = []
for i in range(n):
lines_drawn.append(["A Name", subplot_recent.plot(values[i][0], values[i][1], colors[i])[0]])
mat_figure.legend((i[1] for i in lines_drawn), (i[0] for i in lines_drawn), 'upper right')
subplot_recent.yaxis.set_major_locator(plt.MaxNLocator(10))
mat_canvas.draw()
The error actually was that I forgot to cast the values to int/float, and so matplotlib didn't really know what to do with them all to well.
It's fixed now. Thanks!

Is it possible, to reverse the axis orientation on a Radar-Chart in Python?

I am trying to plot a Radar-Chart using the following code from the this source.
And my goal is, to reverse the r-Axis without having to remap my data points as my data is on a scale from 1 to 5, with 1 indicating very food and 5 very bad. (so I would loose the meaning of the scale, when reversing the datapoints)
(Which has been described here)
My first approach was to use matplotlibs inherent functionality.
So with the source being
# Draw ylabels
ax.set_rlabel_position(0)
plt.yticks([10,20,30], ["10","20","30"], color="grey", size=7)
plt.ylim(0,40)
My approach would be
# Draw ylabels
ax.set_rlabel_position(0)
plt.yticks([30,20,10], ["30","20","10"], color="grey", size=7) # Reversed labels
plt.ylim(40,0) # Reversed axis, as described above
But the problem is, that the lower code never finishes. So i don't even know how to debug it, as i don't get any errors.
I also can't seem to reverse only the Axis labels (as with that approach it would be doable to just reverse the data and the labels)
Have a look at the following... hopefully there's something you can use here. The way I got this to work was plotting rmax-r instead of r. I also reversed the order of the ticks, but kept the tick labels the same.
# Set up the data for plotting.
N=20
angles = 2.0*pi*np.linspace(0,1,N)
rmin = 0
rmax = 10
radii = rmax*np.random.random(N)
# Plot the non-reversed plot
plt.figure()
ax = plt.subplot(111,polar = True)
ax.plot(angles,radii)
ax.fill(angles, radii, 'b', alpha=0.1)
n_labels = 5
ticks1 = np.linspace(rmin,rmax,n_labels)
labels = [str(t) for t in ticks1]
plt.yticks(ticks1, labels)
plt.ylim(rmin,rmax)
# Reverse the plot
r2 = radii.max()-radii
plt.figure()
ax = plt.subplot(111,polar = True)
ax.plot(angles, r2)
ticks2 = np.linspace(rmax,rmin,n_labels)
labels = [str(t) for t in ticks1]
plt.yticks(ticks2, labels)
ax.fill_between(angles,r2,rmax,color='b',alpha = 0.1)
plt.ylim(rmin,rmax)
If you want to go with the reverse labels thing, you have to use
plt.yticks([10,20,30], ["30", "20", "10"], ...) since the first parameter corresponds to axes' values, and as you have not yet inverted it, they should remain in that order.
I checked on the plt.ylim inversion and it does end for me, but throws a rather cryptic error posx and posy should be finite values. Taking into account posx and posy are not parameters for this functions, there must be an underlying function that does not like this. In addition, having tested this for a non-polar plot, I guess the problem comes from the polar coordinates.
Looking around, I found both a github issue and an SO question, which resulted in a PR and posterior merge in Dec 2018. It should be available and work if you have the latest matplotlib version.

How to improve this seaborn countplot?

I used the following code to generate the countplot in python using seaborn:
sns.countplot( x='Genres', data=gn_s)
But I got the following output:
I can't see the items on x-axis clearly as they are overlapping. How can I correct that?
Also I would like all the items to be arranged in a decreasing order of count. How can I achieve that?
You can use choose the x-axis to be vertical, as an example:
g = sns.countplot( x='Genres', data=gn_s)
g.set_xticklabels(g.get_xticklabels(),rotation=90)
Or, you can also do:
plt.xticks(rotation=90)
Bring in matplotlib to set up an axis ahead of time, so that you can modify the axis tick labels by rotating them 90 degrees and/or changing font size. To arrange your samples in order, you need to modify the source. I assume you're starting with a pandas dataframe, so something like:
data = data.sort_values(by='Genres', ascending=False)
labels = # list of labels in the correct order, probably your data.index
fig, ax1 = plt.subplots(1,1)
sns.countplot( x='Genres', data=gn_s, ax=ax1)
ax1.set_xticklabels(labels, rotation=90)
would probably help.
edit Taking andrewnagyeb's suggestion from the comments to order the plot:
sns.countplot( x='Genres', data=gn_s, order = gn_s['Genres'].value_counts().index)

Matplotlib: Change color of individual grid lines

I've only been using Python for about a month now, so I'm sorry if there's some simple solution to this that I overlooked.
Basically I have a figure with 4 subplots, the 2 on the left show longitudinal plots and the ones on the right show scatter plots at certain points of the longitudinal plots. You can click through the scatter plots at different points of the longitudinal plot with buttons, and the tick label of the longitudinal plot you're currently at will be highlighted in blue.
Coloring a certain tick label already works with this:
xlabels = []
labelcolors = []
for i, item in enumerate(mr.segmentlst):
if re.search('SFX|MC|MQ|MS|MKC', item):
xlabels.append(mr.segmentlst[i])
else:
xlabels.append('')
for i, item in enumerate(mr.segmentlst):
if re.search('SFX', item):
labelcolors.append('black')
else:
labelcolors.append('gray')
labelcolors[self.ind]='blue'
[t.set_color(i) for (i,t) in zip(labelcolors, ax1.xaxis.get_ticklabels())]
[t.set_color(i) for (i,t) in zip(labelcolors, ax2.xaxis.get_ticklabels())]
It only shows certain tick labels and changes their colors accordingly (I don't know if there is another solution for this, it's the only one I could find). Don't mind the mr.segmentlist, I've currently hardcoded the plot to use an attribute from another method so I can easily keep testing it in Spyder.
I'd like to also change the grid line color of the currently highlighted tick label (only xgridlines are visible) in the longitudinal plots, is there some kind of similar way of doing this? I've searched the internet for a solution for about 2 hours now and didn't really find anything helpful.
I thought something like ax1.get_xgridlines() might be used, but I have no idea how I could transform it into a useful list.
Thanks,
Tamara
get_xgridlines() returns a list of Line2D objects, so if you can locate which line you want to modify, you can modify any of their properties
x = np.random.random_sample((10,))
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(x,x)
ax.grid()
a = ax.get_xgridlines()
b = a[2]
b.set_color('red')
b.set_linewidth(3)
since the above solution only works with major gridlines
(since get_gridlines() is currently hardcoded to use only the major ones),
here's how you can also access the minor gridlines by adapting
the get_gridlines() function (from here):
from matplotlib import cbook
def get_gridlines(ax, which):
'''
Parameters:
ax : ax.xaxis or ax.yaxis instance
which : 'major' or 'minor'
Returns:
The grid lines as a list of Line2D instance
'''
if which == 'major':
ticks = ax.get_major_ticks()
if which == 'minor':
ticks = ax.get_minor_ticks()
return cbook.silent_list('Line2D gridline',
[tick.gridline for tick in ticks])

Categories