How to rotate a simple matplotlib Axes - python

is it possible to rotate matplotlib.axes.Axes as it is for matplotlib.text.Text
# text and Axes instance
t = figure.text(0.5,0.5,"some text")
a = figure.add_axes([0.1,0.1,0.8,0.8])
# rotation
t.set_rotation(angle)
a.set_rotation()???
a simple set_rotation on a text instance will rotate the text by the angle value about its coordinates axes. Is there any way to do to same for the axes instance ?

Are you asking how to rotate the entire axes (and not just the text)?
If so, yes, it's possible, but you have to know the extents of the plot beforehand.
You'll have to use axisartist, which allows more complex relationships like this, but is a bit more complex and not meant for interactive visualization. If you try to zoom, etc, you'll run into problems.
import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D
import mpl_toolkits.axisartist.floating_axes as floating_axes
fig = plt.figure()
plot_extents = 0, 10, 0, 10
transform = Affine2D().rotate_deg(45)
helper = floating_axes.GridHelperCurveLinear(transform, plot_extents)
ax = floating_axes.FloatingSubplot(fig, 111, grid_helper=helper)
fig.add_subplot(ax)
plt.show()

Yes, it is possible. But you have to rotate each label separately. Therefore, you can try using an iteration:
from matplotlib import pyplot as plt
figure = plt.figure()
ax = figure.add_subplot(111)
t = figure.text(0.5,0.5,"some text")
t.set_rotation(90)
labels = ax.get_xticklabels()
for label in labels:
label.set_rotation(45)
plt.show()

Related

Adding the range of colorbar to a pandas subplot

