Plot secondary axis on multiple subplots in python - python-3.x

I am trying to plot on the secondary axis of all subplots of a bar chart, but I was only successful showing the secondary plot on one of the subplots (see image below).
I tried:
df[['loan_amnt','int_rate']].plot(kind='bar',subplots=True,layout=(1,2), figsize=(15,5))
df['dti'].plot(secondary_y=True, marker='d', style='g:');
and got see below:
What can I add to this code to ensure that the secondary plot is displayed on both subplots.

I was able to solve this using the code below:
fig = plt.figure(figsize=(15,5))
cx0 = fig.add_subplot(121)
cx1 = cx0.twinx()
cx2 = plt.subplot(122)
cx3 = cx2.twinx()
rate_amnt_byGrade['loan_amnt'].plot(kind='bar', ax=cx0)
rate_amnt_byGrade['dti'].plot(ax=cx1, secondary_y=True)
rate_amnt_byGrade['int_rate'].plot(kind='bar', ax=cx2)
rate_amnt_byGrade['dti'].plot(ax=cx3, secondary_y=True)

Related

Plotly candlestick cannot be drawn on top of scatter

I'm trying to draw a candlestick plot and an scatter line below it, but the candlestick chart is always drawn first/at the bottom:
data_sep = []
# Price
data_sep.append(
go.Scatter(
x=data['Index'],
y=data['Values'],
mode='lines',
line=dict(color='darkgray', width=1),
name='Price'
)
)
data_sep.append(
go.Candlestick(
x=data['Index'],
open=data['Values'].shift(1),
high=data['Values_HIGH'],
low=data['Values_LOW'],
close=data['Values'],
name='Candlestick',))
fig = go.Figure(data=data_sep)
fig.show()
Changing the order of the code blocks do not change the ending result.
Is it possible to do this this way?
I'm using an array to save figures because later on I need to add several other scatters, all of them to be shown below the candlestick.

Python 3 matplotlib add a watermark with multiple scale axis

In python 3, i am trying to add watermark with multiple scale axis in the following pandas data frame
index,as_of_date,Total_10bn,close
0,2020-08-05,620.55975367473,332.11
1,2020-08-12,621.9414641848599,337.44
2,2020-08-19,628.88298116372,337.23
3,2020-08-26,627.26943375402,347.57
4,2020-09-02,630.01703674403,357.7
5,2020-09-09,630.70673674269,339.79
6,2020-09-16,637.50390815142,338.82
I can make the multiple scale works
df_soma_spy=pd.read_csv('df_soma_spy.csv')
print(df_soma_spy)
# create figure and axis objects with subplots()
fig,ax = plt.subplots()
plt.xticks(rotation=90)
ax.plot(df_soma_spy.as_of_date, df_soma_spy.Total_10bn, color="red") ## , marker="o"
# set x-axis label
ax.set_xlabel("Date", fontsize=12)
# set y-axis label
ax.set_ylabel("Fed SOMA ($10bn)",color="red",fontsize=14)
plt.grid(True, axis='both', which='both')
# twin object for two different y-axis on the sample plot
ax2=ax.twinx()
# make a plot with different y-axis using second axis object
ax2.plot(df_soma_spy.as_of_date, df_soma_spy["close"], color="black") ## , marker="o"
ax2.set_ylabel("$SPY Price",color="black",fontsize=14)
plt.title('Federal Reserves SOMA Total vs $SPY')
plt.show()
# save the plot as a file
fig.savefig('soma_spy.png',
format='jpeg',
dpi=300,
bbox_inches='tight')
Now I am trying to add a logo behind the picture. But no matter how I try, it will mess up one of the axis.
For example
import matplotlib.image as image
im = image.imread('xxx.png')
myaximage = ax.imshow(im, aspect='auto', extent=(0.1,0.1,0.1,0.1), alpha=0.5, zorder=-1)
In this case, the logo doesn't show up and the red axis is totally messed up.
There are some other solutions but none of them seems to work.
Scale image in matplotlib without changing the axis
Matplotlib automate placement of watermark
Scale image in matplotlib without changing the axis
Any thoughts? Thank you!
Instead of ax.imshow(), you can use fig.figimage() as shown below and described here. Just insert the following two lines in your code:
logo = image.imread(fname='logo.png')
fig.figimage(logo,alpha= 0.1)
Using the partial data you provided, here is the saved image:

