matplotlib (mplot3d) - comment augmenter la taille d'un axe (stretch) dans un graphique 3D?
j'ai ceci:
x,y,z = data.nonzero()
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x, y, z, zdir='z', c= 'red')
plt.savefig("plot.png")
Qui crée:
ce que j'aimerais faire c'est étirer ça pour faire l'axe Z 9 fois plus haut et garder X et Y les mêmes. J'aimerais garder les mêmes coordonnées.
jusqu'à présent, j'ai essayé ce gars-là:
fig = plt.figure(figsize=(4.,35.))
Mais que vient s'étend de la parcelle.Image png.
6 réponses
L'exemple de code ci-dessous fournit un moyen à l'échelle de chaque axe par rapport aux autres. Cependant, pour ce faire, vous devez modifier L'Axes3D.fonction get_proj. Ci-dessous est un exemple basé sur l'exemple fourni par matplot lib: http://matplotlib.org/1.4.0/mpl_toolkits/mplot3d/tutorial.html#line-plots
(Il existe une version plus courte à la fin de cette réponse)
from mpl_toolkits.mplot3d.axes3d import Axes3D
from mpl_toolkits.mplot3d import proj3d
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
#Make sure these are floating point values:
scale_x = 1.0
scale_y = 2.0
scale_z = 3.0
#Axes are scaled down to fit in scene
max_scale=max(scale_x, scale_y, scale_z)
scale_x=scale_x/max_scale
scale_y=scale_y/max_scale
scale_z=scale_z/max_scale
#Create scaling matrix
scale = np.array([[scale_x,0,0,0],
[0,scale_y,0,0],
[0,0,scale_z,0],
[0,0,0,1]])
print scale
def get_proj_scale(self):
"""
Create the projection matrix from the current viewing position.
elev stores the elevation angle in the z plane
azim stores the azimuth angle in the x,y plane
dist is the distance of the eye viewing point from the object
point.
"""
relev, razim = np.pi * self.elev/180, np.pi * self.azim/180
xmin, xmax = self.get_xlim3d()
ymin, ymax = self.get_ylim3d()
zmin, zmax = self.get_zlim3d()
# transform to uniform world coordinates 0-1.0,0-1.0,0-1.0
worldM = proj3d.world_transformation(
xmin, xmax,
ymin, ymax,
zmin, zmax)
# look into the middle of the new coordinates
R = np.array([0.5, 0.5, 0.5])
xp = R[0] + np.cos(razim) * np.cos(relev) * self.dist
yp = R[1] + np.sin(razim) * np.cos(relev) * self.dist
zp = R[2] + np.sin(relev) * self.dist
E = np.array((xp, yp, zp))
self.eye = E
self.vvec = R - E
self.vvec = self.vvec / proj3d.mod(self.vvec)
if abs(relev) > np.pi/2:
# upside down
V = np.array((0, 0, -1))
else:
V = np.array((0, 0, 1))
zfront, zback = -self.dist, self.dist
viewM = proj3d.view_transformation(E, R, V)
perspM = proj3d.persp_transformation(zfront, zback)
M0 = np.dot(viewM, worldM)
M = np.dot(perspM, M0)
return np.dot(M, scale);
Axes3D.get_proj=get_proj_scale
"""
You need to include all the code above.
From here on you should be able to plot as usual.
"""
mpl.rcParams['legend.fontsize'] = 10
fig = plt.figure(figsize=(5,5))
ax = fig.gca(projection='3d')
theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
z = np.linspace(-2, 2, 100)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
ax.plot(x, y, z, label='parametric curve')
ax.legend()
plt.show()
sortie Standard:
multipliée par (1, 2, 3):
ci-Dessous est une version plus courte du code.
from mpl_toolkits.mplot3d.axes3d import Axes3D
from mpl_toolkits.mplot3d import proj3d
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
mpl.rcParams['legend.fontsize'] = 10
fig = plt.figure(figsize=(5,5))
ax = fig.gca(projection='3d')
theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
z = np.linspace(-2, 2, 100)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
"""
Scaling is done from here...
"""
x_scale=1
y_scale=1
z_scale=2
scale=np.diag([x_scale, y_scale, z_scale, 1.0])
scale=scale*(1.0/scale.max())
scale[3,3]=1.0
def short_proj():
return np.dot(Axes3D.get_proj(ax), scale)
ax.get_proj=short_proj
"""
to here
"""
ax.plot(z, y, x, label='parametric curve')
ax.legend()
plt.show()
veuillez noter que la réponse ci-dessous simplifie le patch, mais utilise le même principe sous-jacent que la réponse de @ChristianSarofeen.
Solution
comme déjà indiqué dans d'autres réponses, ce n'est pas une fonctionnalité qui est actuellement implémentée dans matplotlib. Cependant, puisque ce que vous demandez est simplement un transformation 3D qui peut être appliqué à la matrice de projection existante utilisée par matplotlib, et grâce à merveilleuses caractéristiques de Python, ce problème peut être résolu avec un simple oneliner:
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([scale_x, scale_y, scale_z, 1]))
où scale_x
,scale_y
et scale_z
sont des valeurs de 0 à 1 qui vont redimensionner votre tracé le long de chacun des axes en conséquence. ax
est simplement les axes 3D que l'on peut obtenir avec ax = fig.gca(projection='3d')
Explication
pour expliquer, la fonction get_proj
Axes3D
génère la matrice de projection à partir de la position courante. Multipliant par une mise à l'échelle de la matrice:
scale_x, 0, 0
0, scale_y, 0
0, 0, scale_z
0, 0, 1
inclut la mise à l'échelle dans la projection utilisée par le client. Donc, ce que nous faisons ici c'est Remplacer l'original get_proj
fonction avec une expression prenant le résultat de l'original get_proj
et le multiplie par la matrice de mise à l'échelle.
Exemple
pour illustrer le résultat avec l'exemple de fonction paramétrique standard:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
z = np.linspace(-2, 2, 100)
r = z ** 2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
# OUR ONE LINER ADDED HERE:
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([0.5, 0.5, 1, 1]))
ax.plot(x, y, z)
plt.show()
pour les valeurs 0.5, 0.5, 1
, nous obtenir:
alors que pour les valeurs 0.2, 1.0, 0.2
, on obtient:
je ressemble par défaut, mplot3d laissera un peu de place en haut et en bas d'une très haute de la parcelle. Mais, vous pouvez le piéger pour remplir cet espace en utilisant fig.subplots_adjust
, et l'extension du haut et du bas hors de la zone de pointage normale (c.-à-d. top > 1
et bottom < 0
). Certains d'essai et d'erreur ici est probablement nécessaire pour votre parcelle.
j'ai créé quelques tableaux aléatoires pour x, y, et z avec des limites similaires à votre graphique, et j'ai trouvé les paramètres ci-dessous (bottom=-0.15
, top = 1.2
) semblent bien fonctionner.
Vous pouvez également modifier ax.view_init
pour établir un bel angle d'observation.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
from numpy import random
# Make some random data with similar limits to the OP's example
x,y,z=random.rand(3,100)
z*=250
y*=800
y+=900
x*=350
x+=1200
fig=plt.figure(figsize=(4,35))
# Set the bottom and top outside the actual figure limits,
# to stretch the 3D axis
fig.subplots_adjust(bottom=-0.15,top=1.2)
ax = fig.add_subplot(111, projection='3d')
# Change the viewing angle to an agreeable one
ax.view_init(2,None)
ax.scatter(x, y, z, zdir='z', c= 'red')
plt.savefig("plot.png")
on dirait que vous essayez d'ajuster l'échelle de l'intrigue. Je ne pense pas qu'il y ait un moyen de s'étirer!--8-->linéaire échelle selon les spécifications de l'utilisateur, mais vous pouvez utiliser set_yscale()
,set_xscale()
,set_zscale()
modifier les échelles les unes par rapport aux autres.
Intuitivement, set_yscale(log)
,set_xscale(log)
,set_zscale(linear)
pourrait résoudre vos problèmes.
une option probablement meilleure: spécifiez un stretch, définissez-les tous à symlog avec la même base de log puis spécifiez l'échelle symlog de l'axe Z avec linscalex/linscaley
kwargs selon vos spécifications.
la suite ici:
j'ai trouvé ceci en cherchant sur un problème similaire. Après quelques expériences, je peux peut-être partager quelques-unes de mes découvertes préliminaires ici..la bibliothèque matplotlib est vaste!! (suis un nouveau venu). Notez que tout à fait semblable à cette question, tout ce que je voulais était "visuellement" étirer la carte sans la déformer.
histoire de Fond ( seuls les extraits de code clé sont affichés pour éviter le désordre inutile pour ceux qui connaissent la bibliothèque, et si vous voulez un code exécutable s'il Vous Plaît laisser tomber un commentaire): J'ai trois ndarrays 1-d représentant respectivement les points de données X,Y et Z. Clairement Je ne peux pas utiliser plot_surface (comme il nécessite ndarrays 2d pour chaque dim) donc j'ai choisi le très utile plot_trisurf:
fig = plt.figure()
ax = Axes3D(fig)
3d_surf_obj = ax.plot_trisurf(X, Y, Z_defl, cmap=cm.jet,linewidth=0,antialiased=True)
>>>> Z.) Bien que les points de l'intrigue soient corrects, je voulais quelque chose de plus "étiré" visuellement à tout le moins. Je cherchais une solution rapide, si je peux me permettre. Longue histoire courte, j'ai trouvé un peu de succès avec..."figure.Réglage général de figsize( voir l'extrait ci-dessous).
matplotlib.rcParams.update({'font.serif': 'Times New Roman',
'font.size': 10.0,
'axes.labelsize': 'Medium',
'axes.labelweight': 'normal',
'axes.linewidth': 0.8,
###########################################
# THIS IS THE IMPORTANT ONE FOR STRETCHING
# default is [6,4] but...i changed it to
'figure.figsize':[15,5] # THIS ONE #
})
c'est pas mal!!
alors j'ai commencé à la pousser.... et s'est levé à [20,6] avant de décider de régler y..
si vous voulez essayer d'étirer visuellement l'axe vertical, essayez avec des rapports comme... [7,10], ce qui en l'espèce me donne ...
pas trop minable !
devrait le faire pour les prouesses visuelles.
multipliez toutes vos valeurs de z par 9,
ax.scatter(x, y, 9*z, zdir='z', c= 'red')
et ensuite donner les étiquettes de tracé et l'espacement de l'axe z personnalisé.
ax.ZTick = [0,-9*50, -9*100, -9*150, -9*200];
ax.ZTickLabel = {'0','-50','-100','-150','-200'};