Markus W - 11 months ago 175

Python Question

I have a dataframe

`df`

`Year Product Sales`

2010 A 111

B 20

C 150

2011 A 10

B 28

C 190

… …

and I would like to plot this in

`matplotlib`

`Year`

`Sales`

`Product`

I have been trying the following:

`from mpl_toolkits.mplot3d import axes3d`

fig = plt.figure()

ax = fig.add_subplot(111, projection='3d')

X = dfgrouped['Year']

Y = dfgrouped['Sales']

Z = dfgrouped['Product']

ax.bar(X, Y, Z, color=cs, alpha=0.8)

unfortunately I am getting

"ValueError: incompatible sizes: argument 'height' must be length 7 or

scalar"

Answer Source

You could plot a 3D Bar graph using `Pandas`

as shown:

**Setup:**

```
arrays = [[2010, 2010, 2010, 2011, 2011, 2011],['A', 'B', 'C', 'A', 'B', 'C']]
tuples = list(zip(*arrays))
index = pd.MultiIndex.from_tuples(tuples, names=['Year', 'Product'])
df = pd.DataFrame({'Sales': [111, 20, 150, 10, 28, 190]}, index=index)
print (df)
Sales
Year Product
2010 A 111
B 20
C 150
2011 A 10
B 28
C 190
```

**Data Wrangling:**

```
import numpy as np
import pandas as pd
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
# Set plotting style
plt.style.use('seaborn-white')
```

Grouping similar entries (*get_group*) occuring in the Sales column and iterating through them and later appending them to a `list`

. This gets stacked horizontally using `np.hstack`

which forms the `z`

dimension of the 3d plot.

```
L = []
for i, group in df.groupby(level=1)['Sales']:
L.append(group.values)
z = np.hstack(L).ravel()
```

Letting the labels on both the x and y dimensions take unique values of the respective levels of the Multi-Index Dataframe. The x and y dimensions then take the range of these values.

```
xlabels = df.index.get_level_values('Year').unique()
ylabels = df.index.get_level_values('Product').unique()
x = np.arange(xlabels.shape[0])
y = np.arange(ylabels.shape[0])
```

Returning coordinate matrices from coordinate vectors using `np.meshgrid`

```
x_M, y_M = np.meshgrid(x, y, copy=False)
```

**3-D plotting:**

```
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111, projection='3d')
# Making the intervals in the axes match with their respective entries
ax.w_xaxis.set_ticks(x + 0.5/2.)
ax.w_yaxis.set_ticks(y + 0.5/2.)
# Renaming the ticks as they were before
ax.w_xaxis.set_ticklabels(xlabels)
ax.w_yaxis.set_ticklabels(ylabels)
# Labeling the 3 dimensions
ax.set_xlabel('Year')
ax.set_ylabel('Product')
ax.set_zlabel('Sales')
# Choosing the range of values to be extended in the set colormap
values = np.linspace(0.2, 1., x_M.ravel().shape[0])
# Selecting an appropriate colormap
colors = plt.cm.Spectral(values)
ax.bar3d(x_M.ravel(), y_M.ravel(), z*0, dx=0.5, dy=0.5, dz=z, color=colors)
plt.show()
```

**Note:**

Incase of unbalanced `groupby`

objects, you could still do it by `unstacking`

and filling `Nans`

with 0's and then `stacking`

it back as follows:

```
df = df_multi_index.unstack().fillna(0).stack()
```

where `df_multi_index.unstack`

is your original multi-index dataframe.

For the new values added to the Multi-index Dataframe, following plot is obtained: