Matplotlib pyplot: Is it possible to have exponential axis? - python

i need "the opposite" of loglog for my pyplot chart.
How can i achive exponential axis?
Data looks like:
x = [1, 2, 4]
y = [1, 2, 4]
z = [2, 2, 2]
quadranten = plt.figure()
s = [20*4**n for n in z]
fig, ax = plt.subplots()
ax.axis([1, 5, 1, 5])
ax.loglog() <-- opposite function?
xstart, xend = ax.get_xlim()
ax.xaxis.set_ticks(np.arange(xstart, xend, 0.712123))
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
ystart, yend = ax.get_ylim()
ax.yaxis.set_ticks(np.arange(ystart, yend, 0.712123))
ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
plt.xlabel('x')
plt.ylabel('y')
plt.scatter(x,y,s=s)
plt.show()
The goal is the x axis to have evenly sized steps: 0, 1, 2, 4, 8,...
Same for the y axis with evenly sized steps getting exponentially bigger by a factor (for example two): 0, 1, 2, 4, 8, ...
Is this possible?
Something like this:

loglog takes arguments basex and basey that control the base of the logarithms:
loglog(arange(1,100), arange(1,100)**2, basex=2, basey=2)

Related

Prevent axes from cutting off dots in matplotlib scatter plots

import matplotlib.pyplot as plt
x = [1, 2, 3, 4]
y = [0, 1, 7, 2]
plt.scatter(x, y, color='red')
plt.title('number of iterations')
plt.xlim([1, 4])
plt.ylim([1, 8])
If one was to plot this data, the dots on the axes are partially cut off. Is there a way to prevent this (i.e. can the dots be plotted on top of the axes)?
Setting the clip_on attribute to False allows you to go beyond the axes, but by default the axes will be on top. For example, the script
x = [1, 2, 3, 4]
y = [0, 1, 7, 2]
plt.scatter(x, y, color="red", clip_on=False)
plt.title('number of iterations')
plt.xlim([1, 4])
plt.ylim([1, 8])
Yields the following.
Note that the axes "cut through" the dots. If you want the dots to go on top of the axes/labels, you need to change the default zorder. For example, the script
x = [1, 2, 3, 4]
y = [0, 1, 7, 2]
plt.scatter(x, y, color="red", clip_on=False, zorder = 10)
plt.title('number of iterations')
plt.xlim([1, 4])
plt.ylim([1, 8])
yields
Note: any zorder value 3 or greater will work here.
Set clip_on to False:
plt.scatter(x, y, color='red', clip_on=False)

Determine plot size with grid Matplotlib

I'm plotting some data using plt.scatter(), and I want to change the size of the plot which is it on, however the only results which come up when you search 'change plot size' are things to do with changing the figure's size, which I am not looking to do.
To visualise my issue, I have a reproducible example where I'm trying to plot 4 points on a 10x10 grid, however the size of the scatter plot is determined by the data not the grid
The two graphs above demonstrate my problem, I am trying to plot the four points on the left graph on the 10x10 grid seen on the right graph. I have added in a datapoint at (10, 10) to show this.
My code is currently:
x = [1, 2, 3, 4]
y = [1, 2, 3, 4]
fig = plt.figure()
ax = fig.gca()
ax.set_xticks(np.arange(0, 11, 1))
ax.set_yticks(np.arange(0, 11, 1))
plt.grid()
plt.scatter(x, y)
Which produces the left graph.
IIUC:
x = [1, 2, 3, 4]
y = [1, 2, 3, 4]
fig = plt.figure()
plt.xlim(0, 10)
plt.ylim(0, 10)
plt.grid()
plt.scatter(x, y)
Output:
Just change the limits of x and y axes:
plt.xlim(0,11)
plt.ylim(0,11)

How to use matplotlib to draw 3D barplot with specific color according to the bar height

