0

I was trying to plot a function with absolute values and it's piecewise counterpart, but matplotlib was ploting the function with jagged corners.

Here is my full code:

import matplotlib.pyplot as plt
import numpy as np

plt.style.use("dark_background")

def f(x):
    return abs(2 * x - 3) + abs(3 * x + 1) - abs(5 * x - 3)

def g(x):
    if x < -1/3:
        return -1
    if -1/3 < x < 3/5:
        return 6 * x + 1
    if 3/5 < x < 3/2:
        return -4 * x + 7
    if x > 3/2:
        return 1

gf = np.vectorize(g)
x = np.linspace(-1, 2, 300)

plt.figure(figsize = (10, 4))

plt.subplot(1, 2, 1)
plt.plot(x, f(x), color = "blue")

plt.subplot(1, 2, 2)
plt.plot(x, gf(x), color = "red")

plt.show()

And here is the plot that was generated: Plot of the functions

The solution to this problem I've found was to add a small of x to the constant output:

def g(x):
    if x < -1/3:
        return -1 + 0.000001 * x
    if -1/3 < x < 3/5:
        return 6 * x + 1
    if 3/5 < x < 3/2:
        return -4 * x + 7
    if x > 3/2:
        return 1 + 0.000001 * x

This solved my problem, but I still don't know why it works, or what's wrong with the first plot I've made.

Fixed jagged plot of piecewise function

If anyone knows a simpler solution or the source of the problem, let me know.

2 Answers 2

2

This is caused by np.vectorize. Instead, just use np.piecewise.

def g(x):
    return np.piecewise(x,
                        [x < -1/3, 
                         (-1/3 < x) & (x < 3/5), 
                         (3/5 < x) & (x < 3/2), 
                         (x > 3/2)],
                        [lambda x: -np.ones_like(x), 
                         lambda x: 6*x + 1, 
                         lambda x: -4*x + 7, 
                         lambda x: np.ones_like(x)])

You can then just do g(x) for plotting.


As for why np.vectorize is causing a problem here, it's because it infers the output type from the first input and it's incorrectly infering an integer output (if you check gf(x).dtype you'll see it is a vector of integers). If you feel strongly about using np.vectorize, make sure to specify the output type by doing gf = np.vectorize(g, otypes=[float]).

Sign up to request clarification or add additional context in comments.

Comments

0

This may not exactly be the answer, but converting the values returned by g to floats also seems to work:

import matplotlib.pyplot as plt
import numpy as np

plt.style.use("dark_background")

def f(x):
    return abs(2 * x - 3) + abs(3 * x + 1) - abs(5 * x - 3)

def g(x):
    if x < -1/3:
        return -1.0  # note the additional decimal place
    if -1/3 < x < 3/5:
        return float(6 * x + 1)
    if 3/5 < x < 3/2:
        return float(-4 * x + 7)
    if x > 3/2:
        return 1.0  # note the additional decimal place

gf = np.vectorize(g)
x = np.linspace(-1, 2, 300)

plt.figure(figsize = (10, 4))

plt.subplot(1, 2, 1)
plt.plot(x, f(x), color = "blue")

plt.subplot(1, 2, 2)
plt.plot(x, gf(x), color = "red")

plt.show()

That said, there may be a more elegant solution...I'm just not sure what it is offhand.

FWIW: your approach (i.e. adding 0.000001 * x to each value) also works because adding that small decimal value forces the return values of g into float; it's necessarily less accurate than directly using float(), but that's why it still mostly does what you expect!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.