Making a transparent area within a stacked area chart in Matplotlib - python

I am trying to build this type of chart: a mix between a line chart and a stacked area chart using Matplotlib and seaborn. I just want the white area below to be fully transparent. I tried changing the alpha parameter but it does not make the area transparent, just white at best. I am using the below code:
plt.plot(df.index,"5y Avg",data=df,
color=avg_color,
linestyle="dotted",
label= '5y Avg')
plt.stackplot(df.index,df["5Y Max"],color="#B1B3B6",labels= ['5y Range'])
plt.stackplot(df_test.index,df["5Y Min"],color="white",alpha=1)

You can get the effect you want simply by changing the approach to the problem: in place of making transparent the area of the bottom stackplot, you can color only the portion of the graph you want with matplotlib.axes.Axes.fill_between:
ax.fill_between(x = df.index, y1 = df['5Y Min'], y2 = df['5Y Max'], color = '#B1B3B6', label = '5y Range')
Complete Code
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
df = pd.DataFrame()
df['index'] = np.arange(1, 53 + 1, 1)
df['5y Avg'] = 2000/53*df['index'] + 100*np.random.rand(len(df))
df['5Y Max'] = 3200/53*df['index'] + 100*np.random.rand(len(df))
df['5Y Min'] = 1000/53*df['index'] + 100*np.random.rand(len(df))
avg_color = '#45A1A2'
df = df.set_index('index')
plt.style.use('seaborn-whitegrid')
fig, ax = plt.subplots()
ax.plot(df.index, df['5y Avg'],
color = avg_color,
linestyle = 'dotted',
label = '5y Avg')
ax.fill_between(x = df.index, y1 = df['5Y Min'], y2 = df['5Y Max'], color = '#B1B3B6', label = '5y Range')
ax.legend(frameon = True)
plt.show()
Plot

Related

Readable values in on axis with Matplotlib

I am working with Matplotlib and trying to plot a combo box with bars and lines. Below you can see my data:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.cm import get_cmap
from matplotlib.ticker import FormatStrFormatter
# Data
data = {
'Year': ['2010','2011','2012','2013','2014','2015','2016','2017','2018','2019'],
'Rate':[10,10,9,7,5,5,5,5,5,5],
'ChangeRate_1':[7,-50,24,150,8,10,60,5,180,5],
'ChangeRate_2':[7,6,-3,1,8,5,8,5,15,5],
}
df = pd.DataFrame(data, columns = ['Year',
'Rate',
'ChangeRate_1',
'ChangeRate_2'
])
df
Below you can see code :
# Ploting combo plot
fig, ax_1 = plt.subplots(figsize = (8, 5))
ax_2 = ax_1.twinx()
ax_3 = ax_2.twinx() ### <---- Problem is probably here
cmap = get_cmap('tab10')
ax_1.bar(df['Year'], df['Rate'], label = 'Rate', color = cmap(0))
ax_2.plot(df['Year'], df['ChangeRate_1'], label = 'ChangeRate_2', color = cmap(0.1),linewidth = '3.5')
ax_3.plot(df['Year'], df['ChangeRate_2'], label = 'ChangeRate_2', color = cmap(0.2),linewidth = '3.5')
handles_1, labels_1 = ax_1.get_legend_handles_labels()
handles_2, labels_2 = ax_2.get_legend_handles_labels()
handles_3, labels_3 = ax_3.get_legend_handles_labels()
ax_1.set_ylim(0, 16)
ax_2.set_ylim(-50,180)
ax_1.legend(handles = handles_1 + handles_2 + labels_3,
labels = labels_1 + labels_2 + labels_3,
loc = 'upper right',
shadow = True)
ax_1.grid(axis = 'y')
ax_1.set_title('Comparison of revenues',fontsize=11)
ax_1.set_ylabel('Rate')
ax_2.set_ylabel('ChangeRate_1')
ax_3.set_ylabel('ChangeRate_2')
ax_1.xaxis.set_major_formatter(FormatStrFormatter('%.0f'))
plt.savefig('ComparisonOfRevenues.pdf')
plt.show()
The above code produces a plot that is shown below.
As shown in the above plot, values for the y-axis for the left and for the right side overlap with values and are not readable.
For the left side, the scale for the 'Rate' should be in the range of 0 to 16, while for the right side, for ChangeRate_1 and ChangeRate_2, from -50 to 180.
So can anybody help me how to solve this problem ?
The instantiation of the third Axes object with ax_3 = ax_2.twinx() can be circumvented by using just one extra y-axis on the right and plotting ChangeRate_1 and ChangeRate_2 on that axis keeping the (right) y-axis label as ChangeRate and then assigning correct labels to the lines.
Code:
fig, ax_1 = plt.subplots(figsize=(8, 5))
ax_2 = ax_1.twinx()
cmap = get_cmap('tab10')
ax_1.bar(df['Year'], df['Rate'], label='Rate', color=cmap(0))
ax_2.plot(df['Year'], df['ChangeRate_1'], label='ChangeRate_1', color=cmap(0.1), linewidth='3.5')
ax_2.plot(df['Year'], df['ChangeRate_2'], label='ChangeRate_2', color=cmap(0.2), linewidth='3.5')
handles_1, labels_1 = ax_1.get_legend_handles_labels()
handles_2, labels_2 = ax_2.get_legend_handles_labels()
ax_1.set_ylim(0, 16)
ax_2.set_ylim(-50,180)
ax_1.legend(handles=handles_1 + handles_2, labels=labels_1 + labels_2,
loc='upper right', shadow=True)
ax_1.grid(axis='y')
ax_1.set_title('Comparison of revenues',fontsize=11)
ax_1.set_ylabel('Rate')
ax_2.set_ylabel('ChangeRate')
ax_1.xaxis.set_major_formatter(FormatStrFormatter('%.0f'))
plt.show()

