arrow in a matplotlib polar plot by defining length and angle - python-3.x

I am trying to plot arrows in a polar plot in matplotlib. I shall have 2 sets, one set points towards the center of the plot, I can get it easily; one set should tangent to the circle of radius r at the point considered. Both length should be a function of the angle theta.
I can get there by some cumbersome trigonometric consideration for the second set, but I was wondering if there would be a more elegant (and readable) way to do so. Especially as this is for a demo for students.
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
matplotlib.rcParams['figure.figsize'] = (8,8)
theta=np.linspace(0,2*np.pi,13)
rayon=np.linspace(R,R,13)
ax = plt.subplot(111, projection='polar')
ax.plot(theta, rayon,'.',markersize=10)
ax.set_rmax(0.6)
plt.arrow(theta[1], 0.5, 0, -0.25, width = 0.015, edgecolor = 'red', lw = 3,head_width=0.1, head_length=0.05)
l=.5
plt.arrow(theta[1], 0.5, np.arctan(l/.5), (np.sqrt(.5**2+l**2)-.5), width = 0.015,
edgecolor = 'green', lw = 3,head_width=0.1, head_length=0.05)
plt.show()

Related

How to combine two geometries into one plot in Python

Question background: I am trying to make two geometries in a one plot in python. I have made one geometry which is an object having mesh as shown in figure below. The respective code is also mentioned here.
df_1_new = pd.DataFrame()
df_1_new['X_coordinate']=pd.Series(x_new)
df_1_new['Y_coordinate']=pd.Series(y_new)
df_1_new['node_number'] = df_1_new.index
df_1_new = df_1_new[['node_number','X_coordinate','Y_coordinate']]
plt.scatter(x_new, y_new)
plt.show
The second geometry, which is a circle and I made this geometry running below code.
from matplotlib import pyplot as plt, patches
plt.rcParams["figure.figsize"] = [9.00, 6.50]
plt.rcParams["figure.autolayout"] = True
fig = plt.figure()
ax = fig.add_subplot()
circle1 = plt.Circle((2, 2), radius=5, fill = False)
ax.add_patch(circle1)
ax.axis('equal')
plt.show()
My question: How can I combine both geometries mentioned above in a one plot. I would like to place my circle around my geometry (object). Geometry has a centroid (2, 2) and I want to place my circle's centroid exactly on the centroid of geometry therefore I will be having a circle around my geometry. What code I should write. Kindly help me on this.
For your reference: I want my plot just like in below picture.
you need to do all the plotting between the subplot creation and before you issue the plt.show() command, as any command after it will create a new figure.
from matplotlib import pyplot as plt, patches
plt.rcParams["figure.figsize"] = [9.00, 6.50]
plt.rcParams["figure.autolayout"] = True
fig = plt.figure()
ax = fig.add_subplot()
# other plt.scatter or plt.plot here
plt.scatter([3,4,5,6,4],[5,4,2,3,2]) # example
circle1 = plt.Circle((2, 2), radius=5, fill = False)
ax.add_patch(circle1)
ax.axis('equal')
plt.show()
image example
to get the points inside the circle, you need to play with the circle radius and center till you get it right.
something you can do is to make the circle at the np.median of your x and y values, so you are sure about the center position.

Matplotlib sum of colors as result of plot overlapping [duplicate]

