pyplot font python matplotlib plot

font - subplot python



Matplotlib: anotando un diagrama de dispersiĆ³n 3D (7)

Aquí hay una forma ligeramente más general de la excelente respuesta de HYRY. Funciona para cualquier lista de puntos y etiquetas.

import numpy as np from matplotlib import pyplot as plt from mpl_toolkits.mplot3d import Axes3D from mpl_toolkits.mplot3d import proj3d points = np.array([(1,1,1), (2,2,2)]) labels = [''billy'', ''bobby''] fig = plt.figure() ax = fig.add_subplot(111, projection = ''3d'') xs, ys, zs = np.split(points, 3, axis=1) sc = ax.scatter(xs,ys,zs) # if this code is placed inside a function, then # we must use a predefined global variable so that # the update function has access to it. I''m not # sure why update_positions() doesn''t get access # to its enclosing scope in this case. global labels_and_points labels_and_points = [] for txt, x, y, z in zip(labels, xs, ys, zs): x2, y2, _ = proj3d.proj_transform(x,y,z, ax.get_proj()) label = plt.annotate( txt, xy = (x2, y2), xytext = (-20, 20), textcoords = ''offset points'', ha = ''right'', va = ''bottom'', bbox = dict(boxstyle = ''round,pad=0.5'', fc = ''yellow'', alpha = 0.5), arrowprops = dict(arrowstyle = ''->'', connectionstyle = ''arc3,rad=0'')) labels_and_points.append((label, x, y, z)) def update_position(e): for label, x, y, z in labels_and_points: x2, y2, _ = proj3d.proj_transform(x, y, z, ax.get_proj()) label.xy = x2,y2 label.update_positions(fig.canvas.renderer) fig.canvas.draw() fig.canvas.mpl_connect(''motion_notify_event'', update_position) plt.show()

Hay un molesto problema de espacio de nombres que solo pude solucionar al usar (hackosamente) una variable global. Si alguien puede proporcionar una mejor solución o explicar lo que está pasando, ¡por favor hágamelo saber!

Intento generar un diagrama de dispersión 3D usando Matplotlib. Me gustaría anotar puntos individuales como el caso 2D aquí: Matplotlib: Cómo poner etiquetas individuales para un diagrama de dispersión .

Intenté usar esta función y consulté el archivo de Matplotlib, pero me pareció que la biblioteca no admite la anotación 3D. ¿Alguien sabe como hacer esto?

¡Gracias!


Calcula la posición 2D del punto y úsala para crear la anotación. Si necesita interactuar con la figura, puede volver a calcular la ubicación cuando se suelta el mouse.

import pylab from mpl_toolkits.mplot3d import Axes3D from mpl_toolkits.mplot3d import proj3d fig = pylab.figure() ax = fig.add_subplot(111, projection = ''3d'') x = y = z = [1, 2, 3] sc = ax.scatter(x,y,z) # now try to get the display coordinates of the first point x2, y2, _ = proj3d.proj_transform(1,1,1, ax.get_proj()) label = pylab.annotate( "this", xy = (x2, y2), xytext = (-20, 20), textcoords = ''offset points'', ha = ''right'', va = ''bottom'', bbox = dict(boxstyle = ''round,pad=0.5'', fc = ''yellow'', alpha = 0.5), arrowprops = dict(arrowstyle = ''->'', connectionstyle = ''arc3,rad=0'')) def update_position(e): x2, y2, _ = proj3d.proj_transform(1,1,1, ax.get_proj()) label.xy = x2,y2 label.update_positions(fig.canvas.renderer) fig.canvas.draw() fig.canvas.mpl_connect(''button_release_event'', update_position) pylab.show()


En caso de que quiera hacer girar la respuesta de @msch:

from mpl_toolkits.mplot3d import axes3d import matplotlib.pyplot as plt from numpy.random import rand m = rand(3,3) # m is an array of (x,y,z) coordinate triplets fig = plt.figure() ax = fig.add_subplot(111, projection=''3d'') for i in range(len(m)): # plot each point + it''s index as text above x = m[i,0] y = m[i,1] z = m[i,2] label = i ax.scatter(x, y, z, color=''b'') ax.text(x, y, z, ''%s'' % (label), size=20, zorder=1, color=''k'') ax.set_xlabel(''x'') ax.set_ylabel(''y'') ax.set_zlabel(''z'') for angle in range(0, 360): ax.view_init(30, angle) plt.draw() plt.pause(.001)


