def create_bounds(features,padding_factor=1,negativeAllowed=False):
    """Create the search bounds for the feature space.
    
    Feature bounds are created to limit the search space 
    of optimizers to points near the data set.
    
    Args:
        features(array_like): Input features to the machine learning model with dimensions 
            of (number_of_samples,cardinality_of_feature_vector).
        
        padding_factor(float,optional): The factor to extrapolate outside the data set. 
            For example, if a particular element in the feature vector has a maximum value of 10
            and the padding_factor is set to 2, the upper bound for that element of the feature
            vector will be 20. Default 1.
    
        negativeAllowed(bool): Are negative bound values allowed? Default False.
    
    Returns:
        list: A list of tuples where each element of the list contains a tuple of length 2 
            that contains the minimum and maximum bound for the corresponding element of the
            feature vector.

    """
    
    features_transpose = list(map(list, zip(*features)))
    bounds = []
    for dim in range(len(features[0])):
        maxVal = max(features_transpose[dim])
        minVal = min(features_transpose[dim])
        mu = (maxVal + minVal)/2
        shift = (maxVal - minVal) * padding_factor / 2
        
        if negativeAllowed or mu - shift > 0:
            bounds.append((mu-shift,mu+shift))
        else:
            bounds.append((0.1*(mu+shift),mu+shift))
    return bounds