0

I am trying to show the progress of a certain computation in python. I do this by issuing print statements but I would rather do that in a plot. I checked animation of matplotlib but that requires an interval while in my case I want to "order" the plot to plot a new value in the plot. I thought that maybe the axes were persistent and I initiualized these once while successively adding plots, see below.

import numpy as np
import matplotlib.pyplot as plt

results =[
[   0, 2.1899, 0.1399, 0.1297],
[ 100, 1.2034, 0.4417, 0.3743],
[ 200, 1.1814, 0.4810, 0.4194],
[ 300, 1.1407, 0.4705, 0.4116],
[ 400, 1.1278, 0.4937, 0.4224],
[ 500, 1.1185, 0.4976, 0.4136],
[ 600, 1.1002, 0.4885, 0.4194],
[ 700, 1.1596, 0.5046, 0.4263],
[ 800, 1.0914, 0.4304, 0.3870],
[ 900, 1.0824, 0.5129, 0.4322],
[1000, 1.0779, 0.5049, 0.4479],
[1100, 1.0579, 0.4849, 0.4322],
[1200, 1.0691, 0.5134, 0.4578],
[1300, 1.2005, 0.3770, 0.3330],
[1400, 1.0754, 0.5320, 0.4725],
[1500, 1.0534, 0.5283, 0.4676],
[1600, 1.0539, 0.5278, 0.4676],
[1700, 1.0420, 0.5427, 0.4921],
[1800, 1.0560, 0.5266, 0.4587],
[1900, 1.0551, 0.5076, 0.4470],
[2000, 1.0542, 0.5095, 0.4617],
[2100, 1.0436, 0.5200, 0.4607]]

def plot_init (data):
    fig, ax1 = plt.subplots()
    ax2 = ax1.twinx()

    ax1.set_ylabel('Cost', color='r')
    ax2.set_ylabel('Accuracy (train and test (dashed))', color='b')
    ax2.set_ylim (0, 1)

    return ax1, ax2

def plot_data (ax1, ax2, data, n):
    ax1.plot(data [:n, 0], data [:n, 1], color = 'red', linewidth = 2)
    maximum = int (max (data [:, 0]))
    ax1.set_xlim(0, maximum)
    ax1.set_ylim(bottom=0)

    ax2.plot (data [:n, 0], data [:n, 2], color = 'blue')
    ax2.plot (data [:n, 0], data [:n, 3], color = 'blue', linestyle = 'dashed')
    plt.title("Cost and accuracy")
    plt.show ()

data = np.array (results).reshape (22, 4)
axis_1, axis_2 = plot_init (data)
for i in range (10,20):
    plot_data (axis_1, axis_2, data, i)

However, this generates 10 plots and only the first one contains a plot.

Does anyone have an idea how I can incrementally plot the results of my computation inside the same plot without having to create a new plot for each new result?

Edit

The solution of @user8153 worked well in another situation: to show a succeeding sequence of images in the same plot window. The code is shown below. I noticed that a plt.pause (0.5) is crucial for it working correctly (0.5 may be 0.1 or even 0.01) because it appears that during that pause the image is being refreshed. In my case it didn't work without the call to pause. Later on I decided to use a subplot to plot all 20 images into one plot. To see the image plotted as soon as it is ready a call to pause() is necessary. Notice that @user8153 in his answer uses a call to pause() as well. I hadn't paid much attention to it, until I started working with a sequence of images.

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

def plot_init (data):
    fig, ax1 = plt.subplots()
    plt.title("Image")
    image = plt.imshow (data)

    return image

def plot_data (image, data, i):
    plt.title("Image " + str (i))
    image.set_data(data)
    plt.draw()

data = np.random.rand(100, 100)
image = plot_init (data)
for i in range(10):
    data = np.random.rand(100, 100)
    plot_data (image, data, i)
    plt.pause(0.5)

plt.show()
Arnold
  • 4,578
  • 6
  • 52
  • 91

2 Answers2

1

The idea would be to use FuncAnimation. The plot from the question would then look as follows.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

results =[
[   0, 2.1899, 0.1399, 0.1297],
[ 100, 1.2034, 0.4417, 0.3743],
[ 200, 1.1814, 0.4810, 0.4194],
[ 300, 1.1407, 0.4705, 0.4116],
[ 400, 1.1278, 0.4937, 0.4224],
[ 500, 1.1185, 0.4976, 0.4136],
[ 600, 1.1002, 0.4885, 0.4194],
[ 700, 1.1596, 0.5046, 0.4263],
[ 800, 1.0914, 0.4304, 0.3870],
[ 900, 1.0824, 0.5129, 0.4322],
[1000, 1.0779, 0.5049, 0.4479],
[1100, 1.0579, 0.4849, 0.4322],
[1200, 1.0691, 0.5134, 0.4578],
[1300, 1.2005, 0.3770, 0.3330],
[1400, 1.0754, 0.5320, 0.4725],
[1500, 1.0534, 0.5283, 0.4676],
[1600, 1.0539, 0.5278, 0.4676],
[1700, 1.0420, 0.5427, 0.4921],
[1800, 1.0560, 0.5266, 0.4587],
[1900, 1.0551, 0.5076, 0.4470],
[2000, 1.0542, 0.5095, 0.4617],
[2100, 1.0436, 0.5200, 0.4607]]

def plot_init (data):
    fig, ax1 = plt.subplots()
    ax2 = ax1.twinx()
    ax1.set_title("Cost and accuracy")
    ax1.set_ylabel('Cost', color='r')
    ax2.set_ylabel('Accuracy (train and test (dashed))', color='b')
    ax2.set_ylim (0, 1)

    return fig, ax1, ax2

def plot_data (n, ax1, ax2, data):
    ax1.plot(data [:n, 0], data [:n, 1], color = 'red', linewidth = 2)
    maximum = int (max (data [:, 0]))
    ax1.set_xlim(0, maximum)
    ax1.set_ylim(bottom=0)

    ax2.plot (data [:n, 0], data [:n, 2], color = 'blue')
    ax2.plot (data [:n, 0], data [:n, 3], color = 'blue', linestyle = 'dashed')

data = np.array (results).reshape (22, 4)
fig, axis_1, axis_2 = plot_init (data)

ani = FuncAnimation(fig,plot_data, frames=range (10,20), 
                    fargs=(axis_1, axis_2, data), interval=500)

plt.show()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • FuncAnimation requires a fixed interval. What I need is a way to add new data to results and next have that added to the plot. That's what I meant by ' ordering' it to plot the new result. Sorry for bein not so clear. – Arnold Sep 03 '17 at 20:13
1

Ideally you create the plot and the three lines only once, and only add data points to the lines as you iterate over your array. You could use a FuncAnimation, or simply looping through your data while making sure that the system has an opportunity to update the plot:

import numpy as np
import matplotlib.pyplot as plt

results =[
[   0, 2.1899, 0.1399, 0.1297],
[ 100, 1.2034, 0.4417, 0.3743],
[ 200, 1.1814, 0.4810, 0.4194],
[ 300, 1.1407, 0.4705, 0.4116],
[ 400, 1.1278, 0.4937, 0.4224],
[ 500, 1.1185, 0.4976, 0.4136],
[ 600, 1.1002, 0.4885, 0.4194],
[ 700, 1.1596, 0.5046, 0.4263],
[ 800, 1.0914, 0.4304, 0.3870],
[ 900, 1.0824, 0.5129, 0.4322],
[1000, 1.0779, 0.5049, 0.4479],
[1100, 1.0579, 0.4849, 0.4322],
[1200, 1.0691, 0.5134, 0.4578],
[1300, 1.2005, 0.3770, 0.3330],
[1400, 1.0754, 0.5320, 0.4725],
[1500, 1.0534, 0.5283, 0.4676],
[1600, 1.0539, 0.5278, 0.4676],
[1700, 1.0420, 0.5427, 0.4921],
[1800, 1.0560, 0.5266, 0.4587],
[1900, 1.0551, 0.5076, 0.4470],
[2000, 1.0542, 0.5095, 0.4617],
[2100, 1.0436, 0.5200, 0.4607]]

def plot_init (data):
    fig, ax1 = plt.subplots()
    ax2 = ax1.twinx()

    ax1.set_ylabel('Cost', color='r')
    maximum = int (max (data [:, 0]))
    ax1.set_xlim(0, maximum)
    ax1.set_ylim(0, max(data[:,1]))

    ax2.set_ylabel('Accuracy (train and test (dashed))', color='b')
    ax2.set_ylim (0, 1)

    line1 = ax1.plot([], [], '*-', color = 'red', linewidth = 2)[0]
    line2 = ax2.plot([], [], '*-', color = 'blue')[0]
    line3 = ax2.plot([], [], '*-', color = 'blue', linestyle = 'dashed')[0]

    plt.title("Cost and accuracy")

    return line1, line2, line3

def plot_data (line1, line2, line3, data, n):

    line1.set_data(data [:n, [0,1]].T)
    line2.set_data(data [:n, [0,2]].T)
    line3.set_data(data [:n, [0,3]].T)

    plt.draw()

data = np.array (results).reshape (22, 4)
line1, line2, line3 = plot_init (data)
for i in range(len(data)):
    plot_data (line1, line2, line3, data, i)
    plt.pause(0.1)    

plt.show()
user8153
  • 4,049
  • 1
  • 9
  • 18
  • Only one plot is plotted instead of 22 in my case: a big improvement compared to my code. But the problem is, it waits until all pauses have paused (in this case 2.2 seconds) and next plots the total graph. FuncAnimatrion has the same problem. What I need is that it plots immediately the change and then waits for the results of the next computation (which can last between seconds and minutes) – Arnold Sep 02 '17 at 07:23
  • 1
    How do you execute the code? If run it from the command line (`python filename.py`) I get a window, and then it draws the 22 points one by one. It sounds like you might be in matplotlib's interactive mode that [blocks](https://stackoverflow.com/questions/28269157/plotting-in-a-non-blocking-way-with-matplotlib) after `plt.show()`. What backend are you using? – user8153 Sep 02 '17 at 17:02
  • I am using Anaconda3 and Spyder 3. I tried using python as you suggested and that gave the result I wanted! Maybe a suggestion how to do this in spyder? – Arnold Sep 03 '17 at 20:00
  • On my desktop PC it works great with Spyder! There must be some configuration difference between my laptop and my PC. Thanks very much for your answer! – Arnold Sep 03 '17 at 20:10