docs: def plot_punchcard(df, metric='lines', title='punchcard', by=None):
      """
      Uses modified plotting code from https://bitbucket.org/birkenfeld/hgpunchcard
  
      :param df:
      :param metric:
      :param title:
      :return:
      """
  
      if not HAS_MPL:
          raise ImportError('Must have matplotlib installed to use the plotting functions')
  
      # find how many plots we are making
      if by is not None:
          unique_vals = set(df[by].values.tolist())
      else:
          unique_vals = ['foo']
      for idx, val in enumerate(unique_vals):
          if by is not None:
              sub_df = df[df[by] == val]
          else:
              sub_df = df
          fig = plt.figure(figsize=(8, title and 3 or 2.5), facecolor='#ffffff')
          ax = fig.add_subplot('111', axisbg='#ffffff')
          fig.subplots_adjust(left=0.06, bottom=0.04, right=0.98, top=0.95)
          if by is not None:
              ax.set_title(title + ' (%s)' % (str(val), ), y=0.96).set_color('#333333')
          else:
              ax.set_title(title, y=0.96).set_color('#333333')
          ax.set_frame_on(False)
          ax.scatter(sub_df['hour_of_day'], sub_df['day_of_week'], s=sub_df[metric], c='#333333', edgecolor='#333333')
          for line in ax.get_xticklines() + ax.get_yticklines():
              line.set_alpha(0.0)
          dist = -0.8
          ax.plot([dist, 23.5], [dist, dist], c='#555555')
          ax.plot([dist, dist], [dist, 6.4], c='#555555')
          ax.set_xlim(-1, 24)
          ax.set_ylim(-0.9, 6.9)
          ax.set_yticks(range(7))
          for tx in ax.set_yticklabels(['Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat', 'Sun']):
              tx.set_color('#555555')
              tx.set_size('x-small')
          ax.set_xticks(range(24))
          for tx in ax.set_xticklabels(['%02d' % x for x in range(24)]):
              tx.set_color('#555555')
              tx.set_size('x-small')
          ax.set_aspect('equal')
          if idx + 1 == len(unique_vals):
              plt.show(block=True)
          else:
              plt.show(block=False)