As mentioned last time, I thought I would continue by looking at using a different bin size for a the chart we animated last time. I was using a default size of 1. I will start by looking at bins of size 0.5.

Command Line Parameters

But first I thought I’d add a command line parameter or two to control what happens when the module runs. I.E. save to file only or display on screen only. If the argument is ‘y’ I will save to a default file name. If it is a number (decimal), I will use that in the file name. If it is ’n’, or missing, no save, just display the animated chart on screen. Oh yes, I will add code to determine the correct path for the directory to which to save the files, population/play/img.

Let’s start with the file path. Keeping thinking I should build a module for this kind of thing, which I can import into modules that need it. Perhaps in the future or on another project.

import pathlib

# Test for some of the most likely starting working directories.
# If found set I_PATH accordingly. Using pathlib to cover operating system issues.
# If not one of them print message and exit. The play/img directory must already exist.
I_PATH = ''
p = pathlib.Path.cwd()
# print(p)
if p.name == 'py_play':
  I_PATH = pathlib.Path('./population/play/img')
elif p.name == 'population':
  I_PATH = pathlib.Path('./play/img')
else:
  print(f"\nCurrent working directory is an unexpected location, {p}!")
  print(f"Please run the application from the root directory. Quitting application")
  exit(1)
...
# down in the appropriate section, the ani.save line becomes
# I'll get to the 'save_fl' variable in a bit
  ani.save(I_PATH / save_fl, writer=writer)

For the command line parameters I am using the following.

# up in the import area
import argparse

...

# before the main module code (i.e. above the current data line)
save_ani = ""
save_fl = "ani_no_name.mp4"

parser = argparse.ArgumentParser()
# long name preceded by --, short by single -, get it as an integer so can use to access test data array
parser.add_argument('--save_ani', '-s', help=f'Save animation to file (y or n or a number)', default='n')

args = parser.parse_args()
if args.save_ani == 'y':
  save_ani = "mp4"
  save_fl = f"hist_x9.{save_ani}"
if args.save_ani.isdecimal():
  save_ani = "mp4"
  save_fl = f"ani_hist_{args.save_ani}.{save_ani}"

...

# At the bottom/end of the module
if save_ani == 'mp4':
  # Set up formatting for the movie files
  Writer = animation.writers['ffmpeg']
  writer = Writer(metadata=dict(artist='Me'))
  ani.save(I_PATH / save_fl, writer=writer)
else:
  plt.show()

A few simple tests imply things work as expected.

Bin Size

Now I know we didn’t need to animate the charts to look at the effect of changing the bin size. And, it does take a bit of time to generate/save the file. But, I didn’t feel like running a different module. I also decided I’d like to have the saved file name reflect the change in bin width. So, I am adding a new variable, bin_wd, with a default value of 1. And an if block to modify the file name if the bin width is not 1. In the process, I decided to re-arrange my code. I have moved all the variable definitions, argparse code and other module code below the defintion of animate(). To some extent this was necessary to simplify the file name change if required.

  max_x = int(round((max(data) + 4) / 5) * 5)
+ bin_wd = 1
- bins = np.arange(min_x, max_x)
+ bins = np.arange(min_x, max_x, bin_wd)

And further down in the module.

args = parser.parse_args()
if args.save_ani == 'y':
  save_ani = "mp4"
  if bin_wd == 1:
    save_fl = f"hist_x9.{save_ani}"
  else:
    save_fl = f"hist_x9_bw{bin_wd}.{save_ani}"
if args.save_ani.isdecimal():
  save_ani = "mp4"
  if bin_wd == 1:
    save_fl = f"ani_hist_{args.save_ani}.{save_ani}"
  else:
    save_fl = f"ani_hist_{args.save_ani}_bw{bin_wd}.{save_ani}"

You can view the complete module code in the related post, Module Code: animated_chart.py/

I expect playing with bin sizes should only require editing one line in the module and running it. Let’s see.

- bin_wd = 1
+ bin_wd = 0.5

And, bingo.

If I go with a bin size of 0.125 it looks like the following.

I find it interesting that the most frequent bin in this sample (for all the three sizes we tried) contains the actual world average age, even though the mean of the 100 repeated samples is a touch low. And, it is more frequent by a goodly margin. Perhaps we should be using the mode rather than the mean for our estimated world average age?

That’s It for This One

I am still in a bit of a doldrum and having some difficulty producing quality and timely posts. Though some of that is other demands on my time and mind. In addition, I still have no idea exactly where to go next. Regardless, I have for some time been thinking that I am trying to put too much content into each of my posts. So, I am going to call it quits for this one. Sort of like separation of concerns — one thing unto itself.

Next time I am going to continue by looking at using files in JSON format to provide my module with data from different sampling sessions. If you’re not too sure about JSON see the resources section below. But, you will very quickly see it looks a lot like Python dictionaries — perhaps just slightly more strigent rules.

Resources