Matplotlib change colour of every second element in yaxis - python

So I have this plot here:
What I want to do is to have every second element of yaxis to be coloured for example in blue and the rest in red.
Here is the result I want to get:
and here is the code I got:
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
mpl.rcParams['toolbar'] = 'None'
plt.style.use('fivethirtyeight')
result_7_s = amount * s_7_days
result_14_s = amount * s_14_days
result_21_s = amount * s_21_days
result_7_fc = amount * fc_7_days
result_14_fc = amount * fc_14_days
result_21_fc = amount * fc_21_days
final_y = np.array([int(result_7_s), int(result_14_s),
int(result_21_s), int(result_7_fc),
int(result_14_fc), int(result_21_fc)])
fig, ax = plt.subplots(num = 'Test')
x = np.array([7, 14, 21])
plt.xticks(ticks = x, labels = x)
plt.yticks(ticks = final_y, labels = final_y)
plt.title(f'Prices for {amount} people')
plt.xlabel('Days')
plt.ylabel('Price')
plt.tight_layout()
ax.bar(x - 0.5, final_y[:3], width=1, color='#444444', label='Standard')
ax.bar(x + 0.5, final_y[3:], width=1, color='#e5ae38', label='First Class')
ax.tick_params(axis='y', colors = 'blue') # <-------
ax.yaxis.set_major_formatter('{x}$')
plt.legend()
plt.savefig('result.png')
plt.show()

Iterate over the tick labels to apply the desired color to each one of them:
for n, tick_label in enumerate(ax.yaxis.get_ticklabels()):
tick_label.set_color("red" if n%2 else "blue")

Here is the solution I came with:
for i in range(0, 3):
plt.gca().get_yticklabels()[i].set_color('blue')
for i in range(3, 6):
plt.gca().get_yticklabels()[i].set_color('red')

Related

bar x-tick not as same as the image

Im not sure if i use the wrong data or if there is and edit i need to do and not seeing it. It would be nice if someone could take a look at the code. The problem here is that yerr at the first bar is at x=0 and in the image the yerr is somewhere around 2.5
Does someone know what i did wrong or forgot to edit?
the end result should be:
my code:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1)
y_raw = np.random.randn(1000).cumsum() + 15
x_raw = np.linspace(0, 24, y_raw.size)
x_pos = x_raw.reshape(-1, 100).min(axis=1)
y_avg = y_raw.reshape(-1, 100).mean(axis=1)
y_err = y_raw.reshape(-1, 100).ptp(axis=1)
bar_width = x_pos[1] - x_pos[0]
x_pred = np.linspace(0, 30)
y_max_pred = y_avg[0] + y_err[0] + 2.3 * x_pred
y_min_pred = y_avg[0] - y_err[0] + 1.2 * x_pred
barcolor, linecolor, fillcolor = 'wheat', 'salmon', 'lightblue'
fig, axes = fig, ax = plt.subplots()
axes.set_title(label="Future Projection of Attitudes", fontsize=15)
plt.xlabel('Minutes since class began', fontsize=12)
plt.ylabel('Snarkiness (snark units)', fontsize=12)
fig.set_size_inches(8, 6, forward=True)
axes.fill_between(x_pred, y_min_pred, y_max_pred ,color='lightblue')
axes.plot(x_raw, y_raw, color='salmon')
vert_bars = axes.bar(x_pos, y_avg, yerr=y_err, color='wheat', width = bar_width, edgecolor='grey',error_kw=dict(lw=1, capsize=5, capthick=1, ecolor='gray'))
axes.set(xlim=[0, 30], ylim=[0,100])
plt.show()
yerr is meant to be the difference between the mean and the min/max. Now you're using the full difference between max and min. You might divide it by 2 to get a better approximation. To obtain the exact values, you could calculate them explicitly (see code example).
Further, by default, the bars are center aligned vs their x-position. You can use align='edge' to left-align them (as x_pos is calculated as the minimum of the range the bar represents). You could also set clip_on=False in the err_kw to make sure the error bars are never clipped by the axes.
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1)
y_raw = np.random.randn(1000).cumsum() + 15
x_raw = np.linspace(0, 24, y_raw.size)
x_pos = x_raw.reshape(-1, 100).min(axis=1)
y_avg = y_raw.reshape(-1, 100).mean(axis=1)
y_min = y_raw.reshape(-1, 100).min(axis=1)
y_max = y_raw.reshape(-1, 100).max(axis=1)
bar_width = x_pos[1] - x_pos[0]
x_pred = np.linspace(0, 30)
y_max_pred = y_avg[0] + y_err[0] + 2.3 * x_pred
y_min_pred = y_avg[0] - y_err[0] + 1.2 * x_pred
barcolor, linecolor, fillcolor = 'wheat', 'salmon', 'lightblue'
fig, ax = plt.subplots(figsize=(8, 6))
ax.set_title(label="Future Projection of Attitudes", fontsize=15)
ax.set_xlabel('Minutes since class began', fontsize=12)
ax.set_ylabel('Snarkiness (snark units)', fontsize=12)
ax.fill_between(x_pred, y_min_pred, y_max_pred, color='lightblue')
ax.plot(x_raw, y_raw, color='salmon')
vert_bars = ax.bar(x_pos, y_avg, yerr=(y_avg - y_min, y_max - y_avg),
color='wheat', width=bar_width, edgecolor='grey', align='edge',
error_kw=dict(lw=1, capsize=5, capthick=1, ecolor='grey', clip_on=False))
ax.set(xlim=[0, 30], ylim=[0, 100])
plt.tight_layout()
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:

Append data with different colour in matplotlib in real time

I'm updating dynamically a plot in a loop:
dat=[0, max(X[:, 0])]
fig = plt.figure()
ax = fig.add_subplot(111)
Ln, = ax.plot(dat)
Ln2, = ax.plot(dat)
plt.ion()
plt.show()
for i in range(1, 40):
ax.set_xlim(int(len(X[:i])*0.8), len(X[:i])) #show last 20% data of X
Ln.set_ydata(X[:i])
Ln.set_xdata(range(len(X[:i])))
Ln2.set_ydata(Y[:i])
Ln2.set_xdata(range(len(Y[:i])))
plt.pause(0.1)
But now I want to update it in a different way: append some values and show them in other colour:
X.append(other_data)
# change colour just to other_data in X
The result should look something like this:
How could I do that?
Have a look at the link I posted. Linesegments can be used to plot colors at a particular location differently. If you want to do it in real-time you can still use line-segments. I leave that up to you.
# adjust from https://stackoverflow.com/questions/38051922/how-to-get-differents-colors-in-a-single-line-in-a-matplotlib-figure
import numpy as np, matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm
# my func
x = np.linspace(-2 * np.pi, 2 * np.pi, 100)
y = 3000 * np.sin(x)
# select how to color
cmap = ListedColormap(['r','b'])
norm = BoundaryNorm([2000,], cmap.N)
# get segments
xy = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.hstack([xy[:-1], xy[1:]])
# control which values have which colors
n = y.shape[0]
c = np.array([plt.cm.RdBu(0) if i < n//2 else plt.cm.RdBu(255) for i in range(n)])
# c = plt.cm.Reds(np.arange(0, n))
# make line collection
lc = LineCollection(segments,
colors = c
# norm = norm,
)
# plot
fig, ax = plt.subplots()
ax.add_collection(lc)
ax.autoscale()
ax.axvline(x[n//2], linestyle = 'dashed')
ax.annotate("Half-point", (x[n//2], y[n//2]), xytext = (4, 1000),
arrowprops = dict(headwidth = 30))
fig.show()

ax.annotate text partially appearing outside the figure box

Apologies, rather unskilled with programming and stackoverflow too. I am drawing bar plots on some data and have managed to add percentages beside the bars, using ax.annotate. However for the bar with highest responses I always get part of the percentage number outside the figure box, as per image below. Have tried different ideas but none worked to fix this. Looking for some suggestions on how to fix this.
Here is my code
from matplotlib import pyplot as plt
import seaborn as sns
def plot_barplot(df):
plt.rcParams.update({'font.size': 18})
sns.set(font_scale=2)
if (len(df) > 1):
fig = plt.figure(figsize=(12,10))
ax = sns.barplot(x='count', y=df.columns[0], data=df, color='blue')
else:
fig = plt.figure(figsize=(5,7))
ax = sns.barplot(x=df.columns[0], y='count', data=df, color='blue')
fig.set_tight_layout(True)
plt.rcParams.update({'font.size': 14})
total = df['count'].sum()
for p in ax.patches:
percentage ='{:.2f}%'.format(100 * p.get_width()/total)
print(percentage)
x = p.get_x() + p.get_width() + 0.02
y = p.get_y() + p.get_height()/2
ax.annotate(percentage, (x, y))
Dataframe looks like this
I would suggest you increase the axes' margins (in the x direction in that case). That is the space there is between the maximum of your data and the maximum scale on the axis. You will have to play around with the value depending on your needs, but it looks like a value of 0.1 or 0.2 should be enough.
add:
plt.rcParams.update({'axes.xmargin': 0.2})
to the top of your function
full code:
from matplotlib import pyplot as plt
import seaborn as sns
import pandas as pd
def plot_barplot(df):
plt.rcParams.update({'font.size': 18})
plt.rcParams.update({'axes.xmargin': 0.1})
sns.set(font_scale=2)
if (len(df) > 1):
fig = plt.figure(figsize=(12, 10))
ax = sns.barplot(x='count', y=df.columns[0], data=df, color='blue')
else:
fig = plt.figure(figsize=(5, 7))
ax = sns.barplot(x=df.columns[0], y='count', data=df, color='blue')
fig.set_tight_layout(True)
plt.rcParams.update({'font.size': 14})
total = df['count'].sum()
for p in ax.patches:
percentage = '{:.2f}%'.format(100 * p.get_width() / total)
print(percentage)
x = p.get_x() + p.get_width() + 0.02
y = p.get_y() + p.get_height() / 2
ax.annotate(percentage, (x, y))
df = pd.DataFrame({'question': ['Agree', 'Strongly agree'], 'count': [200, 400]})
plot_barplot(df)
plt.show()

How to add text values in bar plot seaborn python?

i want visualyze with seaborn and add the text. this my code:
# barplot price by body-style
fig, ax = plt.subplots(figsize = (12,8))
g = data[['body-style','price']].groupby(by = 'body-
style').sum().reset_index().sort_values(by='price')
x = g['body-style']
y = g['price']
ok = sns.barplot(x,y, ci = None)
ax.set_title('Price By Body Style')
def autolabel(rects):
for idx,rect in enumerate(ok):
height = rect.get_height()
g.text(rect.get_x() + rect.get_width()/2., 0.2*height,
g['price'].unique().tolist()[idx],
ha='center', va='bottom', rotation=90)
autolabel(ok)
but i go error:
You need a few changes:
As you already created the ax, you need sns.barplot(..., ax=ax).
autolabel() needs to be called with the list of bars as argument. With seaborn you get this list via ax.patches.
for idx,rect in enumerate(ok): shouldn't use ok but rects.
You can't use g.text. g is a dataframe and doesn't have a .text function. You need ax.text.
Using g['price'].unique().tolist()[idx] as the text to print doesn't have any relationship with the plotted bars. You could use height instead.
Here is some test code with toy data:
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
fig, ax = plt.subplots(figsize=(12, 8))
g = data[['body-style','price']].groupby(by = 'body-style').sum().reset_index().sort_values(by='price')
x = g['body-style']
y = g['price']
# x = list('abcdefghij')
# y = np.random.randint(20, 100, len(x))
sns.barplot(x, y, ci=None, ax=ax)
ax.set_title('Price By Body Style')
def autolabel(rects):
for rect in rects:
height = rect.get_height()
ax.text(rect.get_x() + rect.get_width() / 2., 0.2 * height,
height,
ha='center', va='bottom', rotation=90, color='white')
autolabel(ax.patches)
plt.show()
PS: You can change the fontsize of the text via a parameter to ax.text: ax.text(..., fontsize=14).

Categories