def most_similar_catchments(self, subject_catchment, similarity_dist_function, records_limit=500,
                                include_subject_catchment='auto'):
        """
        Return a list of catchments sorted by hydrological similarity defined by `similarity_distance_function`

        :param subject_catchment: subject catchment to find similar catchments for
        :type subject_catchment: :class:`floodestimation.entities.Catchment`
        :param similarity_dist_function: a method returning a similarity distance measure with 2 arguments, both
                                         :class:`floodestimation.entities.Catchment` objects
        :param include_subject_catchment: - `auto`: include subject catchment if suitable for pooling and if urbext < 0.03
                                          - `force`: always include subject catchment having at least 10 years of data
                                          - `exclude`: do not include the subject catchment
        :type include_subject_catchment: str
        :return: list of catchments sorted by similarity
        :type: list of :class:`floodestimation.entities.Catchment`
        """
        if include_subject_catchment not in ['auto', 'force', 'exclude']:
            raise ValueError("Parameter `include_subject_catchment={}` invalid.".format(include_subject_catchment) +
                             "Must be one of `auto`, `force` or `exclude`.")

        query = (self.db_session.query(Catchment).
                 join(Catchment.descriptors).
                 join(Catchment.amax_records).
                 filter(Catchment.id != subject_catchment.id,
                        Catchment.is_suitable_for_pooling,
                        or_(Descriptors.urbext2000 < 0.03, Descriptors.urbext2000 == None),
                        AmaxRecord.flag == 0).
                 group_by(Catchment).
                 having(func.count(AmaxRecord.catchment_id) >= 10))  # At least 10 AMAX records
        catchments = query.all()

        # Add subject catchment if required (may not exist in database, so add after querying db
        if include_subject_catchment == 'force':
            if len(subject_catchment.amax_records) >= 10:  # Never include short-record catchments
                catchments.append(subject_catchment)
        elif include_subject_catchment == 'auto':
            if len(subject_catchment.amax_records) >= 10 and subject_catchment.is_suitable_for_pooling and \
               (subject_catchment.descriptors.urbext2000 < 0.03 or subject_catchment.descriptors.urbext2000 is None):
                catchments.append(subject_catchment)

        # Store the similarity distance as an additional attribute for each catchment
        for catchment in catchments:
            catchment.similarity_dist = similarity_dist_function(subject_catchment, catchment)
        # Then simply sort by this attribute
        catchments.sort(key=attrgetter('similarity_dist'))

        # Limit catchments until total amax_records counts is at least `records_limit`, default 500
        amax_records_count = 0
        catchments_limited = []
        for catchment in catchments:
            catchments_limited.append(catchment)
            amax_records_count += catchment.record_length
            if amax_records_count >= records_limit:
                break

        return catchments_limited
def most_similar_catchments ( self , subject_catchment , similarity_dist_function , records_limit = 500 , include_subject_catchment = 'auto' ) : if include_subject_catchment not in [ 'auto' , 'force' , 'exclude' ] : raise ValueError ( "Parameter `include_subject_catchment={}` invalid." . format ( include_subject_catchment ) + "Must be one of `auto`, `force` or `exclude`." ) query = ( self . db_session . query ( Catchment ) . join ( Catchment . descriptors ) . join ( Catchment . amax_records ) . filter ( Catchment . id != subject_catchment . id , Catchment . is_suitable_for_pooling , or_ ( Descriptors . urbext2000 < 0.03 , Descriptors . urbext2000 == None ) , AmaxRecord . flag == 0 ) . group_by ( Catchment ) . having ( func . count ( AmaxRecord . catchment_id ) >= 10 ) ) catchments = query . all ( ) if include_subject_catchment == 'force' : if len ( subject_catchment . amax_records ) >= 10 : catchments . append ( subject_catchment ) elif include_subject_catchment == 'auto' : if len ( subject_catchment . amax_records ) >= 10 and subject_catchment . is_suitable_for_pooling and ( subject_catchment . descriptors . urbext2000 < 0.03 or subject_catchment . descriptors . urbext2000 is None ) : catchments . append ( subject_catchment ) for catchment in catchments : catchment . similarity_dist = similarity_dist_function ( subject_catchment , catchment ) catchments . sort ( key = attrgetter ( 'similarity_dist' ) ) amax_records_count = 0 catchments_limited = [ ] for catchment in catchments : catchments_limited . append ( catchment ) amax_records_count += catchment . record_length if amax_records_count >= records_limit : break return catchments_limited
Return a list of catchments sorted by hydrological similarity defined by `similarity_distance_function`

        :param subject_catchment: subject catchment to find similar catchments for
        :type subject_catchment: :class:`floodestimation.entities.Catchment`
        :param similarity_dist_function: a method returning a similarity distance measure with 2 arguments, both
                                         :class:`floodestimation.entities.Catchment` objects
        :param include_subject_catchment: - `auto`: include subject catchment if suitable for pooling and if urbext < 0.03
                                          - `force`: always include subject catchment having at least 10 years of data
                                          - `exclude`: do not include the subject catchment
        :type include_subject_catchment: str
        :return: list of catchments sorted by similarity
        :type: list of :class:`floodestimation.entities.Catchment`