I'm wanting to draw a figure with a hexagonal grid. The end result should look like a honeycomb. However, I'm having trouble getting my hexagons sized correctly using matplotlib.collections.RegularPolyCollection. Can anyone see what I am doing wrong, or offer another solution. I imagine this has been done before, so no need for me to reinvent the wheel.
import matplotlib.pyplot as plt
from matplotlib import collections, transforms
from matplotlib.colors import colorConverter
import numpy as np
# Make some offsets, doing 4 polygons for simplicity here
xyo = [(0,0), (1,0), (0,1), (1,1)]
# length of hexagon side
hexside = 1
# area of circle circumscribing the hexagon
circ_area = np.pi * hexside ** 2
fig, ax = plt.subplots(1,1)
col = collections.RegularPolyCollection(6, np.radians(90), sizes = (circ_area,),
offsets=xyo,transOffset=ax.transData)
ax.add_collection(col, autolim=True)
colors = [colorConverter.to_rgba(c) for c in ('r','g','b','c')]
col.set_color(colors)
ax.autoscale_view()
plt.show()
Whoever struggles with the same issue in 2020+, check out my hexalattice module:
It allows to create hexagonal grids (hexagonal lattices) in 2D with fine control over spatial distribution of the hexagons, circular clop of the lattice and rotations around the central slot.
Usage and graphical output:
from hexalattice.hexalattice import *
hex_centers, _ = create_hex_grid(nx=10,
ny=10,
do_plot=True)
plt.show() # import matplotlib.pyplot as plt
Installation:
'>> pip install hexalattice'
Advanced features
The module allows stacking of few grids, arbitrary grid rotation around its center, advanced control over gaps between the hexagons etc.
Example:
hex_grid1, h_ax = create_hex_grid(nx=50,
ny=50,
rotate_deg=0,
min_diam=1,
crop_circ=20,
do_plot=True)
create_hex_grid(nx=50,
ny=50,
min_diam=1,
rotate_deg=5,
crop_circ=20,
do_plot=True,
h_ax=h_ax)
Related
I want to create a smooth cylinder using matplotlib/pyplot. I've adapted a tutorial online and produced the following minimal example:
from numpy import meshgrid,linspace,pi,sin,cos,shape
from matplotlib import pyplot
import matplotlib.tri as mtri
from mpl_toolkits.mplot3d import Axes3D
u,v = meshgrid(linspace(0,10,10),linspace(0,2*pi,20))
u = u.flatten()
v = v.flatten()
x = u
z = sin(v)
y = cos(v)
tri = mtri.Triangulation(u, v)
fig = pyplot.figure()
ax = fig.add_axes([0,0,1,1],projection='3d')
ax.plot_trisurf(x,y,z,triangles=tri.triangles,linewidth=0)
pyplot.show()
which produces a cylinder. I set linewidth=0 to remove the wireframe, however, there is now the "ghost" of the wireframe because the triangulation has (presumably) been spaced assuming the wireframe is there to fill in the gaps. This looks to be specific to plot_trisurf, because there are other 3d plotting examples (e.g., using plot_surface) which set linewidth=0 without these gaps showing up.
Doing an mtri.Triangulation?, it seems like it might not be possible to "perfectly" fill in the gaps, since it states
>Notes
> -----
> For a Triangulation to be valid it must not have duplicate points,
> triangles formed from colinear points, or overlapping triangles.
One partial solution is to just color the wireframe the same shade of blue, but after I've fixed this problem I also want to add a light source/shading on the surface, which would put me back at square one.
Is there a way to make this work? Or can someone suggest a different approach? Thanks for any help.
ax.plot_trisurf(x,y,z,triangles=tri.triangles,linewidth=0, antialiased=False)
I am working on project to find similarity between two sentences/documents using tf-idf measure.
Now my question is how can I show the similarity in a graphical/Visualization format. Something like a Venn diagram where intersection value becomes the similarity measure or any other plots available in matplotlib or any python libraries.
I tried the following code:
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
documents = (
"The sky is blue",
"The sun is bright"
)
tfidf_vectorizer = TfidfVectorizer()
tfidf_matrix = tfidf_vectorizer.fit_transform(documents)
print tfidf_matrix
cosine = cosine_similarity(tfidf_matrix[0:1], tfidf_matrix)
print cosine
import matplotlib.pyplot as plt
r=25
d1 = 2 * r * (1 - cosine[0][0])
circle1=plt.Circle((0,0),d1/2,color='r')
d2 = 2 * r * (1 - cosine[0][1])
circle2=plt.Circle((r,0),d2/2,color="b")
fig = plt.gcf()
fig.gca().add_artist(circle1)
fig.gca().add_artist(circle2)
fig.savefig('plotcircles.png')
plt.show()
But the plot I got was empty. Can some one explain what might be the error.
plotting circle source:plot a circle
Just to explain what's going on, here's a stand-alone example of your problem (if the circle is entirely outside the boundaries, nothing would be shown):
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
fig, ax = plt.subplots()
circ = Circle((1, 1), 0.5)
ax.add_artist(circ)
plt.show()
When you manually add an artist through add_artist, add_patch, etc, autoscaling isn't applied unless you explicitly do so. You're accessing a lower-level interface of matplotlib that's what the higher-level functions (e.g. plot) are built on top of. However, this is also the easiest way to add a single circle in data coordinates, so the lower-level interface is what you want in this case.
Furthermore, add_artist is too general for this. You actually want add_patch (plt.Circle is matplotlib.patches.Circle). The difference between add_artist and add_patch may seem arbitrary, but add_patch has extra logic to calculate the extent of a patch for autoscaling, whereas add_artist is the "bare" lower-level function that can take any artist, but doesn't do anything special. Autoscaling won't work correctly for a patch if you add it with add_artist.
To autoscale the plot based on the artists that you've added, call ax.autoscale():
As a quick example of autoscaling a manually added patch:
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
fig, ax = plt.subplots()
circ = Circle((1, 1), 0.5)
ax.add_patch(circ)
ax.autoscale()
plt.show()
Your next question might be "why isn't the circle round?". It is, in data coordinates. However, the x and y scales of the plot (this is the aspect ratio, in matplotlib terminology) are currently different. To force them to be the same, call ax.axis('equal') or ax.axis('scaled'). (We can actually leave out the call to autoscale in this case, as ax.axis('scaled'/'equal') will effectively call it for us.):
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
fig, ax = plt.subplots()
circ = Circle((1, 1), 0.5)
ax.add_patch(circ)
ax.axis('scaled')
plt.show()
The Plots are not empty, but I guess, your circles are to big!
I don't have sklearn installed, so I start at the point where you print cosine:
## set constants
r = 1
d = 2 * r * (1 - cosine[0][1])
## draw circles
circle1=plt.Circle((0, 0), r, alpha=.5)
circle2=plt.Circle((d, 0), r, alpha=.5)
## set axis limits
plt.ylim([-1.1, 1.1])
plt.xlim([-1.1, 1.1 + d])
fig = plt.gcf()
fig.gca().add_artist(circle1)
fig.gca().add_artist(circle2)
## hide axes if you like
# fig.gca().get_xaxis().set_visible(False)
# fig.gca().get_yaxis().set_visible(False)
fig.savefig('venn_diagramm.png')
That also answers your other question, where I also added this piece of code!
I have difficulties in plotting e.g. polygons across the boundaries of a map generated using matplotlib-basemap. In the example below, the map boundary is specified by the dateline. I try to plot a triangle across the dateline by specifying the coordinates of vertices of a triangle. This works fine when all coordinates are within the map, but if they go across the map boundary, basemap performs strange extrapolation, as it seems not to know how to draw the rectangles in the right way.
Right way would mean in my sense that the triangle is drawn until the map boundary and would then continue at the other side of the map.
Below is a minimum code example and a figure illustrating the general problem.
Any ideas how to solve this problem in a general way are highly welcome.
from mpl_toolkits.basemap import Basemap
import matplotlib.pylab as plt
import numpy as np
import matplotlib.path as mpath
import matplotlib.patches as mpatches
import matplotlib as mpl
from matplotlib.collections import PatchCollection
![plt.close('all')
Path = mpath.Path
fig=plt.figure(); ax=fig.add_subplot(121); ax1=fig.add_subplot(122)
def do_plot(ax,lons,lats,title):
patches=\[\]
m = Basemap(projection='robin', resolution='c',lon_0=0.,ax=ax) #todo: how to make it properly work for other projections ???
m.drawmapboundary(fill_color='grey')
m.drawcoastlines()
#--- generate first sample with no problem
x,y=m(lons,lats)
verts = np.asarray(\[x,y\]).T
codes = \[Path.MOVETO,Path.LINETO,Path.LINETO\]
patches.append(mpatches.PathPatch(mpath.Path(verts, codes,closed=True)))
#--- generate collection
cmap = plt.cm.get_cmap('jet', 50); norm = mpl.colors.Normalize(vmin=None, vmax=None) #colorbar mapping
collection = PatchCollection(patches, cmap=cmap,norm=norm, alpha=1.,match_original=False) #construct library of all objects
colors = np.asarray(np.random.random(len(patches)))
collection.set_array(np.array(colors)) #assign data values here
#--- do actual plotting
im=m.ax.add_collection(collection)
ax.set_title(title)
do_plot(ax,\[-10.,0.,20.\],\[30.,50.,20.\],'This works')
do_plot(ax1,\[170,180,-175\],\[30.,50.,20.\],'... and here is the boundary problem')
plt.show()][1]
You cannot get around this problem with Basemap in a simple way. In your line x,y=m(lons,lats) you have transformed the points to map coordinates, and drawing the polygon just draws between those projected points.
You might try using Cartopy, which can do this. This example may help.
I want to draw some lines and circles on the screen using of matplotlib. I do not need the X axis and Y axis. Is this possible? How can I do it?
You can hide the axes with axes.get_xaxis().set_visible(False) or by using axis('off').
Example:
from pylab import *
gca().get_xaxis().set_visible(False) # Removes x-axis from current figure
gca().get_yaxis().set_visible(False) # Removes y-axis from current figure
a = arange(10)
b = sin(a)
plot(a, b)
show() # Plot has no x and y axes
If you don't want axes, and are happy to work in the range 0-1:
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
fig = plt.figure()
fig.patches.append(mpatches.Circle([0.5, 0.5], 0.25, transform=fig.transFigure))
fig.show()
There are a couple of benefits to using #Dhara's solution. The primary being you can use a data coordinate system which automatically scales to your data, but if you just want to draw a couple of shapes, my solution works pretty well.
Some useful documentation if you go down the route I have explained:
http://matplotlib.sourceforge.net/api/artist_api.html#matplotlib.patches.Circle
http://matplotlib.sourceforge.net/api/artist_api.html#matplotlib.lines.Line2D
http://matplotlib.sourceforge.net/api/artist_api.html#matplotlib.patches.Rectangle
Using matplotlib in Python I drew a 3D graph. When I rotate the graph I noticed that the axes labels swap automatically which does not look interesting or helping to me. In fact it disturbs my focusing on the purpose of rotation which is to explore visually the presented data.
Q: How to disable auto-swapping axes labels while rotating in matplotlib?
I grabbed some ideas from SO, examined many and finally developed the following solution. It simply works.
from __future__ import division
import scipy as sp
import mpl_toolkits.mplot3d as a3d
import pylab as pl
nan = sp.nan
def axesoff():
box = [[-1,1,1,-1,-1,1,1,-1,-1,-1,nan,1,1,nan,1,1,nan,-1,-1],
[-1,-1,-1,-1,1,1,1,1,-1,-1,nan,-1,1,nan,1,-1,nan,1,1],
[-1,-1,1,1,1,1,-1,-1,-1,1,nan,-1,-1,nan,1,1,nan,-1,1]]
ax3.plot(*box,color='k')
for axis in (ax3.w_xaxis,ax3.w_yaxis,ax3.w_zaxis):
for obj in axis.get_ticklines(): obj.set_visible(False)
axis.set_ticklabels('')
axis.line.set_visible(False)
axis.pane.set_visible(False)
ax3.grid(False)
ax3.axis('equal')
#------here we go
x,y,z = sp.random.uniform(low=-1,high=1,size=(3,1000))
c = (x+1)+(y+1)+(z+1)
s = c*10
ax3 = a3d.Axes3D(pl.figure())
ax3.scatter(x,y,z,lw=0,s=s,c=c,alpha=0.5)
axesoff()
pl.show()