I am trying to add a colorbar to the right side of my plot. It should show the color to the current iteration :
the code for my custom colorbar:
BLUE = '#00549F'
LIGHT_BLUE = "#407FB7"
LIGHTER_BLUE = '#8EBAE5'
LIGHTEST_BLUE = '#C7DDF2'
cmap = mcolors.LinearSegmentedColormap.from_list("n", [LIGHTEST_BLUE,LIGHTER_BLUE,LIGHT_BLUE,BLUE])
The plot shows the Force of a machine that repeats the same process like 3000 times. Now I want to color code the iteration number. Pandas offers the .plot(cmap=cmap) function which does that, but I don't know how to add the colorbar as an orientation for the viewer.
Code for the plots:
fig,axs = plt.subplots(2,1,figsize=(10,8))
df_data_blanking_stempel.plot(legend=False,colormap=cmap,ax=axs[0])
axs[0].set_title('Stamp')
axs[0].set_ylabel('Force[Newton]')
df_data_blanking_niederhalter.plot(legend=False,colormap=cmap,ax = axs[1])
axs[1].set_ylabel('Force[Newton]')
axs[1].set_xlabel('Time')
axs[1].set_title('Blankholder')
plt.suptitle(r'\textbf{Force Trajectories}',fontsize=16)
plt.show()
One idea I have, is to generate the colorbar with a heatmap and add adjust and add it later with photoshop. But I am sure there is a better solution.
Custom colorbars can be created from just a colormap (optionally together with a norm). Here is an example (as the question doesn't contain example data, some dummy labels are used):
from matplotlib import pyplot as plt
from matplotlib.cm import ScalarMappable
from matplotlib.colors import LinearSegmentedColormap
import numpy as np
BLUE = '#00549F'
LIGHT_BLUE = "#407FB7"
LIGHTER_BLUE = '#8EBAE5'
LIGHTEST_BLUE = '#C7DDF2'
cmap = LinearSegmentedColormap.from_list("n", [LIGHTEST_BLUE, LIGHTER_BLUE, LIGHT_BLUE, BLUE])
fig, axs = plt.subplots(nrows=2)
for ax in axs:
cbar = plt.colorbar(ScalarMappable(cmap=cmap), ax=ax, ticks=np.linspace(0, 1, 4))
cbar.ax.yaxis.set_ticklabels(['first', 'second', 'third', 'fourth'])
plt.tight_layout()
plt.show()
To create a colorbar similar to the one of the example heatmap, one could use:
plt.colorbar(ScalarMappable(cmap=cmap, norm=plt.Normalize(0, 850000)), ax=ax)

Add my own picture as marker in animation in Python [duplicate]

I would like to utilize customer markers in both scatter and line charts. How can I make custom marker out of a PNG file?
I don't believe matplotlib can customize markers like that. See here for the level of customization, which falls way short of what you need.
As an alternative, I've coded up this kludge which uses matplotlib.image to place images at the line point locations.
import matplotlib.pyplot as plt
from matplotlib import image
# constant
dpi = 72
path = 'smile.png'
# read in our png file
im = image.imread(path)
image_size = im.shape[1], im.shape[0]
fig = plt.figure(dpi=dpi)
ax = fig.add_subplot(111)
# plot our line with transparent markers, and markersize the size of our image
line, = ax.plot((1,2,3,4),(1,2,3,4),"bo",mfc="None",mec="None",markersize=image_size[0] * (dpi/ 96))
# we need to make the frame transparent so the image can be seen
# only in trunk can you put the image on top of the plot, see this link:
# http://www.mail-archive.com/matplotlib-users#lists.sourceforge.net/msg14534.html
ax.patch.set_alpha(0)
ax.set_xlim((0,5))
ax.set_ylim((0,5))
# translate point positions to pixel positions
# figimage needs pixels not points
line._transform_path()
path, affine = line._transformed_path.get_transformed_points_and_affine()
path = affine.transform_path(path)
for pixelPoint in path.vertices:
# place image at point, centering it
fig.figimage(im,pixelPoint[0]-image_size[0]/2,pixelPoint[1]-image_size[1]/2,origin="upper")
plt.show()
Produces:
Following on from Mark's answer. I just thought I would add to this a bit because I tried to run this and it does what I want with the exception of actually displaying the icons on the graph. Maybe something has changed with matplotlib. It has been 4 years.
The line of code that reads:
ax.get_frame().set_alpha(0)
does not seem to work, however
ax.patch.set_alpha(0)
does work.
The other answer may lead to problems when resizing the figure. Here is a different approach, positionning the images inside annotation boxes, which are anchored in data coordinates.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
path = "https://upload.wikimedia.org/wikipedia/commons/b/b5/Tango-example_icons.png"
image = plt.imread(path)[116:116+30, 236:236+30]
x = np.arange(10)
y = np.random.rand(10)
fig, ax = plt.subplots()
ax.plot(x,y)
def plot_images(x, y, image, ax=None):
ax = ax or plt.gca()
for xi, yi in zip(x,y):
im = OffsetImage(image, zoom=72/ax.figure.dpi)
im.image.axes = ax
ab = AnnotationBbox(im, (xi,yi), frameon=False, pad=0.0,)
ax.add_artist(ab)
plot_images(x, y, image, ax=ax)
plt.show()

Aligning annotated text with colorbar label text

I'd like to find a way to make an annotation that automatically aligns with the label text of a colorbar. Take this example:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(5,10))
data = np.arange(1000, 0, -10).reshape(10, 10)
im = ax.imshow(data, cmap='Blues')
clb = plt.colorbar(im, shrink=0.4)
clb.ax.annotate('text', xy=(1, -0.075), xycoords='axes fraction')
I want to have the last t of "text" to be on the same x coordinate as the last 0 of 1000 in the colorbar label. I can do so manually by adjusting the xy parameter in annotate, but I have to do this for many graphs and would like to find a way to get the parameter from somewhere automatically.
How can I get the maximum x coordinate of the text labes and annotate in a way where the annotation ends on that coordinate? Could someone point me in the right direction? Thanks a lot!
Since the labels are left-aligned, but you want to align your additional text according to the end of that label, I fear there is no other choice than to find out the coordinates from the drawn figure and place the label accordingly.
import matplotlib.pyplot as plt
from matplotlib import transforms
import numpy as np
fig, ax = plt.subplots(figsize=(5,4))
data = np.arange(1000, 0, -10).reshape(10, 10)
im = ax.imshow(data, cmap='Blues')
cbar = plt.colorbar(im)
# draw figure first to be able to retrieve coordinates
fig.canvas.draw()
# get the bounding box of the last label
bbox = cbar.ax.get_yticklabels()[-1].get_window_extent()
# calculate pixels back to axes coords
labx,_ = cbar.ax.transAxes.inverted().transform([bbox.x1,0])
ax.annotate('text', xy=(labx, -0.075), xycoords=cbar.ax.transAxes,
ha = "right")
plt.show()
Note that this approach will fail once you change the figure size afterwards or change the layout in any other way. It should hence always come last in your code.

Add Second Colorbar to a Seaborn Heatmap / Clustermap

I was trying to help someone add a colorbar for the vertical blue bar in the image below. We tried many variations of plt.colorbar(row_colors) (like above and below sns.clustermap()) and looked around online for 2 hours, but no luck. We just want to add a colorbar for the blues, please help!
import pickle
import numpy as np
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
feat_mat, freq, label = pickle.load(open('file.pkl', 'rb'))
feat_mat_df = pd.DataFrame(feat_mat[4])
freq_df = pd.DataFrame(freq)
freq_df_transposed = freq_df.transpose()
my_palette = dict(zip(set(freq_df_transposed[int('4')]), sns.color_palette("PuBu", len(set(freq_df_transposed[int('4')]))))))
row_colors = freq_df_transposed[int('4')].map(my_palette)
sns.clustermap(feat_mat_df, metric="euclidean", standard_scale=1, method="complete", cmap="coolwarm", row_colors = row_colors)
plt.show()
This is where he based his code from: #405 Dendrogram with heatmap and coloured leaves
I think something like this should work for your purposes- I didn't have a clustermap example available but the logic is the same to do what you want to do. Basically-you're going to take that list of colors you made and imshow it, then hide the imshow plot, and plot the colorbar in its place.
In my example, I use make_axes_locatable to place axes next to the plot with your data to put the colorbar inside - https://matplotlib.org/2.0.2/mpl_toolkits/axes_grid/users/overview.html. I find placing a new axes for other objects (legends color maps or otherwise) easier than trying to draw them on the same axes.
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(0)
import seaborn as sns
from mpl_toolkits.axes_grid1 import make_axes_locatable
import random
uniform_data = np.random.rand(10, 12)
fig, ax = plt.subplots(1,1, figsize = (5,5))
divider = make_axes_locatable(ax)
axDivY = divider.append_axes( 'right', size=0.2, pad= 0.1)
axDivY2 = divider.append_axes( 'right', size=0.2, pad= 0.2)
# we will use this for the colorscale bar
axDivY3 = divider.append_axes( 'right', size=0.2, pad= 0.2)
ax1 = sns.heatmap(uniform_data, ax=ax, cbar_ax=axDivY)
# the palette you were using to make the label column on the clustermap
# some simulated labels for your data with values
color_label_list =[random.randint(0,20) for i in range(20)]
pal = sns.color_palette("PuBu", len(set(color_label_list)))
n = len(pal)
size = 1
# plot the colors with imshow to make a colormap later
ax2 = axDivY2.imshow(np.array([color_label_list]),
cmap=mpl.colors.ListedColormap(list(pal)),
interpolation="nearest", aspect="auto")
# turn off the axes so they aren't visible- note that you need ax.axis('off) if you have older matplotlib
axDivY2.set_axis_off()
axDivY2.set_visible(False)
# plot the colorbar on the other axes (which is on top of the one that we turned off)
plt.colorbar(ax2, cax = axDivY3) ;

