2
import matplotlib.pyplot as plt
import numpy as np

def arc():
    x = np.arange((-np.pi)/4, (np.pi)/4, 0.001)

    f1 = lambda x: 3 * (np.cos(2 * x) )**0.5
    plt.vlines(0, 0, 5)
    plt.plot(x, f1(x), label = '$r=3\sqrt{\cos{2\phi}}$')

    plt.xlabel('$\phi$')
    plt.ylabel('$r(\phi)$')
    plt.legend(loc='best')


    axes = plt.gca()
    axes.set_xlim([-np.pi, np.pi])
    axes.set_ylim([0, 5])
    plt.show()

arc()

enter image description here

I don't understand why it doesn't go all the way to the \phi axis on the right side. What can I do to fix this?

AlvinL
  • 438
  • 5
  • 24

3 Answers3

6

The best way to get what you want is to use linspace. To get a vector x that is almost the same as in your example, you should do

x = np.linspace(-np.pi/4, np.pi/4, round(np.pi / 2 / 0.001) + 1)

or simply

x = np.linspace(-np.pi/4, np.pi/4, npoints)

This gives you exactly npoints points, and the start and stop values are guaranteed to be included.

Using np.arange(start, stop, step) gives you the points start, start+step, start+2*step, ... until the final point for which start+n*step < stop. Due to floating point inaccuracies, it is not obvious what is the n of the final point. The manual of arange remarks:

When using a non-integer step, such as 0.1, the results will often not be consistent. It is better to use linspace for these cases.

It is easy to demonstrate that this leads to unpredictable behavior by generating some aranges between 0 and 1 with variable step size:

In [21]: for i in range(97,109):
    ...:     r = np.arange(0, 1, 1. / i)
    ...:     print 'step = 1.0 / {}, length = {}, last value = {}'.format(
    ...:         i, len(r), r[-1])
    ...:     
step = 1.0 / 97, length = 97, last value = 0.989690721649
step = 1.0 / 98, length = 99, last value = 1.0
step = 1.0 / 99, length = 99, last value = 0.989898989899
step = 1.0 / 100, length = 100, last value = 0.99
step = 1.0 / 101, length = 101, last value = 0.990099009901
step = 1.0 / 102, length = 102, last value = 0.990196078431
step = 1.0 / 103, length = 104, last value = 1.0
step = 1.0 / 104, length = 104, last value = 0.990384615385
step = 1.0 / 105, length = 105, last value = 0.990476190476
step = 1.0 / 106, length = 106, last value = 0.990566037736
step = 1.0 / 107, length = 108, last value = 1.0
step = 1.0 / 108, length = 108, last value = 0.990740740741

See also this answer to a related question.

Community
  • 1
  • 1
Bas Swinckels
  • 18,095
  • 3
  • 45
  • 62
1

The reason the graph doesn't go all the way to zero on the right is because arange starts at start (the first argument) and adds step (the third argument) until it reaches end (the second argument. However, end is not added at the end. What you should do is this:

x = np.concatenate((np.arange(-np.pi/4, np.pi/4, 0.001), [np.pi/4]))

or use the shorthand np.r_:

x = np.r_[np.arange(-np.pi/4, np.pi/4, 0.001), np.pi/4]
tennabey
  • 295
  • 3
  • 16
  • 1
    This solution might work for this particular problem, but it might give unexpected results in the general case. As my example showed, the final value of `arange` can become very close to the stop value, for example `np.arange(0, 1, 1.0/98)[-1] - 1 == -1.11e-16`, so by simply concatenating the final value, the final 2 points would be *very* close. – Bas Swinckels May 10 '15 at 20:37
1

I changed x = np.arange((-np.pi)/4, (np.pi)/4, 0.001)

to:

x = np.arange((-np.pi)/4, (np.pi)/4, 0.00001)

and I got A closer to perfect plot

Essentially, just change the step to get closer to the end of the curve. However, this is more of an example of what not to do (my guess turned out to be erroneous). As @Bas Swinckels' suggestion is even better:

x = np.linspace(-np.pi/4, np.pi/4, npoints)

It entirely gets rid of the need to change the last argument to a value closer to zero, which only makes the gap smaller (It doesn't get rid of the gap). The linspace is better for floating point inaccuracies.

Community
  • 1
  • 1
IronManMark20
  • 1,298
  • 12
  • 28