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
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
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
def test_separate_none(self): separate_none = separate_elements([None]) self.assertEqual(separate_none, {'numerical': [], 'other': [None]})
def test_separate_mixed(self): separate_mixed = separate_elements(mixed_vector) self.assertEqual(separate_mixed, { 'numerical': [1, 3, 5], 'other': ['two', 'four'] })
def test_separate_all_strings(self): separate_all_strings = separate_elements(string_vector) self.assertEqual(separate_all_strings, { 'numerical': [], 'other': string_vector })
def test_separate_all_numbers(self): separate_all_numbers = separate_elements(first_vector) self.assertEqual(separate_all_numbers, { 'numerical': first_vector, 'other': [] })
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
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