When dealing with overlapping high density scatter or line plots of different colors it can be convenient to implement additive blending schemes, where the RGB colors of each marker add together to produce the final color in the canvas. This is a common operation in 2D and 3D render engines.
However, in Matplotlib I've only found support for alpha/opacity blending. Is there any roundabout way of doing it or am I stuck with rendering to bitmap and then blending them in some paint program?
Edit: Here's some example code and a manual solution.
This will produce two partially overlapping random distributions:
x1 = randn(1000)
y1 = randn(1000)
x2 = randn(1000) * 5
y2 = randn(1000)
scatter(x1,y1,c='b',edgecolors='none')
scatter(x2,y2,c='r',edgecolors='none')
This will produce in matplotlib the following:
As you can see, there are some overlapping blue points that are occluded by red points and we would like to see them. By using alpha/opacity blending in matplotlib, you can do:
scatter(x1,y1,c='b',edgecolors='none',alpha=0.5)
scatter(x2,y2,c='r',edgecolors='none',alpha=0.5)
Which will produce the following:
But what I really want is the following:
I can do it manually by rendering each plot independently to a bitmap:
xlim = plt.xlim()
ylim = plt.ylim()
scatter(x1,y1,c='b',edgecolors='none')
plt.xlim(xlim)
plt.ylim(ylim)
scatter(x2,y2,c='r',edgecolors='none')
plt.xlim(xlim)
plt.ylim(ylim)
plt.savefig(r'scatter_blue.png',transparent=True)
plt.savefig(r'scatter_red.png',transparent=True)
Which gives me the following images:
What you can do then is load them as independent layers in Paint.NET/PhotoShop/gimp and just additive blend them.
Now ideal would be to be able to do this programmatically in Matplotlib, since I'll be processing hundreds of these!
If you only need an image as the result, you can get the canvas buffer as a numpy array, and then do the blending, here is an example:
from matplotlib import pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.scatter(x1,y1,c='b',edgecolors='none')
ax.set_xlim(-4, 4)
ax.set_ylim(-4, 4)
ax.patch.set_facecolor("none")
ax.patch.set_edgecolor("none")
fig.canvas.draw()
w, h = fig.canvas.get_width_height()
img = np.frombuffer(fig.canvas.buffer_rgba(), np.uint8).reshape(h, w, -1).copy()
ax.clear()
ax.scatter(x2,y2,c='r',edgecolors='none')
ax.set_xlim(-4, 4)
ax.set_ylim(-4, 4)
ax.patch.set_facecolor("none")
ax.patch.set_edgecolor("none")
fig.canvas.draw()
img2 = np.frombuffer(fig.canvas.buffer_rgba(), np.uint8).reshape(h, w, -1).copy()
img[img[:, :, -1] == 0] = 0
img2[img2[:, :, -1] == 0] = 0
fig.clf()
plt.imshow(np.maximum(img, img2))
plt.subplots_adjust(0, 0, 1, 1)
plt.axis("off")
plt.show()
the result:
This feature is now supported by my matplotlib backend https://github.com/anntzer/mplcairo (master only):
import matplotlib; matplotlib.use("module://mplcairo.qt")
from matplotlib import pyplot as plt
from mplcairo import operator_t
import numpy as np
x1 = np.random.randn(1000)
y1 = np.random.randn(1000)
x2 = np.random.randn(1000) * 5
y2 = np.random.randn(1000)
fig, ax = plt.subplots()
# The figure and axes background must be made transparent.
fig.patch.set(alpha=0)
ax.patch.set(alpha=0)
pc1 = ax.scatter(x1, y1, c='b', edgecolors='none')
pc2 = ax.scatter(x2, y2, c='r', edgecolors='none')
operator_t.ADD.patch_artist(pc2) # Use additive blending.
plt.show()

How to draw Scatter plot on top of background using Basemap Python

