Matplotlib, animation by points in list - python

I want to have a list of points like
list = [[1.0, 1.0], ... ,[17.3, 39.4]]
and I want to make an animation in matplotlib, where my object would in first frame be in the first point the second frame in second point, and so on and on...
How would I do this?
I just want to know the general idea.. the most basic code possible, because I am new to this python library.

Here is a simple example:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
# list of points
lst = [
[1.0, 1.0], [1.0, 2.0], [1.0, 3.0], [1.0, 4.0], [1.0, 5.0],
[2.0, 5.0], [3.0, 5.0], [4.0, 5.0], [5.0, 5.0], [5.0, 4.0],
[5.0, 3.0], [5.0, 2.0], [5.0, 1.0], [4.0, 1.0], [3.0, 1.0],
[2.0, 1.0], [1.0, 1.0]
]
# resolve max values for x and y axes
max_x = max([pt[0] for pt in lst]) + 1
max_y = max([pt[1] for pt in lst]) + 1
# create figure and set limits
fig = plt.figure()
plt.xlim(0, max_x)
plt.ylim(0, max_y)
# create graph
graph, = plt.plot([], [], 'o')
# hide figure
plt.close()
# define function for animation
# it sets point coordinates based an frame number
def animate(i):
graph.set_data(lst[i][0], lst[i][1])
return graph
# init FuncAnimation
ani = FuncAnimation(fig, animate, frames=len(lst), interval=200, repeat=False)
# is needed to make animation available in jupiter / colab
HTML(ani.to_jshtml())

Related

Can I assign an ID to a vtkPoint myself?

I want to create a VTK Unstructured Grid from FE data in Python.
I have the nodes/points and their ID in an np.Array. Can I now write a VTK file with it and keep the ID assigned by the FE model?
I need a VTK point to which I can assign any ID.
Example:
points = np.array([
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[1.0, 1.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
[1.0, 0.0, 1.0],
[1.0, 1.0, 1.0],
[0.0, 1.0, 1.0]
])
ids = np.array([10, 21, 22, 23, 34, 15, 36, 7])
Thanks for any help.
When I try to use vtk.SetPoint I get the following error: ValueError: expects 0 <= id && id <GetNumberOfPoints()
In VTK, id means "the index in the array list". So it always follows the constraint displayed in the error message.
To keep a trace of your initial ids, you can create a PointData array:
ugrid.Points = points
ugrid.PointData.append(ids, "originalIds")
see some documentation here

Adjusting the size of marker in legend in plt.fill

I want to plot a line against dataset that is actually two lines that are very close together, so I am plotting it with plt.fill. Now, I want to adjust the width of the line of the legend for the fillplot to have a similar width to the line in plt.plot. I tried using linewidth= 0.2, but with linewidth I can only change the width in the plt.plot legend.
I changed the original data to the area between these arrays, because I didn't want to upload the data file. Also changing the data of a measurement that I am comparing my theory against seems disingenuous, so I probably shouldn't do that.
import matplotlib.pyplot as plt
import numpy as np
a_seq = [0.,0.5, 1.5 ,2., 2.5, 3., 3.5, 4., 4., 4. ]
l1 = [0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 3.0, 4.0, 4.0, 4.0]
l2 = [0.0, 0.2, 1.1, 1.2, 1.2, 2.1, 3.2, 4.0, 4.0, 4.0]
plt.plot(np.arange(0,len(a_seq)),a_seq)
#plt.plot(l1)
#plt.plot(l2)
plt.fill_between( np.arange(0,len(l1)), l2, l1, alpha=0.5)
plt.legend(['line3','line4'])
plt.show()
You need to access that legend object with legendHandles and then set the height of it with set_height to your desired height, like so:
import matplotlib.pyplot as plt
import numpy as np
a_seq = [0.,0.5, 1.5 ,2., 2.5, 3., 3.5, 4., 4., 4. ]
l1 = [0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 3.0, 4.0, 4.0, 4.0]
l2 = [0.0, 0.2, 1.1, 1.2, 1.2, 2.1, 3.2, 4.0, 4.0, 4.0]
plt.plot(np.arange(0,len(a_seq)),a_seq)
plt.fill_between( np.arange(0,len(l1)), l2, l1, alpha=0.5)
legend = plt.legend(['line3','line4'])
legend.legendHandles[1].set_height(2.0)
plt.show()
Output:

Equating the lengths of the arrays in an array of arrays

Given an array of arrays with different lengths. Is there a cleaner (shorter) way to equate the lengths of the arrays by filling the shorter ones with zeros other than the following code:
a = [[1.0, 2.0, 3.0, 4.0],[2.0, 3.0, 1.0],[5.0, 5.0, 5.0, 5.0],[1.0, 1.0]]
max =0
for x in a:
if len(x) > max:
max = len(x)
print max
new = []
for x in a:
if len(x)<max:
x.extend([0.0]* (max-len(x)) )
new.append(x)
print new
You can find the length of the largest list within a using either:
len(max(a, key=len))
or
max(map(len, a))
and also use a list comprehension to construct a new list:
>>> a = [[1.0, 2.0, 3.0, 4.0], [2.0, 3.0, 1.0], [5.0, 5.0, 5.0, 5.0], [1.0, 1.0]]
>>> m = len(max(a, key=len))
>>> new = [x + [0]*(m - len(x)) for x in a]
>>> new
[[1.0, 2.0, 3.0, 4.0], [2.0, 3.0, 1.0, 0], [5.0, 5.0, 5.0, 5.0], [1.0, 1.0, 0, 0]]
In: b = [i+[0.]*(max(map(len,a))-len(i)) for i in a]
In: b
Out:
[[1.0, 2.0, 3.0, 4.0],
[2.0, 3.0, 1.0, 0.0],
[5.0, 5.0, 5.0, 5.0],
[1.0, 1.0, 0.0, 0.0]]

Plotting mesh data from vtk python using matplotlib

The following questions makes use of vtk python but what I am attempting to do should not require any knowledge of vtk because I have converted the data I wish to plot into numpy arrays described below. If anyone does know of an improvement to the way I go about actually processing the vtk data into numpy, please let me know!
I have some data that I have extracted using vtk python. The data consists of a 3D unstructured grid and has several 'blocks'. The block I am interested in is block0. The data is contained at each cell rather than at each point. I wish to plot a contourf plot of a scalar variable on this grid using matplotlib. In essence my problem comes down to the following:
Given a set of cell faces with known vertices in space and a known scalar field variable, create a contour plot as one would get if one had created a numpy.meshgrid and used plt.contourf/plt.pcolormesh etc. Basically I post process my vtk data like so:
numCells = block0.GetCells().GetNumberOfCells()
# Array of the 8 vertices that make up a cell in 3D
cellPtsArray = np.zeros((numCells,8,3))
# Array of the 4 vertices that make up a cell face
facePtsArray = np.zeros((numCells,4,3))
#Array to store scalar field value from each cell
valueArray = np.zeros((numCells,1))
for i in xrange(numCells):
cell = block0.GetCell(i)
numCellPts = cell.GetNumberOfPoints()
for j in xrange(numCellPts):
cellPtsArray[i,j,:] = block0.GetPoint(cell.GetPointId(j))
valueArray[i] = block0.GetCellData().GetArray(3).GetValue(i)
xyFacePts = cell.GetFaceArray(3)
facePtsArray[i,:,:] = cellPtsArray[i,xyFacePts,:]
Now I wish to create a contour plot of this data (fill each cell in space according to an appropriate colormap of the scalar field variable). Is there a good built in function in matplotlib to do this? Note that I cannot use any form of automatic triangulation-the connectivity of the mesh is already specified by facePtsArray by the fact that connections between points of a cell have been ordered correctly (see my plot below)
Here is some test data:
import numpy as np
import matplotlib.pyplot as plt
# An example of the array containing the mesh information: In this case the
# dimensionality is (9,4,3) denoting 9 adjacent cells, each with 4 vertices and
# each vertex having (x,y,z) coordinates.
facePtsArray = np.asarray([[[0.0, 0.0, 0.0 ],
[1.0, 0.0, 0.0 ],
[1.0, 0.5, 0.0 ],
[0.0, 0.5, 0.0 ]],
[[0.0, 0.5, 0.0 ],
[1.0, 0.5, 0.0 ],
[1.0, 1.0, 0.0 ],
[0.0, 1.0, 0.0 ]],
[[0.0, 1.0, 0.0 ],
[1.0, 1.0, 0.0 ],
[1.0, 1.5, 0.0 ],
[0.0, 1.5, 0.0 ]],
[[1.0, 0.0, 0.0 ],
[2.0, -0.25, 0.0],
[2.0, 0.25, 0.0],
[1.0, 0.5, 0.0]],
[[1.0, 0.5, 0.0],
[2.0, 0.25, 0.0],
[2.0, 0.75, 0.0],
[1.0, 1.0, 0.0]],
[[1.0, 1.0, 0.0],
[2.0, 0.75, 0.0],
[2.0, 1.25, 0.0],
[1.0, 1.5, 0.0]],
[[2.0, -0.25, 0.0],
[2.5, -0.75, 0.0],
[2.5, -0.25, 0.0 ],
[2.0, 0.25, 0.0]],
[[2.0, 0.25, 0.0],
[2.5, -0.25,0.0],
[2.5, 0.25, 0.0],
[2.0, 0.75, 0.0]],
[[2.0, 0.75, 0.0],
[2.5, 0.25, 0.0],
[2.5, 0.75, 0.0],
[2.0, 1.25, 0.0]]])
valueArray = np.random.rand(9) # Scalar field values for each cell
plt.figure()
for i in xrange(9):
plt.plot(facePtsArray[i,:,0], facePtsArray[i,:,1], 'ko-')
plt.show()

Python: adjust coordinates

I have this Python script which should adjust the coordinates of a set of triangular elements. The script should change the coordinates of the nodes from an element towards the centre of gravity of the element. The below image is a sketch I made of the problem.
However something is wrong in my script, and I can't figure out what. The coordinates are not changed in the correct direction and extra new coordinates are generated, while I only want to adjust the existing coordinates.
Does anyone know how to correctly program this in Python?
coords = [[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [0.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 1.0], [1.0, 1.0], [0.0, 2.0], [0.0, 2.0], [1.0, 1.0], [1.0, 2.0], [1.0, 1.0], [2.0, 1.0], [1.0, 2.0], [1.0, 2.0], [2.0, 1.0], [2.0, 2.0], [1.0, 1.0], [2.0, 0.0], [2.0, 1.0], [1.0, 0.0], [2.0, 0.0], [1.0, 1.0]]
elems = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14], [15, 16, 17], [18, 19, 20], [21, 22, 23]]
#define vectors
def add_vectors(*points):
new_x = 0.0
new_y = 0.0
for point in points:
new_x += point[0]
new_y += point[1]
return [new_x, new_y]
def subtract_vectors(a, b):
new_x = a[0] - b[0]
new_y = a[1] - b[1]
return [new_x, new_y]
def mul_by_scalar(vector, scalar):
new_x = vector[0] * scalar
new_y = vector[1] * scalar
return [new_x, new_y]
#define triangles
triangles = []
for elem in elems:
triangles += [coords[e] for e in elem]
#adjust coordinates
CM = mul_by_scalar(add_vectors(*triangles), 1.0/3)
point_to_CM_vectors = []
for point in triangles:
point_to_CM_vectors.append(subtract_vectors(CM, point))
new_triangle = []
for elem in elems:
for point, motion in zip(triangles, point_to_CM_vectors):
new_triangle.append(add_vectors(point, mul_by_scalar(motion, 0.01)))
print 'triangles =', triangles
print 'new_triangle =', new_triangle
Thanks in advance for any help!
Here's a reworking of your problem using the vectorization operators provided by numpy.
import numpy as np
#define triangles
triangles = np.array([[coords[e] for e in elem] for elem in elems])
#find centroid of each triangle
CM = np.mean(triangles,axis=1)
#find vector from each point in triangle pointing towards centroid
point_to_CM_vectors = CM[:,np.newaxis] - triangles
#calculate similar triangles 1% smaller
new_triangle = triangles + 0.01*point_to_CM_vectors

Categories

Resources