Цикл for Python всегда отображает одну и ту же линию в 3D (с использованием matplotlib)

Я строю несколько строк на одном графике, используя matplotlib в Python, используя цикл for для добавления каждой строки на ось.

При построении в 2D с каждой линией поверх другой это работает нормально.

Однако при построении графика в 3D python отображает одни и те же графические данные каждый раз, когда я запускаю цикл for, даже если данные значительно различаются.

Изменить: я не считаю, что этот вопрос является дубликатом "Как узнать, создает ли NumPy представление или копию?", поскольку он выделяет один конкретный случай неожиданного поведения.

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d

###### Unimportant maths not relevant to the question ######

def rossler(x_n, y_n, z_n, h, a, b, c):
    #defining the rossler function
    x_n1=x_n+h*(-y_n-z_n)
    y_n1=y_n+h*(x_n+a*y_n)
    z_n1=z_n+h*(b+z_n*(x_n-c))   
    return x_n1,y_n1,z_n1

#defining a, b, and c
a = 1.0/5.0
b = 1.0/5.0
c = 5

#defining time limits and steps
t_0 = 0
t_f = 50*np.pi
h = 0.01
steps = int((t_f-t_0)/h)

#create plotting values
t = np.linspace(t_0,t_f,steps)
x = np.zeros(steps)
y = np.zeros(steps)
z = np.zeros(steps)

##### Relevant to the question again #####

init_condition_array = [[0,0,0],[0.1,0,0],[0.2,0,0],[0.3,0,0]]
color_array = ["red","orange","green","blue"]
color_counter = 0
zs_array = [0, 0.1, 0.2, 0.3]

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

for row in init_condition_array:
    x[0] = row[0]
    y[0] = row[1]
    z[0] = row[2]

    for i in range(x.size-1):
        #re-evaluate the values of the x-arrays depending on the initial conditions
        [x[i+1],y[i+1],z[i+1]]=rossler(x[i],y[i],z[i],t[i+1]-t[i],a,b,c)

    plt.plot(t,x,zs=zs_array[color_counter],zdir="z",color=color_array[color_counter])
    color_counter += 1

ax.set_xlabel('t')
ax.set_ylabel('x(t)')
plt.show()  

Как видите, графики должны выглядеть совершенно иначе;

это 2D-изображение графиков на той же оси с некоторыми изменениями в коде (показано ниже):

Хотя это график, созданный 3D-графиком:

.

Двухмерный график был создан путем внесения этих небольших изменений в код; ничего выше первой строки не изменилось:

init_condition_array = [[0,0,0],[0.1,0,0],[0.2,0,0],[0.3,0,0]]
color_array = ["red","orange","green","blue"]
color_counter = 0

fig = plt.figure()
ax = fig.add_subplot(111)

for row in init_condition_array:
    x[0] = row[0]
    y[0] = row[1]
    z[0] = row[2]

    for i in range(x.size-1):
        #re-evaluate the values of the x-arrays depending on the initial conditions
        [x[i+1],y[i+1],z[i+1]]=rossler(x[i],y[i],z[i],t[i+1]-t[i],a,b,c)

    plt.plot(t,x,color=color_array[color_counter],lw=1)
    color_counter += 1

ax.set_xlabel('t')
ax.set_ylabel('x(t)')
plt.show()  

person Ari Cooper-Davis    schedule 01.04.2016    source источник
comment
Перемещение x = np.zeros(steps) внутри цикла for row in init_condition_array устраняет/предотвращает проблему. Похоже (каким-то образом) x хранится внутри объектов Line3D, возвращаемых plt.plot, но изменение x влияет на значения, хранящиеся в других Line3D...   -  person unutbu    schedule 01.04.2016
comment
Возможный дубликат Как узнать, NumPy создает представление или копию?   -  person ivan_pozdeev    schedule 01.04.2016
comment
Спасибо @unutbu; с этим разобрались - интересно, мне было бы интересно увидеть документацию по этому вопросу.   -  person Ari Cooper-Davis    schedule 01.04.2016


Ответы (1)


Перемещение x = np.zeros(steps) внутри цикла for row in init_condition_array устраняет/предотвращает проблему. x хранится внутри объектов Line3D, возвращаемых plt.plot, и изменение x влияет на значения, хранящиеся в другом Line3Ds.

введите здесь описание изображения


Если вы проследите исходный код для Line3D< /a> вы обнаружите, что данные, которые вы передаете plt.plot, попадают в атрибут _verts3d Line3D. Данные не копируются; кортеж _verts3d содержит ссылки на одни и те же массивы.

И этот атрибут _verts3d напрямую доступен позже при рендеринге:

def draw(self, renderer):
    xs3d, ys3d, zs3d = self._verts3d

Таким образом, изменение данных — даже после вызова plt.plot — приводит к изменению self._verts3d. Этот простой пример демонстрирует проблему:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
t = np.linspace(0, 1, 5)
x = np.sin(t)
line, = plt.plot(t, x, 0)

Здесь у нас есть исходные значения x:

print(line._verts3d[1])
# [ 0.          0.24740396  0.47942554  0.68163876  0.84147098]

И это показывает, что мутация x изменяет line._verts3d:

x[:] = 1
print(line._verts3d[1])
# [ 1.  1.  1.  1.  1.]

# The result is a straight line, not a sine wave.
plt.show()

Этой удивительной ловушки не происходит при построении двухмерных линейных графиков, потому что атрибут Line2D._xy, который содержит данные, используемые для рендеринга, хранит копию исходных данных.


Эту проблему можно исправить в исходном коде, изменив эта строка в art3d.Line3D.set_3d_properties от

self._verts3d = art3d.juggle_axes(xs, ys, zs, zdir)

to

import copy
self._verts3d = copy.deepcopy(art3d.juggle_axes(xs, ys, zs, zdir))
person unutbu    schedule 01.04.2016