Hey guys, I am using matplotlib to draw 3D barplot. I wonder whether it is possible to give each bar (total 9) a different color according to their value (dz) and also show the corresponding level in this figure.
Many thanks in advance!!!
My code is below:
fig_stat=plt.figure(dpi=100)
ax_stat = fig_stat.add_subplot(111, projection='3d')
xpos = [1, 1, 1, 3, 3, 3, 5, 5, 5]
ypos = [1, 3, 5, 1, 3, 5, 1, 3, 5]
# initial z position (x-y plane), np.zeros(x), x
zpos = [0, 0, 0, 0, 0, 0, 0, 0, 0]
# x is the number of points you need
dx = np.ones(9)
dy = np.ones(9)
# dz = [1, 2, 3, 4, 10, 6, 7, 8, 9] # signal intensity
dz = col_stat.iloc[:, -2]
ax_stat.set_xlabel('X axis')
ax_stat.set_ylabel('Y axis')
ax_stat.set_zlabel('Signal Intensity')
ax_stat.bar3d(xpos, ypos, zpos, dx, dy, dz, color='#00ceaa')
plt.title("Position_{}".format(L))
The bar3d method allows to enter as color an array of the same length as the data arrays. Therefore, a colormap can be used to get the colors corresponding to each dz:
from matplotlib import cm
from matplotlib.colors import Normalize
cmap = cm.get_cmap('plasma')
norm = Normalize(vmin=min(dz), vmax=max(dz))
colors = cmap(norm(dz))
And use this colors as input in the bar3d color parameter instead of '#00ceaa'. Afterwards, the colorbar can be shown using this piece of code:
sc = cm.ScalarMappable(cmap=cmap,norm=norm)
sc.set_array([])
plt.colorbar(sc)
Which will generate a plot looking like this:

Suggestions to plot overlapping lines in matplotlib?

Does anybody have a suggestion on what's the best way to present overlapping lines on a plot? I have a lot of them, and I had the idea of having full lines of different colors where they don't overlap, and having dashed lines where they do overlap so that all colors are visible and overlapping colors are seen.
But still, how do I that.
I have the same issue on a plot with a high degree of discretization.
Here the starting situation:
import matplotlib.pyplot as plt
grid=[x for x in range(10)]
graphs=[
[1,1,1,4,4,4,3,5,6,0],
[1,1,1,5,5,5,3,5,6,0],
[1,1,1,0,0,3,3,2,4,0],
[1,2,4,4,3,2,3,2,4,0],
[1,2,3,3,4,4,3,2,6,0],
[1,1,3,3,0,3,3,5,4,3],
]
for gg,graph in enumerate(graphs):
plt.plot(grid,graph,label='g'+str(gg))
plt.legend(loc=3,bbox_to_anchor=(1,0))
plt.show()
No one can say where the green and blue lines run exactly
and my "solution"
import matplotlib.pyplot as plt
grid=[x for x in range(10)]
graphs=[
[1,1,1,4,4,4,3,5,6,0],
[1,1,1,5,5,5,3,5,6,0],
[1,1,1,0,0,3,3,2,4,0],
[1,2,4,4,3,2,3,2,4,0],
[1,2,3,3,4,4,3,2,6,0],
[1,1,3,3,0,3,3,5,4,3],
]
for gg,graph in enumerate(graphs):
lw=10-8*gg/len(graphs)
ls=['-','--','-.',':'][gg%4]
plt.plot(grid,graph,label='g'+str(gg), linestyle=ls, linewidth=lw)
plt.legend(loc=3,bbox_to_anchor=(1,0))
plt.show()
I am grateful for suggestions on improvement!
Just decrease the opacity of the lines so that they are see-through. You can achieve that using the alpha variable. Example:
plt.plot(x, y, alpha=0.7)
Where alpha ranging from 0-1, with 0 being invisible.
imagine your panda data frame is called respone_times, then you can use alpha to set different opacity for your graphs. Check the picture before and after using alpha.
plt.figure(figsize=(15, 7))
plt.plot(respone_times,alpha=0.5)
plt.title('a sample title')
plt.grid(True)
plt.show()
Depending on your data and use case, it might be OK to add a bit of random jitter to artificially separate the lines.
from numpy.random import default_rng
import pandas as pd
rng = default_rng()
def jitter_df(df: pd.DataFrame, std_ratio: float) -> pd.DataFrame:
"""
Add jitter to a DataFrame.
Adds normal distributed jitter with mean 0 to each of the
DataFrame's columns. The jitter's std is the column's std times
`std_ratio`.
Returns the jittered DataFrame.
"""
std = df.std().values * std_ratio
jitter = pd.DataFrame(
std * rng.standard_normal(df.shape),
index=df.index,
columns=df.columns,
)
return df + jitter
Here's a plot of the original data from Markus Dutschke's example:
And here's the jittered version, with std_ratio set to 0.1:
Replacing solid lines by dots or dashes works too
g = sns.FacetGrid(data, col='config', row='outputs', sharex=False)
g.map_dataframe(sns.lineplot, x='lag',y='correlation',hue='card', linestyle='dotted')
Instead of random jitter, the lines can be offset just a little bit, creating a layered appearance:
import matplotlib.pyplot as plt
from matplotlib.transforms import offset_copy
grid = list(range(10))
graphs = [[1, 1, 1, 4, 4, 4, 3, 5, 6, 0],
[1, 1, 1, 5, 5, 5, 3, 5, 6, 0],
[1, 1, 1, 0, 0, 3, 3, 2, 4, 0],
[1, 2, 4, 4, 3, 2, 3, 2, 4, 0],
[1, 2, 3, 3, 4, 4, 3, 2, 6, 0],
[1, 1, 3, 3, 0, 3, 3, 5, 4, 3]]
fig, ax = plt.subplots()
lw = 1
for gg, graph in enumerate(graphs):
trans_offset = offset_copy(ax.transData, fig=fig, x=lw * gg, y=lw * gg, units='dots')
ax.plot(grid, graph, lw=lw, transform=trans_offset, label='g' + str(gg))
ax.legend(loc='upper left', bbox_to_anchor=(1.01, 1.01))
# manually set the axes limits, because the transform doesn't set them automatically
ax.set_xlim(grid[0] - .5, grid[-1] + .5)
ax.set_ylim(min([min(g) for g in graphs]) - .5, max([max(g) for g in graphs]) + .5)
plt.tight_layout()
plt.show()

Two point segment plot in matplotlib

How can I plot a two point line segment plot as shown in the following figure
The data is like below
x = [1,2,3,4,5,6]
y = [1.2,1.2,-2.1, -2.1, 4.1, -4.1] #these y values are always in pair such that I need a solid line to connect these equivalent values and then a dotted line between this pair and the next pair.
Does this achieve what you were hoping?
import numpy as np
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5, 6]
y = [1.2, 1.2, 2.1, 2.1, -4.1, -4.1]
plt.plot(x, y, 'm--')
pair_x_array = np.reshape(x, (-1, 2))
pair_y_array = np.reshape(y, (-1, 2))
for i, pair_x in enumerate(pair_x_array):
pair_y = pair_y_array[i]
plt.plot(pair_x, pair_y, 'm', linewidth=3)
plt.show()
Do you mean something like this?
import pylab
xdata = [0, 1, 2, 3, 4, 5]
ydata = [0, 1, 2, 2, 1, 0]
# Assuming xdata, ydata are of suitable length and type
plots = [pylab.plot(xdata[i:i + 2], ydata[i:i + 2], **whatever_keyword_arguments) for i in xrange(0, len(xdata), 2)]
pylab.show()
Edit after OP edit:
I see what you mean, and it's trivial to add the lines in dashes.
def plot_broken(xseq, yseq, even=True, **plot_kwargs):
size = len(xseq)
assert size == len(yseq)
assert size % 2 == 0
start = 0 if even else 1
return [pylab.plot(xseq[i:i + 2], yseq[i:i + 2], **plot_kwargs)
for i in xrange(start, size, 2)]
plots = plot_broken(xdata, ydata, even=True, color="m",
linestyle="solid")
plots += plot_broken(xdata, ydata, even=False, color="m",
linestyle="dashed")

Categories