Python: Image Background on a plot - python

I have this plot in which I can adapt the curve as I want. My problem is I need to draw on an image. I donĀ“t know how to put both together.
1
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
#theta = np.arange(0, 2*np.pi, 0.1)
#r = 1.5
#xs = r*np.cos(theta)
#ys = r*np.sin(theta)
xs = (921, 951, 993, 1035, 1065, 1045, 993, 945)
ys = (1181, 1230, 1243, 1230, 1181, 1130, 1130, 1130)
poly = Polygon(list(zip(xs, ys)), animated=True)
fig, ax = plt.subplots()
ax.add_patch(poly)
p = PolygonInteractor(ax, poly, visible=False)
ax.set_title('Click and drag a point to move it')
ax.set_xlim((800, 1300))
ax.set_ylim((1000, 1300))
plt.show()

Try call ax.imshow before draw the polygon? Like this:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from scipy import misc
xs = (21, 51, 93, 135, 100, 90, 21, 10)
ys = (111, 130, 143, 230, 501, 530, 530, 513)
poly = Polygon(list(zip(xs, ys)), color='r')
fig, ax = plt.subplots()
ax.imshow(misc.face(), origin='lower')
ax.add_patch(poly)
# ax.set_xlim([0,2000])
# ax.set_ylim([0,2000])
fig.show()
BTW, your xlim and ylim is also not proper. Your image is in the range of y=0~700, but your polygon is y=1000~1300. You at least need to ax.set_ylim([0,1400]) for your image and polygon shown together.

Related

3D data contour ploting using a kde

I have two Arrays of positional Data (X,Y) and a corresponding 1D Array of Integers (Z) that weighs the positional Data. So my Data set looks like that:
X = [ 507, 1100, 1105, 1080, 378, 398, 373]
Y = [1047, 838, 821, 838, 644, 644, 659]
Z = [ 300, 55, 15, 15, 55, 15, 15]
I want to use that Data to create a KDE thats equivalent to a KDE that gets only X and Y as input but gets the X and Y values Z times. To apply that KDE to a np.mgrid to create a contourplot.
I already got it working by just iterating over the arrays in a FOR Loop and adding Z times X and Y, but that looks to me like a rather inelegant Solution and I hope you can help me to find a better way of doing this.
You could use the weights= parameter of scipy.stats.gaussian_kde:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
import numpy as np
from scipy import stats
X = [ 507, 1100, 1105, 1080, 378, 398, 373]
Y = [1047, 838, 821, 838, 644, 644, 659]
Z = [ 300, 55, 15, 15, 55, 15, 15]
kernel = stats.gaussian_kde(np.array([X, Y]), weights=Z)
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
xs, ys = np.mgrid[0:1500:30j, 0:1500:30j]
zs = kernel(np.array([xs.ravel(), ys.ravel()])).reshape(xs.shape)
ax.plot_surface(xs, ys, zs, cmap="hot_r", lw=0.5, rstride=1, cstride=1, ec='k')
plt.show()

Matplotlib - overlaying line chart on bar chart and aligning yticks

