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
def file_change_rates ( self , branch = 'master' , limit = None , coverage = False , days = None , ignore_globs = None , include_globs = None ) : 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' } ) 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 ) ) 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 ( ',' ) ) ) ) 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
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