3

I am trying to get a 3D barplot with error bars. I am open to use matplotlib, seaborn or any other python library or tool

Searching in SO I found 3D bar graphs can be done by drawing several 2D plots (here for example). This is my code:

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D


dades01 = [54,43,24,104,32,63,57,14,32,12]
dades02 = [35,23,14,54,24,33,43,55,23,11]
dades03 = [12,65,24,32,13,54,23,32,12,43]

df_3d = pd.DataFrame([dades01, dades02, dades03]).transpose()
colors = ['r','b','g','y','b','p']


fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
z= list(df_3d)
for n, i in enumerate(df_3d):
    print 'n',n
    xs = np.arange(len(df_3d[i]))
    ys = [i for i in df_3d[i]]
    zs = z[n]

    cs = colors[n]
    print ' xs:', xs,'ys:', ys, 'zs',zs, ' cs: ',cs
    ax.bar(xs, ys, zs, zdir='y', color=cs, alpha=0.8)


ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')

plt.show()

I get the 3D 'ish' plot.

3D bar plot without error bars

My question is: How do I add error bars?

To make it easy, lets try to add the same error bars to all the plots:

yerr=[10,10,10,10,10,10,10,10,10,10]

If I add my error bars in each '2D' plot:

ax.bar(xs, ys, zs, zdir='y', color=cs,yerr=[10,10,10,10,10,10,10,10,10,10], alpha=0.8)

Doesn't work:

AttributeError: 'LineCollection' object has no attribute 'do_3d_projection'

I have also tried to add:

#ax.errorbar(xs, ys, zs, yerr=[10,10,10,10,10,10,10,10,10,10], ls = 'none')

But again an error:

TypeError: errorbar() got multiple values for keyword argument 'yerr'

Any idea how I could get 3D plot bars with error bars?

2 Answers 2

5

There is no direct way to the best of my knowledge to do it in 3d. However, you can create a workaround solution as shown below. The solution is inspired from here. The trick here is to pass two points lying vertically and then use _ as the marker to act as the error bar cap.

yerr=np.array([10,10,10,10,10,10,10,10,10,10])

for n, i in enumerate(df_3d):
    xs = np.arange(len(df_3d[i]))
    ys = [i for i in df_3d[i]]
    zs = z[n]
    cs = colors[n]
    ax.bar(xs, ys, zs, zdir='y', color=cs, alpha=0.8)
    for i, j in enumerate(ys):
        ax.plot([xs[i], xs[i]], [zs, zs], [j+yerr[i], j-yerr[i]], marker="_", color=cs)

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')

enter image description here

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

1 Comment

Great solution! Just what I asked for.
2

First of all, don't use a 3D plot when a 2D plot would suffice, which in this case, it would. Using 3D plots for 2D data unnecessarily obfuscates things.

Second, you can use a combination of a MultiIndex pandas dataframe to get your desired result:

df = pd.DataFrame({
    'a': list(range(5))*3,
    'b': [1, 2, 3]*5,
    'c': np.random.randint(low=0, high=10, size=15)
}).set_index(['a', 'b'])

fig, ax = plt.subplots(figsize=(10,6))

y_errs = np.random.random(size=(3, 5))
df.unstack().plot.bar(ax=ax, yerr=y_errs)

This produces a plot like the following:

enter image description here

I'm using the 'bmh' style here (i.e., I called plt.style.use('bmh') earlier in my notebook that I had opened), which is why it looks the way it does.

1 Comment

Thanks for your answer too. This is also very helpful. You are right, I wouldn't need a 3d plot for this data, but I just simplified the data to make the code easier.

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.