Overlaying a lineCollection on a plot in matplotlib - how to get the two to line up.

I'm trying to do a heat map over a shape file in python. I need to make quite a few of these so don't want to read in the .shp every time.
Instead, I thought I could create a lineCollection instance of the map boundaries and overlay the two images. Problem is - I can't seem to get the two to line up correctly.
Here is the code, where linecol is the lineCollection object.
fig = plt.figure()
ax = fig.add_subplot(111)
ax.contourf(xi,yi,zi)
ax.add_collection(linecol, autolim = False)
plt.show()
Is there an easy way to fix the limits of linecol to match those of the other plot? I've had a play with set_xlim and transforms.Bbox, but can't seem to manage it.
Thank you very much for your help!
Transforms are tricky because of the various coordinate systems involved. See http://matplotlib.sourceforge.net/users/transforms_tutorial.html.
I managed to scale a LineCollection to the appropriate size like this. The key was to realize that I needed to add + ax.transData to the new transform I set on the LineCollection. (When you don't set any transform on an artist object, ax.transData is the default. It converts data coordinates into display coordinates.)
from matplotlib import cm
import matplotlib.pyplot as plt
import matplotlib.collections as mc
import matplotlib.transforms as tx
import numpy as np
fig = plt.figure()
# Heat map spans 1 x 1.
ax = fig.add_subplot(111)
xs = ys = np.arange(0, 1.01, 0.01)
zs = np.random.random((101,101))
ax.contourf(xs, ys, zs, cmap=cm.autumn)
lines = mc.LineCollection([[(5,1), (9,5), (5,9), (1,5), (5,1)]])
# Shape spans 10 x 10. Resize it to 1 x 1 before applying the transform from
# data coords to display coords.
trans = tx.Affine2D().scale(0.1) + ax.transData
lines.set_transform(trans)
ax.add_collection(lines)
plt.show()
(Output here: http://i.stack.imgur.com/hDNN8.png Not enough reputation to post inline.)
It should be easy to modify this if you need the shape translated or scaled unequally on x and y.

Categories