简体   繁体   中英

Animating a bar graph with Matplotlib (Python)

I recently wrote this to scrape a log and show a matplotlib.pyplot.bar plot of the most used words in it

import re
from datetime import datetime
from collections import Counter

import matplotlib.pyplot as plt
from matplotlib import animation

def read_log(path, index, separator=chr(9)):
    data = []
    my_file = open(path,"r+")
    rows = my_file.readlines()
    for row in rows:
        line = re.sub(r'\r\n|\r|\n','',row, flags=re.M)
        if line != '':
            data.append(line.split(separator)[index])
    my_file.close()
    return Counter(data)

def set_plot(counter_data):
    plt.title('This is a title')
    plt.bar(range(len(counter_data)), list(counter_data.values()), align='center')
    plt.xticks(range(len(counter_data)), list(counter_data.keys()))
    plt.tight_layout()
    plt.show()

counter_data = read_log(r'logfile.txt',2)
print(counter_data)
set_plot(counter_data)

I would love to animate said plot, however, I can't grasp animation.FuncAnimation()

Can you help me out?

I added these lines:

fig = plt.Figure()
animation.FuncAnimation(fig, set_plot(counter_data), frames=20)

and deleted plt.show()

So I could give FuncAnimation an empty figure (fig) and the function. But it doesn't work. EDIT: And it doesn't print an error either.

The main problem is that FuncAnimation expects a callable which returns artist objects. The callable will be called repeatedly with a frame argument.

In your example, set_plot() is called once. It's return value ( None ) is passed to FuncAnimation . Instead you should have a method, eg update_plot() , which loads the data from the file, updates the bar plot and returns the bar plot. This function (the function itself) should be passed to FuncAnimation

 animation.FuncAnimation(fig, update_plot, frames=20)

without calling it! Note the missing parenthesis after update_plot . The animitation documentation shows examples how this can be done.

It seems your data is static (you get it from file once and it doesn't change), so I don't really understand what you are trying to animate. But, your code contains errors that need to be fixed, so for demonstration purposes I will add increment each of the heights in each step of animation.

The first mistake is in the way you pass arguments to your function. For arguments you have to use fargs parameter, otherwise in your version you are passing the result of function not the function itself.

You must have a function ( animate in my version, set_plot in yours) that updates the plot for each step of your animation. (in your case you just put the same data every time)

That function needs to accept at least one parameter ( val ) which is used my FuncAnimation which passes values got from iterator passed to its frames parameter.

The final code looks like this

import re
from datetime import datetime
from collections import Counter
import matplotlib.pyplot as plt
from matplotlib import animation
# uncomment if using in jupyter notebook
# %matplotlib nbagg 

def read_log(path, index, separator=chr(9)):
    data = []
    my_file = open(path,"r+")
    rows = my_file.readlines()
    for row in rows:
        line = re.sub(r'\r\n|\r|\n','',row, flags=re.M)
        if line != '':
            data.append(line.split(separator)[index])
    my_file.close()
    return Counter(data)

fig = plt.figure()
ax = fig.add_subplot()
counter_data = read_log(r'tmp.csv',2)
plt.title('This is a title')
bar = ax.bar(range(len(counter_data)), list(counter_data.values()), align='center')
plt.xticks(range(len(counter_data)), list(counter_data.keys()))
plt.tight_layout()
plt.ylim((0, 30))


def animate(val, counter_data):
    data = list(counter_data.values())
    for i in range(len(data)):
        bar[i].set_height(data[i]+val)

animation.FuncAnimation(fig, func=animate, frames=20, fargs=[counter_data], save_count=10)

and we get the following animation:

动画


Edit :

For errors you can try to save your animation to gif, and the errors will show up

anim = animation.FuncAnimation(fig, func=animate, frames=20, fargs=[counter_data], save_count=10)
anim.save('anim.gif', 'imagemagick')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM