How do you rotate axis text on Seaborn JointGrid? - seaborn

How do you rotate the text along the x-axis to avoid unreadable text like the one above?
plt.figure(figsize = (10,8))
g = sns.JointGrid(x='u',y='t',data = tdata)
g.plot(sns.scatterplot, sns.histplot)
plt.show()
And plt.figure also can't work on JoinGrid,

You can get axes with g.ax_joint and from here your can set you axis attributes.
Solution 1: tick_params()
plt.figure(figsize=(10, 8))
g = sns.JointGrid(x="u", y="t", data=tdata)
g.plot(sns.scatterplot, sns.histplot)
g.ax_joint.tick_params(axis="x", rotation=90)
plt.show()
Solution 2: set_xticklabels()
plt.figure(figsize=(10, 8))
g = sns.JointGrid(x="u", y="t", data=tdata)
g.plot(sns.scatterplot, sns.histplot)
g.ax_joint.set_xticklabels(g.ax_joint.get_xticks(), rotation=45)
plt.show()
Solution 3: get_xticklabels()
With this you can iterate over all the ticks and change their rotation individually.
plt.figure(figsize=(10, 8))
g = sns.JointGrid(x="u", y="t", data=tdata)
g.plot(sns.scatterplot, sns.histplot)
for tick in g.ax_joint.get_xticklabels():
tick.set_rotation(30)
plt.show()
PS:
xticklabels can be different for precision based on the precision value return by each function.

Related

Flip over colorbar of Seaborn heatmap

I am trying to flip over the colorbar of my Heatmap in Seaborn.
Here is how it looks at the moment.
What I would like to have is the colorbar starting from the top
with the value 0 (Green) and going to the bottom with the value 8 (red).
Please note that the Y-axis points are sorted based on the average values
from min (top) to max (bottom) and I would like to keep them this way.
Any idea if it is possible to do that?
Here is an example of the current code:
cmap1 = mcolors.LinearSegmentedColormap.from_list("n",['#00FF00','#12FF00','#24FF00','#35FF00','#47FF00','#58FF00','#6AFF00','#7CFF00','#8DFF00','#9FFF00','#B0FF00','#C2FF00','#D4FF00','#E5FF00','#F7FF00','#FFF600','#FFE400','#FFD300','#FFC100','#FFAF00','#FF9E00','#FF8C00','#FF7B00','#FF6900','#FF5700','#FF4600','#FF3400','#FF2300','#FF1100','#FF0000',])
plt.figure(figsize=(22, 12))
df = pd.DataFrame( AgainReorderindSortedEDPList, index=sortedProgrammingLanguagesBasedOnAverage, columns=sortedTasksBasedOnAverage)
mask = df.isnull()
sns.heatmap(df, annot=True, fmt="g", cmap=cmap1, mask=mask)
plt.yticks(fontsize = 12)
plt.yticks(rotation=0)
plt.xticks(fontsize = 11)
plt.ylabel('Programming Languages', size = 15)
plt.xlabel('Programming Tasks', size = 15)
plt.xticks(rotation=-45)
plt.show()
The AgainReorderindSortedEDPList, sortedProgrammingLanguagesBasedOnAverage, and sortedTasksBasedOnAverage
are the data I am using to plot this heatmap.
You simply need to call invert_yaxis() on the axes that contain the colorbar. How to do that depends a bit on how you are creating your heatmap, but unfortunately you have not provided your code.
Here is the most simple example:
uniform_data = np.random.rand(10, 12)
ax = sns.heatmap(uniform_data)
plt.gcf().axes[1].invert_yaxis()
plt.gcf() gets a reference to the current figure. Figure.axes is a list of axes in the figure. axes[1] is the second axes, which should correspond to the axes created by heatmap to plot the colorbar.

How can I decrease the width of a boxplot rectangle?

