how to plot error bar only positive side in seaborn? - seaborn

I´m trying to plot a chart and I have some problems to solve, sorry but I´m new in program language.
First one:
How to plot only one chart? I got that example from the internet and when a plot there is two figure for each code and two of them is blank.
The second one:
Is it possible to plot only a positive error bar?
Third one:
Is it possible to plot these two charts side by side in one figure?
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
Treat1 =pd.DataFrame({'Treatment': 1, 'weight': np.random.randint(low=1, high=100, size=40)})
Treat2 =pd.DataFrame({'Treatment': 2, 'weight': np.random.randint(low=1, high=100, size=40)})
df = pd.concat([Treat1, Treat2])
Treat3 =pd.DataFrame({'Treatment': 1, 'weight': np.random.randint(low=100, high=300, size=40)})
Treat4 =pd.DataFrame({'Treatment': 2, 'weight': np.random.randint(low=100, high=300, size=40)})
df2 = pd.concat([Treat3, Treat4])
sns.set(style="ticks")
fig, ax = plt.subplots()
color_map = dict(pos="indianred", neg="steelblue")
g = sns.catplot(x= "Treatment", y="weight", hue="Treatment", capsize=.07, ci ="sd",
data=df, kind="bar", palette = 'coolwarm', edgecolor="white")
plt.text(-0.22,99, "B")
plt.text(1.18,99, "A")
plt.ylabel('weight, kg')
plt.xticks([-0.2, 1.2], ['Group 1', 'Group 2'])
plt.ylim(0, 100)
color_map = dict(pos="indianred", neg="steelblue")
g = sns.catplot(x= "Treatment", y="weight", hue="Treatment", capsize=.07, ci ="sd",
data=df2, kind="bar", palette = 'coolwarm', edgecolor="white")
plt.text(-0.22,300, "B")
plt.text(1.18,300, "A")
plt.ylabel('weight, kg')
plt.xticks([-0.2, 1.2], ['Group 1', 'Group 2'])
plt.ylim(0, 300)
Thank you so much!

A seaborn catplot is a figure level plot, which creates and occupies a new figure. To have such a plot as a subplot, sns.barplot can be called directly. Supplying an ax tells into which subplot the barplot should go.
The barplot gets a legend, which in this case is superfluous, but it can be removed.
To only have the upper error bar visible, the rectangles of the bars can be plot on top of them. A zorder larger than the zorder of the lines of the errorbar (2) takes care of this.
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
Treat1 = pd.DataFrame({'Treatment': 1, 'weight': np.random.randint(low=1, high=100, size=40)})
Treat2 = pd.DataFrame({'Treatment': 2, 'weight': np.random.randint(low=1, high=100, size=40)})
df1 = pd.concat([Treat1, Treat2])
Treat3 = pd.DataFrame({'Treatment': 1, 'weight': np.random.randint(low=100, high=300, size=40)})
Treat4 = pd.DataFrame({'Treatment': 2, 'weight': np.random.randint(low=100, high=300, size=40)})
df2 = pd.concat([Treat3, Treat4])
sns.set(style="ticks")
fig, axs = plt.subplots(ncols=2, figsize=(10, 4))
for ax, df, height in zip(axs, [df1, df2], [100, 300]):
color_map = {1: "indianred", 2: "steelblue"}
g = sns.barplot(x="Treatment", y="weight", hue="Treatment", capsize=.07, ci="sd",
data=df, palette=color_map, edgecolor="white", ax=ax)
g.legend_.remove()
for bar in g.patches:
bar.set_zorder(3)
ax.text(-0.2, height * 0.95, "B", ha='center')
ax.text(1.2, height * 0.95, "A", ha='center')
ax.set_ylabel('weight, kg')
ax.set_xticks([-0.2, 1.2])
ax.set_xticklabels(['Group 1', 'Group 2'])
ax.set_ylim(0, height)
plt.tight_layout()
plt.show()
PS: Note that the code can be simplified somewhat if you don't use hue=. This also puts the bars in a more logical position.
fig, axs = plt.subplots(ncols=2, figsize=(10, 4))
for ax, df, height in zip(axs, [df1, df2], [100, 300]):
color_map = {1: "indianred", 2: "steelblue"}
g = sns.barplot(x="Treatment", y="weight", capsize=.07, ci="sd",
data=df, palette=color_map, edgecolor="white", ax=ax)
for bar in g.patches:
bar.set_zorder(3)
ax.text(0, height * 0.97, "B", ha='center', va='top')
ax.text(1, height * 0.97, "A", ha='center', va='top')
ax.set_ylabel('weight, kg')
ax.set_ylim(0, height)
ax.set_xticklabels(['Group 1', 'Group 2'])
plt.tight_layout()
plt.show()

