0

I'm coding the Bubble Sort algorithm, where it takes in a list of random integers, sorts them in increasing order, and plots the sorted list of integers as the y-values of a bar graph. My first bar chart is the unsorted list and the second one is the sorted one.

I want the graph to update in real time. I would do this by feeding matplotlib lists that are sorted a bit more and more. (ie new lists each time)

I've looked up many tutorials on updating charts in matplotlib, but they are mostly for ones like scatter plots where one point is added at a time. I've also seen Updating a matplotlib bar graph?, but I cannot understand it for the life of me...

So, how would I go about making this bar graph update in real time? Thank you!

My code is below. You can ignore the sorting algorithm. All that you need to know is that it returns a list of sorted numbers in the form [1, 2, 3, 4, 5].

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


print("bubble sort")

plt.style.use('fivethirtyeight')

n = int(input("How many items would you like to sort? "))

list_num = np.random.randint(0,500, n)

orig_list_num = list_num.copy()

def is_sorted(list_num):
  Flag = True
  j=-1
  while Flag == True:
    Flag=False
    j+=1
    for i in range(0, (len(list_num))-j):
      if i+1 >= len(list_num):
        break
      if list_num[i] > list_num[i+1]:
        tempvar=list_num[i]
        list_num[i] = list_num[i+1]
        list_num[i+1] = tempvar
        Flag=True
  return list_num

list_num = is_sorted(list_num)

#unsorted graph
indices = np.arange(n)
plt.subplot(1, 2, 1)
plt.bar(indices, orig_list_num)
plt.xlabel("Unsorted")

#sorted graph
plt.subplot(1, 2, 2)
plt.bar(indices, list_num)
plt.xlabel("Sorted")

plt.show()
  • Have you looked at [`matplotlib.animation`](https://matplotlib.org/stable/api/animation_api.html)? – Bill Apr 28 '21 at 01:14
  • what is wrong with examples which add one point at a time ? Probably it could add 100 new points at the time and also remove 100 old points at the time - so it could replace all points at the time - you have to only try it. – furas Apr 28 '21 at 02:11
  • example in `Updating a matplotlib bar graph?` uses fact that it can be built on `tkinter` GUI - so it use `tkinter` functions to update GUI. But `matplotlit` may use different GUIs and then this example will not work. – furas Apr 28 '21 at 02:14
  • if you get `ax` from `plt` then you should have access to `ax.data` with all values in plot - and then you can replace them. You may need also run `update()` to update plot on screen. – furas Apr 28 '21 at 02:16

1 Answers1

0

It is first version without FuncAnimation for animation. It may be slow.
And it may have problem to close window befor end of animation.

First I use

plt.show(block=False) 

to display plots without blocking code

and next I run is_sorted() to update plot.

Inside is_sorted() I use

plt.cla() 

to clear last drawn axis

And now I can draw bar again in the same place but with new data.

plt.bar(indices, list_num)

After that I slow down code so it has time to display plot in window

plt.pause(0.01)

without pause it may display empty window.


BTW:

if you use -1 in (len(list_num))-j-1 then you don't need if i+1 >= len(list_num): break


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

# --- functions --

def is_sorted(list_num):

    flag = True
    j = -1
    
    while flag:
        flag = False
        j += 1
        
        for i in range(0, (len(list_num))-j-1):
                
            if list_num[i] > list_num[i+1]:

                # in Python you can do it without `tempvar`
                list_num[i], list_num[i+1] = list_num[i+1], list_num[i]
                
                flag = True
                
                plt.cla()
                plt.bar(indices, list_num)
                plt.pause(0.01)

            if closed:
                flag = False
                break  # it exits `for`-loop but not `while`-loop and I need `flag = False` 
                
def on_close(event):
    global closed   # to assign value to external variable which I need in `is_sorted
    
    closed = True

    print('Closing window')

# --- main ---

closed = False  # default value at start

print("bubble sort")

plt.style.use('fivethirtyeight')

#n = int(input("How many items would you like to sort? "))
n = 20

list_num = np.random.randint(0, 500, n)
orig_list_num = list_num.copy()

indices = np.arange(n)

#unsorted graph
plt.subplot(1, 2, 1)
plt.bar(indices, orig_list_num)
plt.xlabel("Unsorted")

#sorted graph
plt.subplot(1, 2, 2)
plt.bar(indices, list_num)
plt.xlabel("Sorted")

plt.ion() # `Interaction ON` siliar to `block=False`
#plt.show(block=False)

# assign function to plot
fig = plt.gcf()
fig.canvas.mpl_connect('close_event', on_close)

is_sorted(list_num)

input("Press ENTER to exit")

EDIT:

Using idea from your link I created second version - I think it can run little faster but I expected much better speed.

In this version I assign bar to variable

bar2 = plt.bar(indices, list_num)

so later I can chagen bar heigh without drawing all again

bar2[i].set_height(list_num[i])
bar2[i+1].set_height(list_num[i+1])

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

# --- functions --

def is_sorted(list_num):

    flag = True
    j = -1
    
    while flag:
    
        flag = False
        j += 1
        
        for i in range(0, (len(list_num))-j-1):
                
            if list_num[i] > list_num[i+1]:

                # in Python you can do it without `tempvar`
                list_num[i], list_num[i+1] = list_num[i+1], list_num[i]
                
                flag = True

                # replace all values
                #for rect, value in zip(bar2, list_num):
                #    rect.set_height(value)
                
                # replace only two new values
                bar2[i].set_height(list_num[i])
                bar2[i+1].set_height(list_num[i+1])
                
                plt.pause(0.001)

            if closed:
                flag = False
                break  # it exits `for`-loop but not `while`-loop and I need `flag = False` 
                               

def on_close(event):
    global closed   # to assign value to external variable which I need in `is_sorted
    
    closed = True

    print('Closing window')

# --- main ---

closed = False  # default value at start

print("bubble sort")

plt.style.use('fivethirtyeight')

#n = int(input("How many items would you like to sort? "))
n = 20

list_num = np.random.randint(0, 500, n)
orig_list_num = list_num.copy()

indices = np.arange(n)

#unsorted graph
plt.subplot(1, 2, 1)
plt.bar(indices, orig_list_num)
plt.xlabel("Unsorted")

#sorted graph
plt.subplot(1, 2, 2)
bar2 = plt.bar(indices, list_num)
plt.xlabel("Sorted")

plt.ion() # `Interaction ON` siliar to `block=False`
#plt.show(block=False)

# assign function to plot
fig = plt.gcf()
fig.canvas.mpl_connect('close_event', on_close)

is_sorted(list_num)

input("Press ENTER to exit")

I was thinking about version with FuncAnimation but it would need to change a lot cod. It would need to create code which use single for-loop instead while+for - and then FuncAnimation would repeate code instead this for-loop.

I had other idea - in plt.plot() you could replace in plot data with ax.set_ydata() but plt.bar() doesn't have this function. But set_height() seems similar.


In answer to question How do I correctly implement a bubble sort algorithm in python tkinter? I created animation directly in tkinter without matplotlib

furas
  • 134,197
  • 12
  • 106
  • 148