Milky Fresh
Milky Fresh

Reputation: 29

How to plot a triangular wave using a piecewise function in Python?

I am currently taking my first Python coding course and I am very stuck on this assignment. The goal is to define a function 'F(t)' using a piecewise function that would make a graph of a triangle wave that looks identical to:

enter image description here

This is what I have tried:

def F(t):
 if (t >= 0) & (t <= 1/2):
  return 4*t
elif (t >= 1/2) & (t < 1):
 return -4*t
else:
 return 0

f = np.vectorize(F)
t = np.linspace(0,4,num=8)
plt.plot(t,f(t))

Obviously, to no luck. The above code matches the ideal graph from 0 to 1 so I'm really just struggling with how to make the graph a continuous triangle wave for all t > 0.

Any tips or suggestions are much appreciated

Upvotes: 1

Views: 881

Answers (2)

pho
pho

Reputation: 25489

Your function is wrong. If you want the troughs to be at odd multiples of 0.5 and the peaks to be at even multiples of 0.5, and you want the value to be 1 and -1, your function needs to be -4 * t + 1 between 0 and 0.5, and 4 * t - 3 between 0.5 and 1.

Also, your function returns 0 for t < 0 and for t > 1, so your output doesn't match the expected output outside those bounds.

Since your if conditions describe the correct behavior when you have only the part of the input after the decimal point, you can extract that part and use it instead of t:

def F(t):
    decimal_part = t % 1
    if 0 <= decimal_part <= 0.5:
        return -4*decimal_part + 1
    else:
        return 4*decimal_part - 3

Which gives this:

Expected plot

Remember to discretize the x-axis so it has enough points. I plotted the blue line with t = np.linspace(0, 4, 1000), and the red line with t = np.linspace(0, 4, 10) and everything else remained the same.

The np.vectorize() function doesn't really vectorize a function though. If you want to actually see speed improvements, you're going to have to vectorize the function yourself by expressing its logic as a vector operation.

def g(t):
    dec = (t % 1) - 0.5
    return (np.abs(dec) - 0.25) * 4

Both give the same plots, but timing the execution shows the difference:

import timeit

t1 = timeit.timeit('f(t)', setup='from __main__ import np, f, t', number=10000)
t2 = timeit.timeit('g(t)', setup='from __main__ import np, g, t', number=10000)

# t1 = 3.322270042001037
# t2 = 0.17450489799375646

print(t1 / t2)
# 19.038262422410074

The actually vectorized function is ~19x faster on my computer (Intel Python 3.7 with numpy+MKL on Macbook Air)

Upvotes: 2

furas
furas

Reputation: 142651

First you could use print() to see what you have in variables.

In F() you get t like 0.5714285714285714, 1.1428571428571428 but if you would use num=9 instead of num=8 then you would get exactly 0.5, 1.0 and it would be much easier to draw it.

And using print() to display results for different combinations I created

def F(t):
    return 1 - ((t*2) % 2)

Which can be reduced to

def F(t):
    return 1 - (t % 1)

How I get it:

You have y in range (-1, 1) which gives length 2 so I started with y = t % 2 to get values in range (0, 2) which also gives length 2. Because for 0 I got 0 and I needed 1 so next I used 1 - ... to get a "mirror image". So I get y=1 for x=0 but next top was for y=2 but I needed for y=1 so I had to use *2 to get it. And finally I released that (t*2) % 2 can be (t % 2) * (2 % 2) so it gives (t % 2) * 1 and this gives (t % 2)


Minimal working code

import numpy as np
import matplotlib.pyplot as plt

def F(t):
    #y = t % 2
    #y = 1 - (t % 2)
    #y = 1 - ((t*2) % 2)
    y = 1 - (t % 1)
    print(t, y)
    return y

f = np.vectorize(F)
t = np.linspace(0, 4, num=9)
plt.plot(t, f(t))
plt.show()

enter image description here

Upvotes: 0

Related Questions