Related

matplotlib pie chart cmap color variation according to correlation red , white , blue [-1,0,1]

I am struggling to make a pie chart with only three color variations according to correlation values between two quantities, the color only varies between red(-1 peak), white (0), and blue(+1 peak). this is my code, I copied some parts from StackOverflow.
import NumPy as np
import matplotlib.pyplot as plt
def mypie(slices, labels, colors, val):
colordict = {}
for l, c in zip(labels, colors):
colordict[l] = c
fig = plt.figure(figsize=[10, 10])
ax = fig.add_subplot(111)
# , autopct=make_autopct(slices))
pie_wedge_collection = ax.pie(slices, labels=labels, labeldistance=1.05)
for pie_wedge in pie_wedge_collection[0]:
pie_wedge.set_edgecolor('white')
pie_wedge.set_facecolor(colordict[pie_wedge.get_label()])
titlestring = val
ax.set_title(titlestring)
return fig, ax, pie_wedge_collection
for idx,val in enumerate(pf.index):
slices = np.abs(pf.iloc[idx,:])
labels = oct12.columns
label_df = pd.DataFrame(labels, columns=['labels'])
label_df['slices'] = slices
label_df['label'] = label_df['labels'] + ': ' + label_df['slices'].apply(lambda x: '{:.2g}'.format(x))
# sort by slices
label_df = label_df.sort_values(by='slices', ascending=False)
cmap = plt.cm.prism
colors = cmap(np.linspace(1.024, 0.004, len(slices)))
labels = oct12.columns
sizes = [30 for i in range(len(oct12.columns))]
fig, ax, pie_wedge_collection = mypie(sizes, label_df.label, colors, val)
plt.show()
This is what I am getting, here I am hardcoding the colors, however, I need to have color variations between [red, white, blue] according to the variations in correlation in slices variable.
However, I want something like below:

convert hued displot of X to plot of hue vs mode(X given hue)?

