Animate a plot over an image in Python - python

I am trying to animate a plot of geographic coordinates over an image, in this case a snippet of a map. I've managed to produce a static plot but cannot get it to animate. I've tried to animate using the matplotlib animation function, but haven't had any success with it. I am using pandas to read the csv in to Python and matplotlib.pyplot to plot. Below is the code for the static plot.
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('mydata.csv', header=0)
# find max/min, plug into a website, snip area as png and insert as plotmap
BBox = ((df.LONGITUDE.min(), df.LONGITUDE.max(),
df.LATITUDE.min(), df.LATITUDE.max()))
#read the image in, plot points over image
plotmap = "myimage.png"
truthplot = plt.imread(plotmap)
fig, ax = plt.subplots(figsize = (8,8),linewidth = 0.1)
ax.scatter(df.LONGITUDE, df.LATITUDE, zorder=1, alpha= 0.5, c='b', s=10)
plottitle = "test"
ax.set_title(plottitle)
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
ax.set_xlim(BBox[0], BBox[1])
ax.set_ylim(BBox[2], BBox[3])
ax.imshow(truthplot, zorder=0, extent = BBox, aspect= 'equal')
plt.show()
Some example coordinates:
LATITUDE LONGITUDE
30.112342 10.678982
29.443459 11.678997
29.334221 11.889544
28.993448 12.003847
I'm still a newbie; any help is appreciated.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
df = pd.read_csv('mydata.csv', header=0)
# find max/min, plug into a website, snip area as png and insert as plotmap
BBox = ((df.LONGITUDE.min(), df.LONGITUDE.max(),
df.LATITUDE.min(), df.LATITUDE.max()))
#read the image in, plot points over image
plotmap = "myimage.png"
truthplot = plt.imread(plotmap)
fig, ax = plt.subplots(figsize = (8,8),linewidth = 0.1)
plottitle = "test"
ax.set_title(plottitle)
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
ax.set_xlim(BBox[0], BBox[1])
ax.set_ylim(BBox[2], BBox[3])
scat = ax.scatter(df.LONGITUDE, df.LATITUDE, zorder=1, alpha= 0.5, c='b', s=10)
color_data = np.random.random((500, len(df.LATITUDE)))
def update(frame):
scat.set_array(color_data[frame])
return scat,
ani = FuncAnimation(fig, update, frames=range(500), blit=True)
ax.imshow(truthplot, zorder=0, extent = BBox, aspect= 'equal')
plt.show()
I am not sure what you wanted to be animated, that's why I just made the points blink.
But you can easily change your scatter plot all you want in the update function.
scat is a PathCollection, its functions you can find here:
https://matplotlib.org/3.2.1/api/collections_api.html#matplotlib.collections.PathCollection
UPDATE
If you want to build the path step by step, manipulation the PathCollention is not very convenient. I would recommend recreating the obj.
scat = ax.scatter(df.LONGITUDE[0], df.LATITUDE[0], zorder=1, alpha= 0.5, c='b', s=10)
max_frames = 10
def update(frame):
scat = ax.scatter(df.LONGITUDE[:(frame * (len(df.LONGITUDE) + 1))//max_frames],
df.LATITUDE[:(frame * (len(df.LATITUDE) + 1))//max_frames],
zorder=1, alpha= 0.5, c='b', s=10)
return scat,

You can try this, if you want to use a scatter plot on a DataFrame directly:
BBox = ((df['LONGITUDE'].min(), df['LONGITUDE'].max(),
df['LATITUDE'].min(), df['LATITUDE'].max()))
plotmap = 'myimage.png'
truthplot = plt.imread(plotmap)
ax = df.plot.scatter(x='LONGITUDE', y='LATITUDE', c='Red')
plottitle = "test"
ax.set_title(plottitle)
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
ax.set_xlim(BBox[0], BBox[1])
ax.set_ylim(BBox[2], BBox[3])
ax.imshow(truthplot, zorder=0, extent = BBox, aspect= 'equal')
plt.show()

Related

how to adjust space between columns in xarray faceted plot with basemap?

I want to make a faceted plot using xarray. The problem occurs when I try to add a bassemap to each subplot using basemap. Too much space is put between columns. I have already tried plt.subplots_adjust(hspace=0.01, wspace=0) and plt.tight_layout(pad=0), but no luck yet. How can I control the space between columns?
The sample dataset (ds_sub) can be found here. The following code produces the below figure.
Thanks.
import pickle
from matplotlib import pyplot
from matplotlib import cm, colors
from mpl_toolkits.basemap import Basemap
import xarray as xr
with open('/data/direcotory/test_data.pkl', "rb") as f:
ds_sub = pickle.load(f)
f.close()
plt.close("all")
plt.figure()
levels = np.arange(0, ds_sub['sum'].quantile(0.99),
(ds_sub['sum'].quantile(0.99) / 10)).tolist()
norm = colors.BoundaryNorm(levels, len(levels))
p = ds_sub['sum'].loc[[0, 3, 6, 9], :, :].plot(add_colorbar=False, row='time', cmap='OrRd', col_wrap=2,
norm=norm)
mappable = p.axes[0][0].collections[0]
cax = plt.axes([0.85, 0.2, 0.05, 0.6])
cbar1 = plt.colorbar(mappable, ticks=levels,
values=levels, cax=cax, orientation='vertical')
cbar1.ax.tick_params(labelsize=14)
for i, ax in enumerate(p.axes.flatten()):
ax.set_xlabel('')
ax.set_ylabel('')
ax.margins(0, 0)
lon_0 = ds_sub['lon'].mean()
lat_0 = ds_sub['lat'].mean()
m = Basemap(resolution='f',
lat_ts=20, lat_0=lat_0, lon_0=lon_0,
llcrnrlon=ds_sub['lon'].min(),
llcrnrlat=ds_sub['lat'].min(),
urcrnrlon=ds_sub['lon'].max(),
urcrnrlat=ds_sub['lat'].max(), ax=ax)
m.drawmapboundary()
m.drawrivers()
m.drawcoastlines()
m.drawcountries()
m.drawstates()
dohax, dohay = m(51.534817, 25.286106)
print(i)
plt.subplots_adjust(hspace=0.01, wspace=0)
plt.tight_layout(pad=0)
plt.show()

How to rotate a Subplot by 45 degree in Matplotlib?

I am trying to explore a subplot 2 plots with square in shape rotated by 45 degree.
import matplotlib.pyplot as plt
from matplotlib import colors
import numpy as np
data = np.random.rand(10, 10) * 20
# create discrete colormap
cmap = colors.ListedColormap(['red', 'blue','green'])
bounds = [0,5,10,15]
norm = colors.BoundaryNorm(bounds, cmap.N)
fig, ax= plt.subplots(1,2)
ax[0].imshow(data, cmap=cmap, norm=norm)
# draw gridlines
ax[0].grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
ax[0].set_xticks(np.arange(-.5, 10, 1));
ax[0].set_yticks(np.arange(-.5, 10, 1));
ax[1].imshow(data, cmap=cmap, norm=norm)
# draw gridlines
ax[1].grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
ax[1].set_xticks(np.arange(-.5, 10, 1));
ax[1].set_yticks(np.arange(-.5, 10, 1));
plt.show()
Actual Result is :-
I want to rotate individual plot by 45 degree. Something like:-
I am trying to find in Matplotlib Documentation. Still not getting. Any help?
Please note this is NOT DUPLICATE OF
Is there a way to rotate a matplotlib plot by 45 degrees?
The mentioned URL is for a plot. and the solution is to rotate IMAGE. However this is pertaining to Subplot. I want to rotate PLOT not image as whole.
Based on this link and documentation about floating_axes, you can try something like this:
from mpl_toolkits.axisartist.grid_finder import DictFormatter
import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib import colors
import numpy as np
def setup_axes1(fig, rect, angle):
tr = Affine2D().scale(2, 2).rotate_deg(angle)
#We create dictionarys to keep the xticks and yticks after the rotation
dictio={i:str(val) for i,val in enumerate(np.arange(-.5, 10, 1).tolist())}
reversedictio={i:dictio[val] for i,val in enumerate(list(reversed(sorted(dictio.keys()))))}
grid_helper = floating_axes.GridHelperCurveLinear(
tr, extremes=(-0.5, 9.5,-0.5, 9.5), tick_formatter1= DictFormatter(dictio),
tick_formatter2=DictFormatter(reversedictio))
ax1 = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper)
fig.add_subplot(ax1)
aux_ax = ax1.get_aux_axes(tr)
grid_helper.grid_finder.grid_locator1._nbins = 10 #Number of rows
grid_helper.grid_finder.grid_locator2._nbins = 10 #Number of columns
return aux_ax
fig1, axes=plt.subplots(2,figsize=(20,20))
plt.rcParams.update({'font.size': 27})
#We erase the first previous axes
fig1.delaxes(axes[0])
fig1.delaxes(axes[1])
data = np.random.rand(10, 10) * 20
#We create the floating_axes
ax0 = setup_axes1(fig1, 121,-45)
ax1 = setup_axes1(fig1, 122,-45)
# create discrete colormap
cmap = colors.ListedColormap(['red', 'blue','green'])
bounds = [0,5,10,15]
norm = colors.BoundaryNorm(bounds, cmap.N)
ax0.imshow(data, cmap=cmap, norm=norm,interpolation="nearest")
# draw gridlines
ax0.grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
ax1.imshow(data, cmap=cmap, norm=norm,interpolation="nearest")
# draw gridlines
ax1.grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
plt.show()
Output:
Or, as an other alternative, I found a "tricky" way to do it, and it's about catching the figures in the buffer, rotate them -45 degrees, and then merge them into a single image, and since you have the same two images, you can try something like this:
import matplotlib
import io
from PIL import Image
import matplotlib.pyplot as plt
from matplotlib import colors
import numpy as np
##PLOTING THE FIGURE##
data = np.random.rand(10, 10) * 20
# create discrete colormap
cmap = colors.ListedColormap(['red', 'blue','green'])
bounds = [0,5,10,15]
norm = colors.BoundaryNorm(bounds, cmap.N)
#We change style values to get the image with better quality
plt.rcParams.update({'font.size': 46})
plt.figure(figsize=(20,20))
plt.imshow(data, cmap=cmap, norm=norm)
# draw gridlines
plt.grid(which='major', axis='both', linestyle='-', color='k', linewidth=0)
plt.gca().set_xticks(np.arange(-.5, 10, 1));
plt.gca().set_yticks(np.arange(-.5, 10, 1));
##SAVING THE FIGURE INTO AN IMAGE##
#We save the current figure as a Image
buf = io.BytesIO()
plt.savefig(buf, format='png',bbox_inches='tight')
buf.seek(0)
im = Image.open(buf) #We open the current image saved in the buffer
#We rotate the image and fill the background with white
img_01=im.rotate(-45, Image.NEAREST, expand = 1, fillcolor = (255,255,255))
buf.close()
##MERGING THE TWO FIGURES##
new_im = Image.new('RGB', (2*img_01.size[0]+20,img_01.size[1]), 'white')
mouse_mask = img_01.convert('RGBA')
new_im.paste(img_01, (0,0))
new_im.paste(img_01, (img_01.size[0]+8,0))
new_im.save("merged_images.png", 'PNG') #Important(just to clarify): save the image, since the buffer is renewed every time you run the script
new_im.show()
Output:
I helped myself with these links:
How-to-merge-images-with-same-size-using-the-python-3-module-pillow
how-to-save-a-pylab-figure-into-in-memory-file-which-can-be-read-into-pil-image
python-pillow-rotate-image-90-180-270-degrees
specify-image-filling-color-when-rotating-in-python-with-pil-and-setting-expand

matplotlib/seaborn violin plot with colormap

I want to create a violin plot, with either matplotlib or searborn, in which the plot is colored according to a colormap.
This is what I get:
This is what I would like to get (I used Photoshop here):
How can I obtain the desired plot?
I thought there would be a better was to do this, but, based on #ImportanceOfBeingErnest's comment, I guess this is actually the way to go:
from matplotlib.path import Path
from matplotlib.patches import PathPatch
from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable
x = [np.random.normal(loc=i, scale=1, size=(100,)) for i in range(5)]
fig, ax = plt.subplots()
violins = ax.violinplot(x)
ymin, ymax = ax.get_ylim()
xmin, xmax = ax.get_xlim()
# create a numpy image to use as a gradient
Nx,Ny=1,1000
imgArr = np.tile(np.linspace(0,1,Ny), (Nx,1)).T
cmap = 'hsv'
for violin in violins['bodies']:
path = Path(violin.get_paths()[0].vertices)
patch = PathPatch(path, facecolor='none', edgecolor='none')
ax.add_patch(patch)
img = ax.imshow(imgArr, origin="lower", extent=[xmin,xmax,ymin,ymax], aspect="auto",
cmap=cmap,
clip_path=patch)
# colorbar
ax_divider = make_axes_locatable(ax)
cax = ax_divider.append_axes("right", size="5%", pad="2%")
norm = matplotlib.colors.Normalize(vmin=ymin, vmax=ymax)
cb = matplotlib.colorbar.ColorbarBase(cax, cmap=matplotlib.cm.get_cmap(cmap),
norm=norm,
orientation='vertical')

how to get color from a line plot

I want to plot several normal distributions, and add labels to each one in its line color. However the color does not seem to update.
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import scipy.stats as stats
def single_plot(mu, sigma, ax, label=None):
x = np.linspace(mu - 4*sigma, mu + 4*sigma, 1000)
ax = sns.lineplot(x, stats.norm.pdf(x, mu, sigma), ax=ax, label=label, zorder=2)
#my code to get color
color = ax.get_lines()[0].get_c() #fetch color of line
ax.text(mu, max(stats.norm.pdf(x, mu, sigma)), label, fontsize=16, color=color)
When put to use, however, this does not update the color with each line. If I try:
fig, ax = plt.subplots()
ax = single_plot(mu=1000, sigma=100, ax=ax, label='test1')
ax = single_plot(mu=1500, sigma=200, ax=ax, label='test2')
fig.show()
I am getting this figure. The label for "test2" was not updated.
I am wondering where I was wrong and how to fix this problem.

Cmap w/ line plot. Can either plot under line (but solid) or gradient (but over line)

I am trying a visualization problem based off of a dataset processed with pandas and matplotlib. I plotted the data in a line plot. My goal is to have the area underneath the curve be gradient-ed with cmap (ex 'plasma')
However, both of my best attempts are wrong for different reasons. The first will color with a gradient, but only over the line. The second will color under the line, but only with a solid color. I have been stuck for a very long time... Thank you!
ax = plot_chance_death.plot(kind='line', x = 'State', y = 'Percent Chance',
ax=ax, color='indigo')
l1 = ax.lines[0]
x1 = l1.get_ydata()
y1 = l1.get_xdata()
fig, ax = plt.subplots()
# plot only the outline of the polygon, and capture the result
poly, = ax.fill(x1, y1, facecolor='none')
# get the extent of the axes
xmin, xmax = ax.get_xlim()
ymin, ymax = ax.get_ylim()
# create a dummy image
img_data = np.arange(ymin,ymax,(ymax-ymin)/100.)
img_data = img_data.reshape(img_data.size,1)
# plot and clip the image
im = ax.imshow(img_data, aspect='auto', origin='upper', cmap='plasma',
extent=[xmin,xmax,ymin,ymax], vmin=1., vmax=y1.max())
#this shows the gradient but above the line
im.set_clip_path(poly)
###this solution colors underneath but solid color
ax.fill_between(x1, y1, y2=0, cmap='plasma', norm=(0,.5))
It makes sense to include the bottom of the region to use as clip_path into the Path. You may create the Path from a plot of the data and then add the two bottom points to it.
import pandas as pd
import numpy as np; np.random.seed(42)
import matplotlib.pyplot as plt
from matplotlib.path import Path
df = pd.DataFrame({"x" : np.linspace(0,0.05,40),
"y" : np.cumsum(np.random.rand(40))[::-1]*3})
fig, ax = plt.subplots()
l, = ax.plot(df.x, df.y, color="k")
# get the extent of the axes
xmin, xmax = ax.get_xlim()
ymin, ymax = ax.get_ylim()
# create a dummy image
img_data = np.arange(ymin,ymax,(ymax-ymin)/100.)
img_data = img_data.reshape(img_data.size,1)
# plot and clip the image
im = ax.imshow(img_data, aspect='auto', origin='upper', cmap='plasma',
extent=[xmin,xmax,ymin,ymax], vmin=1., vmax=df.y.max())
px,py = l.get_data()
p0 = [[px[-1], py.min()], [px[0], py.min()]]
p = np.concatenate((np.c_[px,py],p0))
path = Path(p)
im.set_clip_path(path, transform=ax.transData)
plt.show()

Categories