I am able to plot a seaborn barplot and lineplot on the the same plot with the axes aligned at 0 and the same number of tickers. However, I cannot cut off any empty spaces dynamically. The code for the plot can be found below. What I would like to end up with is the same graph I have attached, but with the tickers ending at -49.3 and -13.63 (to get rid of any blank spaces). I do not want to hard code it, it needs to be dynamic so that it is able to adjust for any input data. Also, both axes still need to align at 0 and have the same number of tickers.
import numpy as np
import pandas as pd
import math
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.dates as md
import matplotlib.ticker as ticker
from datetime import datetime, timedelta
# create dataframes that will be used
date_today = datetime.now()
days = pd.date_range(date_today, date_today + timedelta(1000), freq='D')
np.random.seed(seed=1111)
data_a = np.random.randint(-10, high=20, size=len(days))
data_b = np.random.randint(-30, high=70, size=len(days))
a = pd.DataFrame({'date': days, 'a': data_a})
a = a.set_index('date')
b = pd.DataFrame({'date': days, 'b': data_b})
b = b.set_index('date')
# result dataframe which will be used for the plotting
result = pd.concat([a, b], axis=1)
# make sure only the dates are being used
result = result.reset_index()
result['date'] = result['date'].dt.date
result = result.set_index('date')
# set- up for the plot
matplotlib.rc_file_defaults()
ax1 = sns.set_style(style=None, rc=None)
fig, ax1 = plt.subplots(figsize=(12,6))
ax2 = ax1.twinx()
# bar plot
result_date = result.copy()
result_date = result_date.reset_index()
b_plot = sns.barplot(data = result_date, x=result_date.iloc[:, 0], y=result_date.iloc[:, 2], ax=ax1)
# pointplot
a_plot = sns.pointplot(data=result, x=result.index, y=result.iloc[:, 0], color="black", ax=ax2, markers = 'o', scale=0.4)
# set the x tickers to be those of the bar plot
ax1.set_xticks(np.arange(len(result_date)))
ax1.set_xticklabels(result_date.date.apply(lambda x: str(x.year)))
ax1.xaxis.set_major_locator(ticker.AutoLocator())
# to align the axes and make them start at 0
max1 = np.nanmax(np.abs(ax1.get_ybound())) # in case you have nan values
max2 = np.nanmax(np.abs(ax2.get_ybound()))
nticks = 7 #or other odd number
ax1.set_yticks(np.linspace(-max1, max1, nticks))
ax2.set_yticks(np.linspace(-max2, max2, nticks))
EDIT: Here is another method I tried using answers from other posts, but it still does not achieve what I want:
import numpy as np
import pandas as pd
import math
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.dates as md
import matplotlib.ticker as ticker
from datetime import datetime, timedelta
# create dataframes that will be used
date_today = datetime.now()
days = pd.date_range(date_today, date_today + timedelta(1000), freq='D')
np.random.seed(seed=1111)
data_a = np.random.randint(-10, high=20, size=len(days))
data_b = np.random.randint(-30, high=70, size=len(days))
a = pd.DataFrame({'date': days, 'a': data_a})
a = a.set_index('date')
b = pd.DataFrame({'date': days, 'b': data_b})
b = b.set_index('date')
# result dataframe which will be used for the plotting
result = pd.concat([a, b], axis=1)
# make sure only the dates are being used
result = result.reset_index()
result['date'] = result['date'].dt.date
result = result.set_index('date')
# set- up for the plot
matplotlib.rc_file_defaults()
ax1 = sns.set_style(style=None, rc=None)
fig, ax1 = plt.subplots(figsize=(12,6))
ax2 = ax1.twinx()
# bar plot
result_date = result.copy()
result_date = result_date.reset_index()
b_plot = sns.barplot(data = result_date, x=result_date.iloc[:, 0], y=result_date.iloc[:, 2], ax=ax1)
# pointplot
a_plot = sns.pointplot(data=result, x=result.index, y=result.iloc[:, 0], color="black", ax=ax2, markers = 'o', scale=0.4)
# set the x tickers to be those of the bar plot
ax1.set_xticks(np.arange(len(result_date)))
ax1.set_xticklabels(result_date.date.apply(lambda x: str(x.year)))
ax1.xaxis.set_major_locator(ticker.AutoLocator())
ax1_ylims = ax1.axes.get_ylim() # Find y-axis limits set by the plotter
ax1_yratio = ax1_ylims[0] / ax1_ylims[1] # Calculate ratio of lowest limit to highest limit
ax2_ylims = ax2.axes.get_ylim() # Find y-axis limits set by the plotter
ax2_yratio = ax2_ylims[0] / ax2_ylims[1] # Calculate ratio of lowest limit to highest limit
# If the plot limits ratio of plot 1 is smaller than plot 2, the first data set has
# a wider range range than the second data set. Calculate a new low limit for the
# second data set to obtain a similar ratio to the first data set.
# Else, do it the other way around
if ax1_yratio < ax2_yratio:
ax2.set_ylim(bottom = ax2_ylims[1]*ax1_yratio)
nticks = len(ax1.yaxis.get_ticklabels()) # number of ticks for the wider axis
ax2.set_yticks(np.linspace(ax2.get_ylim()[0], ax2.get_ylim()[-1], nticks))
else:
ax1.set_ylim(bottom = ax1_ylims[1]*ax2_yratio)
nticks = len(ax2.yaxis.get_ticklabels()) # number of ticks for the wider axis
ax1.set_yticks(np.linspace(ax1.get_ylim()[0], ax1.get_ylim()[-1], nticks))