I have a Seaborn displot with a hued variable:
For each hued variable, I want to extract the mode of the density estimate and then plot each hue variable versus its mode, like so:
How do I do this?
You can use scipy.stats.gaussian_kde to create the density estimation function. And then call that function on an array of x-values to calculate its maximum.
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
df = pd.DataFrame({'x': np.random.normal(0.001, 1, 1300).cumsum() + 30,
'hue': np.repeat(np.arange(0.08, 0.20001, 0.01), 100).round(2)})
g = sns.displot(df, x='x', hue='hue', palette='turbo', kind='kde', fill=True, height=6, aspect=1.5)
plt.show()
from scipy.stats import gaussian_kde
from matplotlib.cm import ScalarMappable
fig, ax = plt.subplots(figsize=(10, 6))
hues = df['hue'].unique()
num_hues = len(hues)
colors = sns.color_palette('turbo', num_hues)
xmin, xmax = df['x'].min(), df['x'].max()
xs = np.linspace(xmin, xmax, 500)
for hue, color in zip(hues, colors):
data = df[df['hue'] == hue]['x'].values
kde = gaussian_kde(data)
mode_index = np.argmax(kde(xs))
mode_x = xs[mode_index]
sns.scatterplot(x=[hue], y=[mode_x], color=color, s=50, ax=ax)
cmap = sns.color_palette('turbo', as_cmap=True)
norm = plt.Normalize(hues.min(), hues.max())
plt.colorbar(ScalarMappable(cmap=cmap, norm=norm), ax=ax, ticks=hues)
plt.show()
Here is another approach, extracting the kde curves. It uses the legend of the kde plot to get the correspondence between the curves and the hue values. sns.kdeplot is the axes-level function used by sns.displot(kind='kde'). fill=False creates lines instead of filled polygons for the curves, for which the values are easier to extract. (ax1.fill_between can fill the curves during a second pass). The x and y axes of the second plot are switched to align the x-axes of both plots.
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
df = pd.DataFrame({'x': np.random.normal(0.007, 0.1, 1300).cumsum() + 30,
'hue': np.repeat(np.arange(0.08, 0.20001, 0.01), 100).round(2)})
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(12, 10), sharex=True)
sns.kdeplot(data=df, x='x', hue='hue', palette='turbo', fill=False, ax=ax1)
hues = [float(txt.get_text()) for txt in ax1.legend_.get_texts()]
ax2.set_yticks(hues)
ax2.set_ylabel('hue')
for hue, line in zip(hues, ax1.lines[::-1]):
color = line.get_color()
x = line.get_xdata()
y = line.get_ydata()
ax1.fill_between(x, y, color=color, alpha=0.3)
mode_ind = np.argmax(y)
mode_x = x[mode_ind]
sns.scatterplot(x=[mode_x], y=hue, color=color, s=50, ax=ax2)
sns.despine()
plt.tight_layout()
plt.show()

How to change seaborn heatmap tick_params text orientation on some axes and make it fit in the picture completely

