Specifically, I have a list of points. I want to connect these points together to create a polygon.
The obvious way to do this is the C-style:
39 def drawPoly(poly):
40 for i in range(0, len(poly)):
41 p1 = poly[i]
42 p2 = poly[i + 1]
43 canvas.create_line(blah)
Is there a way to do this that is more pythonic?
Well, since create_line can take a list of points, all you need to do is copy the first two elements and stuff 'em on the end:
from Tkinter import Tk, Canvas, mainloop
master = Tk()
points = [10, 10, 50, 10, 50, 50, 10, 50 ]
c = Canvas(master, width=200, height=100)
c.pack()
c.create_line(points + points[0:2], fill = "red")
mainloop()
Edit: I think I misunderstood your example, poly is a list of tuples correct? I'm changing my answer to reflect ckhan's observations that create_line is a Tk canvas method, and your polygon is probably not closed.
def drawPoly(poly):
x1 = y1 = None
for x2, y2 in poly + poly[0]:
if x1 is not None:
canvas.create_line(x1, y1, x2, y2)
x1, y1 = x2, y2
Related
I am having difficulties to interpret results of arctangent functions. This behaviour is consistent for all implementations I came across, so I will here limit myself to NumPy and MATLAB.
The idea is to have circle of randomly placed points. The goal is to represent their positions in polar coordinate system and since they are uniformly distributed, I expect the θ angle (which is calculated using atan2 function) to be also distributed randomly over interval -π ... π.
Here is the code for MATLAB:
stp = 2*pi/2^8;
siz = 100;
num = 100000000;
x = randi([-siz, siz], [1, num]);
y = randi([-siz, siz], [1, num]);
m = (x.^2+y.^2) < siz^2;
[t, ~] = cart2pol(x(m), y(m));
figure()
histogram(t, -pi:stp:pi);
And here for Python & NumPy:
import numpy as np
import matplotlib.pyplot as pl
siz = 100
num = 100000000
rng = np.random.default_rng()
x = rng.integers(low=-siz, high=siz, size=num, endpoint=True)
y = rng.integers(low=-siz, high=siz, size=num, endpoint=True)
m = (x**2+y**2) < siz**2
t = np.arctan2(y[m], x[m]);
pl.hist(t, range=[-np.pi, np.pi], bins=2**8)
pl.show()
In both cases I got results looking like this, where one can easily see "steps" for each multiple of π/4.
It looks like some sort of precision error, but strangely for angles where I would not expect that. Also this behaviour is present for ordinary atan function as well.
Notice that you are using integers
So for each pair (p,q) you will have floor(sqrt(p**2 + q**2)/gcd(p,q)/r) pairs that give the same angle arctan(p,q). Then for the multiples of (p,q) the gcd(p,q) is 1
Notice also that p**2+q**2 is 1 for the multiples of pi/2 and 2 for the odd multiples of pi/4, with this we can predict that there will be more items that are even multiples of pi/4 than odd mulitples of pi/4. And this agrees with what we see in your plot.
Example
Let's plot the points with integer coordinates that lie in a circle of radius 10.
import numpy as np
import matplotlib.pyplot as plt
from collections import Counter
def gcd(a,b):
if a == 0 or b == 0:
return max(a,b)
while b != 0:
a,b = b, a%b
return a;
R = 10
x,y = np.indices((R+1, R+1))
m = (x**2 + y**2) <= R**2
x,y = x[m], y[m]
t = np.linspace(0, np.pi / 2)
plt.figure(figsize=(6, 6))
plt.plot(x, y, 'o')
plt.plot(R * np.cos(t), R * np.sin(t))
lines = Counter((xi / gcd(xi,yi),
yi / gcd(xi,yi)) for xi, yi in zip(x,y))
plt.axis('off')
for (x,y),f in lines.items():
if f != 1:
r = np.sqrt(x**2 + y**2)
plt.plot([0, R*x/r], [0, R*y/r], alpha=0.25)
plt.text(R*1.03*x/r, R*1.03*y/r, f'{int(y)}/{int(x)}: {f}')
Here you see on the plot a few points that share the same angle as some other. For the 45 degrees there are 7 points, and for multiples of 90 there are 10. Many of the points have a unique angle.
Basically you have many angles with few poitns and a few angles that hit many points.
But overall the points are distributed nearly uniformly with respect to angle. Here I plot the cumulative frequency that is nearly a straight line (what it would be if the distribution was unifrom), and the bin frequency form some triangular fractal pattern.
R = 20
x,y = np.indices((R+1, R+1))
m = (x**2 + y**2) <= R**2
x,y = x[m], y[m]
plt.figure(figsize=(6,6))
plt.subplot(211)
plt.plot(np.sort(np.arctan2(x,y))*180/np.pi, np.arange(len(x)), '.', markersize=1)
plt.subplot(212)
plt.plot(np.arctan2(x,y)*180/np.pi, np.gcd(x,y), '.', markersize=4)
If the size of the circle increases and you do a histogram with sufficiently wide bins you will not notice the variations, otherwise you will see this pattern in the histogram.
I'd like to make a stacked area chart but it would increase stepwise, like the stairs plot.
It is a cumulative chart, so a stepwise increase would make more sense.
How can it be done?
plt.stackplot accepts extra kwargs which are sent to plt.fill_between. One of those is step='post', creating a horizontal line starting with the given value. (In contrast, step='pre' has the horizontal lines at the height of the ending positions.)
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(1, 6)
y1 = np.random.rand(5) + 1
y2 = np.random.rand(5) + 2
y3 = np.random.rand(5) + 3
plt.stackplot(x, y1, y2, y3, step='post', labels=['A', 'B', 'C'])
plt.xticks(x)
plt.legend()
plt.show()
Consider the snippet:
# Original code was taken from https://benalexkeen.com/linear-programming-with-python-and-pulp-part-1/
import numpy as np
import matplotlib.pyplot as plt
# Constraints:
## 3x0 + 2x1 <= 5
## 4x0 - x1 <= 3
constraints = ["(5 - 3*x)/2.0", "- (3 - 4*x)/1.0"]
x = np.linspace(-10.5, 10.5, 2000)
### Taking x1 at RHS
### 3x0 + 2x1 <= 5
y1 = eval(constraints[0])
### 4x0 - x1 <= 3
y2 = eval(constraints[1])
# Make plot
plt.plot(x, y1, 'blue', label=r'$'+constraints[0]+'$')
plt.plot(x, y2, 'red', label=r'$'+constraints[1]+'$')
plt.xlim((-10, 10))
plt.ylim((-10, 10))
plt.xlabel(r'$x$')
plt.ylabel(r'$y$')
y3 = np.minimum(y1, y2) # Constraints of `<=` type
print (y3)
plt.plot(x, y3, 'green', label=r''+'overlap'+'')
#plt.fill_between(x, y1, y2, color='grey', alpha=0.5)
#plt.fill_between(x, y1, y3, color='pink', alpha=.5)
#plt.fill_between(x, y2, y3, color='yellow', alpha=.5)
plt.fill_between(x, y3, np.maximum(y1,y2), color='gray', alpha=.5)
#plt.fill_between(x, y2, y1, color='gray', alpha=.5)
#plt.fill_between(x, y1<=1, np.minimum(y1,y3), color='gray', alpha=.5)
#plt.fill_between(x, y3, y1, color='grey', alpha=0.5)
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
What I am able to get is the common feasible solution and the non-feasible solution of the inequalities, such as:
What I actually wanted is to shade the partially feasible solutions individually (the upper and the lower triangles in the figure). Say, only the top triangle is shaded, rest is kept as is.
I tried multiple combinations (as can be seen from the code snippet), but could not achieve this. Any help would be appreciated.
You need to declare some upper and lower curves. Horizontal boundaries are good enough. Here y=-500 and y=500 are set up. A large number, i.e. 500 is needed.
y_ge_m500 = (x*0) - 500 #for lower-y bound
y_le_p500 = (x*0) + 500 #for upper-y bound
Then use
plt.fill_between(x, y_ge_m500, np.minimum(y1, y2), color='yellow', alpha=.5) #get lower ^
plt.fill_between(x, y_le_p500, np.maximum(y1, y2), color='pink', alpha=.5) #get Upper V
And you will get a plot similar to this:
I am struggling to remove the empty space in or between subplots. I already read a lot of answers here, but I am not getting anywhere.
I want to make horizontal bar plots with several subplots:
My example is:
import matplotlib.pyplot as plt
x1 = [5]
y1 = [-10]
x2 = [30, 35]
y2 = [-15, -20]
x3 = [15, 5, 20]
y3 = [-10, -15, -30]
xlimits = [-30, 35]
ylimits = [-0.5, 2.5]
fig = plt.figure(figsize=(12,6))
ax1 = fig.add_subplot(3,1,1)
ax1.barh(0, x1, height = 1)
ax1.barh(0, y1, height = 1)
ax2 = fig.add_subplot(3,1,2)
ax2.barh([0, 1], x2, height = 1)
ax2.barh([0, 1], y2, height = 1)
ax3 = fig.add_subplot(3,1,3)
ax3.barh([0, 1, 2], x3, height = 1)
ax3.barh([0, 1, 2], y3, height = 1)
for ax in fig.axes:
ax.set_ylim(ylimits)
ax.set_xlim(xlimits)
plt.show()
will result in:
I used ax.set_ylim(ylimits) to have an equal height of all bars and ax.set_xlim(xlimits) to have "0" in one vertical line.
Now I would like to adjust the bbox to remove the empty space in the subplots (top and middle). But I have no idea how to achieve this. I also tried ax.set_aspect(). In this case I will receive empty space between the subplots.
I would like to do it with subplots to easily add description, swap stuff and so on.
Thanks in advance for any suggestions.
If I understood you correctly, you could try adding this to your code:
fig.subplots_adjust(wspace=0, hspace=0)
I want to use multiple colors in a marker made with matplotlib. Doing two colors was not that difficult, following this example, and with some additional info from this documentation. However, I was wondering if it is possible to make a marker with more than 2 colors. I'm in a situation where I want a single marker to actually get 3 different colors (a point on a map refers to three different observations).
You can do this by following the matplotlib example shown here:
https://matplotlib.org/stable/gallery/lines_bars_and_markers/scatter_piecharts.html
Below I have changed the example slightly to use ax.plot instead of ax.scatter.
Basically this means all your marker must have the same size, and instead of using the s kwarg for scatter, you use the ms (or markersize) kwarg for plot.
Also, instead of facecolor you need to define markerfacecolor.
Other than those changes, everything else remains the same as the original example.
"""
This example makes custom 'pie charts' as the markers for a scatter plot
Thanks to Manuel Metz for the example
"""
import numpy as np
import matplotlib.pyplot as plt
# first define the ratios
r1 = 0.2 # 20%
r2 = r1 + 0.4 # 40%
# define some sizes of the scatter marker
sizes = np.array([60, 80, 120])
# calculate the points of the first pie marker
#
# these are just the origin (0,0) +
# some points on a circle cos,sin
x1 = np.cos(2 * np.pi * np.linspace(0, r1))
y1 = np.sin(2 * np.pi * np.linspace(0, r1))
xy1 = np.row_stack([[0, 0], np.column_stack([x1, y1])])
s1 = np.abs(xy1).max()
x2 = np.cos(2 * np.pi * np.linspace(r1, r2))
y2 = np.sin(2 * np.pi * np.linspace(r1, r2))
xy2 = np.row_stack([[0, 0], np.column_stack([x2, y2])])
s2 = np.abs(xy2).max()
x3 = np.cos(2 * np.pi * np.linspace(r2, 1))
y3 = np.sin(2 * np.pi * np.linspace(r2, 1))
xy3 = np.row_stack([[0, 0], np.column_stack([x3, y3])])
s3 = np.abs(xy3).max()
fig, ax = plt.subplots()
# Here's where I made changes
ax.plot(np.arange(3), np.arange(3), marker=xy1,
ms=20, markerfacecolor='blue', markeredgecolor='None', linestyle='None') # I changed this line
ax.plot(np.arange(3), np.arange(3), marker=xy2,
ms=20, markerfacecolor='green', markeredgecolor='None', linestyle='None') # I changed this line
ax.plot(np.arange(3), np.arange(3), marker=xy3,
ms=20, markerfacecolor='red', markeredgecolor='None', linestyle='None') # I changed this line
plt.margins(0.05)
plt.show()