Create a detailed svg graph with matplotlib - python

I use the Python library matplotlib to draw a graph with a lot of data. Upon executing plt.show() I can zoom in and see the details of the graph. However, I would like to save the graph into a svg file with plt.savefig and see these details which by default are not visible from the default non-zoomed-in view. How can I do that?
Please, note that increasing DPI or inch by inch dimensions is meaningless when working with vector graphics formats such as the svg file.
As an example, consider the following program.
import matplotlib.pyplot as plt
import numpy as np
import math
x = np.arange(0,100,0.00001)
y = x*np.sin(2*math.pi*(x**1.2))
plt.plot(y)
plt.savefig('test.svg')
We will get the following plot which even when we zoom, we cannot see the details of the sine wave periods.
But we can see the details of the sine wave when displaying the image with plt.show instead and then zooming in.

Add the size of the figure:
import matplotlib.pyplot as plt
import numpy as np
import math
x = np.arange(0,100,0.00001)
y = x*np.sin(2*math.pi*(x**1.2))
fig = plt.figure(figsize=(19.20,10.80))
plt.plot(y)
plt.savefig('test.svg')
and you get the kind of resolution you wish.
As correctly observed by JohanC, another good solution is to reduce the width of the line:
import matplotlib.pyplot as plt
import numpy as np
import math
x = np.arange(0,100,0.00001)
y = x*np.sin(2*math.pi*(x**1.2))
#fig = plt.figure(figsize=(19.20,10.80))
plt.plot(y, linewidth=0.1)
plt.savefig('test.svg')

Related

Scaling down a plot when using matplotlib

I've been trying to plot a graph of Epoch vs Accuracy and val_accuracy from a train log I have generated. Whenever I try to plot it, the y-axis starts from 0.93 rather than it being in 0, 0.1 ,0.2... intervals. I'm new at using matplotlib or any plot function.
Here's the code for it:
import pandas as pd
import matplotlib.pyplot as plt
acc = pd.read_csv("train_log", sep = ',')
acc.plot("epoch", ["accuracy","val_accuracy"])
plt.savefig('acc' , dpi = 300)
I'm open to suggestion in complete different ways to do this.
Picture of plot :
[1]: https://i.stack.imgur.com/lgg0W.png
This has already been discussed here. There are a couple of different ways you can do this (using plt.ylim() or making a new variable like axes and then axes.set_ylim()), but the easiest is to use the set_ylim function as it gives you heaps of other handles to manipulate the plot. You can also handle the x axis values using the set_xlim function.
You can use the set_ylim([ymin, ymax]) as follows:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0,5)
y = np.arange(5,10)
axes = plt.gca()
axes.plot(x,y)
axes.set_ylim([0,10])
You can use the plt.ylim() like this:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0,5)
y = np.arange(5,10)
plt.plot(x,y)
plt.ylim([0,10])
This will produce the same plot.
You need to set the lower/bottom limit using ylim().
For details please refer:
https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.ylim.html

How to plot meshgrid on a map in python without changing the background every time?

I am trying to plot a meshgrid on a map in python using netCDF4 in python. Following was the code that was used.
import os
import matplotlib.pyplot as plt
from netCDF4 import Dataset as netcdf_dataset
import numpy as np
import print
from cartopy import config
import cartopy.crs as ccrs
import PIL
from PIL import Image
import cv2
%cd /content/drive/My Drive/Project/Fogs
for i in range(0,608):
sst = fh.variables['Fog_Mask'][i+1][:] - fh.variables['Fog_Mask'][i][:]
lats = fh.variables['latitude'][:]
lons = fh.variables['longitude'][:]
ax = plt.axes(projection=ccrs.PlateCarree())
ax.contourf(lons, lats, sst)
ax.coastlines()
m = globals()
n = locals()
exec("plt.savefig('example{}.png',bbox_inches='tight')".format(i+1) , m , n)
But I find that for certain images the background is different.
Now, for some reason, I can't share the data but latitude and longitude are masked arrays that shall be used while plotting and Fog_Mask is another masked array that is plotted on the maps. I want the boundaries to be the same for all the figures but that is not happening.
PS: I don't want to go to street view and do this. Please suggest a pythonic way to reproduce the maps only.

How to update 3D arrow animation in matplotlib

I am trying to reproduce the left plot of this animation in python using matplotlib.
I am able to generate the vector arrows using the 3D quiver function, but as I read here, it does not seem possible to set the lengths of the arrows. So, my plot does not look quite right:
So, the question is: how do I generate a number of 3D arrows with different lengths? Importantly, can I generate them in such a way so that I can easily modify for each frame of the animation?
Here's my code so far, with the not-so-promising 3D quiver approach:
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d
ax1 = plt.subplot(111,projection='3d')
t = np.linspace(0,10,40)
y = np.sin(t)
z = np.sin(t)
line, = ax1.plot(t,y,z,color='r',lw=2)
ax1.quiver(t,y,z, t*0,y,z)
plt.show()
As Azad suggests, an inelegant, but effective, solution is to simply edit the mpl_toolkits/mplot3d/axes3d.py to remove the normalization. Since I didn't want to mess with my actual matplotlib installation, I simply copied the axes3d.py file to the same directory as my other script and modified the line
norm = math.sqrt(u ** 2 + v ** 2 + w ** 2)
to
norm = 1
(Be sure to change the correct line. There is another use of "norm" a few lines higher.) Also, to get axes3d.py to function correctly when it's outside of the mpl directory, I changed
from . import art3d
from . import proj3d
from . import axis3d
to
from mpl_toolkits.mplot3d import art3d
from mpl_toolkits.mplot3d import proj3d
from mpl_toolkits.mplot3d import axis3d
And here is the nice animation that I was able to generate (not sure what's going wrong with the colors, it looks fine before I uploaded to SO).
And the code to generate the animation:
import numpy as np
import matplotlib.pyplot as plt
import axes3d_hacked
ax1 = plt.subplot(111,projection='3d')
plt.ion()
plt.show()
t = np.linspace(0,10,40)
for index,delay in enumerate(np.linspace(0,1,20)):
y = np.sin(t+delay)
z = np.sin(t+delay)
if delay > 0:
line.remove()
ax1.collections.remove(linecol)
line, = ax1.plot(t,y,z,color='r',lw=2)
linecol = ax1.quiver(t,y,z, t*0,y,z)
plt.savefig('images/Frame%03i.gif'%index)
plt.draw()
plt.ioff()
plt.show()
Now, if I could only get those arrows to look prettier, with nice filled heads. But that's a separate question...
EDIT: In the future, matplotlib will not automatically normalize the arrow lengths in the 3D quiver per this pull request.
Another solution is to call ax.quiever on each arrow, individually - with each call having an own length attribute. This is not very efficient but it will get you going.
And there's no need to change MPL-code

How to plot a 3d scatterplot with the color determined from another variable?

I made a 3d scatterplot that displays the position of galaxies in a cluster (basically like the latitude and longitude) as a function of their velocity. However, I've been asked to make the color of the data points be determined by another variable, h in the code. The purpose of the variable isn't important to know, but that in my actual code, every data point is determined from 4 arrays. After spending a long time looking up how to do this, I finally (almost) have it. The only problem is that when I plot it, the colors of the dots change as soon as I move the plot around to see it from a different direction. Also, I've been having issues trying to display a colorbar.
import pylab as p
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.cm as cm
ra=np.random.random((100))
dec=np.random.random((100))
h=np.random.random((100))
z=np.random.random((100))
datamin=min(h)
datamax=max(h)
fig=p.figure()
ax3D=fig.add_subplot(111, projection='3d')
ax3D.scatter(ra, dec, z, c=h, vmin=datamin, vmax=datamax,
marker='o', cmap=cm.Spectral)
p.title("MKW4s-Position vs Velocity")
p.show()
'Changing color upon redraw' issue was a bug but looks like it's fixed in the latest release (1.1.1). I've tested and confirmed that it's working as it should with 1.1.1.
For the colorbar, it needs a mappable. You can use the collection returned from scatter:
import pylab as p
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.cm as cm
ra=np.random.random((100))
dec=np.random.random((100))
h=np.random.random((100))
z=np.random.random((100))
datamin=min(h)
datamax=max(h)
fig=p.figure()
ax3D=fig.add_subplot(111, projection='3d')
collection = ax3D.scatter(ra, dec, z, c=h, vmin=datamin, vmax=datamax,
marker='o', cmap=cm.Spectral)
p.colorbar(collection)
p.title("MKW4s-Position vs Velocity")
p.show()

PyLab: Plotting axes to log scale, but labelling specific points on the axes

Basically, I'm doing scalability analysis, so I'm working with numbers like 2,4,8,16,32... etc and the only way graphs look rational is using a log scale.
But instead of the usual 10^1, 10^2, etc labelling, I want to have these datapoints (2,4,8...) indicated on the axes
Any ideas?
There's more than one way to do it, depending on how flexible/fancy you want to be.
The simplest way is just to do something like this:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
x = np.exp2(np.arange(10))
plt.semilogy(x)
plt.yticks(x, x)
# Turn y-axis minor ticks off
plt.gca().yaxis.set_minor_locator(mpl.ticker.NullLocator())
plt.show()
If you want to do it in a more flexible manner, then perhaps you might use something like this:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
x = np.exp2(np.arange(10))
fig = plt.figure()
ax = fig.add_subplot(111)
ax.semilogy(x)
ax.yaxis.get_major_locator().base(2)
ax.yaxis.get_minor_locator().base(2)
# This will place 1 minor tick halfway (in linear space) between major ticks
# (in general, use np.linspace(1, 2.0001, numticks-2))
ax.yaxis.get_minor_locator().subs([1.5])
ax.yaxis.get_major_formatter().base(2)
plt.show()
Or something like this:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
x = np.exp2(np.arange(10))
fig = plt.figure()
ax = fig.add_subplot(111)
ax.semilogy(x)
ax.yaxis.get_major_locator().base(2)
ax.yaxis.get_minor_locator().base(2)
ax.yaxis.get_minor_locator().subs([1.5])
# This is the only difference from the last snippet, uses "regular" numbers.
ax.yaxis.set_major_formatter(mpl.ticker.ScalarFormatter())
plt.show()

Categories