Plotting 3D trajectory from CSV data using matplotlib - python

I'm trying to plot a 3D trajectory of a vehicle that comes from a CSV file, plotting is easy, I want to make the animation, actually a "replay" of the movements. I based my code from this example (http://matplotlib.org/examples/animation/simple_3danim.html) and then just modify it to only plot one line and to read the data from a CSV file being read by pandas, the code looks like this:
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation
import pandas as pd
def update_lines(num, data, line):
# NOTE: there is no .set_data() for 3 dim data...
x = data['x'].values[num]
y = data['y'].values[num]
z = data['z'].values[num]
line[0].set_data(x,y)
line[0].set_3d_properties(z)
print z
return line
# Attaching 3D axis to the figure
fig = plt.figure()
ax = p3.Axes3D(fig)
# Reading the data from a CSV file using pandas
data = pd.read_csv('data.csv',sep=',',header=0)
# Creating fifty line objects.
# NOTE: Can't pass empty arrays into 3d version of plot()
x = np.array([0])
y = np.array([0])
z = np.array([0])
line = ax.plot(x, y, z)
# Setting the axes properties
ax.set_xlim3d([0.0, 3.0])
ax.set_xlabel('X')
ax.set_ylim3d([0.0, 3.0])
ax.set_ylabel('Y')
ax.set_zlim3d([0.0, 2.0])
ax.set_zlabel('Z')
ax.set_title('3D Test')
# Creating the Animation object
line_ani = animation.FuncAnimation(fig, update_lines, len(data), fargs=(data, line),
interval=10, blit=False)
plt.show()
I print the z just to see if the data is being iterated correctly, but all I get is a white plot like this:
Plot showing absolutely nothing.

at least, there are two issues with your code:
the way of how data is build
length of frames per second
here is the modified working example, please take a look how data variable
was arranged:
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation
import pandas as pd
from sys import exit
def update_lines(num, data, line):
# NOTE: there is no .set_data() for 3 dim data...
line.set_data(data[0:2, :num])
line.set_3d_properties(data[2, :num])
return line
# Attaching 3D axis to the figure
fig = plt.figure()
ax = p3.Axes3D(fig)
# Reading the data from a CSV file using pandas
repo = pd.read_csv('data.csv',sep=',',header=0)
data = np.array((repo['x'].values, repo['y'].values, repo['z'].values))
print data.shape[1]
#exit()
# Creating fifty line objects.
# NOTE: Can't pass empty arrays into 3d version of plot()
line = ax.plot(data[0, 0:1], data[1, 0:1], data[2, 0:1])[0]
# Setting the axes properties
ax.set_xlim3d([-2.0, 2.0])
ax.set_xlabel('X')
ax.set_ylim3d([-2.0, 2.0])
ax.set_ylabel('Y')
ax.set_zlim3d([-2.0, 2.0])
ax.set_zlabel('Z')
ax.set_title('3D Test')
# Creating the Animation object
line_ani = animation.FuncAnimation(fig, update_lines, data.shape[1], fargs=(data, line), interval=50, blit=False)
plt.show()
you can watch the beauty of that flight just being tracked

Related

Matplotlib Animation for Plotting Points Being Connected Given Arrays of X and Y values to be coordinates

I have two arrays containing x and y values. Each array has 1274 values in it. I essentially want to create a matplotlib animation where these points are being plotted and also connected by a line. I tried doing this with FuncAnimation, but ran into a lot of trouble. Imagine that x and y are the two arrays that I'm referring to. Rest of the code is what I tried so far.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from os import getcwd, listdir
gif_path = getcwd() + "/gifs"
fig = plt.figure()
graph, = plt.plot([], [], 'o')
def animate(i):
if i > len(x) - 1:
i = len(x) - 1
graph.set_data(x[:i+1], y[:i+1])
return graph
ani = FuncAnimation(fig, animate, interval=200)
ani.save(f"{gif_path}/sample_region.gif", writer="imagemagick")
Any help would kindly be appreciated. Thanks.
Your code seems to be following an example from matplotlib, you just need a few extra changes:
x = range(100)
y = np.random.rand(100)
fig, ax = plt.subplots()
ax.set_xlim(0, 100)
ax.set_ylim(0, 1)
graph, = plt.plot([], [], '-')
def init():
return graph,
def animate(i):
graph.set_data(x[:i],y[:i])
return graph,
ani = FuncAnimation(fig, animate, frames=range(len(x)), interval=50, save_count=len(x),
init_func=init, blit=True)
ani.save('ani.gif', writer='PillowWriter')
This produces this GIF. The changes are:
set up an Axes on fig with set axis limits
change the o to -
add an init function for initializing the animation
add a frames argument to pass indexes used to select data
update animate to handle those frames
But I tried making this with an array of 1200 points, and it didn't seem like my computer could complete it... You can try but you might need to trim the data or plot more data each frame.

