0

here is my problem:

_I have some physical data representing an angle between -90 and 90 degrees. There is a known error associated with this data. I'm working in python3 with numpy and matplotlib.

_I want to plot the data with its error bars for each measurments. The angles range from -90 to 90, and the errors should not go out of these bounds. For example, for an angle of 85+/-10 degrees, I want the upper error bar to cycle back to -85 instead of going to 95.

_Is it possible? How? I'm trying to use $plt.fill_between()$ or $plt.errorbar()$, but it does not work. In the exemple above, even if I try to force the error bar to -85, the error does not cycle through 90...

Here are some examples:

import matplotlib.pyplot as plt
import numpy as np

t = np.arange(10) #time
a = np.linspace(50, 89, 10) #fake angle value
e = np.array([10]*10) #error value
a_up  = a + e #Upper error bars
a_low = a - e #Lower error bars

f, ax = plt.subplots(nrows=2, ncols=2)

###Simple error graph, I don't want it because error bars outside of [-90, 90]
ax[0, 0].errorbar(t, a, yerr = e) #Plot the errors as error bars

### Same but with shaded area
ax[0, 1].fill_between(t, a_low, a_up) #Plot the errors as filled region
ax[0, 1].plot(t, a, "*r")

###My best option right now, put an upper limit everywhere
for i, u in enumerate(a_up):
    if u > 90:
        a_up[i] = 90

ax[1, 0].fill_between(t, a_low, a_up) #Plot the errors
ax[1, 0].plot(t, a, "*r")

###Finally, force all errorbars in [-90, 90] (Just for this exemple, it's generalized in my code)
for i, u in enumerate(a_up):
    if u >= 90:
        a_up[i] -= 90

ax[1, 1].fill_between(t, a_low, a_up) #Plot the errors
ax[1, 1].plot(t, a, "*r")

plt.show()


I hope I'm clear enough, I can't find a solution on the web... Maybe I don't know how to formulate it.

Thanks in advance for your help, after 10 years of using your answers, I finally got the oppurtunity to ask one! :)

Léo

Jongware
  • 22,200
  • 8
  • 54
  • 100
Azireo
  • 145
  • 1
  • 5
  • 1
    Hi Azireo, welcome to Stack overflow. Can you post a minimal example of what you tried so far? – SKPS Jan 24 '20 at 18:36
  • Not really an answer, but related: [how-to-plot-error-bars-in-polar-coordinates-in-python](https://stackoverflow.com/questions/26583620/how-to-plot-error-bars-in-polar-coordinates-in-python) – JohanC Jan 24 '20 at 19:46
  • Thanks, I added some examples. It is a time serie measured along with other data that I want to compare, I can not plot it as a polar graph... – Azireo Jan 24 '20 at 19:50

2 Answers2

1

From David answer, I changed a few things, to make it more general:

import matplotlib.pyplot as plt

angles = [30, 40, 50, -60, 10, 85, -85, 72, 2, 35]
errors = [2, 10, 10, 40, 4, 30, 30, 10, 12, 4]

x = [i for i, j in enumerate(angles)]


ls = dict()

for i, error, angle in zip(x, errors, angles):
    if angle > 0 and angle + error > 90:
        temp = angle + error - 180
        ls.update({i:[-90, temp]})
    if angle < 0 and angle - error < -90:
        temp = angle - error + 180
        ls.update({i:[90, temp]})


plt.figure()
plt.ylim(-90, 90)
plt.errorbar(x, angles, yerr=errors, fmt='C0 ', marker='o')
# plt.errorbar(list(ls.keys()), [-90, 90], yerr=list(ls.values()), fmt='C0 ')
for i, a in ls.items():
    plt.vlines(i, a[0], a[1], colors='C0')

plt.ylabel('Angle')
plt.xlabel('Time (s)')

plt.show()

It might not be optimised, but it works well for me :) Tanks again!

Azireo
  • 145
  • 1
  • 5
0

There is not an easy way to do that:

import matplotlib.pyplot as plt

angles = [30, 40, 50, -60, 10, 85, -85, 72, 2, 35]
errors = [2, 10, 10, 20, 4, 30, 30, 10, 12, 4]

x = [i for i, j in enumerate(angles)]


ls = dict()

for i, error, angle in zip(x, errors, angles):

    if angle > 0 and abs(angle) + error > 90:
        temp = angle + error - 90
        ls.update({i:temp})

    if angle < 0 and abs(angle) + error > 90:
        temp = abs(angle) + error - 90
        ls.update({i:temp})


plt.figure()
plt.ylim(-90, 90)
plt.errorbar(x, angles, yerr=errors, fmt='C0 ', marker='o')
# plt.errorbar(list(ls.keys()), [-90, 90], yerr=list(ls.values()), fmt='C0 ')
plt.vlines(list(ls.keys())[0], list(ls.values())[0] - 90, -90, colors='C0')
plt.vlines(list(ls.keys())[1], 90 - list(ls.values())[1], 90, colors='C0')
plt.ylabel('Angle')
plt.xlabel('Time (s)')

plt.show()

enter image description here

The loop is to find those angles whose errors exceed the 90 degrees limit. With that, you have two options:

# plt.errorbar(list(ls.keys()), [-90, 90], yerr=list(ls.values()), fmt='C0 ')

or

plt.vlines(list(ls.keys())[0], list(ls.values())[0] - 90, -90, colors='C0')
plt.vlines(list(ls.keys())[1], 90 - list(ls.values())[1], 90, colors='C0')

which means that you have to add vertical lines manually, you could write a function to do that, but this is the first attempt. By the way, as you may see, the visualization is not good.

David
  • 435
  • 3
  • 12
  • Thanks a lot! That's what I'm looking for :) I was hoping for some kind of magical function hidden somewhere, but I guess that if you want something done right, you have to do it yourself! (or ask stackoverflow...) – Azireo Jan 27 '20 at 08:58