예제 #1
0
def sinusoidal_roots_derivative_initial_value(first_constant, second_constant, third_constant, fourth_constant, initial_value, precision = 4):
    # Handle input errors
    five_scalars(first_constant, second_constant, third_constant, fourth_constant, initial_value)
    positive_integer(precision)

    # Create intermediary list and list to return
    roots = []
    result = []

    # Identify key ratio
    ratio = initial_value / (first_constant * second_constant)

    # Handle no roots
    if ratio > 1 or ratio < -1:
        result.append(None)
    
    # Handle multiple roots
    else:
        # Handle case in which initial value is zero
        if ratio == 0:
            roots = sinusoidal_roots_first_derivative(first_constant, second_constant, third_constant, fourth_constant, precision)
        
        # Handle general case
        else:
            radians = acos(ratio)
            periodic_radians = radians / second_constant
            periodic_unit = 2 * pi / second_constant
            initial = third_constant + periodic_radians
            roots = generate_elements(initial, periodic_unit, precision)

            # Handle roots that bounce on the x-axis
            if ratio == 1 or ratio == -1:
                pass

            # Handle roots that cross the x-axis
            else:
                alternative_initial = third_constant + periodic_unit - periodic_radians
                generated_elements = generate_elements(alternative_initial, periodic_unit, precision)
                roots.extend(generated_elements)
        
        # Separate numerical roots, string roots, and None results
        separated_roots = separate_elements(roots)
        numerical_roots = separated_roots['numerical']
        other_roots = separated_roots['other']
        
        # Sort numerical roots
        sorted_roots = sorted_list(numerical_roots)

        # Round numerical roots
        rounded_roots = rounded_list(sorted_roots, precision)
        
        # Sort other_roots
        sorted_other_roots = sorted_strings(other_roots)

        # Combine numerical and non-numerical roots
        result.extend(rounded_roots + sorted_other_roots)
    
    # Return result
    return result
예제 #2
0
def shifted_points_within_range(points, minimum, maximum, precision = 4):
    # Handle input errors
    allow_vector_matrix(points, 'first')
    compare_scalars(minimum, maximum, 'second', 'third')
    positive_integer(precision)

    # Grab general points
    general_points = []
    for point in points:
        # Handle coordinate pairs
        if isinstance(point, list):
            if isinstance(point[0], str):
                general_points.append(point[0])
        
        # Handle single coordinates
        else:
            if isinstance(point, str):
                general_points.append(point)
    
    # Generate options for inputs
    optional_points = []
    for point in general_points:
        # Grab initial value and periodic unit
        initial_value_index = point.find(' + ')
        initial_value = float(point[:initial_value_index])
        periodic_unit_index = initial_value_index + 3
        periodic_unit = float(point[periodic_unit_index:-1])
        
        # Increase or decrease initial value to fit into range
        alternative_initial_value = shift_into_range(initial_value, periodic_unit, minimum, maximum)
        
        # Generate additional values within range
        generated_elements = generate_elements(alternative_initial_value, periodic_unit, precision)
        optional_points += generated_elements
    
    # Separate numerical inputs from string inputs
    separated_points = separate_elements(optional_points)
    numerical_points = separated_points['numerical']
    other_points = separated_points['other']

    # Sort numerical inputs
    sorted_points = sorted_list(numerical_points)

    # Reduce numerical inputs to within a given range
    selected_points = [x for x in sorted_points if x >= minimum and x <= maximum]
    
    # Round numerical inputs
    rounded_points = rounded_list(selected_points, precision)
    
    # Sort string inputs
    sorted_other_points = sorted_strings(other_points)
    
    # Combine numerical and string inputs
    result = rounded_points + sorted_other_points
    return result
