J'ai ceci jusqu'à présent:
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")
Ce qui crée:
Ce que j'aimerais faire, c’est d’étirer cela pour que l’axe Z soit 9 fois plus grand et que X et Y restent identiques. J'aimerais cependant garder les mêmes coordonnées.
Jusqu'ici j'ai essayé ce gars:
fig = plt.figure(figsize=(4.,35.))
Mais cela ne fait qu'étendre l'image plot.png.
L'exemple de code ci-dessous fournit un moyen de redimensionner chaque axe par rapport aux autres. Toutefois, pour ce faire, vous devez modifier la fonction Axes3D.get_proj. Voici un exemple basé sur l'exemple fourni par matplot lib: http://matplotlib.org/1.4.0/mpl_toolkits/mplot3d/tutorial.html#line-plots
(Il y a 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:
Échelle de (1, 2, 3):
Mise à l'échelle de (1, 1, 3):
La raison pour laquelle j'aime particulièrement cette méthode, Échangez z et x, mettez à l'échelle par (3, 1, 1):
Vous trouverez ci-dessous une version abrégée 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 correctif, mais utilise le même principe de base que la réponse de @ChristianSarofeen.
Comme déjà indiqué dans d'autres réponses, ce n'est pas une fonctionnalité actuellement implémentée dans matplotlib. Cependant, puisque vous demandez simplement une transformation 3D qui peut être appliquée à la matrice de projection existante utilisée par matplotlib, et grâce aux merveilleuses fonctionnalités 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 redimensionnent votre tracé le long de chacun des axes en conséquence. ax
sont simplement les axes 3D que l'on peut obtenir avec ax = fig.gca(projection='3d')
Pour expliquer, la fonction get_proj
de Axes3D
génère la matrice de projection à partir de la position de visualisation actuelle. En le multipliant par une matrice de mise à l'échelle:
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 rendu. Donc, ce que nous faisons ici, c'est substituer la fonction get_proj
originale avec une expression prenant le résultat du get_proj
original et le multipliant par la matrice de mise à l'échelle.
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
, on obtient:
alors que pour les valeurs 0.2, 1.0, 0.2
, on obtient:
J'ai l’impression que par défaut, mplot3d laissera un peu de place en haut et en bas d’un très grand complot. Mais vous pouvez le tromper en remplissant cet espace en utilisant fig.subplots_adjust
et en prolongeant le haut et le bas hors de la zone de traçage normale (c'est-à-dire top > 1
et bottom < 0
). Quelques essais et erreurs ici sont probablement nécessaires pour votre intrigue particulière.
J'ai créé des tableaux aléatoires pour x, y et z avec des limites similaires à celles de votre graphique, et les paramètres ci-dessous (bottom=-0.15
, top = 1.2
) semblent fonctionner correctement.
Vous pouvez également vouloir changer ax.view_init
pour définir un angle de vision de Nice.
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 soit possible d'étirer une échelle linear conformément aux spécifications de l'utilisateur, mais vous pouvez utiliser set_yscale()
, set_xscale()
, set_zscale()
pour 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 meilleure option est probablement la suivante: spécifiez une extension, réglez-les tous sur symlog avec la même base de journal, puis spécifiez l'échelle de symlog de l'axe Z avec le kwargs linscalex/linscaley
selon vos spécifications.
Plus ici:
J'ai trouvé cela en cherchant un problème similaire. Après avoir essayé un peu, je peux peut-être partager certaines de mes découvertes préliminaires ici .. La bibliothèque matplotlib est VAST !! (je suis un nouveau venu). Notez que tout à fait à côté de cette question, tout ce que je voulais, c’était d’étirer «visuellement» le graphique sans le déformer.
Article de fond (seuls des extraits de code sont affichés pour éviter toute confusion inutile pour ceux qui connaissent la bibliothèque. Si vous voulez un code pouvant être exécuté, veuillez laisser un commentaire): J'ai trois ndarrays à une dimension représentant les points de données X, Y et Z respectivement. Il est clair que je ne peux pas utiliser plot_surface (car il nécessite 2d ndarrays pour chaque dim), je me suis donc tourné vers l'intrigue 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)
Vous pouvez voir l’intrigue comme une barge flottante se déformant par vagues ... Comme vous pouvez le constater, les axes étirés la rendent assez trompeuse visuellement (notez que x est censé être x6 fois plus long que y et >>>>> z ). Bien que les points de l'intrigue soient corrects, je voulais au moins quelque chose de plus étiré visuellement. Je cherchais un correctif rapide, si je peux. Longue histoire courte, j'ai trouvé un peu de succès avec ... le réglage général 'figure.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 #
})
Pour [15,5] j'ai quelque chose comme ...
Génial!!
Alors j'ai commencé à pousser ... et je suis monté à [20,6] avant de décider de m'installer là-bas ..
Si vous voulez essayer d'étirer visuellement l'axe vertical, essayez avec des ratios comme ... [7,10], ce qui dans ce cas me donne ...
Pas trop mal !
Devrait le faire pour les prouesses visuelles.