I am trying to plot a scatter plot on a background using basemap. But it's overwriting the background. How do I retain the background?
I am using this code
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
m = Basemap(projection='merc',llcrnrlat=-80,urcrnrlat=80,llcrnrlon=-180,urcrnrlon=180,lat_ts=20,resolution='c')
m.bluemarble()
x, y = m(list(longitude), list(latitude))
plt.scatter(x,y,1,marker='o',color='Red')
plt.show()
But as soon as I run the scatter plot, its overwriting background image. How can I overlay the scatter plot on the image.
This is how to plot a series of points on top of a raster map. Note that the bluemarble image is huge, so a full scale (1.0 or default) plot of it should be avoided. The code is based on yours.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
# make up some data for scatter plot
lats = np.random.randint(-75, 75, size=20)
lons = np.random.randint(-179, 179, size=20)
fig = plt.gcf()
fig.set_size_inches(8, 6.5)
m = Basemap(projection='merc', \
llcrnrlat=-80, urcrnrlat=80, \
llcrnrlon=-180, urcrnrlon=180, \
lat_ts=20, \
resolution='c')
m.bluemarble(scale=0.2) # full scale will be overkill
m.drawcoastlines(color='white', linewidth=0.2) # add coastlines
x, y = m(lons, lats) # transform coordinates
plt.scatter(x, y, 10, marker='o', color='Red')
plt.show()
The resulting plot:
I realize it's an old question but in case anyone comes here with the same problem as I did.
The trick is to give a higher zorder for the scatter plot than for the .bluemarble().
m.scatter(x, y, 10, marker='o', color='Red', zorder=3)
More info here: https://matplotlib.org/3.1.0/gallery/misc/zorder_demo.html
I'm not entirely sure what you mean by "overwriting the background". When you use plt.scatter(), it will plot the points over the map, so it will display the points over the background.
Just based off the code provided, I think you're issue here is m(list(longitude), list(latitude)).
If you have multiple points in a list, you want to loop over them.
lats = [32, 38, 35]
lons = [-98, -79, -94]
x, y = m(lons, lats)
for i in range(len(lats)):
plt.scatter(x, y, marker = 'o')
If it's only one single point,
lat, lon = 32, -92
x, y = m(lon, lat)
plt.scatter(x, y, marker = 'o')
The styling of the points can be found in the matplotlib documentation.

Plotting multiple density curves on the same plot: weighting the subset categories in Python 3

I am trying to recreate this density plot in python 3: math.stackexchange.com/questions/845424/the-expected-outcome-of-a-random-game-of-chess
End Goal: I need my density plot to look like this
The area under the blue curve is equal to that of the red, green, and purple curves combined because the different outcomes (Draw, Black wins, and White wins) are the subset of the total (All).
How do I have python realize and plot this accordingly?
Here is the .csv file of results_df after 1000 simulations pastebin.com/YDVMx2DL
from matplotlib import pyplot as plt
import seaborn as sns
black = results_df.loc[results_df['outcome'] == 'Black']
white = results_df.loc[results_df['outcome'] == 'White']
draw = results_df.loc[results_df['outcome'] == 'Draw']
win = results_df.loc[results_df['outcome'] != 'Draw']
Total = len(results_df.index)
Wins = len(win.index)
PercentBlack = "Black Wins ≈ %s" %('{0:.2%}'.format(len(black.index)/Total))
PercentWhite = "White Wins ≈ %s" %('{0:.2%}'.format(len(white.index)/Total))
PercentDraw = "Draw ≈ %s" %('{0:.2%}'.format(len(draw.index)/Total))
AllTitle = 'Distribution of Moves by All Outcomes (nSample = %s)' %(workers)
sns.distplot(results_df.moves, hist=False, label = "All")
sns.distplot(black.moves, hist=False, label=PercentBlack)
sns.distplot(white.moves, hist=False, label=PercentWhite)
sns.distplot(draw.moves, hist=False, label=PercentDraw)
plt.title(AllTitle)
plt.ylabel('Density')
plt.xlabel('Number of Moves')
plt.legend()
plt.show()
The code above produces density curves without weights, which I really need to figure out how to generate density curve weights accordingly as well as preserve my labels in the legend
density curves, no weights; help
I also tried frequency histograms, that scaled the distribution heights correctly but I would rather keep the 4 curves overlaid on top of each other for a "cleaner" look...I don't like this frequency plot but this is my current fix at the moment.
results_df.moves.hist(alpha=0.4, bins=range(0, 700, 10), label = "All")
draw.moves.hist(alpha=0.4, bins=range(0, 700, 10), label = PercentDraw)
white.moves.hist(alpha=0.4, bins=range(0, 700, 10), label = PercentWhite)
black.moves.hist(alpha=0.4, bins=range(0, 700, 10), label = PercentBlack)
plt.title(AllTitle)
plt.ylabel('Frequency')
plt.xlabel('Number of Moves')
plt.legend()
plt.show()
If anyone can write the python 3 code that outputs the first plot with 4 density curves with correct subset weights as well as preserves the custom legend that show percentages, that would be much appreciated.
Once the density curves are plotted with the correct subset weights, I am also interested in the python 3 code in finding the max point coordinates of each density curve that shows max frequency of moves once I scale it up to 500,000 iterations.
Thanks
You need to be careful. The plot that you have produced is correct. All the curves shown are probability density functions of the underlying distributions.
In the plot that you want to have, only the curve labeled "All" is a probability density function. The other curves are not.
In any case, you will need to calculate the kernel density estimate yourself, if you want to scale it like shown in the desired plot. This can be done using scipy.stats.gaussial_kde().
In order to reproduce the desired plot, I see two options.
Calculate the kde for all involved cases and scale them with the number of samples.
import numpy as np; np.random.seed(0)
import matplotlib.pyplot as plt
import scipy.stats
a = np.random.gumbel(80, 25, 1000).astype(int)
b = np.random.gumbel(200, 46, 4000).astype(int)
kdea = scipy.stats.gaussian_kde(a)
kdeb = scipy.stats.gaussian_kde(b)
both = np.hstack((a,b))
kdeboth = scipy.stats.gaussian_kde(both)
grid = np.arange(500)
#weighted kde curves
wa = kdea(grid)*(len(a)/float(len(both)))
wb = kdeb(grid)*(len(b)/float(len(both)))
print "a.sum ", wa.sum()
print "b.sum ", wb.sum()
print "total.sum ", kdeb(grid).sum()
fig, ax = plt.subplots()
ax.plot(grid, wa, lw=1, label = "weighted a")
ax.plot(grid, wb, lw=1, label = "weighted b")
ax.plot(grid, kdeboth(grid), color="crimson", lw=2, label = "pdf")
plt.legend()
plt.show()
Calculate the kde for all individual cases, normalize their sum to obtain the total.
import numpy as np; np.random.seed(0)
import matplotlib.pyplot as plt
import scipy.stats
a = np.random.gumbel(80, 25, 1000).astype(int)
b = np.random.gumbel(200, 46, 4000).astype(int)
kdea = scipy.stats.gaussian_kde(a)
kdeb = scipy.stats.gaussian_kde(b)
grid = np.arange(500)
#weighted kde curves
wa = kdea(grid)*(len(a)/float(len(a)+len(b)))
wb = kdeb(grid)*(len(b)/float(len(a)+len(b)))
total = wa+wb
fig, ax = plt.subplots(figsize=(5,3))
ax.plot(grid, wa, lw=1, label = "weighted a")
ax.plot(grid, wb, lw=1, label = "weighted b")
ax.plot(grid, total, color="crimson", lw=2, label = "pdf")
plt.legend()
plt.show()

