Python: how to use classes for plotting different lines? - python

I want to plot different lines using a class method. In order to plot a line. Similar to Passing plots out of a class I use this class to plot a line
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
class Plotter(object):
def __init__(self, xval=None, yval=None, dim1=None, dim2=None):
self.xval = xval
self.yval = yval
self.dim1 = dim1
self.dim2 = dim2
def plotthing(self):
fig, ax = plt.subplots(figsize=(self.dim1,self.dim2))
ax.plot(self.xval, self.yval, 'o-')
return fig
app = Plotter(xval=range(0,10), yval=range(0,10), dim1=5, dim2=5)
plot = app.plotthing()
However I would like to plot different curves in the same plot and define a function inside the class to do so.
Xval = []
Yval = []
xval=range(0,10)
yval=range(0,10)
Xval.append(xval)
Yval.append(yval)
xval=range(0,10)
yval=np.sin(range(0,10))
Xval.append(xval)
Yval.append(yval)
How can I define a function to pass to plotthing?
class Plotter(object):
def __init__(self, xval=None, yval=None, dim1=None, dim2=None):
self.xval = xval
self.yval = yval
self.dim1 = dim1
self.dim2 = dim2
def function_do_plot(x, y):
do something
def plotthing(self):
fig, ax = plt.subplots(figsize=(self.dim1,self.dim2))
for i in range(0, len(self.xval)):
x = xval[i]
y = yval[i]
fig = function_do_plot(x, y)
return fig

Consider looping through your lists of inputs without needing a separate method:
class Plotter(object):
def __init__(self, xval=None, yval=None, dim1=None, dim2=None):
self.xval = xval # LIST
self.yval = yval # LIST
self.dim1 = dim1
self.dim2 = dim2
def plotthing(self):
fig, ax = plt.subplots(figsize=(self.dim1,self.dim2))
for i, j in zip(self.xval, self.yval):
ax.plot(i, j, 'o-')
return fig
# POPULATE LIST OF RANGES
Xval = []; Yval = []
xval = range(0,10)
yval = range(0,10)
Xval.append(xval)
Yval.append(yval)
xval = range(0,10)
yval = np.sin(range(0,10))
Xval.append(xval)
Yval.append(yval)
# PASS IN LIST OF RANGES
app = Plotter(xval=Xval, yval=Yval, dim1=5, dim2=5)
plot = app.plotthing()

Related

live graph with matplotlib report not defined error

I try to create a live graph with matplotlib animation. below code report error:
UnboundLocalError: local variable 'lines' referenced before assignment
lines is defined above!
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import random
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
queue = []
qSize = 20
N = 3
lines = []
xidx = list(range(0, qSize))
def init():
for i in range(qSize):
queue.append([0]*N)
for i in range(N):
xdata = []
ydata = []
line, = ax.plot(xdata, ydata,label=str(i))
lines.append([line,xdata,ydata])
ax.legend(loc=2)
plt.xticks(rotation=45, ha='right')
#plt.subplots_adjust(bottom=0.30)
plt.title('Demo')
plt.ylabel('x')
plt.ylabel('Value')
return
def tick():
arr = []
for i in range(N):
d = random.randint(i*10,i*10+5)
arr.append(d)
queue.append(arr)
lines = lines[-qSize:]
return
def animate(i):
tick()
df = pd.DataFrame(data)
ax.set_xlim(0,qSize)
ax.set_ylim(df.min().min(),df.max().max())
for i,colname in enumerate(df.columns):
line,xdata,ydata = lines[i]
xdata = xidx
ydata = df[colname]
line.set_xdata(xdata)
line.set_ydata(ydata)
plt.draw()
return
init()
ani = animation.FuncAnimation(fig, animate, fargs=(), interval=1000)
plt.show()

Filling shapefile polygons partially in basemap