예제 #3
0
def points_within_range(points, start, end):
    """
    Eliminates all values from a set of points that fall below a lower bound or above an upper bound

    Parameters
    ----------
    points : list of int or float or str
        Set of points to narrow down to only those within a certain range
    start : int or float
        Lower bound of range into which the initial value must be adjusted (final value should be greater than or equal to start)
    end : int or float
        Upper bound of range into which the initial value must be adjusted (final value should be less than or equal to end)

    Raises
    ------
    TypeError
        First argument must be a 1-dimensional list containing elements that are integers, floats, strings, or None
    TypeError
        Second and third arguments must be integers or floats
    ValueError
        Second argument must be less than or equal to third argument

    Returns
    -------
    selected_points : list of int or float or str
        List of all values from original list that fall within specified range; may return a list of None if no points from the original list fall within range or if original list only contained None

    See Also
    --------
    :func:`~regressions.vectors.separate.separate_elements`, :func:`~regressions.statistics.ranges.shift_into_range`, :func:`~regressions.analyses.mean_values.mean_values_derivative`, :func:`~regressions.analyses.mean_values.mean_values_integral`

    Notes
    -----
    - Initial set of points: :math:`p_i = \\{ p_1, p_2, \\cdots, p_n \\}`
    - Lower bound of range: :math:`b_l`
    - Upper bound of range: :math:`b_u`
    - Adjusted set of points within range: :math:`r_i = \\{ r \\mid r \\in p_i, r \\geq b_l, r \\leq b_u \\}`

    Examples
    --------
    Import `points_within_range` function from `regressions` library
        >>> from regressions.analyses.points import points_within_range
    Eliminate all points above 19 or below 6 in the set [1, 5, 6, 7, 18, 20, 50, 127]
        >>> selected_points_int = points_within_range([1, 5, 6, 7, 18, 20, 50, 127], 6, 19)
        >>> print(selected_points_int)
        [6, 7, 18]
    Eliminate all points above 243.7821 or below 198.1735 in the set [542.1234, 237.9109, -129.3214, 199.4321, 129.3214]
        >>> selected_points_float = points_within_range([542.1234, 237.9109, -129.3214, 199.4321, 129.3214], 198.1735, 243.7821)
        >>> print(selected_points_float)
        [237.9109, 199.4321]
    """
    # Handle input errors
    allow_none_vector(points, 'first')
    compare_scalars(start, end, 'second', 'third')

    # Separate numerical results from string results
    separated_results = separate_elements(points)
    numerical_results = separated_results['numerical']
    other_results = separated_results['other']

    # Eliminate numerical results outside of range
    selected_results = [x for x in numerical_results if x >= start and x <= end]

    # Create list to return
    final_results = []

    # Handle no results
    if not selected_results and not other_results:
        final_results.append(None)
    
    # Handle general case
    else:
        final_results.extend(selected_results + other_results)
    
    # Return results
    return final_results
예제 #4
0
 def test_separate_none(self):
     separate_none = separate_elements([None])
     self.assertEqual(separate_none, {'numerical': [], 'other': [None]})
예제 #5
0
 def test_separate_mixed(self):
     separate_mixed = separate_elements(mixed_vector)
     self.assertEqual(separate_mixed, {
         'numerical': [1, 3, 5],
         'other': ['two', 'four']
     })
예제 #6
0
 def test_separate_all_strings(self):
     separate_all_strings = separate_elements(string_vector)
     self.assertEqual(separate_all_strings, {
         'numerical': [],
         'other': string_vector
     })
예제 #7
0
 def test_separate_all_numbers(self):
     separate_all_numbers = separate_elements(first_vector)
     self.assertEqual(separate_all_numbers, {
         'numerical': first_vector,
         'other': []
     })
예제 #8
0
def sinusoidal_roots(first_constant, second_constant, third_constant, fourth_constant, precision = 4):
    """
    Calculates the roots of a sinusoidal function

    Parameters
    ----------
    first_constant : int or float
        Vertical stretch factor of the original sine function; if zero, it will be converted to a small, non-zero decimal value (e.g., 0.0001)
    second_constant : int or float
        Horizontal stretch factor of the original sine function; if zero, it will be converted to a small, non-zero decimal value (e.g., 0.0001)
    third_constant : int or float
        Horizontal shift of the original sine function; if zero, it will be converted to a small, non-zero decimal value (e.g., 0.0001)
    fourth_constant : int or float
        Vertical shift of the original sine function; if zero, it will be converted to a small, non-zero decimal value (e.g., 0.0001)
    precision : int, default=4
        Maximum number of digits that can appear after the decimal place of the resultant roots

    Raises
    ------
    TypeError
        First four arguments must be integers or floats
    ValueError
        Last argument must be a positive integer

    Returns
    -------
    roots : list of float or str
        List of the x-coordinates of the initial x-intercepts within two periods of the original function in float format, along with the general forms in string format that can be used to determine all other x-intercepts by plugging in any integer value for 'k' and evaluating; if the function never crosses the x-axis, then it will return a list of `None`

    See Also
    --------
    :func:`~regressions.analyses.equations.sinusoidal.sinusoidal_equation`, :func:`~regressions.analyses.derivatives.sinusoidal.sinusoidal_derivatives`, :func:`~regressions.analyses.integrals.sinusoidal.sinusoidal_integral`, :func:`~regressions.models.sinusoidal.sinusoidal_model`

    Notes
    -----
    - Standard form of a sinusoidal function: :math:`f(x) = a\\cdot{\\sin(b\\cdot(x - c))} + d`
    - Sinusoidal formula: :math:`x_0 = c + \\frac{1}{b}\\cdot{\\sin^{-1}(-\\frac{d}{a})} + \\frac{2\\pi}{b}\\cdot{k}`

        - :math:`\\text{if} -1 < -\\frac{d}{a} < 0 \\text{ or } 0 < -\\frac{d}{a} < 1, x_1 = c + \\frac{\\pi}{b} - \\frac{1}{b}\\cdot{\\sin^{-1}(-\\frac{d}{a})} + \\frac{2\\pi}{b}\\cdot{k}`
        - :math:`\\text{if} -\\frac{d}{a} = 0, x_1 = c - \\frac{\\pi}{b} + \\frac{2\\pi}{b}\\cdot{k}`
        - :math:`k \\in \\mathbb{Z}`

    Examples
    --------
    Import `sinusoidal_roots` function from `regressions` library
        >>> from regressions.analyses.roots.sinusoidal import sinusoidal_roots
    Calculate the roots of a sinusoidal function with coefficients 2, 3, 5, and 7
        >>> roots_first = sinusoidal_roots(2, 3, 5, 7)
        >>> print(roots_first)
        [None]
    Calculate the roots of a sinusoidal function with coefficients 7, -5, -3, and 2
        >>> roots_second = sinusoidal_roots(7, -5, -3, 2)
        >>> print(roots_second)
        [-8.7128, -7.9686, -7.4562, -6.712, -6.1995, -5.4553, -4.9429, -4.1987, -3.6863, -2.942, '-3.6863 + 1.2566k', '-2.942 + 1.2566k']
    Calculate the roots of a sinusoidal function with all inputs set to 0
        >>> roots_zeroes = sinusoidal_roots(0, 0, 0, 0)
        >>> print(roots_zeroes)
        [-15707.9632, 47123.8899, 109955.743, 172787.596, 235619.4491, '-15707.9632 + 62831.8531k']
    """
    # Handle input errors
    four_scalars(first_constant, second_constant, third_constant, fourth_constant)
    positive_integer(precision)
    coefficients = no_zeroes([first_constant, second_constant, third_constant, fourth_constant], precision)

    # Create list for roots
    roots = []

    # Identify key ratio
    ratio = -1 * coefficients[3] / coefficients[0]

    # Handle no roots
    if ratio > 1 or ratio < -1:
        roots = [None]
    
    # Handle multiple roots
    else:
        # Create intermediary variables
        radians = asin(ratio)
        periodic_radians = radians / coefficients[1]

        # Determine pertinent values
        periodic_unit = 2 * pi / coefficients[1]
        initial_value = coefficients[2] + periodic_radians
        roots = generate_elements(initial_value, periodic_unit, precision)
        
        # Handle roots that bounce on the x-axis
        if ratio == 1 or ratio == -1:
            pass
        
        # Handle roots that cross the x-axis
        else:
            # Determine supplementary values
            alternative_initial_value = coefficients[2] + pi / coefficients[1] - periodic_radians
            generated_elements = generate_elements(alternative_initial_value, periodic_unit, precision)
            
            # Add additional results to roots list
            roots.extend(generated_elements)
    
    # Separate numerical roots, string roots, and None results
    separated_roots = separate_elements(roots)
    numerical_roots = separated_roots['numerical']
    other_roots = separated_roots['other']
    
    # Sort numerical roots
    sorted_roots = sorted_list(numerical_roots)

    # Round numerical roots
    rounded_roots = rounded_list(sorted_roots, precision)
    
    # Sort other_roots
    sorted_other_roots = sorted_strings(other_roots)

    # Combine numerical and non-numerical roots
    result = rounded_roots + sorted_other_roots
    return result