matplotlib box on basemap map

I'm trying to draw a box on a map in relative coordinates (i.e. 0 to 1). The reason is I have a colorbar on my map, but cannot see it clearly. I want a transparent box behind it. I've looked at adding patch Rectangles (see Draw rectangle (add_patch) in pylab mode), but that is in data coordinates, which is not easy to determine on this map. I also found axhspan, which uses relative coordinates for the x span, but data coordinates for the y span.
Is there a way to draw a box in a matplotlib axes object using relative coordinates?
Here's a way to add a boxed text to a relative coordinates:
#!/usr/bin/python3
from matplotlib import pyplot as plt
x = range(5)
y = range(5)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y)
ax.text(0.5, 0.5,
"Relative coords!",
horizontalalignment = 'center',
backgroundcolor = "white",
verticalalignment = 'center',
bbox=dict(facecolor='white', edgecolor='green', alpha=0.65),
transform = ax.transAxes,
)
fig.savefig("mwe.png")
Result:
Edit:
To draw just a box given it's relative coordinates/dimensions with no text in it:
#!/usr/bin/python3
from matplotlib import pyplot as plt
from matplotlib.patches import Rectangle
x = range(5)
y = range(5)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, zorder=1)
plt.gca().add_patch(Rectangle(
(0.4, 0.4), # lower left point of rectangle
0.2, 0.2, # width/height of rectangle
transform=ax.transAxes,
facecolor="white",
edgecolor='green',
alpha=0.65,
zorder=2,
))
fig.savefig("mwe.png")
Result:

Resources