1

After many attempts I'm at a loss for how the func parameter works for animating bar charts. I have the below code:

# Imports
import random
from tkinter import *
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import pandas as pd

# Functions
def gen_rand_array(no=500):
    return_list = []
    for x in range(no):
        return_list.append(random.randint(1,101))
    return return_list

def bubblesort(list):
    for i in range(len(list)):
        for j in range(0,len(list) - i - 1):
            if list[j] > list[j+1]:
                list[j],list[j+1] = list[j+1],list[j]
    yield list

# Runtime
def main():
    # User parameters
    win = Tk()
    win.title("Set Parameters")
    win.minsize(500,500)
    array_size = Scale(win,from_=2,to=5000,orient="horizontal",label="Use the slider to set the size of the initial array:",length=450,sliderlength=10).pack()
    win.mainloop()
    # Build random unsorted list
    unsorted_vals = []
    for index,value in enumerate(gen_rand_array()):
        unsorted_vals.append(value)

    # Formatting
    fig,ax = plt.subplots(figsize=(15,8))

    def animate(x):
        ax.set_title("Test")
        ax.set_ylim(0,100)
        for key,spine in ax.spines.items():
            spine.set_visible(False)
        ax.tick_params(bottom="off",left="off",right="off",top="off")
        ax.set_yticks([])
        ax.set_xticks([])

    ax.bar(range(len(unsorted_vals)), unsorted_vals)

    # Visualise the sort
    sorter = bubblesort(unsorted_vals)

    anim = animation.FuncAnimation(fig,frames=sorter,func=animate)

    plt.show()

main()

So for each iteration of bubblesort() I want to animate the change in order on the bar chart. I've defined frames as my generator but I do not understand what I need to pass to the "func" parameter. I have been looking up examples online but I still don't understand what I need to put here. Whenever I run the code I get errors which do not explain the problem.

Cypher236
  • 527
  • 1
  • 4
  • 13

1 Answers1

1

Inside animate() you need to get the next state of the partially sorted list and you need to update the bar plot accordingly.

You get the updated list by iterating through sorter, but depending on what layer of the nested for loops you place the yield statement your updates are more or less frequent. Currently you only yield the sorted list once. I moved it one loop down, but you could make more frequent updates:

The update of the plot is performed via h_bar (See also this question for updating bar plots in general)

I left some parts of the code out to focus on the main things.

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

def bubblesort(list):
    for i in range(len(list)):
        for j in range(0, len(list) - i - 1):
            if list[j] > list[j+1]:
                list[j], list[j+1] = list[j+1], list[j]
                 # yield list  # two elements swapped place
        yield list  # ith bubble loop completed 
    # yield list  # sorted list

n = 100
unsorted_vals = np.random.randint(0, 101, size=n)
sorter = bubblesort(unsorted_vals)

fig, ax = plt.subplots(figsize=(15, 8))
ax.set_ylim(0, 100)
for key, spine in ax.spines.items():
    spine.set_visible(False)
ax.set_yticks([])
ax.set_xticks([])

h_bar = ax.bar(range(len(unsorted_vals)), unsorted_vals)

def animate(x):
    current_list = sorter.__next__()
    for rect, h in zip(h_bar, current_list):
        rect.set_height(h)

anim = animation.FuncAnimation(fig, frames=n, func=animate, repeat=False)
plt.show()

Right now you always update the whole bar plot, when it is only necessary to update the two elements which switched place, maybe you can speed up the animation via:

def bubblesort(list):
    for i in range(len(list)):
        for j in range(0, len(list) - i - 1):
            if list[j] > list[j+1]:
                list[j], list[j+1] = list[j+1], list[j]
                yield list, j

def animate(x):
    current_list, idx = sorter.__next__()
    h_bar[idx].set_height(current_list[idx])
    h_bar[idx+1].set_height(current_list[idx+1])
    return h_bar
scleronomic
  • 4,392
  • 1
  • 13
  • 43
  • Thanks very much for the great explanation. I'm just about to head out but based on what you've written above I'll modify my code when I'm back and try again. – Cypher236 Mar 07 '20 at 19:40