I've made a boxplot:
I would like to decrease the width of the blue/orange rectangle to get a width like the blue/orange rectangles in this boxplot:
My goal is it to decrease the total width of the boxplot so that it takes less space in a document / I can have text flow beside the plot picture.
Code so far:
ax_v = sns.boxplot(x="hasTopic", y="sentiment_sum", data=videos, orient="v")
ax_v.set(xticklabels=["ohne", "mit"])
ax_v.set(ylim=(-4, 4))
plt.xlabel('Themenbezug Videos', fontsize=14)
plt.ylabel('Sentiment', fontsize=14)
plt.show()
How can I do that in Seaborn?
plt.figure(figsize=(3.4, 4)) # <- this line
ax_v = sns.boxplot(x="hasTopic", y="sentiment_sum", data=videos, width=0.5, orient="v")
ax_v.set(xticklabels=["ohne", "mit"])
ax_v.set(ylim=(-4, 4))
plt.xlabel('Themenbezug Videos', fontsize=14)
plt.ylabel('Sentiment', fontsize=14)
plt.show()
solved it.
To color each rectangle with custom colors use:
my_pal = {0: "#3399ff", 1: "#f1c40f"} # x="hasTopic" has value 0 or 1
plt.figure(figsize=(3.4, 4), dpi=800)
ax_c = sns.violinplot(x="hasTopic", y="sentiment_sum", palette=my_pal, width=0.7, orient="v", data=channels)
ax_c.yaxis.set_major_locator(ticker.MultipleLocator(tick_spacing))
#ax_v = sns.swarmplot(x="hasTopic", y="sentiment_sum", data=comments, color=".25")
ax_c.set(xticklabels=["ohne", "mit"])
ax_c.set(ylim=(-4, 4))
plt.xlabel('Themenbezug Channels', fontsize=14)
plt.ylabel('Sentiment', fontsize=14)
plt.tight_layout()
plt.show()

Erasing previous data/plots in Julia (Plots.jl, GR backend)

I have solved an ODE in Julia describing the motion of a particle, and I have saved the coordinates and respective time in an array. I would like to create an animated gif image of a plot with the particle along the solved trajectory, but to do that (the only way that I have come up) is to plot the position of the particle using scatter, and erase the previous position of the particle every moment. However I only know about scatter! which will add more particles to the plot rather than showing the change of particle position. So how can I erase the previous plot every iteration, or are there more clever ways to do this? And what about if I want to mark down the trajectory of the particle in earlier moments using plots?
Erasing previous data is not possible with Plots.jl. The previous plot can be erased by using plot or scatter commands instead of plot! and scatter!. Here are some examples how animations can be created using the #gif macro (http://docs.juliaplots.org/latest/animations/)
Create some dummy data:
using Plots
t = range(0, 4π, length = 100)
r = range(1, 0, length = 100)
x = cos.(t) .* r
y = sin.(t) .* r
Plot only the last current point in each step:
#gif for i in eachindex(x)
scatter((x[i], y[i]), lims = (-1, 1), label = "")
end
Plot all previous steps with a marker at the current position:
#gif for i in eachindex(x)
plot(x[1:i], y[1:i], lims = (-1, 1), label = "")
scatter!((x[i], y[i]), color = 1, label = "")
end
The same as above with decreasing alpha for older steps (only showing the newest 10 steps):
#gif for i in eachindex(x)
plot(x[1:i], y[1:i], alpha = max.((1:i) .+ 10 .- i, 0) / 10, lims = (-1, 1), label = "")
scatter!((x[i], y[i]), color = 1, label = "")
end

How to break y-axis using seaborn? [duplicate]

I'm trying to create a plot using pyplot that has a discontinuous x-axis. The usual way this is drawn is that the axis will have something like this:
(values)----//----(later values)
where the // indicates that you're skipping everything between (values) and (later values).
I haven't been able to find any examples of this, so I'm wondering if it's even possible. I know you can join data over a discontinuity for, eg, financial data, but I'd like to make the jump in the axis more explicit. At the moment I'm just using subplots but I'd really like to have everything end up on the same graph in the end.
Paul's answer is a perfectly fine method of doing this.
However, if you don't want to make a custom transform, you can just use two subplots to create the same effect.
Rather than put together an example from scratch, there's an excellent example of this written by Paul Ivanov in the matplotlib examples (It's only in the current git tip, as it was only committed a few months ago. It's not on the webpage yet.).
This is just a simple modification of this example to have a discontinuous x-axis instead of the y-axis. (Which is why I'm making this post a CW)
Basically, you just do something like this:
import matplotlib.pylab as plt
import numpy as np
# If you're not familiar with np.r_, don't worry too much about this. It's just
# a series with points from 0 to 1 spaced at 0.1, and 9 to 10 with the same spacing.
x = np.r_[0:1:0.1, 9:10:0.1]
y = np.sin(x)
fig,(ax,ax2) = plt.subplots(1, 2, sharey=True)
# plot the same data on both axes
ax.plot(x, y, 'bo')
ax2.plot(x, y, 'bo')
# zoom-in / limit the view to different portions of the data
ax.set_xlim(0,1) # most of the data
ax2.set_xlim(9,10) # outliers only
# hide the spines between ax and ax2
ax.spines['right'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax.yaxis.tick_left()
ax.tick_params(labeltop='off') # don't put tick labels at the top
ax2.yaxis.tick_right()
# Make the spacing between the two axes a bit smaller
plt.subplots_adjust(wspace=0.15)
plt.show()
To add the broken axis lines // effect, we can do this (again, modified from Paul Ivanov's example):
import matplotlib.pylab as plt
import numpy as np
# If you're not familiar with np.r_, don't worry too much about this. It's just
# a series with points from 0 to 1 spaced at 0.1, and 9 to 10 with the same spacing.
x = np.r_[0:1:0.1, 9:10:0.1]
y = np.sin(x)
fig,(ax,ax2) = plt.subplots(1, 2, sharey=True)
# plot the same data on both axes
ax.plot(x, y, 'bo')
ax2.plot(x, y, 'bo')
# zoom-in / limit the view to different portions of the data
ax.set_xlim(0,1) # most of the data
ax2.set_xlim(9,10) # outliers only
# hide the spines between ax and ax2
ax.spines['right'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax.yaxis.tick_left()
ax.tick_params(labeltop='off') # don't put tick labels at the top
ax2.yaxis.tick_right()
# Make the spacing between the two axes a bit smaller
plt.subplots_adjust(wspace=0.15)
# This looks pretty good, and was fairly painless, but you can get that
# cut-out diagonal lines look with just a bit more work. The important
# thing to know here is that in axes coordinates, which are always
# between 0-1, spine endpoints are at these locations (0,0), (0,1),
# (1,0), and (1,1). Thus, we just need to put the diagonals in the
# appropriate corners of each of our axes, and so long as we use the
# right transform and disable clipping.
d = .015 # how big to make the diagonal lines in axes coordinates
# arguments to pass plot, just so we don't keep repeating them
kwargs = dict(transform=ax.transAxes, color='k', clip_on=False)
ax.plot((1-d,1+d),(-d,+d), **kwargs) # top-left diagonal
ax.plot((1-d,1+d),(1-d,1+d), **kwargs) # bottom-left diagonal
kwargs.update(transform=ax2.transAxes) # switch to the bottom axes
ax2.plot((-d,d),(-d,+d), **kwargs) # top-right diagonal
ax2.plot((-d,d),(1-d,1+d), **kwargs) # bottom-right diagonal
# What's cool about this is that now if we vary the distance between
# ax and ax2 via f.subplots_adjust(hspace=...) or plt.subplot_tool(),
# the diagonal lines will move accordingly, and stay right at the tips
# of the spines they are 'breaking'
plt.show()
I see many suggestions for this feature but no indication that it's been implemented. Here is a workable solution for the time-being. It applies a step-function transform to the x-axis. It's a lot of code, but it's fairly simple since most of it is boilerplate custom scale stuff. I have not added any graphics to indicate the location of the break, since that is a matter of style. Good luck finishing the job.
from matplotlib import pyplot as plt
from matplotlib import scale as mscale
from matplotlib import transforms as mtransforms
import numpy as np
def CustomScaleFactory(l, u):
class CustomScale(mscale.ScaleBase):
name = 'custom'
def __init__(self, axis, **kwargs):
mscale.ScaleBase.__init__(self)
self.thresh = None #thresh
def get_transform(self):
return self.CustomTransform(self.thresh)
def set_default_locators_and_formatters(self, axis):
pass
class CustomTransform(mtransforms.Transform):
input_dims = 1
output_dims = 1
is_separable = True
lower = l
upper = u
def __init__(self, thresh):
mtransforms.Transform.__init__(self)
self.thresh = thresh
def transform(self, a):
aa = a.copy()
aa[a>self.lower] = a[a>self.lower]-(self.upper-self.lower)
aa[(a>self.lower)&(a<self.upper)] = self.lower
return aa
def inverted(self):
return CustomScale.InvertedCustomTransform(self.thresh)
class InvertedCustomTransform(mtransforms.Transform):
input_dims = 1
output_dims = 1
is_separable = True
lower = l
upper = u
def __init__(self, thresh):
mtransforms.Transform.__init__(self)
self.thresh = thresh
def transform(self, a):
aa = a.copy()
aa[a>self.lower] = a[a>self.lower]+(self.upper-self.lower)
return aa
def inverted(self):
return CustomScale.CustomTransform(self.thresh)
return CustomScale
mscale.register_scale(CustomScaleFactory(1.12, 8.88))
x = np.concatenate((np.linspace(0,1,10), np.linspace(9,10,10)))
xticks = np.concatenate((np.linspace(0,1,6), np.linspace(9,10,6)))
y = np.sin(x)
plt.plot(x, y, '.')
ax = plt.gca()
ax.set_xscale('custom')
ax.set_xticks(xticks)
plt.show()
Check the brokenaxes package:
import matplotlib.pyplot as plt
from brokenaxes import brokenaxes
import numpy as np
fig = plt.figure(figsize=(5,2))
bax = brokenaxes(
xlims=((0, .1), (.4, .7)),
ylims=((-1, .7), (.79, 1)),
hspace=.05
)
x = np.linspace(0, 1, 100)
bax.plot(x, np.sin(10 * x), label='sin')
bax.plot(x, np.cos(10 * x), label='cos')
bax.legend(loc=3)
bax.set_xlabel('time')
bax.set_ylabel('value')
A very simple hack is to
scatter plot rectangles over the axes' spines and
draw the "//" as text at that position.
Worked like a charm for me:
# FAKE BROKEN AXES
# plot a white rectangle on the x-axis-spine to "break" it
xpos = 10 # x position of the "break"
ypos = plt.gca().get_ylim()[0] # y position of the "break"
plt.scatter(xpos, ypos, color='white', marker='s', s=80, clip_on=False, zorder=100)
# draw "//" on the same place as text
plt.text(xpos, ymin-0.125, r'//', fontsize=label_size, zorder=101, horizontalalignment='center', verticalalignment='center')
Example Plot:
For those interested, I've expanded upon #Paul's answer and added it to the matplotlib wrapper proplot. It can do axis "jumps", "speedups", and "slowdowns".
There is no way currently to add "crosses" that indicate the discrete jump like in Joe's answer, but I plan to add this in the future. I also plan to add a default "tick locator" that sets sensible default tick locations depending on the CutoffScale arguments.
Adressing Frederick Nord's question how to enable parallel orientation of the diagonal "breaking" lines when using a gridspec with ratios unequal 1:1, the following changes based on the proposals of Paul Ivanov and Joe Kingtons may be helpful. Width ratio can be varied using variables n and m.
import matplotlib.pylab as plt
import numpy as np
import matplotlib.gridspec as gridspec
x = np.r_[0:1:0.1, 9:10:0.1]
y = np.sin(x)
n = 5; m = 1;
gs = gridspec.GridSpec(1,2, width_ratios = [n,m])
plt.figure(figsize=(10,8))
ax = plt.subplot(gs[0,0])
ax2 = plt.subplot(gs[0,1], sharey = ax)
plt.setp(ax2.get_yticklabels(), visible=False)
plt.subplots_adjust(wspace = 0.1)
ax.plot(x, y, 'bo')
ax2.plot(x, y, 'bo')
ax.set_xlim(0,1)
ax2.set_xlim(10,8)
# hide the spines between ax and ax2
ax.spines['right'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax.yaxis.tick_left()
ax.tick_params(labeltop='off') # don't put tick labels at the top
ax2.yaxis.tick_right()
d = .015 # how big to make the diagonal lines in axes coordinates
# arguments to pass plot, just so we don't keep repeating them
kwargs = dict(transform=ax.transAxes, color='k', clip_on=False)
on = (n+m)/n; om = (n+m)/m;
ax.plot((1-d*on,1+d*on),(-d,d), **kwargs) # bottom-left diagonal
ax.plot((1-d*on,1+d*on),(1-d,1+d), **kwargs) # top-left diagonal
kwargs.update(transform=ax2.transAxes) # switch to the bottom axes
ax2.plot((-d*om,d*om),(-d,d), **kwargs) # bottom-right diagonal
ax2.plot((-d*om,d*om),(1-d,1+d), **kwargs) # top-right diagonal
plt.show()
This is a hacky but pretty solution for x-axis breaks.
The solution is based on https://matplotlib.org/stable/gallery/subplots_axes_and_figures/broken_axis.html, which gets rid of the problem with positioning the break above the spine, solved by How can I plot points so they appear over top of the spines with matplotlib?
from matplotlib.patches import Rectangle
import matplotlib.pyplot as plt
def axis_break(axis, xpos=[0.1, 0.125], slant=1.5):
d = slant # proportion of vertical to horizontal extent of the slanted line
anchor = (xpos[0], -1)
w = xpos[1] - xpos[0]
h = 1
kwargs = dict(marker=[(-1, -d), (1, d)], markersize=12, zorder=3,
linestyle="none", color='k', mec='k', mew=1, clip_on=False)
axis.add_patch(Rectangle(
anchor, w, h, fill=True, color="white",
transform=axis.transAxes, clip_on=False, zorder=3)
)
axis.plot(xpos, [0, 0], transform=axis.transAxes, **kwargs)
fig, ax = plt.subplots(1,1)
plt.plot(np.arange(10))
axis_break(ax, xpos=[0.1, 0.12], slant=1.5)
axis_break(ax, xpos=[0.3, 0.31], slant=-10)
if you want to replace an axis label, this would do the trick:
from matplotlib import ticker
def replace_pos_with_label(fig, pos, label, axis):
fig.canvas.draw() # this is needed to set up the x-ticks
labs = axis.get_xticklabels()
labels = []
locs = []
for text in labs:
x = text._x
lab = text._text
if x == pos:
lab = label
labels.append(lab)
locs.append(x)
axis.xaxis.set_major_locator(ticker.FixedLocator(locs))
axis.set_xticklabels(labels)
fig, ax = plt.subplots(1,1)
plt.plot(np.arange(10))
replace_pos_with_label(fig, 0, "-10", axis=ax)
replace_pos_with_label(fig, 6, "$10^{4}$", axis=ax)
axis_break(ax, xpos=[0.1, 0.12], slant=2)

Remove values outside of axis range?

Alright, I have a basic script to plot trajectories of an object. I have basic equations of motion solving the position of the object with respect to time. The plot itself is a 3D representation of the trajectory of the object.
I've successfully got the axis limits set, and now I want to make sure I don't see any values for this trajectory that fall OUTSIDE the axis limits. Right now, the trajectory falls below the x-y plane and continues downwards, outside of the 3D plot... Is there any way to prevent this?
Here's the entire code:
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
mpl.rcParams['legend.fontsize'] = 10
fig = plt.figure()
ax = fig.gca(projection='3d')
### Define Variables ###
time = (10) #Time to calculate the equations
t = np.linspace(0, time, 100)
g = (9.81) #Gravity
vxo = (3) #initial velocity in the x-direction
vyo = (1) #initial velocity in the y-direction
vzo = (0) #initial velocity in the z-direction
xo = (0) #initial x-position
yo = (0) #initial y-position
zo = (9) #initial z-position
### Equations of Motion ###
x = (xo + (vxo * t))
y = (yo + (vyo * t))
z = (10 - (.5 * g * (t**2)))
ax.plot(x, y, z, label=('Trajectory of Soccer Ball ' + str(time)))
ax.legend()
### Set axis limits ###
ax.set_xlim3d(0,10)
ax.set_ylim3d(0,10)
ax.set_zlim3d(0,10)
plt.show()
If your object is going beyond the limits of your axis; it is because the equations are telling it to do that. You'll have to constraint your results and filter before plotting them. Something like:
x = equation1()
if( x > someLimit )
handle error / object bounces by inverting the direction perhaps
plot(x)

Resources