Get bbox in data coordinates in matplotlib - python

I have the bbox of a matplotlib.patches.Rectangle object (a bar from a bar graph) in display coordinates, like this:
Bbox(array([[ 0., 0.],[ 1., 1.]])
But I would like that not in display coordinates but data coordinates. I'm pretty sure this requires a transform. What's the method for doing this?

I'm not sure how you got the Bbox in display coordinates. Almost everything the user interacts with is in data coordinates (those look like axis or data coordinates to me, not display pixels). The following should fully explain the transforms as they apply to Bboxes:
from matplotlib import pyplot as plt
bars = plt.bar([1,2,3],[3,4,5])
ax = plt.gca()
fig = plt.gcf()
b = bars[0].get_bbox() # bbox instance
print b
# box in data coords
#Bbox(array([[ 1. , 0. ],
# [ 1.8, 3. ]]))
b2 = b.transformed(ax.transData)
print b2
# box in display coords
#Bbox(array([[ 80. , 48. ],
# [ 212.26666667, 278.4 ]]))
print b2.transformed(ax.transData.inverted())
# box back in data coords
#Bbox(array([[ 1. , 0. ],
# [ 1.8, 3. ]]))
print b2.transformed(ax.transAxes.inverted())
# box in axes coordinates
#Bbox(array([[ 0. , 0. ],
# [ 0.26666667, 0.6 ]]))

Related

How to rotate an rotation by an euler rotation in python?

What i want
Input: non-normalized axis rotation
Output: quaternion rotation, but additionally rotated by -90 degree y-axis (euler)
What i have
#!/usr/bin/env python3
#from math import radians, degrees, cos, sin, atan2, asin, pow, floor
#import numpy as np
from scipy.spatial.transform import Rotation
#r = Rotation.from_rotvec(rotation_by_axis).as_quat()
r = Rotation.from_quat([-0.0941422, 0.67905384, -0.2797612, 0.67212856]) # example
print("Input (as Euler): " + str(r.as_euler('xyz', degrees=True)))
print("Output (as Euler): " + str(r.apply([0, -90, 0])))
The result:
Input (as Euler): [-83.23902624 59.33323676 -98.88314731]
Output (as Euler): [-22.33941658 -74.31676511 45.58474405]
How to get the output [-83.23902624 -30.66676324 -98.88314731] instead?
Bad workaround
This works only sometimes (why?).
rotation = r.from_quat([rotation.x, rotation.y, rotation.z, rotation.w])
rotation = rotation.as_euler('xyz', degrees=True)
print(rotation)
rotation = r.from_euler('xyz', [rotation[0], rotation[1]-90, rotation[2]], degrees=True)
print(rotation.as_euler('xyz', degrees=True))
rotation = rotation.as_quat()
How to do it a better way?
Because sometimes i get wrong values:
[ -8.25897711 -16.54712028 -1.90525288]
[ 171.74102289 -73.45287972 178.09474712]
[ -7.18492129 22.22525264 0.44373851]
[ -7.18492129 -67.77474736 0.44373851]
[ 7.52491766 -37.71896037 -40.86915413]
[-172.47508234 -52.28103963 139.13084587]
[ -1.79610826 37.83068221 31.20184248]
[ -1.79610826 -52.16931779 31.20184248]
[-113.5719734 -54.28744892 141.73007557]
[ 66.4280266 -35.71255108 -38.26992443]
[ -83.23903078 59.33323752 -98.88315157]
[ -83.23903078 -30.66676248 -98.88315157]
[ -9.67960912 -7.23784945 13.56800885]
[ 170.32039088 -82.76215055 -166.43199115]
[ -6.21695895 5.66996884 -11.16152822]
[ -6.21695895 -84.33003116 -11.16152822]
[ 0. 0. 0. ]
[ 0. -90. 0. ]
[ 0. 0. 0. ]
[ 0. -90. 0. ]
Here wrong:
[ -8.25897711 -16.54712028 -1.90525288]
[ 171.74102289 -73.45287972 178.09474712]
Here okay:
[ -7.18492129 22.22525264 0.44373851]
[ -7.18492129 -67.77474736 0.44373851]
I require it for this:
https://github.com/Arthur151/ROMP/issues/193#issuecomment-1156960708
apply is for applying a rotation to vectors; it won't work on, e.g., Euler rotation angles, which aren't "mathematical" vectors: it doesn't make sense to add or scale them as triples of numbers.
To combine rotations, use *. So, e.g., to rotate by an additional 20 degrees about a y-axis defined by the first rotation:
In [1]: import numpy as np
In [2]: np.set_printoptions(suppress=True) # don't show round-off
In [3]: from scipy.spatial.transform import Rotation
In [4]: def e(x,y,z): return Rotation.from_euler('xyz', [x,y,z], degrees=True)
In [5]: def s(r): return r.as_euler('xyz', degrees=True)
In [6]: display(s(e(0,20,0) * e(10,0,0)))
Out[6]: array([10., 20., 0.])
However, in general this rotation won't just add to the y-component total rotation. This is because the additional rotation's axes are defined by the first rotation, but the total rotation includes everything combined:
In [7]: s(e(0,20,0) * e(0,0,10))
Out[7]: array([ 3.61644157, 19.68349808, 10.62758414])
Combining rotations as shown above is quite standard; e.g., in a multi-jointed robot, to find the orientation of the final element, you'd use the "combining" technique shown above, with one rotation per joint, defined by the appropriate axes (e.g., z for a "hip" yawing rotation, x for a "wrist" rolling rotation)
If you do need to manipulate Euler angles, your "bad workaround" is fine. Bear in mind that the middle rotation in Euler representations is normally limited to be under 90 degrees absolute value:
In [8]: s(e(0,135,0))
Out[8]: array([180., 45., 180.])

What color model does Python's matplotlib colormap use?

I need to convert some colors from RGB and for this, I need to know what the color model of matplotlib's colormap is. My best guess would be CIELab but then I'm wondering about the fourth dimension.
import matplotlib
import numpy as np
scale = np.random.rand(500)
matplotlib.cm.viridis(scale)
# output
array([[0.175707, 0.6979 , 0.491033, 1. ],
[0.127568, 0.566949, 0.550556, 1. ],
[0.166383, 0.690856, 0.496502, 1. ],
...,
[0.169646, 0.456262, 0.55803 , 1. ],
[0.139147, 0.533812, 0.555298, 1. ],
[0.120565, 0.596422, 0.543611, 1. ]])

cmap to rgba in Matplotlib

I have a specific implementation question about taking data mapped out using a colormapping (cmap) and converting it to rgba values. Essentially, I have a bunch of data which I would like to create an errorbar() plot for where the points as well as the errorbars themselves are colored by the size of some other value (for concreteness let's say it's contribution to the chi-square of the fit of some model). Let's say I have an (N,4) array called D, where the first two columns are the X and Y data, the third column is the value of the errorbar, and the last column is its contribution to the chi-square function.
How would I go about first 1) mapping the range of chi-square contribution values to a cmap, and secondly, 2) how can I get rgba values from these in order to loop over the errorbar() function to plot what I was hoping to plot?
This may actually be helpful (http://matplotlib.org/api/cm_api.html), but I'm unable to find any examples or additional information about how to use ScalarMappable() (which does have a to_rgba() method).
Thanks!
You can map scalar values to a colormap by calling the objects in matplotlib.cm on the values. The values should lie between 0 and 1. So, to get RBGA values for some chi-square distributed data (which I'll generate randomly), I would do:
chisq = np.random.chisquare(4, 8)
chisq -= chisq.min()
chisq /= chisq.max()
errorbar_colors = cm.winter(chisq)
Instead of having the color scale start and end at the minimum and maximum actual values, you could subtract off the minimum and divide by the maximum you want.
Now errorbar_colors will be a (8, 4) array of RGBA values from the winter colormap:
array([[ 0. , 0.7372549 , 0.63137255, 1. ],
[ 0. , 0.7372549 , 0.63137255, 1. ],
[ 0. , 0.4745098 , 0.7627451 , 1. ],
[ 0. , 1. , 0.5 , 1. ],
[ 0. , 0.36078431, 0.81960784, 1. ],
[ 0. , 0.47843137, 0.76078431, 1. ],
[ 0. , 0. , 1. , 1. ],
[ 0. , 0.48627451, 0.75686275, 1. ]])
To plot this, you can just iterate over the colors and the datapoints and draw errorbars:
heights = np.random.randn(8)
sem = .4
for i, (height, color) in enumerate(zip(heights, errorbar_colors)):
plt.plot([i, i], [height - sem, height + sem], c=color, lw=3)
plt.plot(heights, marker="o", ms=12, color=".3")
However, none of the built-in matplotlib colormaps are all that well-suited to this task. For some improvement, you could use seaborn to generate a sequential color palette that can be used to color lines:
import numpy as np
import seaborn
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
chisq = np.random.chisquare(4, 8)
chisq -= chisq.min()
chisq /= chisq.max()
cmap = ListedColormap(seaborn.color_palette("GnBu_d"))
errorbar_colors = cmap(chisq)
heights = np.random.randn(8)
sem = .4
for i, (height, color) in enumerate(zip(heights, errorbar_colors)):
plt.plot([i, i], [height - sem, height + sem], c=color, lw=3)
plt.plot(heights, marker="o", ms=12, color=".3")
But even here, I have doubts that this is going to be the best way to get your point across. I don't know exactly what your data look like, but I would advise making two plots, one with the dependent variable you would be plotting here, and a second with the chi square statistic as the dependent variable. Alternatively, if you're interested in the relationship between the size of the error bars and the chi square value, I would plot that directly with a scatterplot.

how do i map a a vector of numbers to colors in python?

I'm having trouble finding a way to convert a vector of numbers to a "good" color scale (ala matplotlib) so that I can simply have just the vector of the color values. This is useful for rendering some static html pages in django. Any thoughts?
Thanks for the answers folks! Really helpful!
You may use the following code:
from __future__ import division
import numpy
import matplotlib.cm as cmap
r = numpy.random.rand(1000) #random numbers
c = cmap.jet(numpy.int_((r-r.min())/r.ptp()*255)) #colors
so you try:
>>> r
array([ 0.88741281, 0.61022571, 0.14819983, 0.3695846 , 0.73832029,
0.6266069 , 0.6917337 , 0.09162752, 0.83532511, 0.54001574, ...,])
>>> c
array([[ 1. , 0.08787219, 0. , 1. ],
[ 0.83175206, 1. , 0.13598988, 1. ],
[ 0. , 0.08039216, 1. , 1. ],
...,
[ 0. , 0.72352941, 1. , 1. ],
[ 0.24984187, 1. , 0.71790006, 1. ],
[ 1. , 0.45098039, 0. , 1. ]])
which are colors each row in the format of RGBA.
you can also give a look at ColorConverter. It provides a few methods to convert valid matplotlib colors to RGB or RGBA.
Valid matplotlib colors are
a letter from the set ‘rgbcmykw’
a hex color string, like ‘#00FFFF’
a standard name, like ‘aqua’ a float, like ‘0.4’, indicating gray on
a 0-1 scale

Why do I get rows of zeros in my 2D fft?

I am trying to replicate the results from a paper.
"Two-dimensional Fourier Transform (2D-FT) in space and time along sections of constant latitude (east-west) and longitude (north-south) were used to characterize the spectrum of the simulated flux variability south of 40degS." - Lenton et al(2006)
The figures published show "the log of the variance of the 2D-FT".
I have tried to create an array consisting of the seasonal cycle of similar data as well as the noise. I have defined the noise as the original array minus the signal array.
Here is the code that I used to plot the 2D-FT of the signal array averaged in latitude:
import numpy as np
from numpy import ma
from matplotlib import pyplot as plt
from Scientific.IO.NetCDF import NetCDFFile
### input directory
indir = '/home/nicholas/data/'
### get the flux data which is in
### [time(5day ave for 10 years),latitude,longitude]
nc = NetCDFFile(indir + 'CFLX_2000_2009.nc','r')
cflux_southern_ocean = nc.variables['Cflx'][:,10:50,:]
cflux_southern_ocean = ma.masked_values(cflux_southern_ocean,1e+20) # mask land
nc.close()
cflux = cflux_southern_ocean*1e08 # change units of data from mmol/m^2/s
### create an array that consists of the seasonal signal fro each pixel
year_stack = np.split(cflux, 10, axis=0)
year_stack = np.array(year_stack)
signal_array = np.tile(np.mean(year_stack, axis=0), (10, 1, 1))
signal_array = ma.masked_where(signal_array > 1e20, signal_array) # need to mask
### average the array over latitude(or longitude)
signal_time_lon = ma.mean(signal_array, axis=1)
### do a 2D Fourier Transform of the time/space image
ft = np.fft.fft2(signal_time_lon)
mgft = np.abs(ft)
ps = mgft**2
log_ps = np.log(mgft)
log_mgft= np.log(mgft)
Every second row of the ft consists completely of zeros. Why is this?
Would it be acceptable to add a randomly small number to the signal to avoid this.
signal_time_lon = signal_time_lon + np.random.randint(0,9,size=(730, 182))*1e-05
EDIT: Adding images and clarify meaning
The output of rfft2 still appears to be a complex array. Using fftshift shifts the edges of the image to the centre; I still have a power spectrum regardless. I expect that the reason that I get rows of zeros is that I have re-created the timeseries for each pixel. The ft[0, 0] pixel contains the mean of the signal. So the ft[1, 0] corresponds to a sinusoid with one cycle over the entire signal in the rows of the starting image.
Here are is the starting image using following code:
plt.pcolormesh(signal_time_lon); plt.colorbar(); plt.axis('tight')
Here is result using following code:
ft = np.fft.rfft2(signal_time_lon)
mgft = np.abs(ft)
ps = mgft**2
log_ps = np.log1p(mgft)
plt.pcolormesh(log_ps); plt.colorbar(); plt.axis('tight')
It may not be clear in the image but it is only every second row that contains completely zeros. Every tenth pixel (log_ps[10, 0]) is a high value. The other pixels (log_ps[2, 0], log_ps[4, 0] etc) have very low values.
Consider the following example:
In [59]: from scipy import absolute, fft
In [60]: absolute(fft([1,2,3,4]))
Out[60]: array([ 10. , 2.82842712, 2. , 2.82842712])
In [61]: absolute(fft([1,2,3,4, 1,2,3,4]))
Out[61]:
array([ 20. , 0. , 5.65685425, 0. ,
4. , 0. , 5.65685425, 0. ])
In [62]: absolute(fft([1,2,3,4, 1,2,3,4, 1,2,3,4]))
Out[62]:
array([ 30. , 0. , 0. , 8.48528137,
0. , 0. , 6. , 0. ,
0. , 8.48528137, 0. , 0. ])
If X[k] = fft(x), and Y[k] = fft([x x]), then Y[2k] = 2*X[k] for k in {0, 1, ..., N-1} and zero otherwise.
Therefore, I would look into how your signal_time_lon is being tiled. That may be where the problem lies.

Categories

Resources