I would like to fill a polygon using basemap/shapefile data, but only a certain %. For example, in the example below, we fill based on the values, but let's say I wanted to fill a % of the polygon based on these values (code from here):
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
import numpy as np
fig= plt.figure()
ax= fig.add_subplot(111)
m=Basemap(projection='cyl',llcrnrlat=34.5,llcrnrlon=19,
urcrnrlat=42,urcrnrlon=28.5,resolution='h')
m.drawmapboundary(fill_color='aqua')
m.fillcontinents(color='w',lake_color='aqua')
m.drawcoastlines()
m.readshapefile('data/nomoi/nomoi','nomoi')
dict1={14464: 1.16, 14465: 1.35, 14466: 1.28, 14467: 1.69, 14468: 1.81, 14418: 1.38}
colvals = dict1.values()
cmap=plt.cm.RdYlBu
norm=plt.Normalize(min(colvals),max(colvals))
patches = []
for info, shape in zip(m.nomoi_info, m.nomoi):
if info['ID_2'] in list(dict1.keys()):
color=cmap(norm(dict1[info['ID_2']]))
patches.append( Polygon(np.array(shape), True, color=color) )
pc = PatchCollection(patches, match_original=True, edgecolor='k', linewidths=1., zorder=2)
ax.add_collection(pc)
#colorbar
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array(colvals)
fig.colorbar(sm, ax=ax)
plt.show()
Thank you.
import math
from shapely.geometry import Polygon as shpoly
#shapefile of main massachusetts shape
iowpoly = state_shapes['Massachusetts'][32]
def return_xy(coords):
return [np.asarray([i[0] for i in coords]), np.asarray([i[1] for i in coords])]
def return_area(coords):
x, y = return_xy(coords)
return 0.5*np.abs(np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1)))
def return_bounding_box(coords):
x, y = return_xy(coords)
return [[min(x), min(y)], [max(x), max(y)]]
def split_x_wise(bbox, weights, split = 2):
lleft = bbox[0]
uright = bbox[1]
dx = abs(uright[0] - lleft[0])
weights = np.cumsum(sorted(weights, reverse=True))
xcoords = [lleft[0]+weights[x-1]*dx for x in range(1, split)]
return xcoords
def generate_splits_by_area(coords, bbox, weights, tolerance = 0.03, div = 100):
xareasplits = {}
weights = np.cumsum(sorted(weights, reverse=True))[:-1]
lleft = bbox[0]
uright = bbox[1]
dx = abs(uright[0] - lleft[0])
xsplit = [lleft[0]+(dx/div)*x for x in range(1, div)]
for w in weights:
xareasplits[str(w)] = None
mainarea = shpoly(coords).area
for i, s in enumerate(xsplit):
poly = []
if i == 0:
continue
for ip, p in enumerate(coords):
if p[0] < s:
poly.append(p)
shpl = shpoly(poly).area
frac = shpl/mainarea
for w in weights:
if abs(w-frac) <= tolerance:
if xareasplits[str(w)] == None:
xareasplits[str(w)] = s
return list(xareasplits.values())
def return_split(coords, weights, split = 2, by_area = False, tolerance = 0.03, div = 100):
polys = {}
for x in range(0, split):
polys[str(x+1)] = {'points':[], 'maxit' : None}
bbox = return_bounding_box(coords)
if not by_area:
xsplit = split_x_wise(bbox, weights, split)
#test = generate_splits_by_area(coords, bbox, weights, tolerance=tolerance, div=div)
else:
xsplit = generate_splits_by_area(coords, bbox, weights, tolerance=tolerance, div=div)
xsplit.append(bbox[0][0])
xsplit.append(bbox[1][0])
xsplit = sorted(xsplit)
#print(xsplit)
#print(test)
for ip, p in enumerate(coords):
for i, splt in enumerate(xsplit):
if i > 0:
if (p[0] > xsplit[i-1]) & (p[0] < splt):
if len(polys[str(i)]['points']) == 0:
polys[str(i)]['points'].append(coords[ip-1])
polys[str(i)]['points'].append(p)
polys[str(i)]['maxit'] = ip
for poly, data in polys.items():
tmaxit = data['maxit']+1
if tmaxit >= len(coords):
data['points'].append(coords[0])
else:
data['points'].append(coords[tmaxit])
return polys
#return [p for p in coords if p[0] > xsplit[0]]
#bboxiowa = return_bounding_box(iowpoly)
splitpoly = return_split(iowpoly, weights = [0.2780539772727273, 0.1953716856060606, 0.19513494318181818, 0.18329782196969696, 0.14814157196969696],by_area = True,split = 5)
for k, v in splitpoly.items():
print (k, len(v['points']))
print (v['maxit'])
test = shpoly(splitpoly["1"]['points'])
test
I managed to write my own code to split and fill shapel polygons from shapefiles. The above code example splits the Massachusetts shapefile into 5 segments, weighted according to weights and by area.
The first 2 parts of the split look like this:

Defining a unique ID for 3d bar animations, matplotlib