How can I add jitter to my seaborn and matplot plots?

I am working on trying to add Jitter to my plots using seaborn and matplot plots. I am getting mixed information form what I am reading online. Some information is saying coding needs to be done and other information show it as being as simple as jitter = True. I there another library or something that I should be importing that I am not aware of? Below is the code that I am running and trying to add jitter to:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
filename = 'https://library.startlearninglabs.uw.edu/DATASCI410/Datasets/JitteredHeadCount.csv'
headcount_df = pd.read_csv(filename)
headcount_df.describe()
%matplotlib inline
ax = plt.figure(figsize=(12, 6)).gca() # define axis
headcount_df.plot.scatter(x = 'Hour', y = 'TablesOpen', ax = ax, alpha = 0.2)
# auto_price.plot(kind = 'scatter', x = 'city-mpg', y = 'price', ax = ax)
ax.set_title('Hour vs TablesOpen') # Give the plot a main title
ax.set_ylabel('TablesOpen')# Set text for y axis
ax.set_xlabel('Hour')
ax = sns.kdeplot(headcount_df.loc[:, ['TablesOpen', 'Hour']], shade = True, cmap = 'PuBu')
headcount_df.plot.scatter(x = 'Hour', y = 'TablesOpen', ax = ax, jitter = True)
ax.set_title('Hour vs TablesOpen') # Give the plot a main title
ax.set_ylabel('TablesOpen')# Set text for y axis
ax.set_xlabel('Hour')
I receive the error: AttributeError: 'PathCollection' object has no property 'jitter' when trying to add the jitter. Any help or more information on this would be much appreciated
To add jitter to a scatter plot, first get a handle to the collection that contains the scatter dots. When a scatter plot is just created on an ax, ax.collections[-1] will be the desired collection.
Calling get_offsets() on the collection gets all the xy coordinates of the dots. Add some small random number to each of them. As in this case all coordinates are integers, adding a random number between 0 and 1 spreads the dots out evenly.
In this case the number of dots is very huge. To better see where the dots are concentrated, they can be made very small (marker=',', linewidth=0, s=1,) and be very transparent (e.g.alpha=0.1).
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
filename = 'https://library.startlearninglabs.uw.edu/DATASCI410/Datasets/JitteredHeadCount.csv'
headcount_df = pd.read_csv(filename)
fig, ax = plt.subplots(figsize=(12, 6))
headcount_df.plot.scatter(x='Hour', y='TablesOpen', marker=',', linewidth=0, s=1, alpha=.1, color='crimson', ax=ax)
dots = ax.collections[-1]
offsets = dots.get_offsets()
jittered_offsets = offsets + np.random.uniform(0, 1, offsets.shape)
dots.set_offsets(jittered_offsets)
ax.set_title('Hour vs TablesOpen') # Give the plot a main title
ax.set_ylabel('TablesOpen') # Set text for y axis
ax.set_xlabel('Hour')
ax.set_xticks(range(25))
ax.autoscale(enable=True, tight=True)
plt.tight_layout()
plt.show()
As there are a huge number of points, drawing the 2D kde takes a long time. The time can be reduced by taking a random sample from the rows. Note that to draw a 2D kde, the latest versions of Seaborn want each column as a separate parameter.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as sns
filename = 'https://library.startlearninglabs.uw.edu/DATASCI410/Datasets/JitteredHeadCount.csv'
headcount_df = pd.read_csv(filename)
fig, ax = plt.subplots(figsize=(12, 6))
N = 5000
rand_sel_df = headcount_df.iloc[np.random.choice(range(len(headcount_df)), N)]
ax = sns.kdeplot(rand_sel_df['Hour'], rand_sel_df['TablesOpen'], shade=True, cmap='PuBu', ax=ax)
ax.set_title('Hour vs TablesOpen')
ax.set_xticks(range(25))
plt.tight_layout()
plt.show()

matplotlib animation doesn't animate with imshow

