Matplotlib - Implement multiple y-axis scales in animated line graph - python

I'm trying to remake an existing animated line graph I made where each line has a uniquely scaled y-axis - one on the left, one on the right. The graph is comparing the value of two cryptocurrencies that have vastly different sizes (eth/btc), which is why I need multiple scales to actually see changes.
My data has been formatted in a pd df (numbers here are random):
Date ETH Price BTC Price
0 2020-10-30 00:00:00 0.155705 1331.878496
1 2020-10-31 00:00:00 0.260152 1337.174272
.. ... ... ...
290 2021-08-15 16:42:09 0.141994 2846.719819
[291 rows x 3 columns]
And code is roughly:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as ani
color = ['cyan', 'orange', 'red']
fig = plt.figure()
plt.xticks(rotation=45, ha="right", rotation_mode="anchor")
plt.subplots_adjust(bottom = 0.2, top = 0.9)
plt.ylabel('Coin Value (USD)')
plt.xlabel('Date')
def buildChart(i=int):
df1 = df.set_index('Date', drop=True)
plt.legend(["ETH Price", "BTC Price"])
p = plt.plot(df1[:i].index, df1[:i].values)
for i in range(0,2):
p[i].set_color(color[i])
animator = ani.FuncAnimation(fig, buildChart, interval = 10)
plt.show()
Resulting Animation
I tried to create a second axis with a twin x to the first axis.
color = ['cyan', 'orange', 'blue']
fig, ax1 = plt.subplots() #Changes over here
plt.xticks(rotation=45, ha="right", rotation_mode="anchor")
plt.subplots_adjust(bottom = 0.2, top = 0.9)
plt.ylabel('Coin Value (USD)')
plt.xlabel('Date')
def buildChart(i=int):
df1 = df.set_index('Date', drop=True)
plt.legend(["ETH Price", "Bitcoin Price"])
data1 = df1.iloc[:i, 0:1] # Changes over here
# ------------- More Changes Start
ax2 = ax1.twinx()
ax2.set_ylabel('Cost of Coin (USD)')
data2 = df1.iloc[:i, 1:2]
ax2.plot(df1[:i].index, data2)
ax2.tick_params(axis='y')
# -------------- More Changes End
p = plt.plot(df1[:i].index, data1)
for i in range(0,1):
p[i].set_color(color[i])
import matplotlib.animation as ani
animator = ani.FuncAnimation(fig, buildChart, interval = 10)
plt.show()
Resulting Animation After Changes
Current issues:
X-Axis start at ~1999 rather than late 2020
---- Causes all changes on the y-axis to be a nearly vertical line
Left Y-Axis label is on a scale of 0-1?
Right y-axis labels are recurring, overlapping, moving.
I believe my approach to making a second scale must have been wrong to get this many errors, but this seems like the way to do it.

