Sunday, 21 April 2013

Making animated gifs

This is a short post in my series of "write it up in a blog post so that I don't forget how to do it":
In this post I'm going to briefly show how to create animated "gifs" easily on a *nix system. I've recently stumbled upon how easy it is to do this and here's one from this blog post:



This is all done very easily using the convert command from the imagemagick command line tool.
In this post I showed how to carry out a simple linear regression on a date stamped data set in python. Here's a slightly modified version of the code from that post:

#!/usr/bin/env python
from scipy import stats
import csv
import datetime
import matplotlib.pyplot as plt
import os


# Import data
print "Reading data"
outfile = open("Data.csv", "rb")
data = csv.reader(outfile)
data = [row for row in data]
outfile.close()

dates = []
numbers = {}
for e in data[1:]:
    dates.append(e[0])
    numbers[e[0]] = eval(e[1])


# Function to fit line
def fit_line(dates, numbers):
    x = [(e - min(dates)).days for e in dates]  # Convert dates to numbers for linear regression
    gradient, intercept, r_value, p_value, std_err = stats.linregress(x, numbers)
    return gradient, intercept


# Function for projection
def project_line(dates, numbers, extra_days):
    extra_date = min(dates) + datetime.timedelta(days=extra_days)
    gradient, intercept = fit_line(dates, numbers)
    projection = gradient * (extra_date - min(dates)).days + intercept
    return extra_date, projection, gradient


# Function to plot data and projection
def plot_projection(dates, numbers, extra_days):
    extra_date, projection, gradient = project_line(dates, numbers, extra_days)
    plt.figure(1, figsize=(15, 6))
    plt.plot_date(dates, numbers, label="Data")
    plt.plot_date(dates + [extra_date], numbers + [projection], '-', label="Linear fit (%.2f join per day)" % gradient)
    plt.legend(loc="upper left")
    plt.grid(True)
    plt.title("Rugby Union Google Plus Community Member Numbers")
    now = max(dates)
    plt.savefig('Rugby_Union_Community_Numbers-%d-%.02d-%.02d.png' % (now.year, now.month, now.day))
    plt.close()

print "Projecting and plotting for all data points"
for i in range(3, len(dates) + 1):
    dates_to_plot = [datetime.datetime.strptime(e, '%Y-%m-%d') for e in dates[-i:]]
    numbers_to_plot = [numbers[e] for e in dates[-i:]]
    plot_projection(dates_to_plot, numbers_to_plot, 365)

The output of this script (if anything is unclear in there just ask) is a series of png files each of which represents a fitted line to for the quantity of data available up to a given date (made using matplotlib). Here are a few of them:
1st 2nd last


Here's the imagemagik convert command to take all these png files to gif:

convert *.png animation.gif

Which gives:




If we want to delay the transition slightly we write:

convert -delay 50 *.png animation.gif

Which gives:

Here's an extremely short screencast demonstrating this: