2

This is my first time posting something on StackOverflow, so I'll try my best to explain my problem.

I'm trying to write a code that shows by plotting how different sorting algorithms work. For this I plot every step the algorithm is doing, so every time I need to update my graph in order to show that. My code can be seen below. Currently I'm doing that by using plt.cla(), plt.draw() and plt.pause(), as seen under 'def Bubble_sort()'. But if there is a big array which is sorted, this is very slow!

I did try to look for a different method, and I found that using figure.canvas.draw(), figure.canvas.update() and such can speed up the process. I tried to apply it in 'def Bubble_sort_fast' But I can't get it to work... Also when I try it, I get the error:

AttributeError: 'FigureCanvasTkAgg' object has no attribute 'update'

I saw some things that it can be due to the 'backend' TkAgg I'm using, but trying others like Qt4Agg or someting, I get an import error.

It would be amazing if you guys could help me out! Let me know if you're missing information!

import random
from matplotlib import pyplot as plt
import numpy as np

end = 100
x = np.array(range(0,end+1))
y = np.array(range(0,end+1))
random.shuffle(y)

def Bubble_sort():
    plt.figure()
    plt.bar(x,y,color='blue')

    sorted = False
    while not sorted:
        for j in range(0,len(x)):
            for i in range(1,len(x)):
                if y[i-1] > y[i]:
                    y[i-1], y[i] = y[i],y[i-1]
                    plt.cla()
                    plt.bar(x,y,color='blue')
                    plt.bar(x[i],y[i],color='red')
                    plt.draw()
                    plt.pause(.01)

        for k in range(1,len(x)+1):
            plt.cla()
            plt.bar(x, y, color='blue')
            plt.bar(x[:k], y[:k], color='#63f542')
            plt.pause(0.01)
            if k == len(x):
                sorted = True

    plt.bar(x,y,color='#63f542')
    plt.show()

def Bubble_sort_fast():
    fig, ax = plt.subplots()
    barz = ax.bar(x, y, 0.95, color='blue')
    fig.canvas.draw()
    plt.show(block=False)

    sorted = False
    while not sorted:
        for j in range(0, len(x)):
            for i in range(1, len(x)):
                if y[i - 1] > y[i]:
                    y[i - 1], y[i] = y[i], y[i - 1]
                    fig.canvas.update()
                    fig.canvas.flush_events()

        for k in range(1,len(x)+1):
            plt.cla()
            plt.bar(x, y, color='blue')
            plt.bar(x[:k], y[:k], color='#63f542')
            plt.pause(0.01)
            if k == len(x):
                sorted = True

    plt.bar(x, y, color='#63f542')
    plt.show()

Bubble_sort()
# Bubble_sort_fast()
mcgraaf
  • 23
  • 5
  • There is no condition to break out the while look, so that loop will just run forever. – ImportanceOfBeingErnest Nov 20 '19 at 12:10
  • That is correct, but that isn't a problem. I have a statement normally that will break out of the while loop, the moment it's finished. I'll add it to the code for convenience. – mcgraaf Nov 20 '19 at 12:32

1 Answers1

2

One would use FuncAnimation for such animations. In this case it could look like

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

end = 100
x = np.array(range(0,end+1))
y = np.array(range(0,end+1))
random.shuffle(y)


fig, ax = plt.subplots()
bars = ax.bar(x,y,color='blue')

def update(y, i=None, k=None):
    for yi, bar in zip(y,bars):
        bar.set_height(yi)
        bar.set_facecolor("blue")
    if i is not None:
        bars[i].set_facecolor("red")
    if k is not None:
        for l in range(k):
            bars[l].set_facecolor('#63f542')

def gen():
    sorted = False
    while not sorted:
        for j in range(0,len(x)):
            for i in range(1,len(x)):
                if y[i-1] > y[i]:
                    y[i-1], y[i] = y[i],y[i-1]
                    yield (y, i, None)

        for k in range(1,len(x)+1):
            yield (y, None, k)

            if k == len(x):
                sorted = True
    ani.event_source.stop()
    yield (y, None, k)


def animate(args):
    update(*args)
    return bars

ani =  FuncAnimation(fig, animate, frames=gen, repeat=False, interval=15, blit=True)  

plt.show()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Thank you for this, this already works a lot faster! But is the speed of this limited by the code itself or set to this value only, or is there a way to regulate the speed even with this? I'd like to hear from you, thank you in advance. – mcgraaf Nov 20 '19 at 14:52
  • Here `interval=15` sets the interval between frames to 15 milliseconds. You can change that number. – ImportanceOfBeingErnest Nov 20 '19 at 15:05
  • Ah clear, thank you very much! It seems that setting it to a lower value, does not speed up the process because of the run time it already has I guess. Setting it higher will slow down the process, so I can play with that. Again, thank you very much. I've learned something new again today because of you (: ! – mcgraaf Nov 20 '19 at 15:18