I have a seaborn heatmap plot as shown below:
import pandas as pd
from matplotlib import pyplot as plt
import seaborn as sns
results_changed = [['equal','equal','smaller','smaller or equal','greater or equal'],
['equal','equal','smaller','smaller','greater or equal'],
['greater or equal','equal','smaller or equal','smaller','smaller'],
['equal','smaller or equal','greater or equal','greater or equal','equal'],
['equal','equal','smaller','equal','equal']]
index = ['axc123abc', 'org567def', 'cf5010wer', 'cm1235ert', 'ext447tyu']
columns = ['axc123abc', 'org567def', 'cf5010wer', 'cm1235ert', 'ext447tyu']
# create a dataframe
res_df = pd.DataFrame(results_changed, columns, index)
#construct dictionary from ordered list
category_order = ['greater', 'greater or equal', 'equal', 'smaller or equal', 'smaller']
value_to_int = {j:i for i,j in enumerate(category_order)}
n = len(value_to_int)
# discrete colormap (n samples from a given cmap)
cmap = sns.color_palette("flare", n)
ax = sns.heatmap(res_df.replace(value_to_int), cmap=cmap, vmin=0, vmax=n)
#modify colorbar:
colorbar = ax.collections[0].colorbar
colorbar.set_ticks([0.5 + i for i in range(n)])
colorbar.set_ticklabels(category_order)
ax.tick_params(right=True, top=True, labelright=True, labeltop=True)
plt.tight_layout()
plt.show()
I would like to make it more readable by adding tick descriptions for axes from each side of the figure (not just bottom and left). I've managed to do that by adding one more line of code as shown below:
import pandas as pd
from matplotlib import pyplot as plt
import seaborn as sns
results_changed = [['equal','equal','smaller','smaller or equal','greater or equal'],
['equal','equal','smaller','smaller','greater or equal'],
['greater or equal','equal','smaller or equal','smaller','smaller'],
['equal','smaller or equal','greater or equal','greater or equal','equal'],
['equal','equal','smaller','equal','equal']]
index = ['axc123abc', 'org567def', 'cf5010wer', 'cm1235ert', 'ext447tyu']
columns = ['axc123abc', 'org567def', 'cf5010wer', 'cm1235ert', 'ext447tyu']
# create a dataframe
res_df = pd.DataFrame(results_changed, columns, index)
#construct dictionary from ordered list
category_order = ['greater', 'greater or equal', 'equal', 'smaller or equal', 'smaller']
value_to_int = {j:i for i,j in enumerate(category_order)}
n = len(value_to_int)
# discrete colormap (n samples from a given cmap)
cmap = sns.color_palette("flare", n)
ax = sns.heatmap(res_df.replace(value_to_int), cmap=cmap, vmin=0, vmax=n)
#modify colorbar:
colorbar = ax.collections[0].colorbar
colorbar.set_ticks([0.5 + i for i in range(n)])
colorbar.set_ticklabels(category_order)
# newly added code line
ax.tick_params(right=True, top=True, labelright=True, labeltop=True)
plt.tight_layout()
plt.show()
The problem is, these added tick descriptions are not fully visible (due to their length). I'm not sure how to rotate ticks descriptions on particular axes. I would like to have ticks on the upper part positioned vertically (just as those at the bottom are) and ticks on the right side positioned horizontally (just as those on the left are). How can I achieve this and fit the heatmap, axes' tick descriptions and the color bar nicely in the figure? I would appreciate any suggestions.
You can do it like this:
import pandas as pd
from matplotlib import pyplot as plt
import seaborn as sns
results_changed = [['equal','equal','smaller','smaller or equal','greater or equal'],
['equal','equal','smaller','smaller','greater or equal'],
['greater or equal','equal','smaller or equal','smaller','smaller'],
['equal','smaller or equal','greater or equal','greater or equal','equal'],
['equal','equal','smaller','equal','equal']]
index = ['axc123abc', 'org567def', 'cf5010wer', 'cm1235ert', 'ext447tyu']
columns = ['axc123abc', 'org567def', 'cf5010wer', 'cm1235ert', 'ext447tyu']
# create a dataframe
res_df = pd.DataFrame(results_changed, columns, index)
# construct dictionary from ordered list
category_order = ['greater', 'greater or equal', 'equal', 'smaller or equal', 'smaller']
value_to_int = {j: i for i,j in enumerate(category_order)}
n = len(value_to_int)
# discrete colormap (n samples from a given cmap)
cmap = sns.color_palette("flare", n)
ax = sns.heatmap(res_df.replace(value_to_int), cmap=cmap, vmin=0, vmax=n, cbar_kws=dict(pad=0.2))
# modify colorbar:
colorbar = ax.collections[0].colorbar
colorbar.set_ticks([0.5 + i for i in range(n)])
colorbar.set_ticklabels(category_order)
# newly added code line
ax.tick_params(right=True, top=True, labelright=True, labeltop=True)
plt.xticks(rotation=90)
plt.yticks(rotation=0)
plt.tight_layout()
plt.show()
Output:

matplotlib: jpg image special effect/transformation as if its on a page of an open book

I wish to appear a figure (and certain text) as if they are printed on a page of an open book. Is it possible to transform an jpg image programmatically or in matplotlib to have such an effect?
You can use a background axis along with an open source book image to do something like this,
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.1, 0.8, 0.8])
ax2 = fig.add_axes([0.2, 0.3, 0.25, 0.3])
#Plot page from a book
im = plt.imread("./book_page.jpg")
implot = ax1.imshow(im, origin='lower')
# Plot a graph and set background to transparent
x = np.linspace(0,4.*np.pi,40)
y = np.sin(x)
ax2.plot(x,y,'-ro',alpha=0.5)
ax2.set_ylim([-1.1,1.1])
ax2.patch.set_alpha(0.0)
from matplotlib import rc
rc('text', usetex=True)
margin = im.shape[0]*0.075
ytext = im.shape[1]/2.+10
ax1.text(margin, ytext, "The following text is an example")
ax1.text(margin, 90, "Figure 1. Showing a sine function")
plt.show()
Which looks like this,
where I used the following book image.
UPDATE: Added non-affine transformation based on scikit-image warp example, but with Maxwell distribution. The solution saves the matplotlib line as an image in order to apply a pointwise transform. Mapping for vector graphics may be possible but I think this will be more complicated...
import numpy as np
import matplotlib.pyplot as plt
def maxwellian_transform_image(image):
from skimage.transform import PiecewiseAffineTransform, warp
rows, cols = image.shape[0], image.shape[1]
src_cols = np.linspace(0, cols, 20)
src_rows = np.linspace(0, rows, 10)
src_rows, src_cols = np.meshgrid(src_rows, src_cols)
src = np.dstack([src_cols.flat, src_rows.flat])[0]
# add maxwellian to row coordinates
x = np.linspace(0, 3., src.shape[0])
dst_rows = src[:, 1] + (np.sqrt(2/np.pi)*x**2 * np.exp(-x**2/2)) * 50
dst_cols = src[:, 0]
dst_rows *= 1.5
dst_rows -= 1.0 * 50
dst = np.vstack([dst_cols, dst_rows]).T
tform = PiecewiseAffineTransform()
tform.estimate(src, dst)
out_rows = image.shape[0] - 1.5 * 50
out_cols = cols
out = warp(image, tform, output_shape=(out_rows, out_cols))
return out
#Create the new figure
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
#Plot page from a book
im = plt.imread("./book_page.jpg")
implot = ax.imshow(im, origin='lower')
# Plot and save graph as image, will need some manipulation of location
temp, at = plt.subplots()
margin = im.shape[0]*0.1
x = np.linspace(margin,im.shape[0]/2.,40)
y = im.shape[1]/3. + 0.1*im.shape[1]*np.sin(12.*np.pi*x/im.shape[0])
at.plot(x,y,'-ro',alpha=0.5)
temp.savefig("lineplot.png",transparent=True)
#Read in plot as an image and apply transform
plot = plt.imread("./lineplot.png")
out = maxwellian_transform_image(plot)
ax.imshow(out, extent=[0,im.shape[1],0,im.shape[0]])
plt.show()
The figure now looks like,

matplotlib [python] : help in explaining an animation example

Hello All and Merry Christmas,
Could someone please explain me how the following sample of code works (http://matplotlib.sourceforge.net/examples/animation/random_data.html) ?
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
timeline = [1,2,3,4,5,6,7,8,9,10] ;
metric = [10,20,30,40,50,60,70,80,90,100] ;
fig = plt.figure()
window = fig.add_subplot(111)
line, = window.plot(np.random.rand(10))
def update(data):
line.set_ydata(data)
return line,
def data_gen():
while True:
yield np.random.rand(10)
ani = animation.FuncAnimation(fig, update, data_gen, interval=5*1000)
plt.show()
In particular, I would like to use lists ("metric") in order to update the list.
Th problem is that FuncAnimation is using generators if I am not mistaken, but, how can I make it work ?
Thank you.
You can feed FuncAnimation with any iterable not just a generator.
From docs:
class matplotlib.animation.FuncAnimation(fig, func, frames=None,
init_func=None, fargs=None, save_count=None, **kwargs)
Makes an animation by repeatedly calling a function func, passing in
(optional) arguments in fargs. frames can be a generator, an iterable,
or a number of frames. init_func is a function used to draw a clear
frame. If not given, the results of drawing from the first item in the
frames sequence will be used.
Thus the equivalen code with lists could be:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
start = [1, 0.18, 0.63, 0.29, 0.03, 0.24, 0.86, 0.07, 0.58, 0]
metric =[[0.03, 0.86, 0.65, 0.34, 0.34, 0.02, 0.22, 0.74, 0.66, 0.65],
[0.43, 0.18, 0.63, 0.29, 0.03, 0.24, 0.86, 0.07, 0.58, 0.55],
[0.66, 0.75, 0.01, 0.94, 0.72, 0.77, 0.20, 0.66, 0.81, 0.52]
]
fig = plt.figure()
window = fig.add_subplot(111)
line, = window.plot(start)
def update(data):
line.set_ydata(data)
return line,
ani = animation.FuncAnimation(fig, update, metric, interval=2*1000)
plt.show()

Resources