I'm trying to plot a set of points with a special feature,
first plot 2 points with a random coordinates x and y, in a range from 0 to 200,
but my problem is how can set this points as fixed or centers, take this center-points and from this points, plot one new point with random coordinates(as pairs of points A-a, B-b, etc), and define the distance that can't be higher than 30 meter or units of distance beetwen this points. To get the points like this
I add part of my code to make this
import matplotlib as mpl
from matplotlib.figure import Figure
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from itertools import product
from matplotlib.lines import Line2D
fig,ax=plt.subplots()
#AP POINTS
###################################################
points_xA=np.random.randint(0,200)
points_yA=np.random.randint(0,200)
points_xB=np.random.randint(0,200)
points_yB=np.random.randint(0,200)
center1=np.array([points_xA,points_yB])
center2=np.array([points_xB,points_yB])
ax.annotate("A",xy=(center1),fontsize=12,bbox={"boxstyle":"circle","color":"orange"})
ax.annotate("B",xy=(center2),fontsize=12,bbox={"boxstyle":"circle","color":"orange"})
#STA POINTS
######################################################
#points_xa=np.random.randint()
#points_ya=np.random.randint()
#points_xb=np.random.randint()
#points_yb=np.random.randint()
######################################################
#LABELS
plt.title('random points')
plt.xlabel('x(m)')
plt.ylabel('y(m)')
plt.xlim(0,210)
plt.ylim(0,210)
plt.grid(True)
plt.show()
i have develop a script that plot points as i wanted, but it have some issues:
1.- The menu or bar where the zoom functions, save image, etc. It disappeared and I can't zoom, which I think would be the most important thing.
2.- The table where is the coordinates of each point, for example, for AP_A it have his STA_A1 o more, depending how many STA's you want( for 3 STA's it would be STA_A1, STA_A2, STA_A3, etc)
but in the table apears as STA_A1, for any STA, in the next image it's more clear
I hope it will be useful to someone, on the other hand if someone can correct those errors in my code it would be great, I thank to this community where I have found some solutions on several occasions.
code:
import matplotlib as mpl
from matplotlib.figure import Figure
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from itertools import product
from matplotlib.lines import Line2D
##########################
#RADIOS
radius1=30
#radius2=30
#AP POINTS
###################################################
def setNodos(n,rango=300,n_clientes=6):
listaNodos = []
for i in range(n):
points_x=np.random.randint(0,rango)
points_y=np.random.randint(0,rango)
listaNodos.append((np.array([points_x,points_y]),n_clientes))
return listaNodos
listaNodos = setNodos(4,300,3)
abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
points_x = []
points_y = []
sta_cant = []
points_sta_x = []
points_sta_y = []
sta_names = []
for nodo,n in listaNodos:
points_x.append(nodo[0])
points_y.append(nodo[1])
sta_cant.append(n)
t_data=[]
########################################
fig = plt.figure(figsize = (15,10))
ax = plt.subplot2grid((3,2), (0, 0),colspan=2,rowspan=2)
l=0
sta_n=0
print(listaNodos)
for centerA,sta_n in listaNodos:
cxA,cyA = centerA
ax.annotate(abc[l],xy=(centerA),fontsize=12,bbox={"boxstyle":"circle","color":"orange"})
#RADIO CIRCULO ROJO
ct1A=np.linspace(0,2*np.pi)
circx11,circy12 = radius1*np.cos(ct1A)+cxA, radius1*np.sin(ct1A)+cyA
plt.plot(circx11, circy12, ls="-",color='red')
#RELLENO CIRCULO ROJO
ax= plt.gca()
t1= plt.Polygon([[i,j] for i, j in zip(circx11,circy12)], color='slategrey', alpha=0.2)
ax.add_patch(t1)
######################################################
#STA POINTS
######################################################
r_sta = np.random.randint(0,radius1,size=sta_n)
tita_sta = np.random.randint(0,359,size=sta_n)
x_sta = np.round(r_sta*np.cos(tita_sta)+cxA,0)
y_sta = np.round(r_sta*np.sin(tita_sta)+cyA,0)
print(x_sta,y_sta)
for x,y in zip(x_sta,y_sta):
#plt.scatter(x,y,c='b',zorder=1000)
x = np.min((300,np.max((0,int(x)))))
y = np.min((300,np.max((0,int(y)))))
ax.annotate(abc[l].lower(),xy=((x,y)),fontsize=10,color='black',
bbox={"boxstyle":"circle","color":"steelblue","alpha":0.5},
)
sta_names.append('STA_%s%i'%(abc[l],l+1))
points_sta_x.append(x)
points_sta_y.append(y)
l+=1
######################################################
#Tabla con coordenadas
plt.xlabel('x(m)')
plt.ylabel('y(m)')
plt.xlim(-10,310)
plt.ylim(-10,310)
ax.grid(True)
plt.title('random points')
t_data.append(points_x+points_sta_x)
t_data.append(points_y+points_sta_y)
print(t_data)
print(sta_n)
collLabels =[('AP_%s'%i) for i in abc[:len(points_x)]]
for name in sta_names:
collLabels.append(name)
print(collLabels)
ax1 = plt.subplot2grid((3,2), (2, 0),colspan=2,rowspan=1)
table=ax1.table(cellText = t_data,
colLabels=collLabels,
rowLabels=('coord_x','coord_y'),
bbox=[0,0.3,0.1+0.05*len(collLabels),0.6+0.01*len(collLabels)]
,cellLoc='center',fontsize=30)
plt.xticks([])
plt.yticks([])
plt.axis('OFF')
plt.tight_layout(rect=[0.01, 0.01, 1.0, 1.0])
#######################################################
#LABELS
ax.set_aspect('equal')
plt.savefig('./salida/escen_random.png')
plt.show()
Related
I'm relatively new to python/mongodb and working on a project but ran into an issue.
I've been given a data set based on Game of Thrones and wanted to see the relationship between Killed Count, How many people they have been killed by on a scatter plot and encircling the data points that have to do with royals. I've created the columns counting the killed/killedBy columns, and have created an encircle data set that only has the rows that have royal=1, but am not sure how to write the encircle() line in order to encircle all the data. Here is the code that i've written below (apologies as I know its probably very inefficient).
from pymongo import MongoClient
import pandas as pd
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import style
import numpy as np
from scipy.spatial import ConvexHull
client=MongoClient()
db=client.GoT
characters = db.characters
M = characters.find()
CDB = {}
for m in M:
CDB[m["characterName"]] = m
CDBdf = pd.DataFrame(CDB)
CDBdf = CDBdf.T
CDBdf["RoyalExists"] = ""
CDBdf["KilledByExists"] = ""
CDBdf["KilledExists"] = ""
for index, row in CDBdf.iterrows():
if (pd.isna(row['royal'])==False):
row['RoyalExists']=1
else:
row['RoyalExists']=0
for index, row in CDBdf.iterrows():
if (np.any(pd.isna(row['killedBy'])== False)==False):
row['KilledByExists']=0
else:
row['KilledByExists']= len(row['killedBy'])
for index, row in CDBdf.iterrows():
if (np.any(pd.isna(row['killed'])== False)==False):
row['KilledExists']=0
else:
row['KilledExists']=len(row['killed'])
x= CDBdf["KilledExists"]
y= CDBdf["KilledByExists"]
fig = plt.figure(figsize=(16, 10), dpi= 80, facecolor='w', edgecolor='k')
plt.scatter(x, y)
def encircle(x,y, ax=None, **kw):
if not ax: ax=plt.gca()
p = np.c_[x,y]
hull = ConvexHull(p)
poly = plt.Polygon(p[hull.vertices,:], **kw)
ax.add_patch(poly)
encircle_data = CDBdf.loc[CDBdf["RoyalExists"]==1]
encircle(encircle_data.CDBdf["KilledExists"],encircle_data.CDBdf["KilledByExists"], ec="k", fc="gold", alpha=0.1)
plt.show()
encircle_data
Was just hoping for some clarification on how to write the line "encircle(encircle_data.CDBdf["KilledExists"],encircle_data.CDBdf["KilledByExists"], ec="k", fc="gold", alpha=0.1)" so that it circles all of the royals data points.
just wondering if anybody has experience with matplotlib custom markers
I want each marker in my plot to be a pie chart. To achieve this, my strategy was to create custom markers using the path class, method wedge.
https://matplotlib.org/stable/api/path_api.html
However is not displaying correctly, in particular with wedges defined with angles in the left quadrants. However, the path defined by the wedge class method seems to be correct and wedges are displayed correctly if using PathPatch and .add_patch()
See example below
import numpy as np
import math
import matplotlib.path as mpath
import matplotlib.cm
import matplotlib.pyplot as plt
import matplotlib.patches as patches
#Create wedges from angles
angles = np.array( [0,140,160,360] ) #Wedges angles
wedges=[]
for i in range(len(angles)-1):
angle0= angles[i]
angle1= angles[i+1]
dangle = angle1-angle0
wedge0=None
if dangle>0:
wedge0= mpath.Path.wedge(angle0, angle1)
wedge0= mpath.Path.wedge(angle0, angle1)
wedges.append(wedge0)
fig = plt.figure(figsize=(10,5))
ax1 = fig.add_subplot(121)
ax1.set_xlim(-1, 1)
ax1.set_ylim(-1, 1)
ax2 = fig.add_subplot(122)
ax2.set_xlim(-2, 2)
ax2.set_ylim(-2, 2)
tab10 = matplotlib.cm.get_cmap('tab10')
for i, w0 in enumerate(wedges):
ax1.scatter(0,0, marker=w0, c = [tab10(i)], s=20000) #Use path markers
patch = patches.PathPatch(w0, color=tab10(i)) #Use patch
ax2.add_patch(patch)
plt.show()
Notice that the wedge on the left plot is sticking out, which is not supposed to.
Is this a bug in the matplotlib markers' code?
I managed to get the pie charts to display correctly.
Scaling by doing affine transforms does not help because the path markaers are all resized, as in
line 495 of markers.py .
def _set_custom_marker(self, path):
rescale = np.max(np.abs(path.vertices)) # max of x's and y's.
self._transform = Affine2D().scale(0.5 / rescale)
self._path = path
My solution is to modify the vertices in the created wedges by inserting new vertices that define a bounding box, slightly larger than the circle with radius 1.
Here is the modified code
import numpy as np
import matplotlib.path as mpath
import matplotlib.cm
import matplotlib.pyplot as plt
import matplotlib.patches as patches
def getBoundedWedge(angle0, angle1):
wedge0= mpath.Path.wedge(angle0, angle1)
#print(f"wedge0:{wedge0}")
vertices = wedge0.vertices
codes = wedge0.codes
#Add ghost vertices to define bounding box
vertices= np.insert( vertices, 0, [[1.1,1.1], [-1.1,1.1] , [-1.1,-1.1], [1.1,-1.1]] , axis=0)
codes = np.insert( codes, 0, [1,1,1,1])
wedgeextra = mpath.Path(vertices, codes)
return wedgeextra
#Create wedges from angles
angles = np.array( [0,140,160,360] ) #Wedges angles
wedges=[]
for i in range(len(angles)-1):
angle0= angles[i]
angle1= angles[i+1]
dangle = angle1-angle0
wedge0=None
if dangle>0:
wedge0= getBoundedWedge(angle0, angle1)
wedges.append(wedge0)
fig = plt.figure(figsize=(10,5))
ax1 = fig.add_subplot(121)
ax1.set_xlim(-1, 1)
ax1.set_ylim(-1, 1)
ax2 = fig.add_subplot(122)
ax2.set_xlim(-2, 2)
ax2.set_ylim(-2, 2)
tab10 = matplotlib.cm.get_cmap('tab10')
for i, w0 in enumerate(wedges):
ax1.scatter(0,0, marker=w0, c = [tab10(i)], s=20000) #Use path markers
patch = patches.PathPatch(w0, color=tab10(i)) #Use patch
ax2.add_patch(patch)
plt.show()
And the output is as follows
I have a problem witch updateing matplotlib chart. The problem is that i have many curve's on it, and after update the number of them may change. In example code I have 2 sets of data, 1st with 90 curves, and 2nd with 80, and i wish I could plot 1st set, and then 2nd, in the same matplotlib window.
import matplotlib.pyplot as plt
from matplotlib.transforms import Bbox
import numpy as np
from numpy.lib.polynomial import RankWarning
import pandas as pd
import sys
fig, ax = plt.subplots()
fig.subplots_adjust(right=0.78)
_x = []
_y = []
_y1 = []
_x1 = []
for x in range(90):
_x.append(np.linspace(0, 10*np.pi, 100))
_y.append(np.sin(_x[x])+x)
for x in range(80):
_x1.append(np.linspace(0, 10*np.pi, 150))
_y1.append(np.tan(_x1[x]+x))
def narysuj(__x, __y):
p = [] # p-pomiar
f = [] # f-czestotliwosc
for x in range(len(__x)):
p.append([])
f.append([])
ax.set_prop_cycle(color=plt.cm.gist_rainbow(np.linspace(0, 1, len(__x))))
for x in range(len(__x)):
for line in range(len(__x[x])):
#print(len(_y[x]), line)
p[x].append(__y[x][line])
f[x].append(__x[x][line])
ax.plot(f[x], p[x], label=f"Label {x}")
plt.show()
narysuj(_x, _y)
narysuj(_x1, _y1)
PS I know the way I'm drawing those charts is highly ineffective.
I found what was the problem. I had to add plt.ion() at the start of program and ax.clear() before drawing.
How does one set the color of a line in matplotlib with scalar values provided at run time using a colormap (say jet)? I tried a couple of different approaches here and I think I'm stumped. values[] is a storted array of scalars. curves are a set of 1-d arrays, and labels are an array of text strings. Each of the arrays have the same length.
fig = plt.figure()
ax = fig.add_subplot(111)
jet = colors.Colormap('jet')
cNorm = colors.Normalize(vmin=0, vmax=values[-1])
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
lines = []
for idx in range(len(curves)):
line = curves[idx]
colorVal = scalarMap.to_rgba(values[idx])
retLine, = ax.plot(line, color=colorVal)
#retLine.set_color()
lines.append(retLine)
ax.legend(lines, labels, loc='upper right')
ax.grid()
plt.show()
The error you are receiving is due to how you define jet. You are creating the base class Colormap with the name 'jet', but this is very different from getting the default definition of the 'jet' colormap. This base class should never be created directly, and only the subclasses should be instantiated.
What you've found with your example is a buggy behavior in Matplotlib. There should be a clearer error message generated when this code is run.
This is an updated version of your example:
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cm as cmx
import numpy as np
# define some random data that emulates your indeded code:
NCURVES = 10
np.random.seed(101)
curves = [np.random.random(20) for i in range(NCURVES)]
values = range(NCURVES)
fig = plt.figure()
ax = fig.add_subplot(111)
# replace the next line
#jet = colors.Colormap('jet')
# with
jet = cm = plt.get_cmap('jet')
cNorm = colors.Normalize(vmin=0, vmax=values[-1])
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
print scalarMap.get_clim()
lines = []
for idx in range(len(curves)):
line = curves[idx]
colorVal = scalarMap.to_rgba(values[idx])
colorText = (
'color: (%4.2f,%4.2f,%4.2f)'%(colorVal[0],colorVal[1],colorVal[2])
)
retLine, = ax.plot(line,
color=colorVal,
label=colorText)
lines.append(retLine)
#added this to get the legend to work
handles,labels = ax.get_legend_handles_labels()
ax.legend(handles, labels, loc='upper right')
ax.grid()
plt.show()
Resulting in:
Using a ScalarMappable is an improvement over the approach presented in my related answer:
creating over 20 unique legend colors using matplotlib
I thought it would be beneficial to include what I consider to be a more simple method using numpy's linspace coupled with matplotlib's cm-type object. It's possible that the above solution is for an older version. I am using the python 3.4.3, matplotlib 1.4.3, and numpy 1.9.3., and my solution is as follows.
import matplotlib.pyplot as plt
from matplotlib import cm
from numpy import linspace
start = 0.0
stop = 1.0
number_of_lines= 1000
cm_subsection = linspace(start, stop, number_of_lines)
colors = [ cm.jet(x) for x in cm_subsection ]
for i, color in enumerate(colors):
plt.axhline(i, color=color)
plt.ylabel('Line Number')
plt.show()
This results in 1000 uniquely-colored lines that span the entire cm.jet colormap as pictured below. If you run this script you'll find that you can zoom in on the individual lines.
Now say I want my 1000 line colors to just span the greenish portion between lines 400 to 600. I simply change my start and stop values to 0.4 and 0.6 and this results in using only 20% of the cm.jet color map between 0.4 and 0.6.
So in a one line summary you can create a list of rgba colors from a matplotlib.cm colormap accordingly:
colors = [ cm.jet(x) for x in linspace(start, stop, number_of_lines) ]
In this case I use the commonly invoked map named jet but you can find the complete list of colormaps available in your matplotlib version by invoking:
>>> from matplotlib import cm
>>> dir(cm)
A combination of line styles, markers, and qualitative colors from matplotlib:
import itertools
import matplotlib as mpl
import matplotlib.pyplot as plt
N = 8*4+10
l_styles = ['-','--','-.',':']
m_styles = ['','.','o','^','*']
colormap = mpl.cm.Dark2.colors # Qualitative colormap
for i,(marker,linestyle,color) in zip(range(N),itertools.product(m_styles,l_styles, colormap)):
plt.plot([0,1,2],[0,2*i,2*i], color=color, linestyle=linestyle,marker=marker,label=i)
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.,ncol=4);
UPDATE: Supporting not only ListedColormap, but also LinearSegmentedColormap
import itertools
import matplotlib.pyplot as plt
Ncolors = 8
#colormap = plt.cm.Dark2# ListedColormap
colormap = plt.cm.viridis# LinearSegmentedColormap
Ncolors = min(colormap.N,Ncolors)
mapcolors = [colormap(int(x*colormap.N/Ncolors)) for x in range(Ncolors)]
N = Ncolors*4+10
l_styles = ['-','--','-.',':']
m_styles = ['','.','o','^','*']
fig,ax = plt.subplots(gridspec_kw=dict(right=0.6))
for i,(marker,linestyle,color) in zip(range(N),itertools.product(m_styles,l_styles, mapcolors)):
ax.plot([0,1,2],[0,2*i,2*i], color=color, linestyle=linestyle,marker=marker,label=i)
ax.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.,ncol=3,prop={'size': 8})
U may do as I have written from my deleted account (ban for new posts :( there was). Its rather simple and nice looking.
Im using 3-rd one of these 3 ones usually, also I wasny checking 1 and 2 version.
from matplotlib.pyplot import cm
import numpy as np
#variable n should be number of curves to plot (I skipped this earlier thinking that it is obvious when looking at picture - sorry my bad mistake xD): n=len(array_of_curves_to_plot)
#version 1:
color=cm.rainbow(np.linspace(0,1,n))
for i,c in zip(range(n),color):
ax1.plot(x, y,c=c)
#or version 2: - faster and better:
color=iter(cm.rainbow(np.linspace(0,1,n)))
c=next(color)
plt.plot(x,y,c=c)
#or version 3:
color=iter(cm.rainbow(np.linspace(0,1,n)))
for i in range(n):
c=next(color)
ax1.plot(x, y,c=c)
example of 3:
Ship RAO of Roll vs Ikeda damping in function of Roll amplitude A44
I'm trying to plot filled polygons of countries on the world map with matplotlib in python.
I've got a shapefile with country boundary coordinates of every country. Now, I want to convert these coordinates (for each country) into a polygon with matplotlib. Without using Basemap. Unfortunately, the parts are crossing or overlapping. Is there a workarund, maybe using the distance from point to point.. or reordering them ?
Ha!
I found out, how.. I completely neglected, the sf.shapes[i].parts information! Then it comes down to:
# -- import --
import shapefile
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
# -- input --
sf = shapefile.Reader("./shapefiles/world_countries_boundary_file_world_2002")
recs = sf.records()
shapes = sf.shapes()
Nshp = len(shapes)
cns = []
for nshp in xrange(Nshp):
cns.append(recs[nshp][1])
cns = array(cns)
cm = get_cmap('Dark2')
cccol = cm(1.*arange(Nshp)/Nshp)
# -- plot --
fig = plt.figure()
ax = fig.add_subplot(111)
for nshp in xrange(Nshp):
ptchs = []
pts = array(shapes[nshp].points)
prt = shapes[nshp].parts
par = list(prt) + [pts.shape[0]]
for pij in xrange(len(prt)):
ptchs.append(Polygon(pts[par[pij]:par[pij+1]]))
ax.add_collection(PatchCollection(ptchs,facecolor=cccol[nshp,:],edgecolor='k', linewidths=.1))
ax.set_xlim(-180,+180)
ax.set_ylim(-90,90)
fig.savefig('test.png')
Then it will look like this:
Here is another piece of code I used to plot polygon shapefiles. It uses GDAL/OGR to read shapefile and plots correctly donut shape polygons:
from osgeo import ogr
import numpy as np
import matplotlib.path as mpath
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
# Extract first layer of features from shapefile using OGR
ds = ogr.Open('world_countries_boundary_file_world_2002.shp')
nlay = ds.GetLayerCount()
lyr = ds.GetLayer(0)
# Get extent and calculate buffer size
ext = lyr.GetExtent()
xoff = (ext[1]-ext[0])/50
yoff = (ext[3]-ext[2])/50
# Prepare figure
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_xlim(ext[0]-xoff,ext[1]+xoff)
ax.set_ylim(ext[2]-yoff,ext[3]+yoff)
paths = []
lyr.ResetReading()
# Read all features in layer and store as paths
for feat in lyr:
geom = feat.geometry()
codes = []
all_x = []
all_y = []
for i in range(geom.GetGeometryCount()):
# Read ring geometry and create path
r = geom.GetGeometryRef(i)
x = [r.GetX(j) for j in range(r.GetPointCount())]
y = [r.GetY(j) for j in range(r.GetPointCount())]
# skip boundary between individual rings
codes += [mpath.Path.MOVETO] + \
(len(x)-1)*[mpath.Path.LINETO]
all_x += x
all_y += y
path = mpath.Path(np.column_stack((all_x,all_y)), codes)
paths.append(path)
# Add paths as patches to axes
for path in paths:
patch = mpatches.PathPatch(path, \
facecolor='blue', edgecolor='black')
ax.add_patch(patch)
ax.set_aspect(1.0)
plt.show()
from fiona import collection
import matplotlib.pyplot as plt
from descartes import PolygonPatch
from matplotlib.collections import PatchCollection
from itertools import imap
from matplotlib.cm import get_cmap
cm = get_cmap('Dark2')
figure, axes = plt.subplots(1)
source_path = "./shapefiles/world_countries_boundary_file_world_2002"
with collection(source_path, 'r') as source:
patches = imap(PolygonPatch, (record['geometry'] for record in source)
axes.add_collection( PatchCollection ( patches, cmap=cm, linewidths=0.1 ) )
axes.set_xlim(-180,+180)
axes.set_ylim(-90,90)
plt.show()
Note this assumes polygons, MultiPolygons can be handles in a similar manner with
map(PolygonPatch, MultiPolygon(record['geometry']))
Regarding to #hannesk's answer, you should add the following imports: from numpy import array and import matplotlib and replace the line cm = get_cmap('Dark2') by cm = matplotlib.cm.get_cmap('Dark2')
(I'm not so famous to add a comment to the noticed post.)