I re-structured your code in order to easily set up a secondary axis animation.
Here the code of the animation with a single y axis:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
df = pd.DataFrame({'date': pd.date_range(start = '2020-01-01', end = '2020-04-01', freq = 'D')})
df['ETH'] = 2*df.index + 300 + 100*np.random.randn(len(df))
df['BTC'] = 5*df.index + 13000 + 200*np.random.randn(len(df))
def update(i):
ax.cla()
ax.plot(df.loc[:i, 'date'], df.loc[:i, 'ETH'], label = 'ETH Price', color = 'red')
ax.plot(df.loc[:i, 'date'], df.loc[:i, 'BTC'], label = 'BTC Price', color = 'blue')
ax.legend(frameon = True, loc = 'upper left', bbox_to_anchor = (1.15, 1))
ax.set_ylim(0.9*min(df['ETH'].min(), df['BTC'].min()), 1.1*max(df['ETH'].max(), df['BTC'].max()))
ax.tick_params(axis = 'x', which = 'both', top = False)
ax.tick_params(axis = 'y', which = 'both', right = False)
plt.setp(ax.xaxis.get_majorticklabels(), rotation = 45)
ax.set_xlabel('Date')
ax.set_ylabel('ETH Coin Value (USD)')
plt.tight_layout()
fig, ax = plt.subplots(figsize = (6, 4))
ani = FuncAnimation(fig = fig, func = update, frames = len(df), interval = 100)
plt.show()
Starting from the code above, you should twin the axis out of the update function: if you keep ax.twinx() inside the function, this operation will be repeated in each iteration and you will get a new axis each time.
Below the code for an animation with a secondary axis:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
df = pd.DataFrame({'date': pd.date_range(start = '2020-01-01', end = '2020-04-01', freq = 'D')})
df['ETH'] = 2*df.index + 300 + 100*np.random.randn(len(df))
df['BTC'] = 5*df.index + 13000 + 200*np.random.randn(len(df))
def update(i):
ax1.cla()
ax2.cla()
line1 = ax1.plot(df.loc[:i, 'date'], df.loc[:i, 'ETH'], label = 'ETH Price', color = 'red')
line2 = ax2.plot(df.loc[:i, 'date'], df.loc[:i, 'BTC'], label = 'BTC Price', color = 'blue')
lines = line1 + line2
labels = [line.get_label() for line in lines]
ax1.legend(lines, labels, frameon = True, loc = 'upper left', bbox_to_anchor = (1.15, 1))
ax1.set_ylim(0.9*df['ETH'].min(), 1.1*df['ETH'].max())
ax2.set_ylim(0.9*df['BTC'].min(), 1.1*df['BTC'].max())
ax1.tick_params(axis = 'x', which = 'both', top = False)
ax1.tick_params(axis = 'y', which = 'both', right = False, colors = 'red')
ax2.tick_params(axis = 'y', which = 'both', right = True, labelright = True, left = False, labelleft = False, colors = 'blue')
plt.setp(ax1.xaxis.get_majorticklabels(), rotation = 45)
ax1.set_xlabel('Date')
ax1.set_ylabel('ETH Coin Value (USD)')
ax2.set_ylabel('BTC Coin Value (USD)')
ax1.yaxis.label.set_color('red')
ax2.yaxis.label.set_color('blue')
ax2.spines['left'].set_color('red')
ax2.spines['right'].set_color('blue')
plt.tight_layout()
fig, ax1 = plt.subplots(figsize = (6, 4))
ax2 = ax1.twinx()
ani = FuncAnimation(fig = fig, func = update, frames = len(df), interval = 100)
plt.show()

Related

Panel is overlapping and has a wrong ratio in mplfinance plot

I'm trying to plot a subplot but there are two problems.
#1 The panel_ratio setting (6,1) is unnoticed.
#2 The y axis of the top panel juts down and overlaps the y axis of the bottom panel, so that the bars are trimmed in the top panel
What is wrong with the code?
import pandas as pd
import numpy as np
from matplotlib.animation import FuncAnimation
import mplfinance as mpf
times = pd.date_range(start='2022-01-01', periods=50, freq='ms')
def get_rsi(df, rsi_period):
chg = df['close'].diff(1)
gain = chg.mask(chg<0,0)
loss = chg.mask(chg>0,0)
avg_gain = gain.ewm(com=rsi_period-1, min_periods=rsi_period).mean()
avg_loss = loss.ewm(com=rsi_period-1, min_periods=rsi_period).mean()
rs = abs(avg_gain/avg_loss)
rsi = 100 - (100/(1+rs))
return rsi
df = pd.DataFrame(np.random.randint(3000, 3100, (50, 1)), columns=['open'])
df['high'] = df.open+5
df['low'] = df.open-2
df['close'] = df.open
df['rsi14'] = get_rsi(df, 14)
df.set_index(times, inplace=True)
lows_peaks = df.low.nsmallest(5).index
fig = mpf.figure(style="charles",figsize=(7,8))
ax1 = fig.add_subplot(1,1,1)
ax2 = fig.add_subplot(2,1,2)
ap0 = [ mpf.make_addplot(df['rsi14'],color='g', ax=ax2, ylim=(10,90), panel=1) ]
mpf.plot(df, ax=ax1, ylim=(2999,3104), addplot=ap0, panel_ratios=(6,1))
mpf.show()
In this case, it is easier to use a panel instead of an external axis. I tried your code and could not improve it. For a detailed reference on panels, see here.
# fig = mpf.figure(style="charles", figsize=(7,8))
# ax1 = fig.add_subplot(1,1,1)
# ax2 = fig.add_subplot(2,1,2)
ap0 = mpf.make_addplot(df[['rsi14']], color='g', ylim=(10,90), panel=1)
mpf.plot(df[['open','high', 'low','close']], addplot=ap0, ylim=(2999,3104), panel_ratios=(6,1), style='charles')
mpf.show()

