docs: def file_change_rates(self, branch='master', limit=None, coverage=False, days=None, ignore_globs=None,
                            include_globs=None):
          """
          This function will return a DataFrame containing some basic aggregations of the file change history data, and
          optionally test coverage data from a coverage_data.py .coverage file.  The aim here is to identify files in the
          project which have abnormal edit rates, or the rate of changes without growing the files size.  If a file has
          a high change rate and poor test coverage, then it is a great candidate for writing more tests.
  
          :param branch: (optional, default=master) the branch to return commits for
          :param limit: (optional, default=None) a maximum number of commits to return, None for no limit
          :param coverage: (optional, default=False) a bool for whether or not to attempt to join in coverage data.
          :param days: (optional, default=None) number of days to return if limit is None
          :param ignore_globs: (optional, default=None) a list of globs to ignore, default none excludes nothing
          :param include_globs: (optinal, default=None) a list of globs to include, default of None includes everything.
          :return: DataFrame
          """
  
          fch = self.file_change_history(
              branch=branch,
              limit=limit,
              days=days,
              ignore_globs=ignore_globs,
              include_globs=include_globs
          )
          fch.reset_index(level=0, inplace=True)
  
          if fch.shape[0] > 0:
              file_history = fch.groupby('filename').agg(
                  {
                      'insertions': [np.sum, np.max, np.mean],
                      'deletions': [np.sum, np.max, np.mean],
                      'message': lambda x: ','.join(['"' + str(y) + '"' for y in x]),
                      'committer': lambda x: ','.join(['"' + str(y) + '"' for y in x]),
                      'author': lambda x: ','.join(['"' + str(y) + '"' for y in x]),
                      'date': [np.max, np.min]
                  }
              )
  
              file_history.columns = [' '.join(col).strip() for col in file_history.columns.values]
  
              file_history = file_history.rename(columns={
                  'message <lambda>': 'messages',
                  'committer <lambda>': 'committers',
                  'insertions sum': 'total_insertions',
                  'insertions amax': 'max_insertions',
                  'insertions mean': 'mean_insertions',
                  'author <lambda>': 'authors',
                  'date amax': 'max_date',
                  'date amin': 'min_date',
                  'deletions sum': 'total_deletions',
                  'deletions amax': 'max_deletions',
                  'deletions mean': 'mean_deletions'
              })
  
              # get some building block values for later use
              file_history['net_change'] = file_history['total_insertions'] - file_history['total_deletions']
              file_history['abs_change'] = file_history['total_insertions'] + file_history['total_deletions']
              file_history['delta_time'] = file_history['max_date'] - file_history['min_date']
  
              try:
                  file_history['delta_days'] = file_history['delta_time'].map(
                      lambda x: np.ceil(x.seconds / (24 * 3600) + 0.01))
              except AttributeError as e:
                  file_history['delta_days'] = file_history['delta_time'].map(
                      lambda x: np.ceil((float(x.total_seconds()) * 10e-6) / (24 * 3600) + 0.01))
  
              # calculate metrics
              file_history['net_rate_of_change'] = file_history['net_change'] / file_history['delta_days']
              file_history['abs_rate_of_change'] = file_history['abs_change'] / file_history['delta_days']
              file_history['edit_rate'] = file_history['abs_rate_of_change'] - file_history['net_rate_of_change']
              file_history['unique_committers'] = file_history['committers'].map(lambda x: len(set(x.split(','))))
  
              # reindex
              file_history = file_history.reindex(
                  columns=['unique_committers', 'abs_rate_of_change', 'net_rate_of_change', 'net_change', 'abs_change',
                           'edit_rate'])
              file_history.sort_values(by=['edit_rate'], inplace=True)
  
              if coverage and self.has_coverage():
                  file_history = file_history.merge(self.coverage(), left_index=True, right_on='filename', how='outer')
                  file_history.set_index(keys=['filename'], drop=True, inplace=True)
          else:
              file_history = DataFrame(
                  columns=['unique_committers', 'abs_rate_of_change', 'net_rate_of_change', 'net_change', 'abs_change',
                           'edit_rate'])
  
          return file_history