En las siguientes publicaciones [1] , [2] se discute el trazado de flechas 3D en matplotlib.

De forma similar, se puede crear la clase Annotation3D (heredada de Annotation):

from mpl_toolkits.mplot3d.proj3d import proj_transform from matplotlib.text import Annotation class Annotation3D(Annotation): ''''''Annotate the point xyz with text s'''''' def __init__(self, s, xyz, *args, **kwargs): Annotation.__init__(self,s, xy=(0,0), *args, **kwargs) self._verts3d = xyz def draw(self, renderer): xs3d, ys3d, zs3d = self._verts3d xs, ys, zs = proj_transform(xs3d, ys3d, zs3d, renderer.M) self.xy=(xs,ys) Annotation.draw(self, renderer)

Además, podemos definir la función annotate3D ():

def annotate3D(ax, s, *args, **kwargs): ''''''add anotation text s to to Axes3d ax'''''' tag = Annotation3D(s, *args, **kwargs) ax.add_artist(tag)

Usando esta función, las etiquetas de anotación se pueden agregar a Axes3d como en el siguiente ejemplo:

import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import axes3d from mpl_toolkits.mplot3d.art3d import Line3DCollection # data: coordinates of nodes and links xn = [1.1, 1.9, 0.1, 0.3, 1.6, 0.8, 2.3, 1.2, 1.7, 1.0, -0.7, 0.1, 0.1, -0.9, 0.1, -0.1, 2.1, 2.7, 2.6, 2.0] yn = [-1.2, -2.0, -1.2, -0.7, -0.4, -2.2, -1.0, -1.3, -1.5, -2.1, -0.7, -0.3, 0.7, -0.0, -0.3, 0.7, 0.7, 0.3, 0.8, 1.2] zn = [-1.6, -1.5, -1.3, -2.0, -2.4, -2.1, -1.8, -2.8, -0.5, -0.8, -0.4, -1.1, -1.8, -1.5, 0.1, -0.6, 0.2, -0.1, -0.8, -0.4] group = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 2, 2, 2, 3, 3, 3, 3] edges = [(1, 0), (2, 0), (3, 0), (3, 2), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0), (9, 0), (11, 10), (11, 3), (11, 2), (11, 0), (12, 11), (13, 11), (14, 11), (15, 11), (17, 16), (18, 16), (18, 17), (19, 16), (19, 17), (19, 18)] xyzn = zip(xn, yn, zn) segments = [(xyzn[s], xyzn[t]) for s, t in edges] # create figure fig = plt.figure(dpi=60) ax = fig.gca(projection=''3d'') ax.set_axis_off() # plot vertices ax.scatter(xn,yn,zn, marker=''o'', c = group, s = 64) # plot edges edge_col = Line3DCollection(segments, lw=0.2) ax.add_collection3d(edge_col) # add vertices annotation. for j, xyz_ in enumerate(xyzn): annotate3D(ax, s=str(j), xyz=xyz_, fontsize=10, xytext=(-3,3), textcoords=''offset points'', ha=''right'',va=''bottom'') plt.show()


Esta respuesta se basa en la respuesta previa del usuario315582. Hice algunas modificaciones para proporcionar una solución sin usar variables globales.

import numpy as np from matplotlib import pyplot as plt from mpl_toolkits.mplot3d import Axes3D from mpl_toolkits.mplot3d import proj3d def main(): fig = plt.figure() ax = fig.add_subplot(111, projection = ''3d'') points = np.array([(1,1,1), (2,2,2)]) labels = [''billy'', ''bobby''] plotlabels = [] xs, ys, zs = np.split(points, 3, axis=1) sc = ax.scatter(xs,ys,zs) for txt, x, y, z in zip(labels, xs, ys, zs): x2, y2, _ = proj3d.proj_transform(x,y,z, ax.get_proj()) label = plt.annotate( txt, xy = (x2, y2), xytext = (-20, 20), textcoords = ''offset points'', ha = ''right'', va = ''bottom'', bbox = dict(boxstyle = ''round,pad=0.5'', fc = ''yellow'', alpha = 0.5), arrowprops = dict(arrowstyle = ''-'', connectionstyle = ''arc3,rad=0'')) plotlabels.append(label) fig.canvas.mpl_connect(''motion_notify_event'', lambda event: update_position(event,fig,ax,zip(plotlabels, xs, ys, zs))) plt.show() def update_position(e,fig,ax,labels_and_points): for label, x, y, z in labels_and_points: x2, y2, _ = proj3d.proj_transform(x, y, z, ax.get_proj()) label.xy = x2,y2 label.update_positions(fig.canvas.renderer) fig.canvas.draw() if __name__ == ''__main__'': main()


Si tiene muchos puntos de datos, la tabla puede saturarse mucho si los anota todos. La siguiente solución (basada en la respuesta de HYRY) implementa una solución de sobreposición (pop-over) para puntos de datos en gráficos 3D. Solo el punto de datos junto a la posición de su mouse será anotado. Después de cada movimiento del mouse, se calcula la distancia del puntero del mouse a todos los puntos de datos, y se anota el punto más cercano.

import matplotlib.pyplot as plt, numpy as np from mpl_toolkits.mplot3d import proj3d def visualize3DData (X): """Visualize data in 3d plot with popover next to mouse position. Args: X (np.array) - array of points, of shape (numPoints, 3) Returns: None """ fig = plt.figure(figsize = (16,10)) ax = fig.add_subplot(111, projection = ''3d'') ax.scatter(X[:, 0], X[:, 1], X[:, 2], depthshade = False, picker = True) def distance(point, event): """Return distance between mouse position and given data point Args: point (np.array): np.array of shape (3,), with x,y,z in data coords event (MouseEvent): mouse event (which contains mouse position in .x and .xdata) Returns: distance (np.float64): distance (in screen coords) between mouse pos and data point """ assert point.shape == (3,), "distance: point.shape is wrong: %s, must be (3,)" % point.shape # Project 3d data space to 2d data space x2, y2, _ = proj3d.proj_transform(point[0], point[1], point[2], plt.gca().get_proj()) # Convert 2d data space to 2d screen space x3, y3 = ax.transData.transform((x2, y2)) return np.sqrt ((x3 - event.x)**2 + (y3 - event.y)**2) def calcClosestDatapoint(X, event): """"Calculate which data point is closest to the mouse position. Args: X (np.array) - array of points, of shape (numPoints, 3) event (MouseEvent) - mouse event (containing mouse position) Returns: smallestIndex (int) - the index (into the array of points X) of the element closest to the mouse position """ distances = [distance (X[i, 0:3], event) for i in range(X.shape[0])] return np.argmin(distances) def annotatePlot(X, index): """Create popover label in 3d chart Args: X (np.array) - array of points, of shape (numPoints, 3) index (int) - index (into points array X) of item which should be printed Returns: None """ # If we have previously displayed another label, remove it first if hasattr(annotatePlot, ''label''): annotatePlot.label.remove() # Get data point from array of points X, at position index x2, y2, _ = proj3d.proj_transform(X[index, 0], X[index, 1], X[index, 2], ax.get_proj()) annotatePlot.label = plt.annotate( "Value %d" % index, xy = (x2, y2), xytext = (-20, 20), textcoords = ''offset points'', ha = ''right'', va = ''bottom'', bbox = dict(boxstyle = ''round,pad=0.5'', fc = ''yellow'', alpha = 0.5), arrowprops = dict(arrowstyle = ''->'', connectionstyle = ''arc3,rad=0'')) fig.canvas.draw() def onMouseMotion(event): """Event that is triggered when mouse is moved. Shows text annotation over data point closest to mouse.""" closestIndex = calcClosestDatapoint(X, event) annotatePlot (X, closestIndex) fig.canvas.mpl_connect(''motion_notify_event'', onMouseMotion) # on mouse motion plt.show() if __name__ == ''__main__'': X = np.random.random((30,3)) visualize3DData (X)


Tal vez más fácil a través de ax.text (...):

from matplotlib import pyplot from mpl_toolkits.mplot3d import Axes3D from numpy.random import rand from pylab import figure m=rand(3,3) # m is an array of (x,y,z) coordinate triplets fig = figure() ax = Axes3D(fig) for i in range(len(m)): #plot each point + it''s index as text above ax.scatter(m[i,0],m[i,1],m[i,2],color=''b'') ax.text(m[i,0],m[i,1],m[i,2], ''%s'' % (str(i)), size=20, zorder=1, color=''k'') ax.set_xlabel(''x'') ax.set_ylabel(''y'') ax.set_zlabel(''z'') pyplot.show()