Matplotlib stacked bar plot: need to swap x and height

Viewed 1306

I am looking at some world ecological footprint data and I want to make a stacked bar chart of each type of footprint where the values stacked on top of each other are the same, but for different countries. So I started using 2 of the footprints just to get something working.

This is what I got to work (sort of):

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# Create DataFrame from CSV file
df = pd.read_csv('countries.csv')

# Slice Series out of DF
cropFoot = df['Cropland Footprint']
grazeFoot = df['Grazing Footprint']

# Convert Series to list
cropFoot = list(cropFoot)
grazeFoot = list(grazeFoot)

X = range(163)    # the lists have 163 entries

plt.bar(X, height=cropFoot)
plt.bar(X, height=grazeFoot, bottom = cropFoot)
plt.show()

Which generates the following plot:

Stacked bar chart

I have 5 separate footprints I want to display on the x-axis so that the footprint data for each country is stacked on top of one another. Essentially, right now the x-axis is showing all 163 countries with the 2 footprints stacked. I want the opposite. So I want 5 x bars with 163 countries stacked on each bar.

Something like this (but with 163 pieces stacked, not 7):

Goal stacked bar

Unsurprisingly, just swapping the X and height... doesn't work. And the result doesn't make any sense at all:

plt.bar(cropFoot, height=X)
plt.bar(grazeFoot, height=X, bottom = cropFoot)

plt.show()

As it looks like this:

Makes no sense at all.

Any advice on how to reverse this properly? This is the dataset I'm using, sourced from Kaggle.

2 Answers

Since you are already using a dataframe, you might want to try the bar plot method provided, which is a little easier to work with. To stack, just need to set the parameter stacked=True. However, what gets stacked is the column names, so you'll have to transpose your dataframe first. It might look something like this:

footprints = ['Cropland Footprint', 'Grazing Footprint', ...]  # fill with other footprints
data = df[footprints].T
data.plot.bar(stacked=True, legend=False)  # you probably don't want a legend with 163 countries

As an example:

df = pd.DataFrame(
    np.arange(200).reshape(40, 5),
    index=[f'i{x}' for x in range(40)],
    columns=[f'c{x}' for x in range(5)]
)
df.T.plot.bar(stacked=True, legend=False)

enter image description here

I think the answer by @busybear using the dataframe bar plot is better, but I thought it might be appropriate to show a solution that uses matplotlib.pyplot.bar since that is in the question, for completeness.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# Creates a randomized set of data to use for demonstration
n = 163
m = 5
footprints = np.zeros((m, n))
for ii in range(n):
    for jj in range(m):
        footprints[jj][ii] = random.random()*10

# This line is unnecessary for the example, it is purely for plot aesthetic
fig = plt.figure(figsize=(5, 5), dpi=200)

# colors can be replaced with any list of colors of any length >= 1
colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
bottom = np.zeros(5)
for ii in range(1, n):
    for jj in range(m):
        plt.bar(jj, height=footprints[jj][ii], bottom=bottom[jj], 
                color=colors[ii % len(colors)])
        bottom[jj] = bottom[jj] + footprints[jj][ii]

plt.show()

Related