Asnap hlines and vlines to whole pixels in matplotlib - python

I want to draw some hlines and vlines snapped to occupy whole pixels on the screen,
not spread across several pixels (rendered, antialiased) as usual.
Is there a transform T() so that
vlines( T(x), T(ylo), T(yhi), linewidth=Twidth(.5) )
draws whole pixels ? Or, is there a way of telling some Mac backend (I use Qt4agg) to do this ?

Do you just want to turn antialiasing off?
For example:
import matplotlib.pyplot as plt
x = [1, 4, 7]
ylow = [0, 3, -2]
yhigh = [1, 4, 2]
width = [8, 15, 6]
plt.vlines(x, ylow, yhigh, linewidth=width,
antialiased=False)
plt.axis([0, 8, -4, 5])
plt.show()

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)

Scaling graphic without scaling labels, text and axis ticks etc

I have a problem with the scaling of my graphics. I use imshow to plot two matrices the first being a 2x2 matrix and the second a 5x5 matrix. I now what both to have the same size of the boxes representing the entries of the matrices. But I want them to be same in absolute size (like pixels).
If i plot both and compare them clearly the 2x2 matrix boxes are much bigger relative to the numbers inside than the 5x5 matrix.
2x2 matrix, too big boxes
5x5 matrix, right box size
I tried to use the "figsize" parameter of the plt.figure() function but this also rescales the numbers in the boxes.
Another thing I tried is the "extent" parameter of imshow, which did not work if i try to just make the boxes smaller. It just scaled them back up. (it works tho if i make the bounding box wider, then it automatically makes them thinner but that's not what i want, example below).
with use of extent: wider and thinner but I don't what that
Now again: I kind of want to resize the boxes but don't change the size of the text/numbers so that it does not look dump if i put the graphics right next to each other in an article. It does not have to be a way to automatically match the two figure box sizes, I'm already happy with any way to resize the boxes, because it does not have to be 100% accurate.
Anyone has an idea how i can do this ?
Thanks a lot already!!
Here is the code for the two graphics with quadratic boxes (what i want, but just changed sizes):
import matplotlib.pyplot as plt
import numpy as np
plt.style.use("seaborn-dark")
def gfx_1():
fig = plt.figure()
ax1 = plt.subplot(111)
data = [[1, 2], [3, 4]]
ax1.imshow(data, interpolation="nearest")
for (i, j), data in np.ndenumerate(data):
ax1.text(i, j, s=str(data), ha='center', va='center')
ax1.set_xticklabels([])
ax1.set_yticklabels([])
ax1.set_xticks(np.arange(-0.5, 1.5, 1.))
ax1.set_yticks(np.arange(-0.5, 1.5, 1.))
ax1.grid(linewidth=2)
plt.savefig("2x2.png")
def gfx_2():
fig = plt.figure()
ax1 = plt.subplot(111)
data = [[1, 2, 3, 4, 5], [3, 4, 5, 6, 7], [6, 7, 8, 9, 10], [9, 10, 11, 12, 13], [12, 13, 14, 15, 16]]
ax1.imshow(data, interpolation="nearest")
for (i, j), data in np.ndenumerate(data):
ax1.text(i, j, s=str(data), ha='center', va='center')
ax1.set_xticklabels([])
ax1.set_yticklabels([])
ax1.set_xticks(np.arange(-0.5, 4.5, 1.))
ax1.set_yticks(np.arange(-0.5, 4.5, 1.))
ax1.grid(linewidth=2)
plt.savefig("5x5.png")
and the modified one with extend (which i don't what):
def gfx_1():
fig = plt.figure()
ax1 = plt.subplot(111)
data = [[1, 2], [3, 4]]
ax1.imshow(data, interpolation="nearest", extent=(-0.5, 3.5, -0.5, 1.5))
for (i, j), data in np.ndenumerate(data):
ax1.text(i*2, j, s=str(data), ha='center', va='center')
ax1.set_xticklabels([])
ax1.set_yticklabels([])
ax1.set_xticks(np.arange(-0.5, 3.5, 2.))
ax1.set_yticks(np.arange(-0.5, 1.5, 1.))
ax1.grid(linewidth=2)
plt.savefig("2x2_wide.png")
I think you already found the correct answer by using figsize. Sure, the resulting image might look bigger with the 2x2 grid, but it's probably just a matter of zoom in your image visualization program. If you were to show them side by side at their native resolution, the squares would look the same size.
In other words, if you create a 2x2 grid in a 2 inches x 2 inches image, then each box would be a little smaller than 1 inch wide (because of the axes and everything else). If you create your 5x5 grid in a 5x5 inches images, then the boxes would still be roughly 1 inch wide
Here is created the two images with the below code, and copy-pasted them side by side in an image editor:
def gfx_1():
fig = plt.figure(figsize=(2,2))
ax1 = plt.subplot(111)
data = [[1, 2], [3, 4]]
ax1.imshow(data, interpolation="nearest")
for (i, j), data in np.ndenumerate(data):
ax1.text(i, j, s=str(data), ha='center', va='center')
ax1.set_xticklabels([])
ax1.set_yticklabels([])
ax1.set_xticks(np.arange(-0.5, 1.5, 1.))
ax1.set_yticks(np.arange(-0.5, 1.5, 1.))
ax1.grid(linewidth=2)
plt.savefig("./2x2.png")
def gfx_2():
fig = plt.figure(figsize=(5,5))
ax1 = plt.subplot(111)
data = [[1, 2, 3, 4, 5], [3, 4, 5, 6, 7], [6, 7, 8, 9, 10], [9, 10, 11, 12, 13], [12, 13, 14, 15, 16]]
ax1.imshow(data, interpolation="nearest")
for (i, j), data in np.ndenumerate(data):
ax1.text(i, j, s=str(data), ha='center', va='center')
ax1.set_xticklabels([])
ax1.set_yticklabels([])
ax1.set_xticks(np.arange(-0.5, 4.5, 1.))
ax1.set_yticks(np.arange(-0.5, 4.5, 1.))
ax1.grid(linewidth=2)
plt.savefig("./5x5.png")
gfx_1()
gfx_2()

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()

Shortening line segments in Matplotlib plots

Is it possible to shorten line segments in a matplotlib plot to produce an image like the one below, where the line segments do not completely reach the point markers?
I've found the following stupid way, but for me it works
import matplotlib.pyplot as plt
x = [0, 1, 2, 3, 4, 5]
y = [1, 3, 2, 5, 3, 1]
plt.figure()
plt.plot(x,y,'b-')
plt.plot(x,y,'wo', markersize=25, markeredgecolor = 'w')
plt.plot(x,y,'bo', markersize=7, markeredgecolor = 'w')
plt.show()

Categories