Cannot save graph made in Networkx and matplotlib - python

I am using NetworkX and matplotlib to draw graph with png images as nodes. Here is my code:
import networkx as nx
import matplotlib.pyplot as plt
G = nx.DiGraph()
#DRAWING EDGES ON AXIS
G.add_edges_from(([1,2],[3,4],[5,6],[7,8]))
pos = nx.circular_layout(G)
fig = plt.figure(figsize=(20, 20))
ax = plt.axes([0, 0, 15, 15])
ax.set_aspect('equal')
nx.draw_networkx_edges(G, pos, ax=ax, arrows=True)
#TRANSFORMING COORDINATES
trans = ax.transData.transform
trans2 = fig.transFigure.inverted().transform
#PUTTING IMAGE INSTEAD OF NODES
size = 0.2
p2 = size / 2.0
for n in G:
xx, yy = trans(pos[n])
xa, ya = trans2((xx, yy))
a = plt.axes([xa - p2, ya - p2, size, size])
a.set_aspect('equal')
a.imshow(image, aspect='auto')
a.axis('off')
plt.savefig('save.png')
plt.show()
Jupyter notebook displays graph. However, when I use Pycharm it shows blank white figure. Saving by plt.savefig() also do not works. I tried to play with dpi in plt.savefig() but doesn't change anything. Will be very grateful for any clues.

Adding bbox_inches='tight' while saving solved the problem:
plt.savefig('save.png',bbox_inches='tight')
This argument cuts unnecessary whitespace margins around output image. Without it only some part of whole figure is saved.
Valuable discussion about how to save pure image in matplotlib is here:
scipy: savefig without frames, axes, only content
You can find the bbox of the image inside the axis (using
get_window_extent), and use the bbox_inches parameter to save only
that portion of the image

I've been similar situation before. Can you please try this:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

plt.axes expects a rectangle with coordinates expressed as a fraction of the figure canvas. Given figsize=(20,20),
ax = plt.axes([0, 0, 15, 15])
should really be
ax = plt.axes([0, 0, 0.75, 0.75])

Related

Save only line without axes, labels and padding in matplotlib

I would like to save matplotlib line chart to transparent png image with aspect ratio 3:1 and without axes or labels. I need the line of the graph to start and end directly at the edge of the image (without any padding).
I found several similar topics, e. g. tight savefig without axes in matplotlib or Removing white space around a saved image in matplotlib, however neither advice helped.
Here is my code:
import matplotlib.pyplot as plt
x = np.arange(1, 10)
y = np.arange(51, 60)
plt.gca().set_axis_off()
plt.subplots_adjust(top=1, bottom=0, right=1, left=0, hspace=0, wspace=0)
plt.margins(0, 0)
plt.gca().xaxis.set_major_locator(plt.NullLocator())
plt.gca().yaxis.set_major_locator(plt.NullLocator())
fig = plt.figure(figsize=(9,3))
ax = fig.add_axes([0, 0, 1, 1], frameon=False)
ax.set_axis_off()
ax.plot(x, y)
# plt.savefig("result.png", format="png", transparent=True, `bbox_inches="tight", pad_inches=0) # Result image is empty.
plt.savefig("result.png", format="png", transparent=True)
plt.show()
Still, there is some padding in result image (there is white background to show padding, but in fact image is transparent):
Is there any way to achieve chart with no padding?
Here is a solution based on one of the question you added:
import matplotlib.pyplot as plt
import numpy as np
import os
x = np.arange(1, 10)
y = np.arange(51, 60)
plt.figure(figsize=(9,3))
plt.plot(x,y)
plt.gca().set_axis_off()
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0,
hspace = 0, wspace = 0)
plt.margins(0,0)
plt.savefig("myfig.png")
#os.system('convert myfig.png -trim myfig.png') #<- a quick workaround if you are on mac or Linux.
plt.show()
Output:

Generate spectrograms with equal height in pixels and their aspect ratio kept intact with matplotlib? [duplicate]