How to modify xtick label of plt in Matplotlib

The objective is to modify the xticklabel upon plotting pcolormesh and scatter.
However, I am having difficulties accessing the existing xtick labels.
Simply
ax = plt.axes()
labels_x = [item.get_text() for item in ax.get_xticklabels()]
which produced:
['', '', '', '', '', '']
or
fig.canvas.draw()
xticks = ax.get_xticklabels()
which produced:
['', '', '', '', '', '']
does not return the corresponding label.
May I know how to properly access axis tick labels for a plt cases.
For readability, I split the code into two section.
The first section to generate the data used for plotting
Second section deal the plotting
Section 1: Generate data used for plotting
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import math
np.random.seed(0)
increment=120
max_val=172800
aran=np.arange(0,max_val,increment).astype(int)
arr=np.concatenate((aran.reshape(-1,1), np.random.random((aran.shape[0],4))), axis=1)
df=pd.DataFrame(arr,columns=[('lapse',''),('a','i'),('a','j'),('b','k'),('c','')])
ridx=df.index[df[('lapse','')] == 3600].tolist()[0]+1 # minus 1 so to allow 3600 start at new row
df[('event','')]=0
df.loc[[1,2,3,10,20,30],[('event','')]]=1
arr=df[[('a','i'),('event','')]].to_numpy()
col_len=ridx
v=arr[:,0].view()
nrow_size=math.ceil(v.shape[0]/col_len)
X=np.pad(arr[:,0].astype(float), (0, nrow_size*col_len - arr[:,0].size),
mode='constant', constant_values=np.nan).reshape(nrow_size,col_len)
mask_append_val=0 # This value must equal to 1 for masking
arrshape=np.pad(arr[:,1].astype(float), (0, nrow_size*col_len - arr[:,1].size),
mode='constant', constant_values=mask_append_val).reshape(nrow_size,col_len)
Section 2 Plotting
fig = plt.figure(figsize=(8,6))
plt.pcolormesh(X,cmap="plasma")
x,y = X.shape
xs,ys = np.ogrid[:x,:y]
# the non-zero coordinates
u = np.argwhere(arrshape)
plt.scatter(ys[:,u[:,1]].ravel()+.5,xs[u[:,0]].ravel()+0.5,marker='*', color='r', s=55)
plt.gca().invert_yaxis()
xlabels_to_use_this=df.loc[:30,[('lapse','')]].values.tolist()
# ax = plt.axes()
# labels_x = [item.get_text() for item in ax.get_xticklabels()]
# labels_y = [item.get_text() for item in ax.get_yticklabels()]
plt.xlabel('X-axis')
plt.ylabel('Y-axis')
plt.title("Plot 2D array")
plt.colorbar()
plt.tight_layout()
plt.show()
Expected output
This is how the plot could be generated using matplotlib's pcolormesh and scatter:
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
import pandas as pd
import numpy as np
np.random.seed(0)
increment = 120
max_val = 172800
aran = np.arange(0, max_val, increment).astype(int)
arr_df = np.concatenate((aran.reshape(-1, 1), np.random.random((aran.shape[0], 4))), axis=1)
df = pd.DataFrame(arr_df, columns=[('lapse', ''), ('a', 'i'), ('a', 'j'), ('b', 'k'), ('c', '')])
df[('event', '')] = 0
df.loc[[1, 2, 3, 10, 20, 30], [('event', '')]] = 1
col_len_lapse = 3600
col_len = df[df[('lapse', '')] == col_len_lapse].index[0]
nrow_size = int(np.ceil(v.shape[0] / col_len))
a_i_values = df[('a', 'i')].values
a_i_values_meshed = np.pad(a_i_values.astype(float), (0, nrow_size * col_len - len(a_i_values)),
mode='constant', constant_values=np.nan).reshape(nrow_size, col_len)
fig, ax = plt.subplots(figsize=(8, 6))
# the x_values indicate the mesh borders, subtract one half so the ticks can be at the centers
x_values = df[('lapse', '')][:col_len + 1].values - increment / 2
# divide lapses for y by col_len_lapse to get hours
y_values = df[('lapse', '')][::col_len].values / col_len_lapse - 0.5
y_values = np.append(y_values, 2 * y_values[-1] - y_values[-2]) # add the bottommost border (linear extension)
mesh = ax.pcolormesh(x_values, y_values, a_i_values_meshed, cmap="plasma")
event_lapses = df[('lapse', '')][df[('event', '')] == 1]
ax.scatter(event_lapses % col_len_lapse,
np.floor(event_lapses / col_len_lapse),
marker='*', color='red', edgecolor='white', s=55)
ax.xaxis.set_major_locator(MultipleLocator(increment * 5))
ax.yaxis.set_major_locator(MultipleLocator(5))
ax.invert_yaxis()
ax.set_xlabel('X-axis (s)')
ax.set_ylabel('Y-axis (hours)')
ax.set_title("Plot 2D array")
plt.colorbar(mesh)
plt.tight_layout() # fit the labels nicely into the plot
plt.show()
With Seaborn things can be simplified, adding new columns for hours and seconds, and using pandas' pivot (which automatically fills unavailable data with NaNs). Adding xtick_labels=5 sets the labels every 5 positions. (The star for lapse=3600 is at 1 hour, 0 seconds).
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
# df created as before
df['hours'] = (df[('lapse', '')].astype(int) // 3600)
df['seconds'] = (df[('lapse', '')].astype(int) % 3600)
df_heatmap = df.pivot(index='hours', columns='seconds', values=('a', 'i'))
df_heatmap_markers = df.pivot(index='hours', columns='seconds', values=('event', '')).replace(
{0: '', 1: '★', np.nan: ''})
fig, ax = plt.subplots(figsize=(8, 6))
sns.heatmap(df_heatmap, xticklabels=5, yticklabels=5,
annot=df_heatmap_markers, fmt='s', annot_kws={'color': 'lime'}, ax=ax)
ax.tick_params(rotation=0)
plt.tight_layout()
plt.show()
Instead of a 'seconds' column, a 'minutes' column also might be interesting.
Here is an attempt to add time information as suggested in the comments:
from matplotlib import patheffects # to add some outline effect
# df prepared as the other seaborn example
fig, ax = plt.subplots(figsize=(8, 6))
path_effect = patheffects.withStroke(linewidth=2, foreground='yellow')
sns.heatmap(df_heatmap, xticklabels=5, yticklabels=5,
annot=df_heatmap_markers, fmt='s',
annot_kws={'color': 'red', 'path_effects': [path_effect]},
cbar=True, cbar_kws={'pad': 0.16}, ax=ax)
ax.tick_params(rotation=0)
ax2 = ax.twinx()
ax2.set_ylim(ax.get_ylim())
yticks = ax.get_yticks()
ax2.set_yticks(yticks)
ax2.set_yticklabels([str(pd.to_datetime('2019-01-15 7:00:00') + pd.to_timedelta(h, unit='h')).replace(' ', '\n')
for h in yticks])
I end up using Seaborn to address this issue.
Specifically, the following lines able to easily tweak the xticklabel
fig.canvas.draw()
new_ticks = [i.get_text() for i in g.get_xticklabels()]
i=[int(idx) for idx in new_ticks]
newlabel=xlabels_to_use_this[i]
newlabel=[np.array2string(x, precision=0) for x in newlabel]
The full code for plotting is as below
import seaborn as sns
fig, ax = plt.subplots()
sns.heatmap(X,ax=ax)
x,y = X.shape
xs,ys = np.ogrid[:x,:y]
# the non-zero coordinates
u = np.argwhere(arrshape)
g=sns.scatterplot(ys[:,u[:,1]].ravel()+.5,xs[u[:,0]].ravel()+0.5,marker='*', color='r', s=55)
fig.canvas.draw()
new_ticks = [i.get_text() for i in g.get_xticklabels()]
i=[int(idx) for idx in new_ticks]
newlabel=xlabels_to_use_this[i]
newlabel=[np.array2string(x, precision=0) for x in newlabel]
ax.set_xticklabels(newlabel)
ax.set_xticklabels(ax.get_xticklabels(),rotation = 90)
for ind, label in enumerate(g.get_xticklabels()):
if ind % 2 == 0: # every 10th label is kept
label.set_visible(True)
else:
label.set_visible(False)
for ind, label in enumerate(g.get_yticklabels()):
if ind % 4 == 0: # every 10th label is kept
label.set_visible(True)
else:
label.set_visible(False)
plt.xlabel('Elapsed (s)')
plt.ylabel('Hour (h)')
plt.title("Rastar Plot")
plt.tight_layout()
plt.show()

How can you edit multiple legend boxes in a seanborn scatter plot?

I want to rename and move the two legends. I want the top legend to be outside of the graph, upper right, and titled 'Average Cost'. I want the second legend to be outside the graph, bottom right, and titled 'Total Revenue'
figure = plt.figure(figsize=(10,5))
ax = sns.scatterplot(
x=top_rev_mean['cost_of_the_order_y'],
y='cost_of_the_order_x',
data=top_rev_mean,
size = "cost_of_the_order_x",
hue='cost_of_the_order_y'
)
plt.ylabel('Total Order Revenue')
plt.xlabel('Average Order Cost Per Cuisine')
plt.show()
I suppose you are working with a dataframe like this one:
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
N = 15
top_rev_mean = pd.DataFrame({'cost_of_the_order_y': 17.6 + 4*np.random.random(N),
'cost_of_the_order_x': 500 + 2000*np.random.random(N)})
cost_of_the_order_y cost_of_the_order_x
0 19.098160 866.809020
1 21.402857 1108.484486
2 20.527976 1549.512863
3 19.994634 1363.890037
4 18.224075 1082.458280
5 18.223978 1723.705789
6 17.832334 778.987721
7 21.064705 1084.289297
8 20.004460 1232.723687
9 20.432290 1412.139968
10 17.682338 2070.351923
11 21.479639 899.347564
12 20.929771 1528.468877
13 18.449356 1684.829138
14 18.327300 592.900825
When you set up the scatterplot, you have to be sure the legend is drawn, in order to get handles and labels later, so you need to pass legend = True parameter to seaborn.scatterplot:
fig, ax = plt.subplots(figsize=(10,5))
sns.scatterplot(ax = ax, data = top_rev_mean, x = 'cost_of_the_order_y', y = 'cost_of_the_order_x', size = "cost_of_the_order_x", hue = 'cost_of_the_order_y', legend = True)
Then you can extract handles and labels of current legend with ax.get_legend_handles_labels:
handles, labels = ax.get_legend_handles_labels()
Now you need to split elements of the first legend from elements of the second one:
legend1 = {}
legend2 = {}
titles = {}
for handle, label in zip(handles, labels):
if label.replace('.', '').isdigit() == False:
titles[handle] = label
else:
if len(list(titles.keys())) == 1:
legend1[handle] = label
else:
legend2[handle] = label
Finally, you can remove the legend drawn from seaborn and draw the two legend you want:
ax.legend().remove()
upper_legend = ax.legend(handles = list(legend1.keys()), labels = list(legend1.values()), title = 'Average Cost', loc = 'upper left', bbox_to_anchor = (1.05, 1))
ax.add_artist(upper_legend)
lower_legend = ax.legend(handles = list(legend2.keys()), labels = list(legend2.values()), title = 'Total Revenue', loc = 'lower left', bbox_to_anchor = (1.05, 0))
Complete Code
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
N = 15
top_rev_mean = pd.DataFrame({'cost_of_the_order_y': 17.6 + 4*np.random.random(N),
'cost_of_the_order_x': 500 + 2000*np.random.random(N)})
fig, ax = plt.subplots(figsize=(10,5))
sns.scatterplot(ax = ax, data = top_rev_mean, x = 'cost_of_the_order_y', y = 'cost_of_the_order_x', size = "cost_of_the_order_x", hue = 'cost_of_the_order_y', legend = True)
handles, labels = ax.get_legend_handles_labels()
legend1 = {}
legend2 = {}
titles = {}
for handle, label in zip(handles, labels):
if label.replace('.', '').isdigit() == False:
titles[handle] = label
else:
if len(list(titles.keys())) == 1:
legend1[handle] = label
else:
legend2[handle] = label
ax.legend().remove()
upper_legend = ax.legend(handles = list(legend1.keys()), labels = list(legend1.values()), title = 'Average Cost', loc = 'upper left', bbox_to_anchor = (1.05, 1))
ax.add_artist(upper_legend)
lower_legend = ax.legend(handles = list(legend2.keys()), labels = list(legend2.values()), title = 'Total Revenue', loc = 'lower left', bbox_to_anchor = (1.05, 0))
ax.set_ylabel('Total Order Revenue')
ax.set_xlabel('Average Order Cost Per Cuisine')
plt.tight_layout()
plt.show()

How to set matplotlib's shared subplots legend to be horizontal and at lower center position?

I'm using Matplotlib and Seaborn to plot four bar graphs with one shared legend. However, I can't make the legend to be horizontal and at the lower center. I tried to set the numbers in this line:
ax.legend(bbox_to_anchor=(0.99, -0.15),
loc=1,
fontsize=13,
# ncol=2
)
but if the legend goes to the middle, then the distance between the two subplot columns would increase as well making it not good.
Here is my code:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import pdb
import pyautogui
import multiprocessing
from time import sleep
from matplotlib import patches as mpatches
def convert_to_grouped_bar_chart_format(data,
col_1_name, col_2_name, col_3_name):
"""
Parameters
----------
data: Pandas dataframe. Format:
Method Class1 Class2 Class3
0 Method_1 0.1 0.2 0.3
1 Method_2 0.6 0.5 0.4
Returns
-------
data_grouped: Pandas dataframe.
"""
cls_list = data.columns[1:].tolist()
col_1 = []
col_2 = []
col_3 = []
(num_of_rows, num_of_cols) = data.shape
for row_idx in range(num_of_rows):
for cls_idx, cls in enumerate(cls_list):
col_1.append(data.iloc[row_idx, 0])
col_2.append(cls)
col_3.append(data.iloc[row_idx, cls_idx+1])
pass
pass
data_grouped_dict = {
col_1_name: col_1,
col_2_name: col_2,
col_3_name: col_3
}
data_grouped = pd.DataFrame(data_grouped_dict, columns = [col_1_name, col_2_name, col_3_name])
return data_grouped
def draw_four_bar_graph_seaborn():
file_list = [
['Measure1_ED.csv', 'Measure1_ES.csv'],
['Measure2_ED.csv', 'Measure2_ES.csv']
]
n_rows = len(file_list)
n_cols = len(file_list[0])
fig, axes = plt.subplots(n_rows, n_cols)
for idx_row in range(n_rows):
# if idx_row > 0:
# continue
for idx_col in range(n_cols):
file_name = file_list[idx_row][idx_col]
data = pd.read_csv(file_name)
col_1_name = 'Method'
col_2_name = 'Class'
col_3_name = file_name.split('_')[0]
data_type = file_name.split('_')[1][:-4]
ax = axes[idx_row, idx_col]
# ax =axes[idx_col]
data_grouped = convert_to_grouped_bar_chart_format(data,
col_1_name, col_2_name, col_3_name)
splot = sns.barplot(
# ax=axes[idx_row, idx_col],
ax=ax,
x=col_2_name,
y=col_3_name,
hue=col_1_name,
palette="magma",
# palette=my_pal,
# sharey=False,
data=data_grouped)
splot.set_xlabel("",fontsize=1)
splot.set_ylabel(col_3_name,fontsize=13)
splot.tick_params(labelsize=13)
title_subplot = 'Title 1'
ax.set_title(title_subplot, fontsize=13)
if col_3_name == 'Measure1':
ax.set_ylim(0, 1.10)
else:
ax.set_ylim(0, 2.25)
for p1 in splot.patches:
splot.annotate('%.3f' % p1.get_height(),
(p1.get_x() + p1.get_width() / 2., p1.get_height()),
ha = 'center', va = 'center',
size=13,
xytext = (0, 8),
textcoords = 'offset points')
if (idx_row == 1) and (idx_col == 0):
ax.legend(
bbox_to_anchor=(1.2, -0.15),
loc=1,
fontsize=13,
# ncol=2
)
else:
splot.get_legend().remove()
# Change width size
# ax = axes[idx_row, idx_col]
new_value = 0.35
for patch in ax.patches :
current_width = patch.get_width()
diff = current_width - new_value
# we change the bar width
patch.set_width(new_value)
# we recenter the bar
patch.set_x(patch.get_x() + diff * .5)
plt.tight_layout(pad=0)
mng = plt.get_current_fig_manager()
mng.window.state('zoomed') #works fine on Windows!
plt.show()
fig.savefig('out.pdf')
plt.close()
def draw_graph_then_save_and_close_automatically(func=None, args=[]):
coords_close_graph = (1365, 12) # Auto click to close graph
multiprocessing.Process(target=func, args=args).start()
sleep(10)
pyautogui.moveTo(coords_close_graph)
pyautogui.click()
def main():
draw_graph_then_save_and_close_automatically(
func=draw_four_bar_graph_seaborn,
args=[])
if __name__ == '__main__':
main()
Please help me, thank you very much.
Use a figure-legend instead of place on on one of your axes and set the number of columns that the legend should have to the number of legend entries. Here is an example (I did find your's to be minimal enough^^)
import numpy as np
from matplotlib import pyplot as plt
# create random data
y = np.random.randint(0,100,size=(10, 3))
# open a figure with two axes
fig,axs = plt.subplots(1,2)
# plot something in the axes
axs[0].plot(y[:,0])
axs[1].plot(y[:,1:])
# define the name of the
legendEntries = ("a","bcdefg","h")
# set figure legend entries, number of columns, location
fig.legend(legendEntries,ncol=len(legendEntries),loc="upper center")
Here is a doc-example, emphasizing to use the argument ncol to force matplotlib to expand the legend horizontally. And here is a tutorial/example how you can place the legend of an axis outside the region of the axis.

any way to improve current time series plot output in python?

I have managed to manipulate my plotting data to render the corresponding time series plot. But I am not quite satisfied with the current output because it is not easy to understand the newly generated plot.
my current data and my output:
here is my data looks like:
update
here is my sketch code that shaped above plot data:
df=df.groupby(['date'])['qty1'].sum().reset_index()
df['year'] = pd.DatetimeIndex(df['date']).year
df['month'] = pd.DatetimeIndex(df['date']).month
plot_data=df.groupby(['year', 'month'])['qty1'].sum().unstack().fillna(0)
plot_data.plot(kind='line')
and based on this data, I am getting this plot:
but this is not what I expected for.
desired plot:
Here is the plot that I actually want it:
I didn't get this plot. How can I get this? any idea?
Is this what you are looking for ?
import pandas as pd
import matplotlib.pyplot as plt
import calendar
%matplotlib inline
df = pd.DataFrame(dic) #dic is the dictionary you provided in the github link
df.columns = [str(i) for i in range(1,13)]
df = df.T
df.columns = ['2014','2015','2016','2017','2018']
df['Avg'] = df.mean(axis =1)
fig,ax = plt.subplots(figsize = (15,7))
plt.plot(df.index,df['2016'], marker='s',color = 'green', linewidth = 1, label="2016")
plt.plot(df.index,df['2017'],"bo-", linewidth = 1.5, label="2017")
plt.plot(df.index,df['2018'], marker='s', ms =10, color = 'red', linewidth = 3, label="2018")
plt.plot(df.index,df['Avg'], "--", color = 'grey', linewidth = 8, label="5-Yr-Avg")
plt.xlabel('\n Months\n', fontsize = 25, color = 'lightslategrey')
plt.legend(frameon = False, loc = "lower center", ncol=len(df.columns), fontsize = 14)
plt.grid(axis='y')
ax.set_xticklabels([calendar.month_abbr[i] for i in range(1,13)])
plt.tick_params( left = False, labelsize= 13)
ax.spines['left'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.show()

Categories

Resources