I'm trying to plot a line chart over a bar chart, but both the ticks and the actual locations of the points aren't aligned. I would like them to be aligned. (Just a note I'm going to be plotting another set of data similarly (but reversed) on the other side, hence the subplots.)
Here's what I have so far
import matplotlib.pyplot as plt
import numpy as np
group = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]
amount1 = [967, 975, 1149, 1022, 852, 975, 1025, 1134, 994, 1057, 647, 1058]
amount2 = [286, 364, 111, 372, 333, 456, 258, 152, 400, 181, 221, 441]
f, (ax1, ax2) = plt.subplots(nrows = 1, ncols = 2, sharey=True, figsize = (17,8))
ax1_2 = ax1.twinx()
# y_pos
y_pos = np.arange(len(group))
# plot men
ax1.barh(y_pos, amount1, align = 'center')
ax1_2.plot(amount2, group, color = 'black', marker = 'o')
# ticks
ax1.set_yticks(y_pos)
ax1.set_yticklabels(group)
ax1.invert_xaxis()
ax1.yaxis.tick_right()
# padding
plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0.05, hspace=None)
plt.show()
plt.close()
I've tried setting the ticks, but the bar graph and line graph seem to have very different notions of that. I've also tried graphing both on ax1, but then the line graph goes way beyond the bar graph and they don't line up at all. I've also tried ax1_2.set_yticks(ax1.get_yticks()) but this has a similar problem.
Any help would be appreciated!
You can plot both in ax1, and remove the y_pos, because at the end both of them share the group variable as y coordinate.
Then, you can add a height to the barh plot.
Here, it is the code:
import matplotlib.pyplot as plt
group = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]
amount1 = [967, 975, 1149, 1022, 852, 975, 1025, 1134, 994, 1057, 647, 1058]
amount2 = [286, 364, 111, 372, 333, 456, 258, 152, 400, 181, 221, 441]
f, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, sharey=True, figsize=(17, 8))
# plot men
a = ax1.barh(group, amount1, height=4)
ax1.plot(amount2, group, color='black', marker='o')
# ticks
ax1.set_yticks(group)
ax1.set_yticklabels(group)
ax1.invert_xaxis()
ax1.yaxis.tick_right()
# padding
plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0.05,
hspace=None)
plt.show()
plt.close()
And an image with the result:
The main problem is that the ylims of both axes aren't aligned. The y of the barh plot goes like 0, 1, 2, 3 till 11. The y of the line plot goes from 0 to 55 in steps of 5. To align them, you could just do ax1_2.set_ylim([y * 5 for y in ax1.get_ylim()]).
An alternative would be to also use ypos for the line graph. Then the limits simply could be copied: ax1_2.set_ylim(ax1.get_ylim()).
Here is the sample code, with the second graph left out for simplicity:
import matplotlib.pyplot as plt
import numpy as np
group = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]
amount1 = [967, 975, 1149, 1022, 852, 975, 1025, 1134, 994, 1057, 647, 1058]
amount2 = [286, 364, 111, 372, 333, 456, 258, 152, 400, 181, 221, 441]
f, ax1 = plt.subplots()
ax1_2 = ax1.twinx()
# y_pos
y_pos = np.arange(len(group))
# plot men
ax1.barh(y_pos, amount1, align='center', color='turquoise')
ax1_2.plot(amount2, group, color='crimson', marker='o')
ax1_2.set_ylim([y * 5 for y in ax1.get_ylim()])
# ticks
ax1.set_yticks(y_pos)
ax1.set_yticklabels(group)
ax1.yaxis.tick_right()
ax1.invert_xaxis()
plt.show()
The plot now has the 0, 10, 20 ticks darker as they come from ax1_2. Just call ax1_2.set_yticks([]) to remove those.
PS: Still another way, is to forget about ypos and only use group also for the y-axis of ax1. Then the height of the bars needs to be adapted, e.g. to 4.5 as it is now measured in the units of group.
In code:
ax1.barh(group, amount1, align='center', color='turquoise', height=4.5)
ax1_2.plot(amount2, group, color='crimson', marker='o')
ax1_2.set_ylim(ax1.get_ylim()) # no need for labels as the ticks have the same value
ax1.set_yticks(group)
ax1_2.set_yticks([])
ax1.yaxis.tick_right()
ax1.invert_xaxis()

QhullError When Plotting Wind Barbs

