Matplotlib - Show gridlines and values of a 2D array? - python

I will start out by showing the plot I'm getting:
What I'm trying to do is to have the value of each position to display at the center of each square, that I managed to do, although, it is not visible because I change the map to be binary (black and white). However when I add the grid lines to have them look separated they intersect on top of the values, as shown in the image above.
The array I'm plotting is only 3x3. Therefore, I would like to see nine equal boxes in total with the values at the center of each respective box. Anyone has an idea on how to do this? Here is the piece of my script that does this.
fig, ax = plt.subplots()
ax.matshow(np.zeros((3,3)), cmap='binary')
ax.grid(which='major', color='black', linestyle='-', linewidth=1)
# To display the values
for (i, j), z in np.ndenumerate(ipcavg):
ax.text(j, i, '{:0.2f}'.format(z), ha='center', va='center')
plt.show()
Thanks!

import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.matshow(np.zeros((3,3)), cmap='binary')
ax.grid(which='major', color='black', linestyle='-', linewidth=1)
# To display the values
ax.set_xlim([0,3])
ax.set_ylim([0,3])
for (i, j), z in [[(0,0),0],[(1,1),1],[(2,2),2],[(3,3),3],[(2,3),4],[(1,3),5],[(2,1),6],[(3,2),7],[(1,2),7],[(3,1),8]]:
ax.text(j-0.5, i-0.5, '{:0.2f}'.format(z), ha='center', va='center')
plt.show()
use set_xlim,set_ylim to draw correctly the boxes
substract from x,y half of box height and provide coordinates for upper rightcorner of box

Related

Plotting points on one line in python. 1 dimension

This is the kind of graph that I would like to plot, without y axis . How can I achieve this in python using matplotlib if possible.
Sadly there is not built-in fonction in matplotlib to create such a graph.
However, You can use the following code to have a similar output. This snippet is removing unwanted spines (left, right and top) and then using scatterplot to simulate a 1d graph.
As Follows:
from matplotlib import pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(10,1))
x = [1,2,3,4,9,10]
idx = np.arange(1,len(x)+1)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_position('zero')
ax.spines['bottom'].set_alpha(0.2)
ax.get_yaxis().set_visible(False)
ax.set_xlabel('Gene 1')
ax.scatter(x, np.zeros(len(x)), s=300, c='lightgreen')
ax.set_xticks([min(x), max(x)], ['Low Values', 'High Values'])
for i in range(len(idx)):
ax.annotate(idx[i], (x[i], 0), textcoords="offset points",
xytext=(0,0), # distance from text to points (x,y)
ha='center')
plt.show()
Outputs:

Python show value in line chart

I had created a chart with values (LSMA5['Low']), I'm able to plot the chart, but I want to show the values at each point of the chart, how can I do that?
Here are the code:
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')
plt.figure(figsize=(12.6,4.6))
plt.plot(stock_store['Close'], label='ABCsTock', alpha=0.35)
plt.plot(LSMA5['Low'], label='LSMA5', alpha=1, linewidth=1)
plt.title('ABCsTock')
plt.xlabel('Jan. 01,2018 - Jul. 30,2020')
plt.ylabel('Price')
plt.legend(loc='upper right')
plt.show()
Thanks with regards
JC
If I understand what you're trying to do, here's a way to do that (with synthetic data):
x_arr = np.arange(10)
y_arr = np.random.randint(0, 10, 10)
plt.plot(x_arr, y_arr)
# zip joins x and y coordinates in pairs
for x,y in zip(x_arr,y_arr):
label = "{:.2f}".format(y)
plt.annotate(label, # this is the text
(x,y), # this is the point to label
textcoords="offset points", # how to position the text
xytext=(0,10), # distance from text to points (x,y)
ha='center') # horizontal alignment can be left, right or center
The output is:

matplotlib: axes border and tick mark/label locations