Show dates in xticks only where value exist in plot chart of multiple dataframes

I have got a matplotlib question about xticks. I wanted to hide all those values that do not occur. I actually did it, but for the second set of values (red chart). I found how to hide for a specific data frame but not for 2 or more.
This is my code:
plt.subplots(figsize=(2, 1), dpi=400)
width = 0.005
xlim = np.arange(0, 1, 0.01)
ylim = np.arange(0, 0.1, 0.001)
plt.xticks(density_2.index.unique(), rotation=90, fontsize=1.5)
plt.yticks(density_2.unique(), fontsize=2)
plt.bar(density_1.index, density_1, width, color='Green', label=condition_1,alpha=0.5)
plt.bar(density_2.index, density_2, width, color='Red', label=condition_2,alpha=0.5)
plt.legend(loc="upper right", fontsize=2)
plt.show()
Link where I saw the solution: show dates in xticks only where value exist in plot chart and hide unnecessary interpolated xtick labels
Thank you very much in advance!
You need to find the intersection of the two lists of density_1's and density_2's ticks, as reported here.
Working example:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
N = 150
values_1 = np.random.randint(low = 5, high = 75, size = N)/100
density_1 = pd.DataFrame({'density_1': values_1})
density_1 = density_1.value_counts().sort_index(ascending = True)
density_1.index = sorted(list(set(values_1)), reverse = False)
values_2 = np.random.randint(low = 35, high = 100, size = N)/100
density_2 = pd.DataFrame({'density_2': values_2})
density_2 = density_2.value_counts().sort_index(ascending = True)
density_2.index = sorted(list(set(values_2)), reverse = False)
width = 0.005
condition_1 = 'Adele'
condition_2 = 'Extremoduro'
fig, ax = plt.subplots(figsize = (10, 5))
ax.bar(density_1.index, density_1, width, color = 'Green', label = condition_1, alpha = 0.5)
ax.bar(density_2.index, density_2, width, color = 'Red', label = condition_2, alpha = 0.5)
ax.legend(loc = 'upper right')
ax.set_xticks(list(set(density_1.index.unique()) & set(density_2.index.unique())), rotation = 90)
plt.show()
In the line:
list(set(density_1.index.unique()) & set(density_2.index.unique()))
you can select ticks which blongs to both density_1 and density_2.
Zoom in:

