Search code examples
pythonnumpyplotlabelpolynomials

Numpy.root is returning the correctly plotted x-intercept but labeling it incorrectly in Python-in-Excel


I am creating programs which produce worksheets for students. In this particular program student must sketch a polynomial given in factored form on a blank grid.

  1. Excel generates random roots on a limited interval.
  2. Excel then calculates the coefficients for the expanded (unfactored) polynomial.
  3. A Python script in an adjacent cell then takes the coefficients and plots a graph of the polynomial, with x-intercepts plotted and labeled, for the worksheet key.

My problem is that some x-intercepts are plotted and labeled correctly and then one or more x-intercepts are plotted correctly but labeled incorrectly. Here is a screen cap with annotation:enter image description here

I have confirmed with a graphing calculator that the coefficients are correct. They do produce the expected roots. I ran the Python script in Spyder and it worked as expected with all roots correctly labeled. Here is the Python script which is inserted in the 3rd spreadsheet cell from the left:

import numpy as np
import matplotlib.pyplot as plt

a = xl("K2")
b = xl("L2")
c = xl("M2")
d = xl("N2")
e = xl("O2")
r1 = xl("F2")
r2 = xl("G2")
r3 = xl("H2")
r4 = xl("I2")
r5 = xl("J2")

# Generate x values
x = np.linspace(r1-1, r3+1, 1000)

# Calculate y values for the polynomial
y = a*x**3 + b*x**2 + c*x + d
def polynomial(x):
    return a*x**3 + b*x**2 + c*x + d
    
# Find x-intercepts using numpy's roots function
coefficients = [a, b, c, d]  # coefficients of expanded polynomial
x_intercepts = np.roots(coefficients)

# Create the plot
plt.figure(figsize=(3,3), dpi = 400)
plt.plot(x, y, 'b-')

# Plot x-intercepts and add labels
for x_int in x_intercepts:
    plt.plot(x_int, polynomial(x_int), 'ro', zorder = 20)  # 'ro' for red circle markers
    plt.text(x_int, 20, f'{int(x_int)}', color = "red", fontsize=9, ha = "center")

# Customize the plot
plt.axhline(y=0, color='black', linestyle='-', alpha=1)  # x-axis
plt.axvline(x=0, color='black', linestyle='-', alpha=1)  # y-axis
plt.xticks([])
plt.yticks([])
plt.xlabel('x')
plt.ylabel('y')
plt.show()

Solution

  • I added the following line to your for loop to see what's going on.

    print(x_int, f"{int(x_int)}")
    

    The output is as follows:

    -7.000000000000009 -7
    5.000000000000005 5
    0.9999999999999997 0
    

    The int function takes a value and chops off the values after the decimal. Because the last term isn't exactly 1 (it's just below 1), int turns it into 0. If you know that you will always have integer roots, you should instead use the round function, i.e. your equivalent line should be as follows:

    plt.text(x_int, 20, round(x_int), color="red", fontsize=9, ha="center")
    

    Resulting plot: