Reputation: 97
My dataframe MedComp
has the following structure:
I now want to do a stacked bar plot, one for each Name
(there are two different ones), stacking by Type
(currently done using color). The y-axis are the remaining columns with a separate plot per column due to very different ranges. For one of the Crops in Name
I would also like to show an error bar for the total, currently calculated in a separate dataframe called FarmQuant
, shown here:
Up to now, I only managed using a loop, thus creating one figure per column:
Midpoint = [['GHG', 'Total climate change [kg CO2e]'],
['Acid', 'Freshwater & ter. acidification [mol H+-Eq]'],
['Terra Eutro', 'Terrestrial eutrophication [mol N-Eq]'],
['Toxicity', 'Freshwater ecotoxicity [CTU]'],
['Marine Eutro', 'Marine eutrophication [kg N-Eq]' ],
]
for MPID in range(0, len(Midpoint)):
print(MPID+1, len(Midpoint), ' ', end='')
q = (ggplot(Farm_Quant))
+ geom_col(MedComb, aes('Name', Midpoint[MPID][0], fill='Type'))
+ scale_fill_brewer(type='div', palette=2)
+ geom_point(Farm_Quant, aes(x=1, y=Farm_Quant.loc['q2',Midpoint[MPID][0]]))
+ geom_errorbar(aes(x=1,ymin = Farm_Quant.loc['q1',Midpoint[MPID][0]], ymax = Farm_Quant.loc['q3',Midpoint[MPID][0]]))
+ theme_matplotlib()
+ theme(figure_size=(2.2, 4), legend_position = (1.25, 0.5),
axis_title_x =element_blank(),
axis_ticks_major_x=element_blank())
+ scale_y_continuous(name=Midpoint[MPID][1])
+ labs(title = Midpoint[MPID][0])
)
fig = q.draw()
fig.show()
However, I would like to do that using facet to have all in one figure with only one legend. Has anyone an idea how to do this with?
Please provide only examples with matplotlib (that to my knowledge doesn't support facetting) or plotnine since I have several other plots already done and would like to keep the same look. Plus those are the ones I'm most familiar with.
Edit: Here now also some test data to play with:
MedComb = pd.DataFrame({
'Name' : ['Crop1', 'Crop1', 'Crop1', 'Crop1', 'Crop2', 'Crop2', 'Crop2', 'Crop2'],
'Type' : ['Area', 'Diesel', 'Fert', 'Pest', 'Area', 'Diesel', 'Fert', 'Pest'],
'GHG': [14.9, 0.0007, 0.145, 0.1611, 2.537, 0.011, 0.1825, 0.115],
'Acid': [0.0125, 0.0005, 0.0029, 0.0044, 0.013, 0.00014, 0.0033, 0.0055],
'Terra Eutro': [0.053, 0.0002, 0.0077, 0.0001, 0.0547, 0.00019, 0.0058, 0.0002]
})
Farm_Quant = pd.DataFrame({
'Amount': [0.388, 0.4129, 0.1945],
'GHG': [8.029, 20.61, 44.32],
'Acid': [0.009, 0.019, 0.044],
'Terra Eutro': [0.039, 0.077, 0.0177]},
index = ['q1', 'q2', 'q3']
)
Upvotes: 0
Views: 1375
Reputation: 1336
Plotnine works best when your geom
aesthetics are in their own column so you can acheive the faceting by melt
ing your dataframe to do that. Also, if you join the Farm_Quant
dataframe to your melted MedComb dataframe then you can just reference that single dataframe and remove your for loop.
# Sample Data
MedComb = pd.DataFrame({
'Name' : ['Crop1', 'Crop1', 'Crop1', 'Crop1', 'Crop2', 'Crop2', 'Crop2', 'Crop2'],
'Type' : ['Area', 'Diesel', 'Fert', 'Pest', 'Area', 'Diesel', 'Fert', 'Pest'],
'GHG': [14.9, 0.0007, 0.145, 0.1611, 2.537, 0.011, 0.1825, 0.115],
'Acid': [0.0125, 0.0005, 0.0029, 0.0044, 0.013, 0.00014, 0.0033, 0.0055],
'Terra Eutro': [0.053, 0.0002, 0.0077, 0.0001, 0.0547, 0.00019, 0.0058, 0.0002]
})
Farm_Quant = pd.DataFrame({
'Amount': [0.388, 0.4129, 0.1945],
'GHG': [8.029, 20.61, 44.32],
'Acid': [0.009, 0.019, 0.044],
'Terra Eutro': [0.039, 0.077, 0.0177]},
index = ['q1', 'q2', 'q3']
)
# melt the MedComb df from wide to long
med_comb_long = pd.melt(MedComb,id_vars=['Name','Type'],
var_name='midpoint',value_name='value')
# Basically transpose the Farm_Quant df in two steps to join with med_comb_long
# convert the Farm_Quant df from wide to long and make the quarters a column
midpoint_long = pd.melt(Farm_Quant.reset_index().drop(columns=['Amount']),
id_vars=['index'],var_name='midpoint',value_name='error_bar')
# make long farm quant df wide again, but with the quarters as the columns and facet names as index
midpoint_reshape = pd.pivot(midpoint_long,index='midpoint',columns='index',
values='error_bar')
# Join the data for the bar charts and error bars/points into single df
plot_data = med_comb_long.join(midpoint_reshape,on='midpoint')
# make the plot
q2 = (ggplot(plot_data)
+ geom_col(aes('Name','value',fill='Type'))
+ scale_fill_brewer(type='div', palette=2)
+ facet_wrap('midpoint',scales='free')
+ geom_errorbar(aes(x=1,ymin='q1',ymax='q3'))
+ geom_point(aes(x=1,y='q2'))
+ theme_matplotlib()
# + theme(figure_size=(2.2, 4), legend_position = (1.25, 0.5),
# axis_title_x =element_blank(),
# axis_ticks_major_x=element_blank())
+ scale_y_continuous(name=Midpoint[MPID][1])
+ labs(title = Midpoint[MPID][0])
)
q2
Upvotes: 1