I'm trying to animate a few simple subplots with imshow but there's apparently an issue.
This is a small demo of my problem:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation
fig,axs=plt.subplots(2,5,figsize=(10,4))
imgs=[]
for row in axs:
for col in row:
col.set_xlim(4.5,-.5)
col.set_ylim(4.5,-.5)
col.set_xticks([])
col.set_yticks([])
#line A
imgs.append(col.imshow([[]],animated=1))
#freezes the animation([[]] is just a placeholder)
#line B
#imgs.append(col.imshow(np.arange(25).reshape((5,5)),animated=1))
#animation does work if the above line is used instead
def func(frm):
for i in range(10):
imgs[i].set_array(np.arange(25).reshape(5,5)*np.log10(frm+1))
return imgs
anim=animation.FuncAnimation(fig,func,10,interval=100)
plt.show()
If I use line A, the animation freezes as if func is not executed (while actually it is), raising no errors. If line B is used instead, the animation works. Am I missing something about imshow animating?
Animation from multiple imshow can be created by ArtistAnimation object using multiple axes generated by subplots command.
from numpy import random
from matplotlib import animation
import matplotlib.pyplot as plt
img_lst_1 = [random.random((368,1232)) for i in range(10)] # Test data
img_lst_2 = [random.random((368,1232)) for i in range(10)] # Test data
fig, (ax1, ax2) = plt.subplots(2,1)
frames = [] # store generated images
for i in range(len(img_lst_1)):
img1 = ax1.imshow(img_lst_1[i], animated=True)
img2 = ax2.imshow(img_lst_2[i], cmap='gray', animated=True)
frames.append([img1, img2])
ani = animation.ArtistAnimation(fig, frames, interval=500, blit=True,
repeat_delay=1000)
ani.save('movie_example.mp4')
Code output:

Python: matplotlib - loop, clear and show different plots over the same figure

I want to see how a plot varies with different values using a loop. I want to see it on the same plot. But i do not want to remains of the previous plot in the figure. In MATLAB this is possible by creating a figure and just plotting over the same figure. Closing it when the loop ends.
Like,
fh = figure();
%for loop here
%do something with x and y
subplot(211), plot(x);
subplot(212), plot(y);
pause(1)
%loop done
close(fh);
I am not able to find the equivalent of this in matplotlib. Usually all the questions are related to plotting different series on the same plot, which seems to come naturally on matplotlib, by plotting several series using plt.plot() and then showing them all finally using plt.show(). But I want to refresh the plot.
There are essentially two different ways to create animations in matplotlib
interactive mode
Turning on interactive more is done using plt.ion(). This will create a plot even though show has not yet been called. The plot can be updated by calling plt.draw() or for an animation, plt.pause().
import matplotlib.pyplot as plt
x = [1,1]
y = [1,2]
fig, (ax1,ax2) = plt.subplots(nrows=2, sharex=True, sharey=True)
line1, = ax1.plot(x)
line2, = ax2.plot(y)
ax1.set_xlim(-1,17)
ax1.set_ylim(-400,3000)
plt.ion()
for i in range(15):
x.append(x[-1]+x[-2])
line1.set_data(range(len(x)), x)
y.append(y[-1]+y[-2])
line2.set_data(range(len(y)), y)
plt.pause(0.1)
plt.ioff()
plt.show()
FuncAnimation
Matplotlib provides an animation submodule, which simplifies creating animations and also allows to easily save them. The same as above, using FuncAnimation would look like:
import matplotlib.pyplot as plt
import matplotlib.animation
x = [1,1]
y = [1,2]
fig, (ax1,ax2) = plt.subplots(nrows=2, sharex=True, sharey=True)
line1, = ax1.plot(x)
line2, = ax2.plot(y)
ax1.set_xlim(-1,18)
ax1.set_ylim(-400,3000)
def update(i):
x.append(x[-1]+x[-2])
line1.set_data(range(len(x)), x)
y.append(y[-1]+y[-2])
line2.set_data(range(len(y)), y)
ani = matplotlib.animation.FuncAnimation(fig, update, frames=14, repeat=False)
plt.show()
An example to animate a sine wave with changing frequency and its power spectrum would be the following:
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
x = np.linspace(0,24*np.pi,512)
y = np.sin(x)
def fft(x):
fft = np.abs(np.fft.rfft(x))
return fft**2/(fft**2).max()
fig, (ax1,ax2) = plt.subplots(nrows=2)
line1, = ax1.plot(x,y)
line2, = ax2.plot(fft(y))
ax2.set_xlim(0,50)
ax2.set_ylim(0,1)
def update(i):
y = np.sin((i+1)/30.*x)
line1.set_data(x,y)
y2 = fft(y)
line2.set_data(range(len(y2)), y2)
ani = matplotlib.animation.FuncAnimation(fig, update, frames=60, repeat=True)
plt.show()
If you call plt.show() inside the loop you will see the plot for each element on the loop as long as you close the window containing the figure. The process, will be plot for the first element, then if you close the window you will see the plot for the second element in the loop, etc