I'm sorry if my title is very unclear. I am working on an animation that represents a histogram of all the numbers as it crawls through a file.
I'm trying to get a unique ID, but they're all returning the same id. Then I thought about setting a unique ID, but I'm not entirely sure how to do that. I was thinking if I could just isolate the result in the sequence to be its own bar, I could clear it and then update it.
It's really hard to imagine what's going on so here's an image:
Here is the code:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
import numpy as np
import random
from collections import defaultdict
from collections import deque
import re
color = f"#{random.randrange(0x1000000):06x}"
color_border = f"#{random.randrange(0x1000000):06x}"
range = [0,1,2,3,4,5,6,7,8,9]
digit_finder = re.compile(r'(\d+)')
fig = plt.figure()
ax = fig.add_subplot(111, projection= '3d')
ax.set_xticks(range)
#def text_crawler(file, place, bptrial):
def animate(i):
global z_line
global color
global color_border
_last = _f.readlines(20)
for line in _last:
_line = line.decode("utf-8")
final = digit_finder.findall(_line)
#if not final: continue
print(final[1])
z_line.append(int(final[1]))
for z in z_line:
squared_num = createdict(str(z))
xs = list(squared_num.keys())
ys = list(squared_num.values())
#ax.clear()
ax.bar(xs, ys, zs=z ,zdir='y', color = color, alpha=0.8,
linewidth=3, edgecolor = color_border)
ax.set_yticks(z_line)
ax.set_xticks(range)
print(Axes3D.bar)
def createdict(it):
x = defaultdict(int)
for k in it:
x[k] += 1
return x
count = 10
#final = list(map(lambda x: createdict(x), nums))
with open('graph_index.txt', 'rb') as index_reader:
i_first = index_reader.readline()
if not i_first:
print('This is where you return index = 0')
index_reader.seek(-2, 2)
while index_reader.read(1) != b"\n":
index_reader.seek(-2,1)
i_last = index_reader.readline().decode("utf-8")
index = digit_finder.findall(i_last)
if not index:
print('This is also index = 0...just in case.')
else:
print(int(index[0]), 'This should be a number, in bytes, where you will continue from.')
z_line = deque([],maxlen=7)
with open('numbers.txt', 'rb') as _f:
first = _f.readline(0)
_f.seek(int(index[0]), 0)
while _f.read(1) != b"\n":
_f.seek(-2,1)
ani = animation.FuncAnimation(fig, animate, interval=10000)
print(ani)
plt.show()```

Tick labels for y axis are very long. How to truncate them in seaborn?

I am plotting "McDonald's menu" data from kaggle. I plotted nutrients with food items. But, y-tick-labels are very long. How to truncate them so they fit in properly ?
grouped = df.groupby(df["Protein"])
item = grouped["Item"].sum()
item_list = item.sort_index()
item_list = item_list[-20:]
#Sizing the image canvas
plt.figure(figsize=(8,9))
#To plot bargraph
sns.barplot(item_list.index,item_list.values)
How to correct it and would it improve size of my plot ?
One idea could be to let the text in the labels roll like a marquee.
import matplotlib.pyplot as plt
import matplotlib.text
import matplotlib.animation
class Roller():
def __init__(self):
self.texts = []
def append(self, text, **kwargs):
RollingText.assimilate(text, **kwargs)
self.texts.append(text)
def roll(self, i=0):
for text in self.texts:
text.roll()
def ticklabelroll(self, i=0):
self.roll()
ticklabels = [t.get_text() for t in self.texts]
plt.gca().set_yticklabels(ticklabels)
class RollingText(matplotlib.text.Text):
n = 10
p = 0
def __init__(self, *args, **kwargs):
self.n = kwargs.pop("n", self.n)
matplotlib.text.Text(*args, **kwargs)
self.set_myprops()
def set_myprops(self, **kwargs):
self.fulltext = kwargs.get("fulltext", self.get_text())
self.n = kwargs.get("n", self.n)
if len(self.fulltext) <=self.n:
self.showntext = self.fulltext
else:
self.showntext = self.fulltext[:self.n]
self.set_text(self.showntext)
def roll(self, by=1):
self.p += by
self.p = self.p % len(self.fulltext)
if self.p+self.n <= len(self.fulltext):
self.showntext = self.fulltext[self.p:self.p+self.n]
else:
self.showntext = self.fulltext[self.p:] + " " + \
self.fulltext[0:(self.p+self.n) % len(self.fulltext)]
self.set_text(self.showntext)
#classmethod
def assimilate(cls, instance, **kwargs):
# call RollingText.assimilate(Text, n=10, ...)
instance.__class__ = cls
instance.set_myprops(**kwargs)
if __name__ == "__main__":
import pandas as pd
import seaborn as sns
fn = r"data\nutrition-facts-for-mcdonald-s-menu\menu.csv"
df = pd.read_csv(fn)
grouped = df.groupby(df["Protein"])
item = grouped["Item"].sum()
item_list = item.sort_index()
item_list = item_list[-20:]
fig, ax = plt.subplots(figsize=(10,7.5))
plt.subplots_adjust(left=0.3)
sns.barplot(item_list.index,item_list.values, ax=ax)
# create a Roller instance
r = Roller()
# append all ticklabels to the Roller
for tl in ax.get_yticklabels():
r.append(tl, n=25)
#animate the ticklabels
ani = matplotlib.animation.FuncAnimation(fig, r.ticklabelroll,
frames=36, repeat=True, interval=300)
ani.save(__file__+".gif", writer="imagemagick")
plt.show()

Python -- Matplotlib user input from mouse for plotting

This class plots a curve. However, the inputs are currently set in main(). I'd like to set them as user-driven from mouse interaction. Some of this is possible and in the Matplotlib docs (see referenced sites below) but it's still not really setting it up to be a 'click and plot'. So, ideally the user would click a button to set the P and then whatever point (on the curve, has to be on the curve) they clicked next would be the new P. Same with Q. I'm sure this is a very simple question for anyone who's used Matplotlib but I'm teaching myself it right now, but it would probably take an entry-level dev just a few minutes to do something that I'm getting nowhere with.
Code:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid.axislines import SubplotZero
from math import sqrt
class ECC123(object):
def __init__(self,a,b,px,qx,qy):
self.a = a
self.b = b
self.pxlam = px
self.qxlam = qx
self.invertQy = qy
self.fig = plt.figure(1)
self.ax = SubplotZero(self.fig, 111)
def drawAxis(self):
#fig = plt.figure(1)
#ax = SubplotZero(fig, 111)
self.fig.add_subplot(self.ax)
for direction in ["xzero", "yzero"]:
self.ax.axis[direction].set_axisline_style("->")
self.ax.axis[direction].set_visible(True)
def plotGraph(self):
self.drawAxis()
y, x = np.ogrid[-10:10:100j, -10:10:100j] # range grid [from : to : how_many_points]
xlist = x.ravel(); ylist = y.ravel()
plt.contour(xlist, ylist, self.elliptic_curve(x,y), [0])
pylam = self.ecclambda(self.pxlam,self.a,self.b) # calculate P from pxlam
qylam = self.ecclambda(self.qxlam,self.a,self.b) # calculate Q from qxlam
if self.invertQy == 1: qylam = -qylam # optional, inverts qy to negative on the plot
plt.plot([self.pxlam,self.qxlam], [pylam,qylam], color = "c", linewidth=1)
plt.plot([self.pxlam], [pylam], "mo"); plt.plot([self.qxlam], [qylam], "mo")
plt.text(self.pxlam-0.25,pylam+0.5, '$P$'); plt.text(self.qxlam-0.25,self.qxlam+0.5, '$Q$')
s = (pylam - qylam)/(self.pxlam - self.qxlam) # calculate s slope
xr = s**2 - self.pxlam - self.qxlam # x-value of R
yr = pylam + s*(xr - self.pxlam) # y-value of -R; -y is R (inverted across x-axis)
plt.plot([xr],[yr],"mo")
plt.plot([xr],[-yr],"co")
plt.plot([self.qxlam,xr], [qylam,yr], color = "c", linewidth=1)
plt.plot([xr,xr], [yr,-yr], "x--")
plt.text(xr+0.25,yr, '$-R$'); plt.text(xr+0.25,-yr, '$R$')
plt.grid(True)
plt.show()
I've been going over the docs in Matplotlib, the scipy cookbook, and related questions here on SO and still not seeing exactly how to do this:
http://matplotlib.org/users/event_handling.html
http://matplotlib.org/1.3.1/api/widgets_api.html#matplotlib.widgets.Button.on_clicked
Cursors for data selection in matplotlib
How can I create a frontend for matplotlib?
http://wiki.scipy.org/Cookbook/Matplotlib
So far, I'm getting little red x's all over when I click and they don't even fall within the curve.
I modified your code a little, so that you can set location of P & Q by left & right click, I didn't accomplish all the graph data updates, the rest is left for you:
from mpl_toolkits.axes_grid.axislines import SubplotZero
import numpy as np
import matplotlib.pyplot as plt
from math import sqrt
class ECC(object):
"""
class to implement elliptic curve and find P+Q=R on the plot
"""
def __init__(self,a,b,px,qx,qy):
"""
initialize input variables
"""
self.a = a
self.b = b
self.pxlam = px
self.qxlam = qx
self.invertQy = qy
self.fig = plt.figure(1)
self.ax = SubplotZero(self.fig, 111)
def drawAxis(self):
"""
draw main x,y axis
"""
#fig = plt.figure(1)
#ax = SubplotZero(fig, 111)
self.fig.add_subplot(self.ax)
for direction in ["xzero", "yzero"]:
self.ax.axis[direction].set_axisline_style("->")
self.ax.axis[direction].set_visible(True)
def ecclambda(self,xl,a,b):
"""
returns points elliptic curve for P and Q
y**2 = x**3 + a*x + b
"""
return sqrt(xl**3 + a*xl + b)
def elliptic_curve(self,x,y):
"""
takes in x,y as set of points, returns the elliptic curve
y**2 = x**3 + a*x + b
"""
return pow(y, 2) - pow(x, 3) - x * self.a - self.b
def onclick(self, event):
x = event.xdata
if event.button == 1:
self.pxlam = x
if event.button == 3:
self.qxlam = x
self.update()
def update(self):
pylam = self.ecclambda(self.pxlam,self.a,self.b) # calculate P from pxlam
qylam = self.ecclambda(self.qxlam,self.a,self.b) # calculate Q from qxlam
self.p.set_data([self.pxlam], [pylam])
self.q.set_data([self.qxlam], [qylam])
self.pt.set_x(self.pxlam-0.25)
self.pt.set_y(pylam+0.5)
self.qt.set_x(self.qxlam-0.25)
self.qt.set_y(qylam+0.5)
plt.gcf().canvas.draw()
def plotGraph(self):
"""
main plotting of elliptic curve and points/line for P+Q=R
P+Q=R --->>> -R is plotted (xr,yr), R is plotted (xr, -yr)
conditional with invertQy allows inversion of Q across x-axis; set option in main()
"""
self.drawAxis()
y, x = np.ogrid[-10:10:100j, -10:10:100j] # range grid [from : to : how_many_points]
xlist = x.ravel(); ylist = y.ravel()
plt.contour(xlist, ylist, self.elliptic_curve(x,y), [0])
pylam = self.ecclambda(self.pxlam,self.a,self.b) # calculate P from pxlam
qylam = self.ecclambda(self.qxlam,self.a,self.b) # calculate Q from qxlam
if self.invertQy == 1: qylam = -qylam # optional, inverts qy to negative on the plot
plt.plot([self.pxlam,self.qxlam], [pylam,qylam], color = "c", linewidth=1)
self.p = plt.plot([self.pxlam], [pylam], "mo")[0]
self.q = plt.plot([self.qxlam], [qylam], "mo")[0]
self.pt = plt.text(self.pxlam-0.25,pylam+0.5, '$P$')
self.qt = plt.text(self.qxlam-0.25,self.qxlam+0.5, '$Q$')
s = (pylam - qylam)/(self.pxlam - self.qxlam) # calculate s slope
xr = s**2 - self.pxlam - self.qxlam # x-value of R
yr = pylam + s*(xr - self.pxlam) # y-value of -R; -y is R (inverted across x-axis)
plt.plot([xr],[yr],"mo")
plt.plot([xr],[-yr],"co")
plt.plot([self.qxlam,xr], [qylam,yr], color = "c", linewidth=1)
plt.plot([xr,xr], [yr,-yr], "x--")
plt.text(xr+0.25,yr, '$-R$'); plt.text(xr+0.25,-yr, '$R$')
plt.text(-9,6,' P: (%s ,%s) \n Q: (%s ,%s) \n R: (%s ,%s) \n a: %s \n b: %s '
%(self.pxlam,pylam,self.qxlam,qylam,xr,-yr,self.a,self.b),
fontsize=10, color = 'blue',bbox=dict(facecolor='tan', alpha=0.5))
plt.title(r"Elliptic Curve Implementation $y^{2} = x^{3} + a*x + b$", fontsize = 16, color = 'b')
self.fig.canvas.mpl_connect('button_press_event', self.onclick)
#[xi,yi] = plt.ginput(0)
##print "ginput ",xi,yi
plt.grid(True)
plt.show()
def main():
a = -2; b = 1; px = -1.55; qx = -0.1
invertQy = 0 # set to 1 if q should be inverted to negative along its y axis
ec = ECC(a,b,px,qx,invertQy)
ec.plotGraph()
if __name__ == '__main__':
main()

Categories

Resources