How to align heights and widths subplot axes with gridspec and matplotlib?

I am trying to use matplotlib with gridspec to create a subplot such that the axes are arranged to look similar to the figure below; the figure was taken from this unrelated question.
My attempt at recreating this axes arrangement is below. Specifically, my problem is that the axes are not properly aligned. For example, the axis object for the blue histogram is taller than the axis object for the image with various shades of green; the orange histogram seems to properly align in terms of width, but I attribute this to luck. How can I properly align these axes? Unlike the original figure, I would like to add/pad extra empty space between axes such that there borders do not intersect; the slice notation in the code below does this by adding a blank row/column. (In the interest of not making this post longer than it has to be, I did not make the figures "pretty" by playing with axis ticks and the like.)
Unlike the original picture, the axes are not perfectly aligned. Is there a way to do this without using constrained layout? By this, I mean some derivative of fig, ax = plt.subplots(constrained_layout=True)?
The MWE code to recreate my figure is below; note that there was no difference between ax.imshow(...) and ax.matshow(...).
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
## initialize figure and axes
fig = plt.figure()
gs = fig.add_gridspec(6, 6, hspace=0.2, wspace=0.2)
ax_bottom = fig.add_subplot(gs[4:, 2:])
ax_left = fig.add_subplot(gs[:4, :2])
ax_big = fig.add_subplot(gs[:4, 2:])
## generate data
x = np.random.normal(loc=50, scale=10, size=100)
y = np.random.normal(loc=500, scale=50, size=100)
## get singular histograms
x_counts, x_edges = np.histogram(x, bins=np.arange(0, 101, 5))
y_counts, y_edges = np.histogram(y, bins=np.arange(0, 1001, 25))
x_mids = (x_edges[1:] + x_edges[:-1]) / 2
y_mids = (y_edges[1:] + y_edges[:-1]) / 2
## get meshed histogram
sample = np.array([x, y]).T
xy_counts, xy_edges = np.histogramdd(sample, bins=(x_edges, y_edges))
## subplot histogram of x
ax_bottom.bar(x_mids, x_counts,
width=np.diff(x_edges),
color='darkorange')
ax_bottom.set_xlim([x_edges[0], x_edges[-1]])
ax_bottom.set_ylim([0, np.max(x_counts)])
## subplot histogram of y
ax_left.bar(y_mids, y_counts,
width=np.diff(y_edges),
color='steelblue')
ax_left.set_xlim([y_edges[0], y_edges[-1]])
ax_left.set_ylim([0, np.max(y_counts)])
## subplot histogram of xy-mesh
ax_big.imshow(xy_counts,
cmap='Greens',
norm=Normalize(vmin=np.min(xy_counts), vmax=np.max(xy_counts)),
interpolation='nearest',
origin='upper')
plt.show()
plt.close(fig)
EDIT:
One can initialize the axes by explicitly setting width_ratios and height_ratios per row/column; this is shown below. This doesn't affect the output, but maybe I'm using it incorrectly?
## initialize figure and axes
fig = plt.figure()
gs = gridspec.GridSpec(ncols=6, nrows=6, figure=fig, width_ratios=[1]*6, height_ratios=[1]*6)
ax_bottom = fig.add_subplot(gs[4:, 2:])
ax_left = fig.add_subplot(gs[:4, :2])
ax_big = fig.add_subplot(gs[:4, 2:])
The problem is with imshow, which resizes the axes automatically to maintain a square pixel aspect.
You can prevent this by calling:
ax_big.imshow(..., aspect='auto')