When attempting to plot wind barbs using matplotlib on a cartopy map, I get a QhullError. I've never seen this error before and my code hasn't changed since I last used it. I've also made sure the packages are up to date and the grib2 file being used is valid by printing the xarray variables. Below is the code:
file = xr.open_dataset('/Users/victoralvarez/prog2/grib/&var_UGRD=on&var_VGRD=on.grb',
engine='cfgrib')
# Mask the barbs where winds < 50.
masknum = int(input('BARB THRESHOLD: '))
# Extract the lon/lat.
x = file.variables['longitude'].values
y = file.variables['latitude'].values
# Extract the desired data.
u_wind = file.variables['u'].values * units('m/s')
v_wind = file.variables['v'].values * units('m/s')
# Calculate the wind speed.
wndspd = mpcalc.wind_speed(u_wind, v_wind).to('kt')
wnds_f = wndspd.astype(float)
mask = np.ma.masked_less_equal(wnds_f, masknum).mask
u_wind[mask] = np.nan
v_wind[mask] = np.nan
fig = plt.figure(1, figsize=(15,15))
ax = plt.axes(projection=ccrs.LambertConformal(central_longitude=-100,
central_latitude=35,
standard_parallels=(30, 60)))
ax.set_extent([-121, -75, 25, 50], ccrs.PlateCarree())
ax.add_feature(cfeature.OCEAN.with_scale('50m'), facecolor='#626262',
edgecolor='black',
zorder=0,
linewidth=.5)
ax.add_feature(cfeature.LAND.with_scale('50m'), edgecolor='black',
facecolor='#626262',
zorder=1)
ax.add_feature(cfeature.STATES.with_scale('50m'), linewidth=.5,
edgecolor='black',
zorder=5)
b1 = ax.barbs(x, y, u_wind.to('kt').m, v_wind.to('kt').m,
color='black', length=4.5, regrid_shape=20, pivot='middle',
linewidth=1.5, zorder=103, transform=ccrs.PlateCarree())
b2 = ax.barbs(x, y, u_wind.to('kt').m, v_wind.to('kt').m,
color='white', length=4.5, regrid_shape=10, pivot='middle',
linewidth=0.5, zorder=104, transform=ccrs.PlateCarree())
plt.savefig('img.png', dpi=300, bbox_inches='tight')
When running the script through terminal, the below errors show:
Traceback (most recent call last):
File "winds_barb.py", line 63, in <module>
linewidth=0.5, zorder=104, transform=ccrs.PlateCarree())
File "/anaconda3/lib/python3.7/site-packages/cartopy/mpl/geoaxes.py", line 1826, in barbs
target_extent=target_extent)
File "/anaconda3/lib/python3.7/site-packages/cartopy/vector_transform.py", line 146, in vector_scalar_to_grid
return _interpolate_to_grid(nx, ny, x, y, u, v, *scalars, **kwargs)
File "/anaconda3/lib/python3.7/site-packages/cartopy/vector_transform.py", line 68, in _interpolate_to_grid
method='linear'),)
File "/anaconda3/lib/python3.7/site-packages/scipy/interpolate/ndgriddata.py", line 222, in griddata
rescale=rescale)
File "interpnd.pyx", line 248, in scipy.interpolate.interpnd.LinearNDInterpolator.__init__
File "qhull.pyx", line 1828, in scipy.spatial.qhull.Delaunay.__init__
File "qhull.pyx", line 354, in scipy.spatial.qhull._Qhull.__init__
scipy.spatial.qhull.QhullError: QH6019 qhull input error: can not scale last coordinate. Input is cocircular
or cospherical. Use option 'Qz' to add a point at infinity.
While executing: | qhull d Qz Qt Qbb Q12 Qc
Options selected for Qhull 2015.2.r 2016/01/18:
run-id 1133843321 delaunay Qz-infinity-point Qtriangulate Qbbound-last
Q12-no-wide-dup Qcoplanar-keep _pre-merge _zero-centrum Qinterior-keep
Pgood
This error is most likely caused by your input latitude coordinate containing the pole. The following code will reproduce the error you are seeing:
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import numpy as np
lons = np.array([-110, -100, -90])
lats = np.array([-90, 30, 40, 50])
u = np.ones([len(lats), len(lons)]) * 10
v = np.ones_like(u) * 10
p = ccrs.LambertConformal(
central_longitude=-100,
central_latitude=35,
standard_parallels=(30, 60),
)
ax = plt.axes(projection=p)
ax.coastlines()
ax.set_extent([-121, -75, 25, 50], crs=ccrs.PlateCarree())
ax.barbs(lons, lats, u, v, regrid_shape=3, transform=ccrs.PlateCarree())
plt.show()
But when the pole is removed there is no error:
lats = np.array([30, 40, 50])
Since your example is not runnable I cannot suggest the exact fix for your case. However, you can likely exclude such points from your input data prior to plotting to avoid this problem and still use your desired projection.
It appears the solution (sorta) to this was to simply use a different projection than the LambertConformal projection I was using originally. Not exactly sure what was wrong so this is only a circumvention to the original problem.