Matplotlib: align bar plots with imgshow once on x axis and once on y axis

I'm trying to make a plot showing the sum of pixel intensities along the x and y axis.
Currently I have the following code:
def example():
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
## <not under my control>
np.random.seed(1455)
width = 5
height = 8
sample = np.random.rand(height,width)
x_hist = np.sum(sample, axis=0)
y_hist = np.sum(sample, axis=1)
## </not under my control>
fig = plt.figure()
gs = fig.add_gridspec(2, 2)
ax_joint = fig.add_subplot(gs[1, 0])
ax_marg_x = fig.add_subplot(gs[0, 0],sharex=ax_joint)
ax_marg_y = fig.add_subplot(gs[1, 1],sharey=ax_joint)
ax_joint.imshow(sample, cmap="Reds")
ax_marg_x.bar(range(width),x_hist)
ax_marg_y.barh(range(height),y_hist)
plt.show()
Which yields the following:
However the x axis of the upper bar plot and image have the same limits but aren't scaled or aligned
Also there is a large gap between the image and the right bar plot.
My desired result would be something along the lines of:
As mentioned in the comments, correct the aspect ratio to automatic and set the size of the figure to portrait. In addition, make the spacing between each narrower.
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
np.random.seed(1455)
width = 5
height = 8
sample = np.random.rand(height,width)
x_hist = np.sum(sample, axis=0)
y_hist = np.sum(sample, axis=1)
fig = plt.figure(figsize=(6,8))
gs = fig.add_gridspec(2, 2, hspace=0.1, wspace=0.1)
ax_joint = fig.add_subplot(gs[1, 0])
ax_marg_x = fig.add_subplot(gs[0, 0],sharex=ax_joint)
ax_marg_y = fig.add_subplot(gs[1, 1],sharey=ax_joint)
ax_joint.imshow(sample, cmap="Reds", aspect='auto')
ax_marg_x.bar(range(width),x_hist)
ax_marg_y.barh(range(height),y_hist)
plt.show()

need to space bars apart on python graph

