I want to create a bar plot with two level of X axis and have a twin axis as well. The plot I managed to get is as below
I want to do two changes in the above plot:
Remove Y labels from the middle Y axis
Have bar plots side by side and not stacked on one above the other.
My code is as below:
stresses = df_rootg_multilevel.index.levels[0] #Get no. of categories of stress(3 in this case)
nplots = stresses.size #Get number of sub plots (3 in this case for 3 stress)
plots_width_ratios = [df_rootg_multilevel.xs(stress).index.size for stress in stresses] #Get relative size for each plot. 4 in yhis case as there are 4 strains in each stress
fig, axes = plt.subplots(nrows=1, ncols=nplots,sharey=True, figsize=(10, 4),
gridspec_kw = dict(width_ratios=plots_width_ratios, wspace=0))
alpha = 0.3 # used for grid lines, bottom spine and separation lines between zones
for stress, ax in zip(stresses, axes):
ax1=ax.twinx()
#df_rootg_multilevel.xs(stress).plot.bar(ax=ax, legend=None, zorder=2)
# Create bar chart with grid lines and no spines except bottom one
df_rootg_multilevel.xs(stress)['rl_mean'].plot.bar(ax=ax, legend=None, zorder=2,color='red')
df_rootg_multilevel.xs(stress)['rfw_mean'].plot.bar(ax=ax1, legend=None, zorder=2,color='green')
#df_rootg_multilevel.xs(stress)['rdw_mean'].plot.bar(ax=ax, legend=None, zorder=2,color='blue')
ax.grid(axis='y', zorder=1, color='black', alpha=alpha)
for spine in ['top', 'left', 'right']:
ax.spines[spine].set_visible(False)
ax.spines['bottom'].set_alpha(alpha)
# Set and place x labels for factory zones
ax.set_xlabel(stress)
ax.xaxis.set_label_coords(x=0.5, y=-0.2)
# Format major tick labels for factory names: note that because this figure is
# only about 10 inches wide, I choose to rewrite the long names on two lines.
ticklabels = [name.replace(' ', '\n') if len(name) > 10 else name
for name in df_rootg_multilevel.xs(stress).index]
ax.set_xticklabels(ticklabels, rotation=0, ha='center')
ax.tick_params(axis='both', length=0, pad=7)
# Set and format minor tick marks for separation lines between zones: note
# that except for the first subplot, only the right tick mark is drawn to avoid
# duplicate overlapping lines so that when an alpha different from 1 is chosen
# (like in this example) all the lines look the same
if ax.is_first_col():
ax.set_xticks([*ax.get_xlim()], minor=True)
else:
ax.set_xticks([ax.get_xlim()[1]], minor=True)
ax.tick_params(which='minor', length=55, width=0.8, color=[0, 0, 0, alpha])
#ax1.get_yaxis().set_visible(False)
# Add legend using the labels and handles from the last subplot
fig.legend(*ax.get_legend_handles_labels(), frameon=False, loc=(0.08, 0.77))
My dataframe looks like below:
How can i do the required modifications.
Related
I am trying to create a heatmap by putting gridlines to some particular positions which I have done. Suppose, I tried to make gridlines in positions 358 and 589 in a matrix of length 640,640. After that, I wanted to change the label from 358 to a defined value of 999 and 589 to a specified value of 1023. However, I cannot change the x and y labels in the center position of two gridlines. For example, I have tried the following:
data = np.random.rand(640, 640)
fig, ax = plt.subplots()
im = ax.imshow(data,cmap='coolwarm')
ax.set_xticks([358,589])
ax.set_yticks([358,589])
ax.set_xticklabels([999,1023])
ax.set_yticklabels([999,1023])
ax.grid(which='major',color='black',linestyle='--',linewidth=1,alpha=0.5)
plt.show()
That create a image as follows:
Heatmap with customized labelling
But I want the labeling in the middle of two gridlines instead of the gridline positions. How can that be done?
By default, both the tick labels and the grid lines are decided via the major ticks. To change this, you could use the minor ticks to position the grid lines and the major ticks for the tick labels:
from matplotlib import pyplot as plt
import numpy as np
data = np.random.randn(640, 640).cumsum(axis=0).cumsum(axis=1)
fig, ax = plt.subplots()
im = ax.imshow(data, cmap='coolwarm')
positions = np.array([358, 589])
ax.set_xticks(positions, minor=True)
ax.set_yticks(positions, minor=True)
borders = np.append(0, positions)
mids = (borders[:-1] + borders[1:]) / 2
ax.set_xticks(mids, [999, 1023], minor=False)
ax.set_yticks(mids, [999, 1023], minor=False)
ax.grid(which='minor', color='black', linestyle='--', linewidth=1, alpha=0.9)
plt.show()
I am trying to display 2 data on a plot, each of them is assigned on primary and secondary axis respectively.
Here are the codes
ax2 = ax1.twinx()
ax1.plot(df['year'], df['present_value']/1000000, 'g-')
ax2.plot(df['year'], df['cum_pv']/1000000, 'b-')
ax1.set_xlabel('X data')
ax1.set_ylabel('Y1 data', color='g')
ax2.set_ylabel('Y2 data', color='b')
plt.show()
But it displays a chart with not equal zero levels, (notice how 0 on primary is on a different level from 0 on secondary axis) `
How can I make the zero scale on the same level from my codes above?
You can use the set_ylim()-method of each axis to adjust them manually.
import matplotlib.pyplot as plt
y1 = [-1,2,3]
y2 = [-10,-5,10]
x = [1,2,3]
fig, ax1 = plt.subplots() # opens a figure with a single axis
ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis
ax1.plot(x,y1, 'g-')
ax2.plot(x,y2, 'b-')
plt.show()
Here is a way, which assumes that there actually is a zero in both y-axes
# range
ax1_range = ax1.get_ylim()[1] - ax1.get_ylim()[0]
fct = (0 - ax1.get_ylim()[0])/ax1_range
ax2_ylim = ax2.get_ylim()
# calculate new values for the second axis
ax2_ylim_new = (ax2_ylim[0],(0 - ax2_ylim[0]) / fct + ax2_ylim[0])
# set new limits
ax2.set_ylim( ax2_ylim_new )
plt.show()
I scaled the second y-axis ax2 so that it exhibits the 0 at the same height of the axis as the first y-axis ax1. This becomes more tricky if one of the axes does not contain zero (so the limits do not have a change of the sign). You could write a little function, which handles the different cases if you like to have a generic solution.
I'm trying to plot the average calculated values as a line through the center of each plotted distribution for my data set.
My code looks like this:
for plot, var in zip(range(1, plot_num+1), var_list):
ax = fig.add_subplot(2, 2, plot)
# calculate averages
sns.stripplot(x=cluster_index_sample[cluster_type], y=cluster_index_sample[var],
jitter=jitter, linewidth=line_width, alpha=alpha, cmap=RS_colorwheel,
size=marker_size, ax=ax)
# create average lines
ax.axhline(y=cluster_index_sample['Average_'+(var)].iloc[0],
linewidth=3, xmin=0.2, xmax=0.5)
ax.set_ylabel(str(var), fontsize=y_lab)
ax.set_xlabel('')
ax.tick_params(axis='both', which='major', pad=10)
But when I plot this the horizontal lines only appear once per cluster_type (x-axis category).
How can I get it so that each set of numbered categorical values gets their own respective averages?
Since you did not provide a MCVE, I can't run your code. Nevertheless, you can try using a second for loop to iterate through all the variables for plotting the horizontal average line as follows. You will also have to modify the xmin and xmax for each line. I leave that up to you.
for plot, var in zip(range(1, plot_num+1), var_list):
ax = fig.add_subplot(2, 2, plot)
sns.stripplot(x=cluster_index_sample[cluster_type], y=cluster_index_sample[var],
jitter=jitter, linewidth=line_width, alpha=alpha, cmap=RS_colorwheel,
size=marker_size, ax=ax)
for v in var_list: # <--- Added here
ax.axhline(y=cluster_index_sample['Average_'+(v)].iloc[0],
linewidth=3, xmin=0.2, xmax=0.5) # <--- Added here
ax.set_ylabel(str(var), fontsize=y_lab)
ax.set_xlabel('')
ax.tick_params(axis='both', which='major', pad=10)
I'd like to get two lines (red and green) with the average of my data points in green and average of my data points in red. I'm using the following code, but it's not working. It's only showing the red and green data points, without the red average line
sns.set(rc={"figure.figsize": (16, 8)})
ax = events_all_metrics[["event_name","kambi_payback"]].plot(x="event_name", style='.',use_index=False, color ='green')
events_all_metrics[["event_name","pinny_payback"]].plot(x="event_name",style='.', color='red', ax=ax)
plt.tick_params(
axis='x', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom='off', # ticks along the bottom edge are off
top='off', # ticks along the top edge are off
labelbottom='off')
plt.legend(loc=4, prop={'size': 15})
pinny_mean = events_all_metrics["pinny_payback"].mean()
ax.plot(pinny_mean, label='Pinny Mean', linestyle='--', color='red')
plt.show()
This is not working because your pinny_mean is a single value in y. plot needs points in y and x. In this case I recommend you use plt.axhline instead of plot. It plots a line of constant y that covers the whole range in x. For your example:
plt.axhline(y=pinny_mean, label='Pinny Mean', linestyle='--', color='red')
I have a plot which has three legends of different length and width. I'm trying to find a way to place them nicely onto the plot. Currently I'm placing them with the default loc= which is great for dealing with the vertical placement. The issue is that by default the legends are right aligned, which looks messy.
Is there a way to use the default loc= to place them on the plot, but to have them left aligned?
Example:
From the legend guide.
import matplotlib.pyplot as plt
line1, = plt.plot([1,2,3], label="Line 1", linestyle='--')
line2, = plt.plot([3,2,1], label="Line 2\nThis is a \nvery long\nlegend", linewidth=4)
line3, = plt.plot([2,2,2], label="Can this be left justified?")
# Create a legend for the first two lines.
# 'loc' puts them in a nice place on the right.
first_legend = plt.legend(handles=[line1], loc=1)
second_legend = plt.legend(handles=[line2], loc=5)
# Add the legends manually to the current Axes.
ax = plt.gca().add_artist(first_legend)
ax = plt.gca().add_artist(second_legend)
# Create another legend for the last line.
plt.legend(handles=[line3], loc=4)
plt.show()
Which gives this
Now what I would really like is for the legends to left aligned but still on the right side of the plot. Like so:
I know I can place them at a specific location but to do this I need to specify both the x and y coords, which will be fiddly since all 3 have variable heights and widths.
You could use bbox_to_anchor to position legends precisely where you want them:
fig, ax = plt.subplots()
line1, = ax.plot([1,2,3], label="Line 1", linestyle='--')
line2, = ax.plot([3,2,1], label="Line 2\nThis is a \nvery long\nlegend", linewidth=4)
line3, = ax.plot([2,2,2], label="Can this be left justified?")
# Create a legend for the first two lines.
# 'loc' sets anchor position of the legend frame relative to itself,
# bbox_to_anchor puts legend's anchor to (x, y) in axes coordinates.
first_legend = ax.legend(handles=[line1], loc='upper left', bbox_to_anchor=(0.65, 1))
second_legend = ax.legend(handles=[line2], loc='center left', bbox_to_anchor=(0.65, 0.5))
# Add the legends manually to the current Axes.
ax.add_artist(first_legend)
ax.add_artist(second_legend)
# Create another legend for the last line.
ax.legend(handles=[line3], loc='lower left', bbox_to_anchor=(0.65, 0))
plt.show()
The only number you would need is x position of the legends' bbox_to_anchor to align to (0.65 in the example above).