Why I can't smooth this curve by B-spline in python?

I check several different method, but why my curve can't be smoothed as what the others did? Here is my code and image.
from scipy.interpolate import splrep, splev
import matplotlib.pyplot as plt
list_x = [296, 297, 425, 460, 510, 532, 597, 601, 602, 611]
list_y = [2, 12, 67, 15, 21, 2037, 1995, 9, 39, 3]
bspl = splrep(list_x,list_y)
bspl_y = splev(list_x,bspl)
plt.figure()
plt.plot(list_x, bspl_y)
plt.xticks(fontsize = 10)
plt.yticks(fontsize = 10)
plt.show()
You don't see the interpolation, because you give matplotlib the same 10 data points for the interpolated curve that you use for your original data presentation. We have to create a higher resolution curve:
from scipy.interpolate import splrep, splev
import matplotlib.pyplot as plt
import numpy as np
list_x = [296, 297, 425, 460, 510, 521, 597, 601, 602, 611]
list_y = [2, 12, 67, 15, 21, 2037, 1995, 9, 39, 3]
bspl = splrep(list_x,list_y, s=0)
#values for the x axis
x_smooth = np.linspace(min(list_x), max(list_x), 1000)
#get y values from interpolated curve
bspl_y = splev(x_smooth, bspl)
plt.figure()
#original data points
plt.plot(list_x, list_y, 'rx-')
#and interpolated curve
plt.plot(x_smooth, bspl_y, 'b')
plt.xticks(fontsize = 10)
plt.yticks(fontsize = 10)
plt.show()
And this is the output we get:

Multiple Broken Axis On A Histogram in Matplotlib

So I've got some data which I wish to plot via a frequency density (unequal class width) histogram, and via some searching online, I've created this to allow me to do this.
import numpy as np
import matplotlib.pyplot as plt
plt.xkcd()
freqs = np.array([3221, 1890, 866, 529, 434, 494, 382, 92, 32, 7, 7])
bins = np.array([0, 5, 10, 15, 20, 30, 50, 100, 200, 500, 1000, 1500])
widths = bins[1:] - bins[:-1]
heights = freqs.astype(np.float)/widths
plt.xlabel('Cost in Pounds')
plt.ylabel('Frequency Density')
plt.fill_between(bins.repeat(2)[1:-1], heights.repeat(2), facecolor='steelblue')
plt.show()
As you may see however, this data stretches into the thousands on the x axis and on the y axis (density) goes from tiny data (<1) to vast data (>100). To solve this I will need to break both axis. The closest to help I've found so far is this, which I've found hard to use. Would you be able to help?
Thanks, Aj.
You could just use a bar plot. Setting the xtick labels to represent the bin values.
With logarithmic y scale
import numpy as np
import matplotlib.pyplot as plt
plt.xkcd()
fig, ax = plt.subplots()
freqs = np.array([3221, 1890, 866, 529, 434, 494, 382, 92, 32, 7, 7])
freqs = np.log10(freqs)
bins = np.array([0, 5, 10, 15, 20, 30, 50, 100, 200, 500, 1000, 1500])
width = 0.35
ind = np.arange(len(freqs))
rects1 = ax.bar(ind, freqs, width)
plt.xlabel('Cost in Pounds')
plt.ylabel('Frequency Density')
tick_labels = [ '{0} - {1}'.format(*bin) for bin in zip(bins[:-1], bins[1:])]
ax.set_xticks(ind+width)
ax.set_xticklabels(tick_labels)
fig.autofmt_xdate()
plt.show()

Categories

Resources