clear ax2 in matplotlib

I'm trying to plot two lines with different scales with matplotlib.
It is currently working, except when I run my code the second Y axis messes up while updating.
Here is the code I'm using:
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.pyplot import cm
from datetime import datetime
import numpy as np
import matplotlib.animation as animation
def animate(i, fig, ax):
# Converter function
datefunc = lambda x: mdates.date2num(datetime.strptime(x, '%d-%m-%Y_%H:%M:%S'))
# Read data from 'file.dat'
dates, levels, temp = np.genfromtxt('datosPlot.txt', # Data to be read
converters={0: datefunc}, # Formatting of column 0
dtype=float, # All values are floats
usecols=(0,1,2), #Leer las tres primeras columnas de datos.txt
unpack=True) # Unpack to several variables
# Configure x-ticks
ax1.clear()
ax1.set_xticks(dates) # Tickmark + label at every plotted point
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%d/%m/%Y %H:%M'))
ax2 = ax1.twinx()
fig.tight_layout()
fig = plt.figure()
ax1 = fig.add_subplot(111)
ani = animation.FuncAnimation(fig, animate, fargs=(fig, ax1), interval=1000)
plt.show()
My data (datosPlot.txt) look like this:
14-01-2017_14:01:16 1 16
14-01-2017_14:01:19 14 22
14-01-2017_14:01:22 2 17
14-01-2017_14:01:25 4 19
14-01-2017_14:01:28 6 24
14-01-2017_14:01:31 12 19
14-01-2017_14:01:34 4 18
14-01-2017_14:01:37 9 20
First column is the X axis (date_time), second column is pH, third column is Temperature.
I've tried adding a ax2.clear() before and after calling ax2 = ax1.twinx(), but it doesn't work. How can I clear it, as I'm able with ax1?
Here is what it looks like when I don't add any ax2.clear():
Try to create the axes outside of your animation and only use as little code as you really need in each animation step.
The following is a runnable example, where you would need to replace the read in function em.genfromtxt() with your original call to np.genfromtxt(....).
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from datetime import datetime
import numpy as np
import matplotlib.animation as animation
##### Emulator to generate data #########
class emulator():
def __init__(self):
self.dates = []
self.levels = []
self.temp = []
def genfromtxt(self):
self.dates.append(mdates.date2num(datetime.now()))
self.levels.append(np.random.randint(1,14))
self.temp.append(np.random.rand(1)*16+4)
return self.dates, self.levels, self.temp
em = emulator()
##### End of Emulator to generate data #########
# Converter function
datefunc = lambda x: mdates.date2num(datetime.strptime(x, '%d-%m-%Y_%H:%M:%S'))
def animate(i):
# Read data from 'file.dat'
# instead we use an emulator here, replace with your original genfromtxt function
dates, levels, temp = em.genfromtxt()
# Configure x-ticks
ax1.clear()
ax2.clear()
ax1.grid(True)
ax2.grid(True)
ax1.plot_date(dates, levels, ls='-', marker='.', color='red', label='pH')
ax2.plot_date(dates, temp, ls='-', marker='.', color='blue', label='Temperatura C')
ax1.set_xticks(dates) # Tickmark + label at every plotted point
ax1.locator_params(axis='x',nbins=10)
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%d/%m/%Y %H:%M'))
#Leyendas
lines = ax1.get_lines() + ax2.get_lines()
plt.legend(lines, [l.get_label() for l in lines], loc=2)
fig.autofmt_xdate(rotation=45)
fig.tight_layout()
fig = plt.figure()
# we create both axes outside the animation and already set those parameters
# which stay the same throughout the animation.
ax1 = fig.add_subplot(111)
ax1.set_title('pH y Temp')
ax1.set_ylabel('pH')
ax2 = ax1.twinx() # This should happen outside the animation already.
ax2.set_ylabel('Temperatura C')
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()

Categories