0
\$\begingroup\$

Program description:

You are given a set of two functions: $$f=x^3-6x^2+x+5; g=(x-2)^2-6$$ Plot them using Matprolib on a user input segment [a; b].

My solution:

import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import interpolate # for smoothing

def func(x):
    return [pow(x, 3) - 6 * pow(x, 2) + x + 5, pow((x-2), 2) - 6]
# plt.plot([1,5, -3, 0.5], [1, 25, 9, 0.5])
# plt.plot(1, 7, "r+")
# plt.plot(-1, 7, "bo")
f = []
f_1 = []
x = []
interval = [int(x) for x in input("Define segment (i.e. a b): ").split(' ')]
for val in range(interval[0], interval[1] + 1):
    x.append(val)
    f.append(func(val)[0])
    f_1.append(func(val)[1])

linear_space = np.linspace(interval[0], interval[1], 300)
a_BSpline = interpolate.make_interp_spline(x, f)
b_BSpline = interpolate.make_interp_spline(x, f_1)

f_new = a_BSpline(linear_space)
f_1_new = b_BSpline(linear_space)

plt.plot(linear_space, f_new, color="#47c984",
linestyle="solid", linewidth=1)

plt.plot(linear_space, f_1_new, color="#fc6703",
linestyle="solid", linewidth=1)

plt.gca().spines["left"].set_position("zero")
plt.gca().spines["bottom"].set_position("zero")
plt.show()

Input: -10 10

Output:

1

Question: Is there any way to make this code more concise?

Thank you in advance.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Why do you need interpolate.make_interp_spline? If it is required, please update the problem statement and title to include the corresponding logic. \$\endgroup\$ – GZ0 Dec 5 '20 at 20:50
  • \$\begingroup\$ @GZ0 when importing interpolate I've intentionally left a comment that it is going to be used for smoothing, thank you for the tip though, I've updated the title accordingly, \$\endgroup\$ – JoshJohnson Dec 5 '20 at 21:03
  • \$\begingroup\$ The logic does not sound right to me. The problem begins with two polynominal functions, which are smooth, rather than piecewise functions which are non-smooth at interval boundaries. Why is it necessary to compute another two sets of smoothing coefficients? \$\endgroup\$ – GZ0 Dec 5 '20 at 22:28
  • \$\begingroup\$ @GZ0 interpolation is used for style purposes only, otherwise functions' graphs look rough. Interval boudaries are given on user input, I don't quite get you here. The problem is simple, just plot two functions. I've provided necessary input and output in my question. \$\endgroup\$ – JoshJohnson Dec 5 '20 at 22:34
  • \$\begingroup\$ @GZ0 sorry, you are right, I have left the part about custom segment out, I've edited the question \$\endgroup\$ – JoshJohnson Dec 5 '20 at 22:37
2
\$\begingroup\$
  • As I pointed out in the comments, I do not think smoothing is needed when plotting functions that are already smooth, such as polynominals. In those cases, the graphs would look rough only if the points plotted are not dense enough (on the x axis).

  • Since you have already imported and used numpy, it would be better to use numpy.polyval for vectorized evaluation of polynominals.

  • Drawing spines at zero would not work well if the input range does not include zero. In that case, the "center" position might be used instead of "zero". (I will not implement this logic in my code below)

  • The top and right spines should not be shown.

  • Consider using plt.style.use for setting common style information. See here for alternative options for style setting.

  • Avoid constants like "#47c984". Name them with appropriate constant names for better readability.

  • It is a good practice to comply to the PEP 8 style when writing Python code. Use an IDE (such as PyCharm) or an online checker (such as this) for automatic PEP 8 violation checking.

  • When running code outside a method / class, it is a good practice to put the code inside a main guard. See here for more explanation.

Here is an improved version:

import numpy as np
import matplotlib.pyplot as plt

if __name__ == "__main__":
    # Define constants (color names come from https://colornamer.robertcooper.me/)
    INPUT_MESSAGE = "Lower and upper bounds of x, separated by a space: "
    NUM_POINTS = 300
    PARIS_GREEN = "#47c984"
    BLAZE_ORANGE = "#fc6703"

    # Read input
    lb, ub = map(float, input(INPUT_MESSAGE).split())

    # Compute coordinates of points to be plotted
    x = np.linspace(lb, ub, NUM_POINTS)
    f_x = np.polyval([1, -6, 1, 5], x)
    g_x = np.square(x - 2) - 6

    # Plotting
    plt.style.use({"lines.linestyle": "solid", "lines.linewidth": 1})
    plt.plot(x, f_x, color=PARIS_GREEN)
    plt.plot(x, g_x, color=BLAZE_ORANGE)

    # Adjust spines
    spines = plt.gca().spines
    spines["left"].set_position("zero")
    spines["bottom"].set_position("zero")
    spines["right"].set_visible(False)
    spines["top"].set_visible(False)

    # Display plot
    plt.show()
\$\endgroup\$
8
  • \$\begingroup\$ Thank you for detailed answer. Why don't you consider spines to be const? \$\endgroup\$ – JoshJohnson Dec 6 '20 at 8:09
  • \$\begingroup\$ Using a constant would be the same as using 0. The coordinates have to vary based on the input range. "center" is just a convenient way to achieve that. \$\endgroup\$ – GZ0 Dec 6 '20 at 8:21
  • \$\begingroup\$ Makes sense, thank you again. \$\endgroup\$ – JoshJohnson Dec 6 '20 at 8:24
  • \$\begingroup\$ By the way, @GZ0, g(x) is not displayed correctly, it's displayed as a plain line. \$\endgroup\$ – JoshJohnson Dec 6 '20 at 8:35
  • \$\begingroup\$ Its should be displayed like this: yotx.ru/#!1/3_h/ubWwf7Wwf7Rgzhf23/aP9g/2DfT0qt7W/… \$\endgroup\$ – JoshJohnson Dec 6 '20 at 8:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.