I try to plot simple rotation matrix result with list data. but My figure with result array have so many index as screen dump image. and the second plot is not exact with my attribute(line style, etc.)
I guess that I do mistake array handling to plot but don't know what.
Any comments are welcome. Thanks in advance.
My code is below.
import numpy as np
import matplotlib.pyplot as plt
theta = np.radians(30)
c, s = np.cos(theta), np.sin(theta)
R = np.matrix('{} {}; {} {}'.format(c, -s, s, c))
x = [-9, -8, -7, -6, -5, -4, -3, -2, -1,0,1,2,3,4,5,6,7,8,9]
y = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
line_b = [x,y]
result_a = R*np.array(line_b)
fig=plt.figure()
ax1 = fig.add_subplot(111)
plt.plot(line_b[0],line_b[1], color="blue", linewidth=2.5, linestyle="-", label='measured')
plt.plot(result_a[0], result_a[1], 'r*-', label='rotated')
ax1.set_ylim(-10,10)
ax1.set_xlim(-10,10)
plt.legend()
# axis center to move 0,0
ax1.spines['right'].set_color('none')
ax1.spines['top'].set_color('none')
ax1.xaxis.set_ticks_position('bottom')
ax1.spines['bottom'].set_position(('data',0))
ax1.yaxis.set_ticks_position('left')
ax1.spines['left'].set_position(('data',0))
plt.show()
The issue is that you are trying to plot the two rows of result_a as if they were 1-dimensional np.ndarrays, when in fact they are np.matrix which are always 2-dimensional. See for yourself:
>>> result_a[0].shape
(1, 19)
To remedy this, you need to convert your vectors result_a[0], result_a[1] to arrays. Simple ways can be found in this answer. For example,
rx = result_a[0].A1
ry = result_a[1].A1
# alternatively, the more compact
# rx, ry = np.array(result_a)
plt.plot(rx, ry, 'r*-', label='rotated')
yields the following (with plt.legend(); plt.show()):
Related
I would like to plot a function of two variables in python. Similar to this article, we can obtain an output like
using this code:
from numpy import exp,arange
from pylab import meshgrid,cm,imshow,contour,clabel,colorbar,axis,title,show
from matplotlib import pyplot
# the function that I'm going to plot
def z_func(x,y):
return (1-(x**2+y**3))*exp(-(x**2+y**2)/2)
x = arange(-3.0,3.0,0.1)
y = arange(-3.0,3.0,0.1)
z = [[0] * y.__len__() for i in range(x.__len__())]
for i in range(0, x.__len__()):
for j in range(0, y.__len__()):
z[j][i] = z_func(x[i], y[j])
im = imshow(z,cmap=cm.RdBu, extent = [-3, 3, -3, 3], interpolation = "none", origin='lower') # drawing the function
# adding the Contour lines with labels
cset = contour(z,arange(-1,1.5,0.2),linewidths=2,cmap=cm.Set2)
clabel(cset,inline=True,fmt='%1.1f',fontsize=10)
colorbar(im) # adding the colobar on the right
# latex fashion title
title('$z=(1-x^2+y^3) e^{-(x^2+y^2)/2}$')
show()
As you can see, the x- and y-labels go from 0 to 59 (which is the count of elements in x and y). How can I correct these values such that they range from -3 to 3?
A minor sub-question: Why do I need to "transpose" in z[j][i] = z_func(x[i], y[j])? Does Python treat the first dimension as "column" and the second as "row"?
You're trying to plot both the z-function and the countour plots. You need to add the "extent" parameter to matplotlib.pyplot.countour plot too.
cset = contour(z, arange(-1,1.5,0.2),
extent = [-3, 3, -3, 3],
linewidths = 2,
cmap = cm.Set2)
I am trying to plot vector addition and I am not getting the result as expected, I am completely new at 3D plotting I need serious help
My plot looks like this:
What I want is to connect the green line to the head of the two arrows. My code looks something like this:
import numpy as np
import matplotlib.pyplot as plt
u = np.array([1, 2, 3]) # vector u
v = np.array([5, 6, 2]) # vector v:
fig = plt.figure()
ax = plt.axes(projection = "3d")
start = [0,0,0]
ax.quiver(start[0],start[1],start[2],u[0],u[1],u[2],color='red')
ax.quiver(start[0],start[1],start[2],v[0],v[1],v[2])
ax.quiver(v[0],v[1],v[2],u[0],u[1],u[2],color="green")
ax.set_xlim([-1,10])
ax.set_ylim([-10,10])
ax.set_zlim([0,10])
plt.show()
Apologies for any kind of mistake , thnks
it's vector addition, just add the vectors
sum_vector = u+v
ax.quiver(start[0], start[1], start[2], sum_vector[0], sum_vector[1], sum_vector[2], color="green")
I have to confess I still have problems understanding the proper setup and relation of the plots and the parts of it with matplotlib, is still confusing how fig with plt with ax relates each other so I just has gone trial and error, docs are sometimes more confusing to me. :-(
I am plotting weather values, from a json and got points. that I can plot with the following code like the image below
fig=plt.figure(figsize=(10,8))
ax=fig.add_subplot(1,1,1,projection=mapcrs)
ax.set_extent([-93,-86,13,19],datacrs)
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.BORDERS, linestyle=':')
ax.scatter(lon,lat,c=dat,transform=datacrs)
and I am able to plot the map
Then I generate interpolation using metpy with this code
gridx, gridy, gridz = interpolate_to_grid(lon, lat, dat, interp_type='rbf', hres=.1, rbf_func='linear', rbf_smooth=0)
fig=plt.figure(figsize=(15,15))
ax=fig.add_subplot(1,1,1,projection=mapcrs)
#ax = fig.add_axes([0, 0, 1, 1], projection=mapcrs)
#ax.set_extent([-93,-86,13,19])
#ax.add_feature(cfeature.COASTLINE)
#ax.add_feature(cfeature.BORDERS, linestyle=':')
ax.contourf(gridx,gridy,gridz,levels=np.arange(10,60,2),cmap='viridis')
plt.plot(lon,lat,'k.',color='white')
I got the interpolation of points as desired but cannot show the features, how is the way to do it? If I uncomment the ax.extent all I see is an empty white figure. If I uncomment the ax.features the interpolation show as the below image but not the map.
thanks for any help and guidance.
You are missing the transform keyword argument in the contourf function in order to give the coordinate system of the interpolated data. Here is a minimal working example with random data, with the obtained output below:
import numpy as np
from cartopy import crs, feature
from matplotlib import pyplot as plt
from scipy.interpolate import griddata
# figure
fig = plt.figure(figsize=(5, 5))
# coordinate systems
crs_map = crs.Mercator()
crs_data = crs.PlateCarree()
# random data
np.random.seed(42) # for repro.
n = 100
lon = -89 + 2 * np.random.randn(n)
lat = 16 + 2 * np.random.randn(n)
dat = np.random.rand(n)
# interpolated data
ilon = np.linspace(-93, -86, 200)
ilat = np.linspace(13, 19, 200)
ilon, ilat = np.meshgrid(ilon, ilat)
idat = griddata((lon, lat), dat, (ilon, ilat), method="linear")
# show up
ax = fig.add_subplot(1, 1, 1, projection=crs_map)
ax.set_extent([-93, -86, 13, 19], crs_data)
ax.add_feature(feature.COASTLINE)
ax.add_feature(feature.BORDERS, ls=":", lw=0.5)
ax.scatter(lon, lat, c=dat, transform=crs_data) # this is invisible with contour
ax.plot(lon, lat, "k.", transform=crs_data) # in order to see the points
ax.contourf(ilon, ilat, idat, levels=np.linspace(0, 1, 10), transform=crs_data)
I have a dataset of N=910 probabilities, and hte probabilites are represented as all integers between 5 and 90 that are divisible by 5. This constitutes my x input. Each probability has a boolean response associated with it, the booleans being encoded using a 0 for false and a 1 for true. Some code to recreate this.
x_inpt = np.random.choice(np.arange(5, 91, 5), 910)
y_inpt = np.random.choice([0, 1], 910)
A lot of the line plots for my actual data look like this.
(and for curiosity sake, here's the original code used for this plot)
plt.scatter(x_inpt, y_inpt)
plt.ylabel("Decisions On Adminstering Experimental Treatment")
plt.xlabel("Harm probabilities")
plt.xticks(range(0, 101, 10))
plt.yticks([0.0, 1.0], labels=["No", "Yes"])
title_str = "Pilot Data From " + str(exp_count) + " Experiments / " + str(num_trials) + " trials"
plt.title(title_str)
plt.tight_layout()
plt.show()
Even though this image has 910 data points, they all get placed on top of one another other. There's multiple instances of the same data point, or multiple instances of the same x y coordinate being plotted in my data.
I wanted to find a way to make data points that have the most instances be darker (or lighter) just to make this graph more clearly informative.
But I'm not really sure how to, and my code is stuck looking like the code sample I posted for the above plot. I seem to be having a rough time parsing matplotlib documentation and figuring out how to implement this.
A perhaps silly solution to this would be something like hashing each point based on (x,y) so it always is unique and counting this up:
# hash (x_inpt,y_input)
def hash(x,y):
# Dummy sum since we have two nice integer arrays
return x+y
hashed_output = hash(x_inpt, y_inpt)
x_y_weights = np.bincount(hashed_output)
color_for_each_sample = x_y_weights[hashed_output]
...
plt.scatter(x_inpt, y_inpt, c=color_for_each_sample)
plt.colorbar()
...
I'm working on a more elegant version now
If you don't mind pandas, you could use something like this
import pandas as pd
df = pd.DataFrame({'x':x_inpt, 'y':y_inpt})
grp = df.groupby(['x','y']).size().reset_index()
a = plt.scatter(grp['x'], grp['y'], c=grp[0], cmap='cool')
cbar = plt.colorbar()
cbar.ax.set_ylabel('Number of points', rotation=-90, va="bottom")
plt.ylabel("Decisions On Adminstering Experimental Treatment")
plt.xlabel("Harm probabilities")
plt.xticks(range(0, 101, 10))
plt.yticks([0.0, 1.0], labels=["No", "Yes"])
title_str = "Pilot Data"
plt.title(title_str)
plt.tight_layout()
plt.show()
Here is a solution using a counter to count each x,y pair. And then use scatter to either change the color or the size of the dots. Or even a number in text form. The size is proportional to the area of the dot, so I squared it in the demo below.
Just to show the possibilities, the three ways are combined in the experimental code. In practise, you'd probably only use one of the methods.
from matplotlib import pyplot as plt
import numpy as np
from collections import Counter
num_trials = 910
x_inpt = np.random.choice(np.arange(5, 91, 5), num_trials)
y_inpt = np.random.choice([0, 1], num_trials)
count = Counter(zip(x_inpt, y_inpt))
xs = np.array([x for (x, y), c in count.items()])
ys = np.array([y for (x, y), c in count.items()])
cs = np.array([c for (x, y), c in count.items()])
cmin = cs.min()
cmax = cs.max()
cmid = (cmin + cmax) / 2
fig, ax = plt.subplots(figsize=(12, 3))
plt.scatter(xs, ys, c=cs, cmap='plasma', s=1200*cs*cs/(cmax * cmax))
for (x, y), c in count.items():
# the maximum fontsize is set to 22
# the color is either white or black the contrast with the color of the scatter dot
ax.text(x, y, c, color='w' if c<cmid else 'k', fontsize=22*c/cmax, ha='center', va='center')
cbar = plt.colorbar()
cbar.ax.set_title('Counts')
plt.ylabel("Decisions On Adminstering\nExperimental Treatment")
plt.xlabel("Harm probabilities")
plt.xticks(range(0, 91, 10))
plt.ylim(-0.5, 1.5)
plt.yticks([0, 1], labels=["No", "Yes"])
title_str = f"Pilot Data From {20} Experiments / {num_trials} trials"
plt.title(title_str)
plt.tight_layout()
plt.show()
Here is another example, supposing the data has a binomial distribution and using the reversed colormap without the numbers.
y_inpt = np.random.choice([0, 1], num_trials)
x_inpt = np.where(y_inpt == 0,
np.random.binomial(20, 0.5, num_trials),
np.random.binomial(20, 0.3, num_trials)) * 5
I would like to plot a vector field with curved arrows in python, as can be done in vfplot (see below) or IDL.
You can get close in matplotlib, but using quiver() limits you to straight vectors (see below left) whereas streamplot() doesn't seem to permit meaningful control over arrow length or arrowhead position (see below right), even when changing integration_direction, density, and maxlength.
So, is there a python library that can do this? Or is there a way of getting matplotlib to do it?
If you look at the streamplot.py that is included in matplotlib, on lines 196 - 202 (ish, idk if this has changed between versions - I'm on matplotlib 2.1.2) we see the following:
... (to line 195)
# Add arrows half way along each trajectory.
s = np.cumsum(np.sqrt(np.diff(tx) ** 2 + np.diff(ty) ** 2))
n = np.searchsorted(s, s[-1] / 2.)
arrow_tail = (tx[n], ty[n])
arrow_head = (np.mean(tx[n:n + 2]), np.mean(ty[n:n + 2]))
... (after line 196)
changing that part to this will do the trick (changing assignment of n):
... (to line 195)
# Add arrows half way along each trajectory.
s = np.cumsum(np.sqrt(np.diff(tx) ** 2 + np.diff(ty) ** 2))
n = np.searchsorted(s, s[-1]) ### THIS IS THE EDITED LINE! ###
arrow_tail = (tx[n], ty[n])
arrow_head = (np.mean(tx[n:n + 2]), np.mean(ty[n:n + 2]))
... (after line 196)
If you modify this to put the arrow at the end, then you could generate the arrows more to your liking.
Additionally, from the docs at the top of the function, we see the following:
*linewidth* : numeric or 2d array
vary linewidth when given a 2d array with the same shape as velocities.
The linewidth can be a numpy.ndarray, and if you can pre-calculate the desired width of your arrows, you'll be able to modify the pencil width while drawing the arrows. It looks like this part has already been done for you.
So, in combination with shortening the arrows maxlength, increasing the density, and adding start_points, as well as tweaking the function to put the arrow at the end instead of the middle, you could get your desired graph.
With these modifications, and the following code, I was able to get a result much closer to what you wanted:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import matplotlib.patches as pat
w = 3
Y, X = np.mgrid[-w:w:100j, -w:w:100j]
U = -1 - X**2 + Y
V = 1 + X - Y**2
speed = np.sqrt(U*U + V*V)
fig = plt.figure(figsize=(14, 18))
gs = gridspec.GridSpec(nrows=3, ncols=2, height_ratios=[1, 1, 2])
grains = 10
tmp = tuple([x]*grains for x in np.linspace(-2, 2, grains))
xs = []
for x in tmp:
xs += x
ys = tuple(np.linspace(-2, 2, grains))*grains
seed_points = np.array([list(xs), list(ys)])
# Varying color along a streamline
ax1 = fig.add_subplot(gs[0, 1])
strm = ax1.streamplot(X, Y, U, V, color=U, linewidth=np.array(5*np.random.random_sample((100, 100))**2 + 1), cmap='winter', density=10,
minlength=0.001, maxlength = 0.07, arrowstyle='fancy',
integration_direction='forward', start_points = seed_points.T)
fig.colorbar(strm.lines)
ax1.set_title('Varying Color')
plt.tight_layout()
plt.show()
tl;dr: go copy the source code, and change it to put the arrows at the end of each path, instead of in the middle. Then use your streamplot instead of the matplotlib streamplot.
Edit: I got the linewidths to vary
Starting with David Culbreth's modification, I rewrote chunks of the streamplot function to achieve the desired behaviour. Slightly too numerous to specify them all here, but it includes a length-normalising method and disables the trajectory-overlap checking. I've appended two comparisons of the new curved quiver function with the original streamplot and quiver.
Here's a way to obtain the desired output in vanilla pyplot (i.e., without modifying the streamplot function or anything that fancy). For reminder, the goal is to visualize a vector field with curved arrows whose length is proportional to the norm of the vector.
The trick is to:
make streamplot with no arrows that is traced backward from a given point (see)
plot a quiver from that point. Make the quiver small enough so that only the arrow is visible
repeat 1. and 2. in a loop for every seed and scale the length of the streamplot to be proportional to the norm of the vector.
import matplotlib.pyplot as plt
import numpy as np
w = 3
Y, X = np.mgrid[-w:w:8j, -w:w:8j]
U = -Y
V = X
norm = np.sqrt(U**2 + V**2)
norm_flat = norm.flatten()
start_points = np.array([X.flatten(),Y.flatten()]).T
plt.clf()
scale = .2/np.max(norm)
plt.subplot(121)
plt.title('scaling only the length')
for i in range(start_points.shape[0]):
plt.streamplot(X,Y,U,V, color='k', start_points=np.array([start_points[i,:]]),minlength=.95*norm_flat[i]*scale, maxlength=1.0*norm_flat[i]*scale,
integration_direction='backward', density=10, arrowsize=0.0)
plt.quiver(X,Y,U/norm, V/norm,scale=30)
plt.axis('square')
plt.subplot(122)
plt.title('scaling length, arrowhead and linewidth')
for i in range(start_points.shape[0]):
plt.streamplot(X,Y,U,V, color='k', start_points=np.array([start_points[i,:]]),minlength=.95*norm_flat[i]*scale, maxlength=1.0*norm_flat[i]*scale,
integration_direction='backward', density=10, arrowsize=0.0, linewidth=.5*norm_flat[i])
plt.quiver(X,Y,U/np.max(norm), V/np.max(norm),scale=30)
plt.axis('square')
Here's the result:
Just looking at the documentation on streamplot(), found here -- what if you used something like streamplot( ... ,minlength = n/2, maxlength = n) where n is the desired length -- you will need to play with those numbers a bit to get your desired graph
you can control for the points using start_points, as shown in the example provided by #JohnKoch
Here's an example of how I controlled the length with streamplot() -- it's pretty much a straight copy/paste/crop from the example from above.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import matplotlib.patches as pat
w = 3
Y, X = np.mgrid[-w:w:100j, -w:w:100j]
U = -1 - X**2 + Y
V = 1 + X - Y**2
speed = np.sqrt(U*U + V*V)
fig = plt.figure(figsize=(14, 18))
gs = gridspec.GridSpec(nrows=3, ncols=2, height_ratios=[1, 1, 2])
grains = 10
tmp = tuple([x]*grains for x in np.linspace(-2, 2, grains))
xs = []
for x in tmp:
xs += x
ys = tuple(np.linspace(-2, 2, grains))*grains
seed_points = np.array([list(xs), list(ys)])
arrowStyle = pat.ArrowStyle.Fancy()
# Varying color along a streamline
ax1 = fig.add_subplot(gs[0, 1])
strm = ax1.streamplot(X, Y, U, V, color=U, linewidth=1.5, cmap='winter', density=10,
minlength=0.001, maxlength = 0.1, arrowstyle='->',
integration_direction='forward', start_points = seed_points.T)
fig.colorbar(strm.lines)
ax1.set_title('Varying Color')
plt.tight_layout()
plt.show()
Edit: made it prettier, though still not quite what we were looking for.