I need to take an image and save it after some process. The figure looks fine when I display it, but after saving the figure, I got some white space around the saved image. I have tried the 'tight' option for savefig method, did not work either. The code:
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
fig = plt.figure(1)
img = mpimg.imread("image.jpg")
plt.imshow(img)
ax = fig.add_subplot(1, 1, 1)
extent = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
plt.savefig('1.png', bbox_inches=extent)
plt.axis('off')
plt.show()
I am trying to draw a basic graph by using NetworkX on a figure and save it. I realized that without a graph it works, but when added a graph I get white space around the saved image;
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import networkx as nx
G = nx.Graph()
G.add_node(1)
G.add_node(2)
G.add_node(3)
G.add_edge(1, 3)
G.add_edge(1, 2)
pos = {1:[100, 120], 2:[200, 300], 3:[50, 75]}
fig = plt.figure(1)
img = mpimg.imread("image.jpg")
plt.imshow(img)
ax = fig.add_subplot(1, 1, 1)
nx.draw(G, pos=pos)
extent = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
plt.savefig('1.png', bbox_inches=extent)
plt.axis('off')
plt.show()
You can remove the white space padding by setting bbox_inches="tight" in savefig:
plt.savefig("test.png",bbox_inches='tight')
You'll have to put the argument to bbox_inches as a string, perhaps this is why it didn't work earlier for you.
Possible duplicates:
Matplotlib plots: removing axis, legends and white spaces
How to set the margins for a matplotlib figure?
Reduce left and right margins in matplotlib plot
I cannot claim I know exactly why or how my “solution” works, but this is what I had to do when I wanted to plot the outline of a couple of aerofoil sections — without white margins — to a PDF file.
(Note that I used matplotlib inside an IPython notebook, with the -pylab flag.)
plt.gca().set_axis_off()
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0,
hspace = 0, wspace = 0)
plt.margins(0,0)
plt.gca().xaxis.set_major_locator(plt.NullLocator())
plt.gca().yaxis.set_major_locator(plt.NullLocator())
plt.savefig("filename.pdf", bbox_inches = 'tight',
pad_inches = 0)
I have tried to deactivate different parts of this, but this always lead to a white margin somewhere. You may even have modify this to keep fat lines near the limits of the figure from being shaved by the lack of margins.
After trying the above answers with no success (and a slew of other stack posts) what finally worked for me was just
plt.gca().set_axis_off()
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0,
hspace = 0, wspace = 0)
plt.margins(0,0)
plt.savefig("myfig.pdf")
Importantly this does not include the bbox or padding arguments.
I found something from Arvind Pereira (http://robotics.usc.edu/~ampereir/wordpress/?p=626) and seemed to work for me:
plt.savefig(filename, transparent = True, bbox_inches = 'tight', pad_inches = 0)
The following function incorporates johannes-s answer above. I have tested it with plt.figure and plt.subplots() with multiple axes, and it works nicely.
def save(filepath, fig=None):
'''Save the current image with no whitespace
Example filepath: "myfig.png" or r"C:\myfig.pdf"
'''
import matplotlib.pyplot as plt
if not fig:
fig = plt.gcf()
plt.subplots_adjust(0,0,1,1,0,0)
for ax in fig.axes:
ax.axis('off')
ax.margins(0,0)
ax.xaxis.set_major_locator(plt.NullLocator())
ax.yaxis.set_major_locator(plt.NullLocator())
fig.savefig(filepath, pad_inches = 0, bbox_inches='tight')
The most straightforward method is to use plt.tight_layout transformation which is actually more preferable as it doesn't do unnecessary cropping when using plt.savefig
import matplotlib as plt
plt.plot([1,2,3], [1,2,3])
plt.tight_layout(pad=0)
plt.savefig('plot.png')
However, this may not be preferable for complex plots that modifies the figure. Refer to Johannes S's answer that uses plt.subplots_adjust if that's the case.
I found the following codes work perfectly for the job.
fig = plt.figure(figsize=[6,6])
ax = fig.add_subplot(111)
ax.imshow(data)
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.set_frame_on(False)
plt.savefig('data.png', dpi=400, bbox_inches='tight',pad_inches=0)
This worked for me
plt.savefig(save_path,bbox_inches='tight', pad_inches=0, transparent=True)
i followed this sequence and it worked like a charm.
plt.axis("off")
fig=plt.imshow(image array,interpolation='nearest')
fig.axes.get_xaxis().set_visible(False)
fig.axes.get_yaxis().set_visible(False)
plt.savefig('destination_path.pdf',
bbox_inches='tight', pad_inches=0, format='pdf', dpi=1200)
A much simpler approach I found is to use plt.imsave :
import matplotlib.pyplot as plt
arr = plt.imread(path)
plt.imsave('test.png', arr)
For anyone who wants to work in pixels rather than inches this will work.
Plus the usual you will also need
from matplotlib.transforms import Bbox
Then you can use the following:
my_dpi = 100 # Good default - doesn't really matter
# Size of output in pixels
h = 224
w = 224
fig, ax = plt.subplots(1, figsize=(w/my_dpi, h/my_dpi), dpi=my_dpi)
ax.set_position([0, 0, 1, 1]) # Critical!
# Do some stuff
ax.imshow(img)
ax.imshow(heatmap) # 4-channel RGBA
ax.plot([50, 100, 150], [50, 100, 150], color="red")
ax.axis("off")
fig.savefig("saved_img.png",
bbox_inches=Bbox([[0, 0], [w/my_dpi, h/my_dpi]]),
dpi=my_dpi)
So the solution depend on whether you adjust the subplot. If you specify plt.subplots_adjust (top, bottom, right, left), you don't want to use the kwargs of bbox_inches='tight' with plt.savefig, as it paradoxically creates whitespace padding. It also allows you to save the image as the same dims as the input image (600x600 input image saves as 600x600 pixel output image).
If you don't care about the output image size consistency, you can omit the plt.subplots_adjust attributes and just use the bbox_inches='tight' and pad_inches=0 kwargs with plt.savefig.
This solution works for matplotlib versions 3.0.1, 3.0.3 and 3.2.1. It also works when you have more than 1 subplot (eg. plt.subplots(2,2,...).
def save_inp_as_output(_img, c_name, dpi=100):
h, w, _ = _img.shape
fig, axes = plt.subplots(figsize=(h/dpi, w/dpi))
fig.subplots_adjust(top=1.0, bottom=0, right=1.0, left=0, hspace=0, wspace=0)
axes.imshow(_img)
axes.axis('off')
plt.savefig(c_name, dpi=dpi, format='jpeg')
You may try this. It solved my issue.
import matplotlib.image as mpimg
img = mpimg.imread("src.png")
mpimg.imsave("out.png", img, cmap=cmap)
In a Jupyter notebook, one can add this line:
%config InlineBackend.print_figure_kwargs = {'pad_inches':0}
Here is a minimal example
import matplotlib.pyplot as plt
import numpy as np
%config InlineBackend.print_figure_kwargs = {'pad_inches':0}
fig, ax = plt.subplots()
ax.axis("off")
ax.imshow(np.fromfunction(lambda i, j: np.sin(j), (15, 15)), cmap="YlGnBu")
This works for me saving a numpy array plotted with imshow to file
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(10,10))
plt.imshow(img) # your image here
plt.axis("off")
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0,
hspace = 0, wspace = 0)
plt.savefig("example2.png", box_inches='tight', dpi=100)
plt.show()

