7

I acquire some data in two arrays: one for the time, and one for the value. When I reach 1000 points, I trigger a signal and plot these points (x=time, y=value).

I need to keep on the same figure the previous plots, but only a reasonable number to avoid slowing down the process. For example, I would like to keep 10,000 points on my graph. The matplotlib interactive plot works fine, but I don't know how to erase the first points and it slows my computer very quickly. I looked into matplotlib.animation, but it only seems to repeat the same plot, and not really actualise it.

I'm really looking for a light solution, to avoid any slowing.

As I acquire for a very large amount of time, I erase the input data on every loop (the 1001st point is stored in the 1st row and so on).

Here is what I have for now, but it keeps all the points on the graph:

import matplotlib.pyplot as plt

def init_plot():
  plt.ion()
  plt.figure()
  plt.title("Test d\'acqusition", fontsize=20)
  plt.xlabel("Temps(s)", fontsize=20)
  plt.ylabel("Tension (V)", fontsize=20)
  plt.grid(True)

def continuous_plot(x, fx, x2, fx2):
  plt.plot(x, fx, 'bo', markersize=1)
  plt.plot(x2, fx2, 'ro', markersize=1)
  plt.draw()

I call the init function once, and the continous_plot is in a process, called every time I have 1000 points in my array.

1
  • Have you made any attempt to program this yourself? If so, please share your code. Commented Jul 16, 2014 at 14:37

4 Answers 4

8

The lightest solution you may have is to replace the X and Y values of an existing plot. (Or the Y value only, if your X data does not change. A simple example:

import matplotlib.pyplot as plt
import numpy as np
import time

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

# some X and Y data
x = np.arange(10000)
y = np.random.randn(10000)

li, = ax.plot(x, y)

# draw and show it
ax.relim() 
ax.autoscale_view(True,True,True)
fig.canvas.draw()
plt.show(block=False)

# loop to update the data
while True:
    try:
        y[:-10] = y[10:]
        y[-10:] = np.random.randn(10)

        # set the new data
        li.set_ydata(y)

        fig.canvas.draw()

        time.sleep(0.01)
    except KeyboardInterrupt:
        break

This solution is quite fast, as well. The maximum speed of the above code is 100 redraws per second (limited by the time.sleep), I get around 70-80, which means that one redraw takes around 4 ms. But YMMV depending on the backend, etc.

Sign up to request clarification or add additional context in comments.

1 Comment

Your solution seems good, but with your code, the axes are blocked and the plotting happend outside of the viewable domain. What can I add auto-regulate the axes? EDIT: found the solution: I need to add these 2 lines before fig.canvas.draw in the loop: ax.relim() ax.autoscale_view(True,True,True)
6

Use a fixed size array and plot that using matplot.

 import collections
 array = collections.deque([None] * 1000, maxlen=1000)

Whenver you append to the array it will remove the first element.

Comments

0

I know I'm late to answer this question, bt for your issue you could look into the "joystick" package. It is based on the line.set_data() and canvas.draw() methods, with optional axes re-scaling. It also allows for interactive text logging or image plotting (in addition to graph plotting). No need to do your own loops in a separate thread, the package takes care of it, just give the update frequency you wish. Plus the console remains available for additional monitoring commands. See http://www.github.com/ceyzeriat/joystick/ or https://pypi.python.org/pypi/joystick (use pip install joystick to install)

try:

import joystick as jk
import numpy as np
import time

class test(jk.Joystick):
    # initialize the infinite loop decorator
    _infinite_loop = jk.deco_infinite_loop()

    def _init(self, *args, **kwargs):
        """
        Function called at initialization, see the doc
        """
        self._t0 = time.time()  # initialize time
        self.xdata = np.array([self._t0])  # time x-axis
        self.ydata = np.array([0.0])  # fake data y-axis
        # create a graph frame
        self.mygraph = self.add_frame(jk.Graph(name="test", size=(500, 500), pos=(50, 50), fmt="go-", xnpts=10000, xnptsmax=10000, xylim=(None, None, 0, 1)))

    @_infinite_loop(wait_time=0.2)
    def _generate_data(self):  # function looped every 0.2 second to read or produce data
        """
        Loop starting with the simulation start, getting data and
    pushing it to the graph every 0.2 seconds
        """
        # concatenate data on the time x-axis
        self.xdata = jk.core.add_datapoint(self.xdata, time.time(), xnptsmax=self.mygraph.xnptsmax)
        # concatenate data on the fake data y-axis
        self.ydata = jk.core.add_datapoint(self.ydata, np.random.random(), xnptsmax=self.mygraph.xnptsmax)
        self.mygraph.set_xydata(t, self.ydata)

t = test()
t.start()
t.stop()

Comments

0

To be totally interactive, you could use Bokeh for this. Concretely you could use an update function that is called every X ms and stream the new data.

Here there is a fragment I use:

def update():
     candle_data.stream(new_data, 300)   

plot = figure(x_axis_type='datetime',x_range=(start_day, final_day), width=1500, height=900, title='Live Chart', sizing_mode='scale_both')
plot.segment(x0='time', y0='highest', x1='time', y1='lowest', color='black', source=candle_data)
plot.vbar(x='time', width = 0.5*60*60*50 ,bottom='open', top='close',fill_color='color', line_color='black', source = candle_data) 
doc.add_root(column([plot]))
doc.add_periodic_callback(update, 20000)
doc.title = "Candle Data Live Rates"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.