Up until recently I have been using Mathematica for my plots. Although it was a real pain and everything had to be done manually, the results where very close to what I wanted. One example is the following:
I really like the grey rounded rectangle in the background of the colorbar. While everything had to be adjusted manually in Mathematica, matplotlib is a lot more automatic and already produced nice results.
But there are still two problems I have:
I don't know how to do a rounded rectangle in the background. I looked at the fancybbox patch but didn't get it to work with the colorbar as I want it to. What is the best way to get something like the Mathematica box? For the legend in plots there seems to be a fancy bbox option... but not for colorbars
When I use the "lesser sign" (<) the label of colorbar moves too far to the right. How can I adjust this (maybe even "in-between" the numbers as in the Mathematica plot)?
I am looking forward to any suggestions pointing in the right direction :).
To your second question: you can use a negative labelpad value to move the label back towards the ticklabels, like this:
import numpy as np
import matplotlib.pyplot as plt
data = np.linspace(0, 10, num=256).reshape(16,16)
cf = plt.contourf(data, levels=(0, 2.5, 5, 7.5, 10))
cb = plt.colorbar(cf)
cb.set_ticklabels([r'$<10^{0}$', 1, 2, r'$10^{14}$', r'$10^{14}+12345678$'])
cb.set_label(r'$n_e$ in $m^{-3}$', labelpad=-40, y=0.45)
plt.show()
Using the parameter y, you can additionally move the label up or down for better symmetry.
The argument of labelpad is given in points (1/72 inch). y accepts values in [0, 1], 0.0 is the lower border and 1.0 the upper.
The result:
Related
For my thesis I use the mplstereonet package to plot stereographic projections of points and planes obtained by using the ObsPy package. For my application I want to use azimuth labels that plot at a given angle outside of the circle. I am not using axis labels since they may overlap with possible data points in the centre of the circle.
The arguments of the set_azimuth_ticks function are:
positions of ticks around the circle in degrees
labels of ticks
distance of ticks from the circle. 1 is on, 0.9 is inside and 1.1 is outside the circle.
This is the code I use alongside my result:
I obtain this result:
enter image description here
As you can see the labels are way too far from the circle.
import mplstereonet
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,8))
ax = fig.add_subplot(111, projection='stereonet')
ax.grid()
ax.set_azimuth_ticks([0],['N'], frac = 0.9)
I'm noticing a difference in behaviour between a python3.7 environment (which places the labels where I expect them) and a python 3.9 environment where they are too far out as the original poster observed. As a workaround, I am using this:
import mplstereonet as mpls
fig, ax = mpls.subplots(figsize=[5, 5])
ax.set_azimuth_ticks([])
just to remove the unsightly, bizarrely far away labels.
I was experiencing same issue as OP even in python3.7. My workaround, if labels are desired, uses ax.text with ax.transAxes transformation to position labels wrt plot axes. Remove bad labels as previous answer and add the following:
...
label = np.arange(0,360,45)
labx= 0.5-0.55*np.cos(np.radians(label+90))
laby= 0.5+0.55*np.sin(np.radians(label+90))
for i in range(len(label)):
ax.text(labx[i],laby[i],str(int(label[i]))+'\N{DEGREE SIGN}', \
transform=ax.transAxes, ha='center', va='center')
Create a function with the code above if additional flexibility is needed. If you're plotting color bar or plot title you'll need to pad elements appropriately.
I am running python 3.9 and have the same issue with the ticks plotting way too far away from the axis. I found this workaround on the github site for this issue:
Add the line "ax._polar.set_position(ax.get_position())" before calling plt.show().
This resolved the issue. Hopefully they fix the code soon in mpl though
I am unsure how to customize scatterplot marker styles in Plotly scatterplots.
Specifically, I have a column predictions that is 0 or 1 (1 represents an unexpected value) and even though I used the symbol parameter in px.scatter_3d to indicate the unexpected value through varying point shape (diamond for 1 and circle for 0), the difference is very subtle and I want it to be more dramatic. I was envisioning something like below (doesn't need to be exactly this), but something along the lines of the diamond shaped points have a different outline colors or an additional shape/bubble around it. How would I do this?
Additionally, I have a set column which can take on one of two values, set A or set B. I used the color parameter inside px.scatter_3d and made that equal to set so the points are colored according to which set it came from. While it is doing what I asked, I don't want the colors to be blue and red, but any two colors I specify. How would I be able to this (let's say I want the colors to be blue and orange instead)? Thank you so much!
Here is the code I used:
fig = px.scatter_3d(X_combined, x='x', y='y', z='z',
color='set', symbol='predictions', opacity=0.7)
fig.update_traces(marker=dict(size=12,
line=dict(width=5,
color='Black')),
selector=dict(mode='markers'))
You can use multiple go.Scatter3d() statements and gather them in a list to format each and every segment or extreme values more or less exactly as you'd like. This can be a bit more demanding than using px.scatter_3d(), but it will give you more control. The following plot is produced by the snippet below:
Plot:
Code:
import plotly.graph_objects as go
import numpy as np
import pandas as pd
# sample data
t = np.linspace(0, 10, 50)
x, y, z = np.cos(t), np.sin(t), t
# plotly data
data=[go.Scatter3d(x=[x[2]], y=[y[2]], z=[z[2]],mode='markers', marker=dict(size=20), opacity=0.8),
go.Scatter3d(x=[x[26]], y=[y[26]], z=[z[26]],mode='markers', marker=dict(size=30), opacity=0.3),
go.Scatter3d(x=x, y=y, z=z,mode='markers')]
fig = go.Figure(data)
fig.show()
How you identify the different segmens, whether it be max or min values will be entirely up to you. Anyway, I hope this approach will be useful!
Using matplotlib 2.2.2 with gridspec in Python 3.6.5, I created a huge plot for a research paper with several subplots. The axes objects are stored in a dictionary called axes. This dictionary is passed to the function adjust_xticklabels(), which is supposed to align the first xticklabel slightly to the right and the last xticklabel slightly to the left in each subplot, such that the xticklabels of neighbouring plots dont get in the way of each other. The function is defined as:
def adjust_xticklabels(axes, rate = 0.1):
for ax in axes.values():
left, right = ax.get_xlim() # get boundaries
dist = right-left # get distance
xtl = ax.get_xticklabels()
if len(xtl) > 1:
xtl[0].set_position((left + rate*dist, 0.)) # (x, y), shift right
xtl[-1].set_position((right - rate*dist, 0.)) # shift left
Calling it has no effect. Of course I also tried it with ridiculously high values. However, is has an effect in y-direction, for instance in case of setting xtl[0].set_position((0.3, 0.3)).
A simple reproduction:
ax = plt.subplot(111)
ax.plot(np.arange(10))
xtl = ax.get_xticklabels()
xtl[4].set_position((0.3, 0.3)) # wlog, 4 corresponds to 6
I spent quite a while on trying to figure out if this is a feature or a bug. Did I miss something or is this a bug? Is there any other way to do the same thing?
This is a feature, no bug. The ticklabels are positionned at drawtime to sit at the correct locations according to the ticker in use. This ensures that the label always sits where the corresponding tick is located. If you change the limits, move or zoom the plot, the label always follows those changes.
You are usually not meant to change this location, but you may, by adding a custom transform to it. This is described in
Moving matplotlib xticklabels by pixel value. The general idea is to set a translating transformation on the label. E.g. to translate the second label by 20 pixels to the right,
import matplotlib.transforms as mtrans
# ...
trans = mtrans.Affine2D().translate(20, 0)
label = ax.get_xticklabels()[1]
label.set_transform(label.get_transform()+trans)
I have a pair of lists of numbers representing points in a 2-D space, and I want to represent the y/x ratios for these points as a 1-dimensional heatmap, with a diverging color map centered around 1, or the logs of my ratios, with a diverging color map centered around 0.
How do I do that?
My current attempt (borrowing somewhat from Heatmap in matplotlib with pcolor?):
from matplotlib import numpy as np
import matplotlib.pyplot as plt
# There must be a better way to generate arrays of random values
x_values = [np.random.random() for _ in range(10)]
y_values = [np.random.random() for _ in range(10)]
labels = list("abcdefghij")
ratios = np.asarray(y_values) / np.asarray(x_values)
axis = plt.gca()
# I transpose the array to get the points arranged vertically
heatmap = axis.pcolor(np.log2([ratios]).T, cmap=plt.cm.PuOr)
# Put labels left of the colour cells
axis.set_yticks(np.arange(len(labels)) + 0.5, minor=False)
# (Not sure I get the label order correct...)
axis.set_yticklabels(labels)
# I don't want ticks on the x-axis: this has no meaning here
axis.set_xticks([])
plt.show()
Some points I'm not satisfied with:
The coloured cells I obtain are horizontally-elongated rectangles. I would like to control the width of these cells and obtain a column of cells.
I would like to add a legend for the color map. heatmap.colorbar = plt.colorbar() fails with RuntimeError: No mappable was found to use for colorbar creation. First define a mappable such as an image (with imshow) or a contour set (with contourf).
One important point:
matplotlib/pyplot always leaves me confused: there seems to be a lot of ways to do things and I get lost in the documentation. I never know what would be the "clean" way to do what I want: I welcome suggestions of reading material that would help me clarify my very approximative understanding of these things.
Just 2 more lines:
axis.set_aspect('equal') # X scale matches Y scale
plt.colorbar(mappable=heatmap) # Tells plt where it should find the color info.
Can't answer your final question very well. Part of it is due to we have two branches of doing things in matplotlib: the axis way (axis.do_something...) and the MATLAB clone way plt.some_plot_method. Unfortunately we can't change that, and it is a good feature for people to migrate into matplotlib. As far as the "Clean way" is concerned, I prefer to use whatever produces the shorter code. I guess that is inline with Python motto: Simple is better than complex and Readability counts.
I would like to position a single boxplot at a custom position like so:
import pylab as plt
import numpy as np
a=np.random.randn(1000)
plt.boxplot(a, positions=np.array([2.]))
but it always appears at 1. Note that plt.violinplot(a, positions=np.array([2.])) works as expected.
I believe it is plotted at the correct position, it's just that the label on the axis is still set to 1. You can see this if you try to plot something else on the axes. For instance, if you do pyplot.plot([1, 2, 3], [3, 0, -3]) you will see that the middle of line crosses through the middle of the boxplot.
This means things will show up in the right places if you're plotting other stuff on the same axes as the boxplot. If you're not plotting anything else on the same axes, it doesn't really matter where the boxes are actually located; you can just set the labels directly by using the labels argument to boxplot.