Colour fill based on values?

I am looking for a way in Python/matplotlib/pandas to create a color fill for a graph similar to this (Source: http://www.scminc.com/resources/SCM_TIPSTRICKS_Petrel_Well_Sections_2013_July14.pdf):
It uses a color map for the fill (left of the image), and based on a specific interval on the x-axis assigns a color to it. Unfortunately, I haven't found a solution, and since I am pretty new to Python in general, I am unable to find a way to do that.
Many thanks
You can plot the fill as a background with imshow, then clip it. You can use fill_betweenx to make the mask.
Here's an example using random data:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import PathPatch
# Make a random x and a y to go with it.
np.random.seed(26)
x = np.random.normal(0, 1, 200).cumsum()
y = np.arange(x.size)
# Set up the figure.
fig, ax = plt.subplots(figsize=(2, 10))
# Make the background 'image'.
im = ax.imshow(x.reshape(-1, 1),
aspect='auto',
origin='lower',
extent=[x.min(), x.max(), y.min(), y.max()]
)
# Draw the path.
paths = ax.fill_betweenx(y, x, x.min(),
facecolor='none',
lw=2,
edgecolor='b',
)
# Make the 'fill' mask and clip the background image with it.
patch = PathPatch(paths._paths[0], visible=False)
ax.add_artist(patch)
im.set_clip_path(patch)
# Finish up.
ax.invert_yaxis()
plt.show()
This yields:

python matplotlib overlapping rectangles at certain size

I'm trying to plot different rectangles with matplotlib which should have a little gap in between them like in following example:
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np
Nmax = 200
xvalues = np.arange(Nmax)
fig = plt.figure()
ax = plt.subplot(111)
for xvalue in xvalues:
rect = Rectangle(
xy=(xvalue - 0.25, xvalue),
width = 0.5,
height = 1.5,
facecolor = 'r',
edgecolor = 'r',
)
ax.add_patch(rect)
ax.autoscale_view()
plt.show()
It's working as I would like to work for Nmax = 20 rectangles. Plot of Nmax=20 below:
As the rectangle positions are always 1 'unit' apart with a width of 0.5 there is always a spacing of 0.5 between two neighbouring rectangles.
However when I try it for example with 200 rectangles the rectangles get thicker and start overlapping. Upon zooming into the graph the rectangles are separated again. But saving the original figure as pdf still yields overlapping rectangles. Zoom of pdf with Nmax=200 below:
I don't know why this is happening, as I'm specifying still their widths to 0.5. I would be glad if someone could give me a hint on this.
I'm not sure but in a vector format it should be possible to determine the rectangle position exactly, so maybe saving it as svg and converting it to pdf would do the trick?
Final solution:
alright, thanks to zephyr the solution is to turn off the rectangle edge:
edgecolor = 'none',
Changing the edgecolor to 'none' in matplotlib.finance would also solve overlapping candlestick bars which seems to be the same problem here
Assuming you do want to use an edgecolor (that is, setting edgecolor='none' is not an option), you could produce a PDF which shows the space between boxes by increasing the figsize and dpi when creating the figure:
fig = plt.figure(figsize=(12,4), dpi=600)
If the figsize and dpi are big enough, the pdf-generating backend will display the whitespace between the rectangles:
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np
Nmax = 200
xvalues = np.arange(Nmax)
fig = plt.figure(figsize=(12,4), dpi=600)
ax = plt.subplot(111)
for xvalue in xvalues:
rect = Rectangle(
xy=(xvalue - 0.25, xvalue),
width = 0.5,
height = 1.5,
facecolor = 'r',
edgecolor = 'r',
)
ax.add_patch(rect)
ax.autoscale_view()
# plt.show()
plt.savefig('/tmp/test.pdf')
Detail:
Another option is to reduce the linewidth when creating the Rectangle:
Rectangle(..., edgecolor='b', linewidth=0.01)
Thanks to zephyr the solution is to turn off the rectangle edge:
edgecolor = 'none',

Removing white space around a saved image

I need to take an image and save it after some process. The figure looks fine when I display it, but after saving the figure, I got some white space around the saved image. I have tried the 'tight' option for savefig method, did not work either. The code:
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
fig = plt.figure(1)
img = mpimg.imread("image.jpg")
plt.imshow(img)
ax = fig.add_subplot(1, 1, 1)
extent = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
plt.savefig('1.png', bbox_inches=extent)
plt.axis('off')
plt.show()
I am trying to draw a basic graph by using NetworkX on a figure and save it. I realized that without a graph it works, but when added a graph I get white space around the saved image;
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import networkx as nx
G = nx.Graph()
G.add_node(1)
G.add_node(2)
G.add_node(3)
G.add_edge(1, 3)
G.add_edge(1, 2)
pos = {1:[100, 120], 2:[200, 300], 3:[50, 75]}
fig = plt.figure(1)
img = mpimg.imread("image.jpg")
plt.imshow(img)
ax = fig.add_subplot(1, 1, 1)
nx.draw(G, pos=pos)
extent = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
plt.savefig('1.png', bbox_inches=extent)
plt.axis('off')
plt.show()
You can remove the white space padding by setting bbox_inches="tight" in savefig:
plt.savefig("test.png",bbox_inches='tight')
You'll have to put the argument to bbox_inches as a string, perhaps this is why it didn't work earlier for you.
Possible duplicates:
Matplotlib plots: removing axis, legends and white spaces
How to set the margins for a matplotlib figure?
Reduce left and right margins in matplotlib plot
I cannot claim I know exactly why or how my “solution” works, but this is what I had to do when I wanted to plot the outline of a couple of aerofoil sections — without white margins — to a PDF file.
(Note that I used matplotlib inside an IPython notebook, with the -pylab flag.)
plt.gca().set_axis_off()
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0,
hspace = 0, wspace = 0)
plt.margins(0,0)
plt.gca().xaxis.set_major_locator(plt.NullLocator())
plt.gca().yaxis.set_major_locator(plt.NullLocator())
plt.savefig("filename.pdf", bbox_inches = 'tight',
pad_inches = 0)
I have tried to deactivate different parts of this, but this always lead to a white margin somewhere. You may even have modify this to keep fat lines near the limits of the figure from being shaved by the lack of margins.
After trying the above answers with no success (and a slew of other stack posts) what finally worked for me was just
plt.gca().set_axis_off()
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0,
hspace = 0, wspace = 0)
plt.margins(0,0)
plt.savefig("myfig.pdf")
Importantly this does not include the bbox or padding arguments.
I found something from Arvind Pereira (http://robotics.usc.edu/~ampereir/wordpress/?p=626) and seemed to work for me:
plt.savefig(filename, transparent = True, bbox_inches = 'tight', pad_inches = 0)
The following function incorporates johannes-s answer above. I have tested it with plt.figure and plt.subplots() with multiple axes, and it works nicely.
def save(filepath, fig=None):
'''Save the current image with no whitespace
Example filepath: "myfig.png" or r"C:\myfig.pdf"
'''
import matplotlib.pyplot as plt
if not fig:
fig = plt.gcf()
plt.subplots_adjust(0,0,1,1,0,0)
for ax in fig.axes:
ax.axis('off')
ax.margins(0,0)
ax.xaxis.set_major_locator(plt.NullLocator())
ax.yaxis.set_major_locator(plt.NullLocator())
fig.savefig(filepath, pad_inches = 0, bbox_inches='tight')
The most straightforward method is to use plt.tight_layout transformation which is actually more preferable as it doesn't do unnecessary cropping when using plt.savefig
import matplotlib as plt
plt.plot([1,2,3], [1,2,3])
plt.tight_layout(pad=0)
plt.savefig('plot.png')
However, this may not be preferable for complex plots that modifies the figure. Refer to Johannes S's answer that uses plt.subplots_adjust if that's the case.
I found the following codes work perfectly for the job.
fig = plt.figure(figsize=[6,6])
ax = fig.add_subplot(111)
ax.imshow(data)
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.set_frame_on(False)
plt.savefig('data.png', dpi=400, bbox_inches='tight',pad_inches=0)
This worked for me
plt.savefig(save_path,bbox_inches='tight', pad_inches=0, transparent=True)
i followed this sequence and it worked like a charm.
plt.axis("off")
fig=plt.imshow(image array,interpolation='nearest')
fig.axes.get_xaxis().set_visible(False)
fig.axes.get_yaxis().set_visible(False)
plt.savefig('destination_path.pdf',
bbox_inches='tight', pad_inches=0, format='pdf', dpi=1200)
A much simpler approach I found is to use plt.imsave :
import matplotlib.pyplot as plt
arr = plt.imread(path)
plt.imsave('test.png', arr)
For anyone who wants to work in pixels rather than inches this will work.
Plus the usual you will also need
from matplotlib.transforms import Bbox
Then you can use the following:
my_dpi = 100 # Good default - doesn't really matter
# Size of output in pixels
h = 224
w = 224
fig, ax = plt.subplots(1, figsize=(w/my_dpi, h/my_dpi), dpi=my_dpi)
ax.set_position([0, 0, 1, 1]) # Critical!
# Do some stuff
ax.imshow(img)
ax.imshow(heatmap) # 4-channel RGBA
ax.plot([50, 100, 150], [50, 100, 150], color="red")
ax.axis("off")
fig.savefig("saved_img.png",
bbox_inches=Bbox([[0, 0], [w/my_dpi, h/my_dpi]]),
dpi=my_dpi)
So the solution depend on whether you adjust the subplot. If you specify plt.subplots_adjust (top, bottom, right, left), you don't want to use the kwargs of bbox_inches='tight' with plt.savefig, as it paradoxically creates whitespace padding. It also allows you to save the image as the same dims as the input image (600x600 input image saves as 600x600 pixel output image).
If you don't care about the output image size consistency, you can omit the plt.subplots_adjust attributes and just use the bbox_inches='tight' and pad_inches=0 kwargs with plt.savefig.
This solution works for matplotlib versions 3.0.1, 3.0.3 and 3.2.1. It also works when you have more than 1 subplot (eg. plt.subplots(2,2,...).
def save_inp_as_output(_img, c_name, dpi=100):
h, w, _ = _img.shape
fig, axes = plt.subplots(figsize=(h/dpi, w/dpi))
fig.subplots_adjust(top=1.0, bottom=0, right=1.0, left=0, hspace=0, wspace=0)
axes.imshow(_img)
axes.axis('off')
plt.savefig(c_name, dpi=dpi, format='jpeg')
You may try this. It solved my issue.
import matplotlib.image as mpimg
img = mpimg.imread("src.png")
mpimg.imsave("out.png", img, cmap=cmap)
In a Jupyter notebook, one can add this line:
%config InlineBackend.print_figure_kwargs = {'pad_inches':0}
Here is a minimal example
import matplotlib.pyplot as plt
import numpy as np
%config InlineBackend.print_figure_kwargs = {'pad_inches':0}
fig, ax = plt.subplots()
ax.axis("off")
ax.imshow(np.fromfunction(lambda i, j: np.sin(j), (15, 15)), cmap="YlGnBu")
This works for me saving a numpy array plotted with imshow to file
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(10,10))
plt.imshow(img) # your image here
plt.axis("off")
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0,
hspace = 0, wspace = 0)
plt.savefig("example2.png", box_inches='tight', dpi=100)
plt.show()

Categories