예제 #9
0
def sign_chart(equation_type, coefficients, derivative_level, precision=4):
    """
    Creates a sign chart for a given derivative

    Parameters
    ----------
    equation_type : str
        Name of the type of function for which the sign chart must be constructed (e.g., 'linear', 'quadratic')
    coefficients : list of int or float
        Coefficients to use to generate the equation to investigate
    derivative_level : int
        Integer corresponding to which derivative to investigate for sign chart (1 for the first derivative and 2 for the second derivative)
    precision : int, default=4
        Maximum number of digits that can appear after the decimal place of the results

    Raises
    ------
    ValueError
        First argument must be either 'linear', 'quadratic', 'cubic', 'hyperbolic', 'exponential', 'logarithmic', 'logistic', or 'sinusoidal'
    TypeError
        Second argument must be a 1-dimensional list containing elements that are integers or floats
    ValueError
        Third argument must be one of the following integers: [1, 2]
    ValueError
        Last argument must be a positive integer

    Returns
    -------
    chart : list of str and float
        Strings describing the sign (e.g., 'positive', 'negative') of the derivative between its critical points; as a result, its elements will alternate between strings (indicating the signs) and floats (indicating the end points); if the function is sinusoidal, then only the initial results within a two-period interval will be listed, but a general form to determine other end points will also be included

    See Also
    --------
    - Roots for key functions: :func:`~regressions.analyses.roots.linear.linear_roots`, :func:`~regressions.analyses.roots.quadratic.quadratic_roots`, :func:`~regressions.analyses.roots.cubic.cubic_roots`, :func:`~regressions.analyses.roots.hyperbolic.hyperbolic_roots`, :func:`~regressions.analyses.roots.exponential.exponential_roots`, :func:`~regressions.analyses.roots.logarithmic.logarithmic_roots`, :func:`~regressions.analyses.roots.logistic.logistic_roots`, :func:`~regressions.analyses.roots.sinusoidal.sinusoidal_roots`
    - Graphical analysis: :func:`~regressions.analyses.criticals.critical_points`, :func:`~regressions.analyses.points.key_coordinates`

    Notes
    -----
    - Critical points for the derivative of a function: :math:`c_i = \\{ c_1, c_2, c_3,  \\cdots, c_{n-1}, c_n \\}`
    - Midpoints and key values of the intervals demarcated by the critical points: :math:`m_i = \\{ c_1 - 1,  \\frac{c_1+c_2}{2}, \\frac{c_2+c_3}{2}, \\cdots, \\frac{c_{n-1}+c_n}{2}, c_n + 1 \\}`
    - Values of the derivative within the intervals: :math:`v_i = \\{ v_1, v_2, v_3, v_4 \\cdots, v_{n-1}, v_n, v_{n+1} \\}`
    - Sign chart: :math:`s = ( v_1, c_1, v_2, c_2, v_3, c_3, v_4, \\dots, v_{n-1}, c_{n-1}, v_n, c_n, v_{n+1} )`

        - :math:`v_j = negative` if :math:`f'(m_j) < 0`
        - :math:`v_j = constant` if :math:`f'(m_j) = 0`
        - :math:`v_j = positve` if :math:`f'(m_j) > 0`

    - |intervals|

    Examples
    --------
    Import `sign_chart` function from `regressions` library
        >>> from regressions.analyses.intervals import sign_chart
    Create the sign chart for the first derivative of a cubic function with coefficients 1, -15, 63, and -7
        >>> chart_cubic = sign_chart('cubic', [1, -15, 63, -7], 1)
        >>> print(chart_cubic)
        ['positive', 3.0, 'negative', 7.0, 'positive']
    Create the sign chart for the second derivative of a sinusoidal function with coefficients 2, 3, 5, and 7
        >>> chart_sinusoidal = sign_chart('sinusoidal', [2, 3, 5, 7], 2)
        >>> print(chart_sinusoidal)
        ['positive', 5.0, 'negative', 6.0472, 'positive', 7.0944, 'negative', 8.1416, 'positive', 9.1888, 'negative', '5.0 + 1.0472k']
    """
    # Handle input errors
    select_equations(equation_type)
    vector_of_scalars(coefficients, 'second')
    select_integers(derivative_level, [1, 2], 'third')
    positive_integer(precision)

    # Create first and second derivatives based on equation type
    both_derivatives = {}
    if equation_type == 'linear':
        both_derivatives = linear_derivatives(*coefficients, precision)
    elif equation_type == 'quadratic':
        both_derivatives = quadratic_derivatives(*coefficients, precision)
    elif equation_type == 'cubic':
        both_derivatives = cubic_derivatives(*coefficients, precision)
    elif equation_type == 'hyperbolic':
        both_derivatives = hyperbolic_derivatives(*coefficients, precision)
    elif equation_type == 'exponential':
        both_derivatives = exponential_derivatives(*coefficients, precision)
    elif equation_type == 'logarithmic':
        both_derivatives = logarithmic_derivatives(*coefficients, precision)
    elif equation_type == 'logistic':
        both_derivatives = logistic_derivatives(*coefficients, precision)
    elif equation_type == 'sinusoidal':
        both_derivatives = sinusoidal_derivatives(*coefficients, precision)

    # Grab specific derivative evaluation based on derivative level
    derivative = lambda x: x
    if derivative_level == 1:
        derivative = both_derivatives['first']['evaluation']
    elif derivative_level == 2:
        derivative = both_derivatives['second']['evaluation']

    # Create critical points for specific derivative
    points = critical_points(equation_type, coefficients, derivative_level,
                             precision)

    # Create sign chart for specific derivative
    result = []

    # Handle no critical points
    if points[0] == None:
        # Test an arbitrary number
        if derivative(10) > 0:
            result = ['positive']
        elif derivative(10) < 0:
            result = ['negative']
        else:
            result = ['constant']

    # Handle exactly one critical point
    elif len(points) == 1:
        # Generate numbers to test
        turning_point = points[0]
        before = turning_point - 1
        after = turning_point + 1

        # Test numbers
        if derivative(before) > 0:
            before = 'positive'
        elif derivative(before) < 0:
            before = 'negative'
        if derivative(after) > 0:
            after = 'positive'
        elif derivative(after) < 0:
            after = 'negative'

        # Store sign chart
        result = [before, turning_point, after]

    # Handle exactly two critical points
    elif len(points) == 2:
        # Generate numbers to test
        sorted_points = sorted_list(points)
        first_point = sorted_points[0]
        second_point = sorted_points[1]
        middle = (first_point + second_point) / 2
        before = first_point - 1
        after = second_point + 1

        # Test numbers
        if derivative(before) > 0:
            before = 'positive'
        elif derivative(before) < 0:
            before = 'negative'
        if derivative(middle) > 0:
            middle = 'positive'
        elif derivative(middle) < 0:
            middle = 'negative'
        if derivative(after) > 0:
            after = 'positive'
        elif derivative(after) < 0:
            after = 'negative'

        # Store sign chart
        result = [before, first_point, middle, second_point, after]

    # Handle more than two critical points
    else:
        # Separate numerical inputs from string inputs
        separated_points = separate_elements(points)
        numerical_points = separated_points['numerical']
        other_points = separated_points['other']

        # Generate numbers to test
        sorted_points = sorted_list(numerical_points)
        difference = sorted_points[1] - sorted_points[0]
        halved_difference = difference / 2
        before_first = sorted_points[0] - halved_difference
        between_first_second = sorted_points[0] + halved_difference
        between_second_third = sorted_points[1] + halved_difference
        between_third_fourth = sorted_points[2] + halved_difference
        between_fourth_last = sorted_points[3] + halved_difference
        after_last = sorted_points[4] + halved_difference
        test_points = [
            before_first, between_first_second, between_second_third,
            between_third_fourth, between_fourth_last, after_last
        ]

        # Test numbers
        signs = []
        for point in test_points:
            if derivative(point) > 0:
                signs.append('positive')
            elif derivative(point) < 0:
                signs.append('negative')

        # Store sign chart
        result = [
            signs[0], sorted_points[0], signs[1], sorted_points[1], signs[2],
            sorted_points[2], signs[3], sorted_points[3], signs[4],
            sorted_points[4], signs[5], *other_points
        ]
    return result