I have three years of data that I am plotting into a bar graph.
How do I separate the bars, space them apart, so that they are not overlapping?
I am struggling using the ax ticks.
New to python.
import matplotlib.pyplot as plt
import numpy as np
success2019 = np.array([30.3,42.3,34.1,36.1,30.5,28,32,51.9])
units2019 = np.array([30,'30A',37,'37A',49,50,51,58])
success2018 = np.array([30.4,34.4,43,47.5])
units2018 = np.array([46,30,'68A','30A'])
success2017 = np.array([33.2,29.1,22,62.2,22.3,36.9])
units2017 = np.array([72,74,78,'30A','32A','37A'])
pltone = plt.bar(units2019, success2019, color = 'blue', label = '2019')
plttwo = plt.bar(units2018, success2018, color = 'purple', label = '2018')
pltthree = plt.bar(units2017, success2017, color = 'green',label = '2017')
ax19 = plt.subplot()
ax19.set_xticks(range(len(success2019)))
ax19.set_xticklabels(units2019)
ax18 = plt.subplot()
ax18.set_xticks(range(len(success2018)))
ax18.set_xticklabels(units2018)
ax17 = plt.subplot()
ax17.set_xticks(range(len(success2017)))
ax17.set_xticklabels(units2017)
plt.title('Hunt unit vs Success Rates 2017-2019')
plt.legend(loc="upper right")
plt.ylabel('Success Rate %')
plt.xlabel('Hunting Units')
plt.legend (['2019', '2018', '2017'])
plt.show()
Your code looks OK for me: All bars are separated by spaces and they are automatically ordered.
However, if you like to create separate subplots, use this snippet:
fig, axs = plt.subplots(1,3)
axs[0].bar(units2019, success2019, color = 'blue', label = '2019')
axs[1].bar(units2018, success2018, color = 'purple', label = '2018')
axs[2].bar(units2017, success2017, color = 'green',label = '2017')
If you want to place them in groups next to each other in a single axis, go with:
fig, ax = plt.subplots()
# create new x-axis points
x2017 = np.linspace(0,len(units2017)-1,len(units2017))
# plot
ax.bar(x2017, success2017, color = 'green',label = '2017')
x2018 = np.linspace(0,len(units2018)-1,len(units2018)) + x2017[-1]+1
ax.bar(x2018, success2018, color = 'purple', label = '2018')
x2019 = np.linspace(0,len(units2019)-1,len(units2019)) + x2018[-1]+1
ax.bar(x2019, success2019, color = 'blue', label = '2019')
# set ticks + labels
ax.set_xticks( np.concatenate((x2017,x2018,x2019)) )
ax.set_xticklabels( np.concatenate((units2017,units2018,units2019)) )
You have full control over the width of the bars with the optional argument width=
fig, ax = plt.subplots()
x2017 = np.linspace(0,len(units2017)-1,len(units2017))
ax.bar(x2017, success2017, color = 'green',label = '2017', width=1)
x2018 = np.linspace(0,len(units2018)-1,len(units2018)) + x2017[-1]+1
ax.bar(x2018, success2018, color = 'purple', label = '2018') # default width=0.8
x2019 = np.linspace(0,len(units2019)-1,len(units2019)) + x2018[-1]+1
ax.bar(x2019, success2019, color = 'blue', label = '2019', width = 0.4)
ax.set_xticks( np.concatenate((x2017,x2018,x2019)) )
ax.set_xticklabels( np.concatenate((units2017,units2018,units2019)) )
Combine the data by year and then use a pivot to transform the columns into yearly data. Create a bar chart with a pandas plot of that transformed data.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
success2019 = np.array([30.3,42.3,34.1,36.1,30.5,28,32,51.9])
units2019 = np.array([30,'30A',37,'37A',49,50,51,58])
success2018 = np.array([30.4,34.4,43,47.5])
units2018 = np.array([46,30,'68A','30A'])
success2017 = np.array([33.2,29.1,22,62.2,22.3,36.9])
units2017 = np.array([72,74,78,'30A','32A','37A'])
df19 = pd.DataFrame({'index':units2019,'year':[2019]*len(success2019),'success':success2019})
df18 = pd.DataFrame({'index':units2018,'year':[2018]*len(success2018),'success':success2018})
df17 = pd.DataFrame({'index':units2017,'year':[2017]*len(success2017),'success':success2017})
ax = dfs.pivot('index', 'year','success').plot.bar()
ax.set_title('Hunt unit vs Success Rates 2017-2019')
ax.set_ylabel('Success Rate %')
ax.set_xlabel('Hunting Units')

Plotting superimposed charts (line and bar) with pandas and matplotlib

I am testing the capabilities of pandas to plot financial data (price and volume) on the same chart. If I try to render both data as lines, it works fine:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
a = pd.date_range('2019-01-01', '2019-06-01',freq = 'D')
b = np.random.normal(size = len(a), loc = 50)
v = np.random.normal(size = len(a), loc = 1000)
c = pd.DataFrame(index = a, data = zip(b,v), columns = ['price', 'volume'])
fig, ax = plt.subplots(figsize = (15,8))
bx = ax.twinx()
c.price.plot.line(ax = ax, color = 'r')
c.volume.plot.line(ax = bx, color = 'g', alpha = .2)
plt.show()
This gives:
However if I try to render one as a line and the other as a bar chart, by replacing the 3 last lines by:
c.price.plot.line(ax = ax, color = 'r')
c.volume.plot.bar(ax = bx, color = 'g', alpha = .2)
plt.show()
This gives the wrong result:
Would anybody know how to make the above code work with line + bar ??
Use Matplotlib plotting library.
Matplotlib's function pyplot has functions bar and plot. You can use them to display data on the same chart.
Example

Categories