The end result I'm attempting to achieve is to have a "thicker" black boarder around my plot, along xmin, xmax, ymin, & ymax. I've tried a couple of different things (such as just drawing a rectangle on the plot, see below), but I have not been able to achieve the desired results for a few reasons.
Because I cannot just use the spines (I've set 2 of them to always be at 0), I need to add some other line or rectangle to create the desired border.
By default the first and last tick labels overhang the axes. I "overcame" this by changing the horizontal or vertical alignment, but they could still use some more padding. I know this is possible, but requires a transform and is a bit clunky.
Now I'd like to remove the first and last tick marks on both axis. This is because given the way the rectangle is drawn it is always inside the plot area, but the first and last tick mark are always outside it, regardless of how thick the rectangle is. Making the rectangle thicker only causes it to overlap the first and last tick label more, which the actual tick mark remains outside the rectangle.
Any other suggestions on how to achieve this kind of border while always maintaining an axis at 0, 0 would be welcomed. That is the overall desired result.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
from matplotlib.patches import Rectangle
X = np.random.randint(low=-9, high=9, size=10)
Y = np.random.randint(low=-9, high=9, size=10)
fig, ax = plt.subplots()
ax.axis([-10, 10, -10, 10])
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.setp(ax.xaxis.get_majorticklabels()[0], ha='left')
plt.setp(ax.xaxis.get_majorticklabels()[-1], ha='right')
plt.setp(ax.yaxis.get_majorticklabels()[0], va='bottom')
plt.setp(ax.yaxis.get_majorticklabels()[-1], va='top')
patPlotBorder = ax.add_artist(Rectangle((-10, -10), 20, 20, fill=False, color='k', linewidth=2))
ax.grid(True)
fig.set_tight_layout(True)
ax.scatter(X, Y, c="b", marker="o", s=40)
plt.show()
Without changing much of your code, you can set the clip_on to False, such that the complete rectangle is shown.
border = Rectangle((-10, -10), 20, 20, fill=False, color='k', linewidth=3, clip_on=False)
ax.add_artist(border)
Since the gridlines are shown above the axes content, you have some grey line within the rectangle border.
Alternatively, you can use two axes. One with all the content and the modified spine positions etc., and one where you just make the spines bold and remove all the rest.
import numpy as np
import matplotlib.pyplot as plt
X = np.random.randint(low=-9, high=9, size=10)
Y = np.random.randint(low=-9, high=9, size=10)
fig, ax = plt.subplots()
ax2 = fig.add_subplot(111)
ax2.patch.set_visible(False)
ax2.tick_params(left=False, bottom=False, labelleft=False, labelbottom=False)
for _, sp in ax2.spines.items():
sp.set_linewidth(3)
ax.axis([-10, 10, -10, 10])
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.setp(ax.xaxis.get_majorticklabels()[0], ha='left')
plt.setp(ax.xaxis.get_majorticklabels()[-1], ha='right')
plt.setp(ax.yaxis.get_majorticklabels()[0], va='bottom')
plt.setp(ax.yaxis.get_majorticklabels()[-1], va='top')
ax.grid(True)
fig.set_tight_layout(True)
ax.scatter(X, Y, c="b", marker="o", s=40)
plt.show()
You can access the individual grid lines by calling get_{x|y}gridlines(). Each grid line is an object of type Line2D, and you can change any of their properties, such as thickness, color, etc.
ax.get_xgridlines()[0].set_linewidth(5)
ax.get_xgridlines()[-1].set_linewidth(5)
ax.get_ygridlines()[0].set_linewidth(5)
ax.get_ygridlines()[-1].set_linewidth(5)

Automated cross stich pattern

I'm trying to create a cross stitch pattern with python as shown in the attached image.
So far I simply have the pixilated image. I could import it in excel and manually add the grid and colors etc. But how can I 'easily' automate this in python? Can I use any of the normal figure plotting functions (pyplot), or should I look into tkinter?
I'm fairly ok making scripts in python for engineering purposes, but completely new to GUI-stuff.
Ideally my output would be a vectored pdf
from scipy import misc
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as plticker
arr = misc.imread('Fox_drawing_pixelized.png', mode= 'RGBA') # 640x480x3 array
fig = plt.figure()
imgplot = plt.imshow(arr) # RGBA
ax = plt.gca()
ax.grid(True)
ax.grid(b=True, which='major', color='b', linestyle='-')
plt.minorticks_on()
loc = plticker.MultipleLocator(base=1)
ax.xaxis.set_minor_locator(loc)
ax.yaxis.set_minor_locator(loc)
ax.grid(b=True, which='minor', color='k', linestyle='-',linewidth=.3)
fig.savefig("foo.pdf", bbox_inches='tight')
How do I set the gridlines at 0.5 rather than on the units (in the middle through each pixel)?
How do I plot text throught each pixel, I already have the image in an array with numbers how to plot this on top?
To shift the gridlines, you can simply change the ticks position:
ax.set_xticks(np.arange(-0.5, arr.shape[1], 5))
will put one major tick each 5 pixels starting from the border of the first pixel.
ax.set_xticks(np.arange(-0.5, arr.shape[1], 1), minor=True)
does the same thing but every pixel for minor ticks. And then do the same for y but with arr.shape[0].
To add text, you can simply use ax.text(x, y, 'text'). I used a dictionary to match the colors (in hex format because rgb lists cannot be dictionary keys) to the texts. What you need to pay attention to is that (i, j) matrix indexes correspond to (y, x) coordinates.
Here is the full code:
from scipy import misc
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as plticker
from matplotlib.colors import to_hex
arr = misc.imread('image.png', mode= 'RGB') # array
# adapt figure size to the image size.
fig = plt.figure(figsize=(0.2*arr.shape[1], 0.2*arr.shape[0]))
imgplot = plt.imshow(arr) # RGB
ax = plt.gca()
ax.grid(True)
ax.grid(b=True, which='major', color='b', linestyle='-')
plt.minorticks_on()
ax.grid(b=True, which='minor', color='k', linestyle='-',linewidth=.3)
# put a major gridline every 5 pixels
ax.set_xticks(np.arange(-0.5, arr.shape[1], 5))
ax.set_yticks(np.arange(-0.5, arr.shape[0], 5))
# set ticks label
ax.set_xticklabels(np.arange(0, arr.shape[1], 5))
ax.set_yticklabels(np.arange(0, arr.shape[0], 5))
# put a minor gridline every pixel
ax.set_xticks(np.arange(-0.5, arr.shape[1], 1), minor=True)
ax.set_yticks(np.arange(-0.5, arr.shape[0], 1), minor=True)
fig.tight_layout(pad=0) # reduce space around image
# display text
colors_to_text = {'#000000': 'B', '#ffffff': 'W', '#f58a35': 'O', '#bcbbbb': 'G'}
for i in range(arr.shape[0]):
for j in range(arr.shape[1]):
# get the text corresponding to the pixel color
txt = colors_to_text.get(to_hex(arr[i,j]/255), '')
# display text (x, y are inverted compared to the i, j indexes of the matrix)
ax.text(j, i, txt, color='#888888', horizontalalignment='center',
verticalalignment='center', fontsize=7)
fig.savefig("foo.pdf", bbox_inches='tight')
The image gave me this result:

Matplotlib Pyplot logo/image in Plot

I'm struggling to achieve a simple goal in matplotlib... I want to put a small logo or indicator in the bottom right of my graph, without altering the axis or the real data that is being displayed. Here is my code now:
fig = plt.figure()
plt.rcParams.update({'font.size': 15})
img = plt.imread('./path/to/image.png')
ax1 = fig.add_subplot(111)
ax1.yaxis.tick_left()
ax1.tick_params(axis='y', colors='black', labelsize=15)
ax1.tick_params(axis='x', colors='black', labelsize=15)
plt.grid(b=True, which='major', color='#D3D3D3', linestyle='-')
plt.scatter([1,2,3,4,5],[5,4,3,2,1], alpha=1.0)
plt.autoscale(enable=True, axis=u'both')
fig.savefig('figure.png')
My output from this is below.
This is now laying the photo over the whole graph -- I'd like it scaled to 20% of width & height (if possible) and anchored to the bottom right. This also ruins my axis, because in this output I should be in the 0-100 range on both x & y. Any ideas to solve this, the scaling is the big issue.
Edit1: I've tried the solution below and linked questions here on SO. The problem is relying on the extent variable being passed to imshow() then doesn't work well when introducing new data. For example plotting a scatter plot coming from a data frame, could be from 0..1000 and 50..100 but using extent won't show the label or the position will be off.
Edit2: There seems to be some progress with getting the figure length with fig.get_size_inches() and passing the variable to extent. Apparently all of matplotlib graph calculations are done through inches, so this may be a promising lead.
import matplotlib.image as image
import matplotlib.pyplot as plt
im = image.imread('debian-swirl.png')
fig, ax = plt.subplots()
ax.imshow(im, aspect='auto', extent=(0.4, 0.6, .5, .7), zorder=-1)
ax.yaxis.tick_left()
ax.tick_params(axis='y', colors='black', labelsize=15)
ax.tick_params(axis='x', colors='black', labelsize=15)
ax.grid(b=True, which='major', color='#D3D3D3', linestyle='-')
ax.scatter([1,2,3,4,5],[5,4,3,2,1], alpha=1.0)
plt.show()
I added a png file to bottom left. Adjust the extent parameter to set the logo position.
Similar to : Scale image in matplotlib without changing the axis
The following is an adaptation of the answer by Kirubaharan J, but adapting the position of the logo to the extent of the graph (but the aspect ratio of the logo itself is not preserved)
import matplotlib.image as image
import matplotlib.pyplot as plt
im =image.imread('debian-swirl.png')
fig, ax = plt.subplots()
ax.yaxis.tick_left()
ax.tick_params(axis='y', colors='black', labelsize=15)
ax.tick_params(axis='x', colors='black', labelsize=15)
ax.grid(b=True, which='major', color='#D3D3D3', linestyle='-')
ax.scatter( [100,90,89,70], [55, 23,76,29], alpha=1.0)
plt.autoscale(enable=True, axis=u'both')
xrng=plt.xlim()
yrng=plt.ylim()
scale=.2 #the image takes this fraction of the graph
ax.imshow(im,aspect='auto',extent=(xrng[0],xrng[0] + scale*(xrng[1]-xrng[0]), yrng[0], yrng[0] + scale*(yrng[1]-yrng[0]) ), zorder=-1)
plt.xlim(xrng)
plt.ylim(yrng)
plt.show()
I've worked on a similar problem to print several pdf with a fix logo on every pages independant of the graph size. The best solution I found was using GridSpec.
fig = plt.figure(figsize = (11,8.5)) # 8.5" x 11" : letter format
G = plt.GridSpec(14,21)
I my case I'v build a grid of 14 square by 21 over an 8.5 x 11 inch template.
Then I just have to allocate a section of the grid for the logo and import it using matplotlib.image
ax = fig.add_subplot(G[2:5,5:14])
logo = mpimg.imread("logo.png")
imagebox = OffsetImage(logo, zoom=0.08)
ab = AnnotationBbox(imagebox, (0.4, 0.6), frameon = False)
ax.add_artist(ab)
You can control the scale using the zoom arg in OffsetImage
You can find the detail at the following link :
https://www.science-emergence.com/Articles/How-to-insert-an-image-a-picture-or-a-photo-in-a-matplotlib-figure/
i think it's best to simply put the image on a new axis...
in this way you have full control on where to put it without having to bother with existing axes
import matplotlib.image as image
import matplotlib.pyplot as plt
# create a plot
f, ax = plt.subplots()
im = image.imread("path-to-logo.png")
# put a new axes where you want the image to appear
# (x, y, width, height)
imax = f.add_axes([0.8, 0.75, 0.1, 0.1])
# remove ticks & the box from imax
imax.set_axis_off()
# print the logo with aspect="equal" to avoid distorting the logo
imax.imshow(im, aspect="equal")

Categories