Using "hue" for a Seaborn visual: how to get legend in one graph?

I created a scatter plot in seaborn using seaborn.relplot, but am having trouble putting the legend all in one graph.
When I do this simple way, everything works fine:
import pandas as pd
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import seaborn as sns
df2 = df[df.ln_amt_000s < 700]
sns.relplot(x='ln_amt_000s', y='hud_med_fm_inc', hue='outcome', size='outcome', legend='brief', ax=ax, data=df2)
The result is a scatter plot as desired, with the legend on the right hand side.
However, when I try to generate a matplotlib figure and axes objects ahead of time to specify the figure dimensions I run into problems:
a4_dims = (10, 10) # generating a matplotlib figure and axes objects ahead of time to specify figure dimensions
df2 = df[df.ln_amt_000s < 700]
fig, ax = plt.subplots(figsize = a4_dims)
sns.relplot(x='ln_amt_000s', y='hud_med_fm_inc', hue='outcome', size='outcome', legend='brief', ax=ax, data=df2)
The result is two graphs -- one that has the scatter plots as expected but missing the legend, and another one below it that is all blank except for the legend on the right hand side.
How do I fix this such? My desired result is one graph where I can specify the figure dimensions and have the legend at the bottom in two rows, below the x-axis (if that is too difficult, or not supported, then the default legend position to the right on the same graph would work too)? I know the problem lies with "ax=ax", and in the way I am specifying the dimensions as matplotlib figure, but I'd like to know specifically why this causes a problem so I can learn from this.
Thank you for your time.
The issue is that sns.relplot is a "Figure-level interface for drawing relational plots onto a FacetGrid" (see the API page). With a simple sns.scatterplot (the default type of plot used by sns.relplot), your code works (changed to use reproducible data):
df = pd.read_csv("https://vincentarelbundock.github.io/Rdatasets/csv/datasets/iris.csv", index_col=0)
fig, ax = plt.subplots(figsize = (5,5))
sns.scatterplot(x = 'Sepal.Length', y = 'Sepal.Width',
hue = 'Species', legend = 'brief',
ax=ax, data = df)
plt.show()
Further edits to legend
Seaborn's legends are a bit finicky. Some tweaks you may want to employ:
Remove the default seaborn title, which is actually a legend entry, by getting and slicing the handles and labels
Set a new title that is actually a title
Move the location and make use of bbox_to_anchor to move outside the plot area (note that the bbox parameters need some tweaking depending on your plot size)
Specify the number of columns
fig, ax = plt.subplots(figsize = (5,5))
sns.scatterplot(x = 'Sepal.Length', y = 'Sepal.Width',
hue = 'Species', legend = 'brief',
ax=ax, data = df)
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles=handles[1:], labels=labels[1:], loc=8,
ncol=2, bbox_to_anchor=[0.5,-.3,0,0])
plt.show()

Using Pandas df.boxplot() in subplots

I am trying to create subplots of a column in pandas dataframe grouped by each of the other columns. Here I create and iterate through subplots and attempt to add a boxplot to each one.
fig, axes = plt.subplots(nrows=2, ncols=2) # create 2x2 array of subplots
axes[0,0] = df.boxplot(column='price') # add boxplot to 1st subplot
axes[0,1] = df.boxplot(column='price', by='bedrooms') # add boxplot to 2nd subplot
# etc.
plt.show()
this results in
As you can see, the boxplots are not being added to the subplots. I am not sure where I am going wrong. All the documentation I have found says that [0,0] is the upper left corner, and the boxplots work..
I need to use df.boxplots() specifically.
You should pass axes as argument to plot function:
fig, axes = plt.subplots(nrows=2, ncols=2) # create 2x2 array of subplots
df.boxplot(column='price', ax=axes[0,0]) # add boxplot to 1st subplot
df.boxplot(column='price', by='bedrooms', ax=axes[0,1]) # add boxplot to 2nd subplot
# etc.
plt.show()

Resources