Related
I have 12 subplots showing changes in ocean temperature for the Celtic Sea. Each subplot is for a different month in the year.
import xarray as xa
import cmocean.cm as cm
import matplotlib.patches as mpatches
from matplotlib.patches import Patch
from matplotlib.lines import Line2D
import matplotlib.pyplot as plt
import matplotlib.animation as ani
import seaborn as sns
import pandas as pd
import plotly.express as px
import pycountry
import cartopy.crs as ccrs
import cartopy.feature as cfeat
# =============================================================================
# Step 1: Get data
# =============================================================================
data_model = xa.open_dataset("PD_tavg_out_atlanticcentric.nc",decode_times = False)
sal_obs_data = xa.open_dataset("sal_obs_CS_all.nc",decode_times = False)
temp_obs_data = xa.open_dataset("temp_obs_CS_all.nc",decode_times = False)
data_obs = xa.merge([sal_obs_data,temp_obs_data])
data_obs = data_obs.rename(lon = "longitude",
lat = "latitude")
# =============================================================================
# Step 2: Define Coordinates
# =============================================================================
long_max = 122.4
long_min = 100.8
lat_max = 25.2
lat_min = 0
dep_max = 100
dep_min = 0
tim_max = 35406.0
tim_min = 35405.0
def extract_shelf_sea(long_max, long_min,
lat_max, lat_min,
dep_max, dep_min,
tim_max, tim_min):
# =============================================================================
# Step 3: Extract data
# =============================================================================
extract_model_data = data_model.sel(longitude = slice(long_min,long_max),
latitude = slice(lat_min,lat_max),
depth = slice(dep_min,dep_max),
time = slice(tim_min,tim_max))
extract_obs_data = data_obs.sel(time = data_obs.time,
longitude = data_obs.longitude,
latitude = data_obs.latitude,
depth = extract_model_data.depth, method="nearest")
# =============================================================================
# Step 4: Calculating the difference in salinity and temperature
# =============================================================================
model_diff = extract_model_data.diff("depth")
obs_diff = extract_obs_data.diff("depth")
return model_diff, obs_diff
# =============================================================================
# Step 5: Plotting
# =============================================================================
def plot_SCS():
CS_model, CS_obs = extract_shelf_sea(2,-17,65,45,100,0,35405.95,35405)
map_proj = ccrs.Orthographic(central_longitude = -8.5, central_latitude = 55)
# =============================================================================
# Observational Data (temperature)
# =============================================================================
temp_obs = CS_obs.temp
map_proj = ccrs.Orthographic(central_longitude = -8.5, central_latitude = 55)
plot = temp_obs.plot(cmap = cm.curl,
cbar_kwargs = {'label':'Change in temperature between 15m and 82m (°C)'},
col='time', col_wrap=4,
transform = ccrs.PlateCarree(),
subplot_kws={"projection":map_proj},
vmin=-6, vmax=6)
plot.fig.suptitle("Celtic Sea - Observational Data")
for ax, title in zip(plot.axes.flat, ['Jan','Feb','Mar','Apr','May','Jun',
'Jul','Aug','Sep','Oct','Nov','Dec']):
ax.set_title(title)
for ax in plot.axes.flat:
ax.add_feature(cfeat.LAND,zorder=100,edgecolor="k",facecolor="gray")
I'd like to animate this, so I only have a single plot, but every second or so it moves to the next month. I've tried a few things with plotly and the matplotlib animation and haven't had any success.
I don't even really know what else to try. Any advice helpful!
Sorry, I can't attach the full original dataset as it's a huge file.
Your case is excellent for the use of ArtistAnimation, i.e., the flipbook approach using precomputed images. Sample code because the format of your animation is not specified:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, axes = plt.subplots(ncols=6, nrows=3, figsize=(15, 10))
#get specs for large image
gs = axes[0, -2].get_gridspec()
#remove unnecessary axis objects
for ax in axes[0:, -2:].flat:
ax.remove()
#update axes list and label all static images
axes = fig.get_axes()
for i, ax in enumerate(axes):
ax.axis("off")
ax.set_title(f"month {i+1}")
#add axis object for large, animated image
ax_large = fig.add_subplot(gs[0:, -2:])
ax_large.axis("off")
#fake images
def f(x, y, i):
return np.sin(x*i/4) * i/6 + np.cos(y* (12-i)/4)
x = np.linspace(0, 2 * np.pi, 80)
y = np.linspace(0, 2 * np.pi, 120).reshape(-1, 1)
all_ims = []
min_v = -3
max_v = 3
ani_cmap = "seismic"
for i, ax_small in enumerate(axes):
#image generation unnecessary for you because your images already exist
arr = f(x, y, i)
#static image into small frame
im_small = ax_small.imshow(arr, vmin=min_v, vmax=max_v, cmap=ani_cmap)
#animated image into large frame
im_large = ax_large.imshow(arr, animated=True, vmin=min_v, vmax=max_v, cmap=ani_cmap)
#animated images are collected in a list
all_ims.append([im_large])
ani = animation.ArtistAnimation(fig, all_ims, interval=200, blit=True)
plt.show()
Sample output:
I am trying to animate a histogram using matplotlib and I want to show the different bars using a colormap, e.g:
I have this working when I clear the complete figure every frame and then redraw everything. But this is very slow, so I am trying out the example by matplotlib itself.
This works and is very fast, but unfortunately I have no idea on how to specify a colormap because it is using the patches.PathPatch object to draw the histogram now. I can only get it to work with the same single color for every individual bar.
How can I specify a gradient or colormap to achieve the desired result shown above?
Here is an example of a working animation with a single color which I am currently using.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.path as path
import matplotlib.animation as animation
# Fixing random state for reproducibility
np.random.seed(19680801)
# histogram our data with numpy
data = np.random.randn(1000)
n, bins = np.histogram(data, 100)
# get the corners of the rectangles for the histogram
left = np.array(bins[:-1])
right = np.array(bins[1:])
bottom = np.zeros(len(left))
top = bottom + n
nrects = len(left)
nverts = nrects * (1 + 3 + 1)
verts = np.zeros((nverts, 2))
codes = np.ones(nverts, int) * path.Path.LINETO
codes[0::5] = path.Path.MOVETO
codes[4::5] = path.Path.CLOSEPOLY
verts[0::5, 0] = left
verts[0::5, 1] = bottom
verts[1::5, 0] = left
verts[1::5, 1] = top
verts[2::5, 0] = right
verts[2::5, 1] = top
verts[3::5, 0] = right
verts[3::5, 1] = bottom
patch = None
def animate(i):
# simulate new data coming in
data = np.random.randn(1000)
n, bins = np.histogram(data, 100)
top = bottom + n
verts[1::5, 1] = top
verts[2::5, 1] = top
return [patch, ]
fig, ax = plt.subplots()
barpath = path.Path(verts, codes)
patch = patches.PathPatch(
barpath, facecolor='green', edgecolor='yellow', alpha=0.5)
ax.add_patch(patch)
ax.set_xlim(left[0], right[-1])
ax.set_ylim(bottom.min(), top.max())
ani = animation.FuncAnimation(fig, animate, 100, repeat=False, blit=True)
plt.show()
I recommend u using BarContainer, you can change bar color individually. In your example, the path is single object, matplotlib seems not to support gradient color for a single patch (not sure though).
import numpy as np
import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
# histogram our data with numpy
data = np.random.randn(1000)
colors = plt.cm.coolwarm(np.linspace(0, 1, 100))
def animate(i):
data = np.random.randn(1000)
bc = ax.hist(data, 100)[2]
for i, e in enumerate(bc):
e.set_color(colors[i])
return bc
fig, ax = plt.subplots(1, 1, figsize=(7.2, 7.2))
ani = animation.FuncAnimation(fig, animate, 100, repeat=False, blit=True)
I am trying to incorporate a gradient fill with multiple histograms using seaborn facet grid where the gradient is determined by the spread of values under each curve, not just by a sequence of row or col using hue. There are some links below that partly perform somewhat similar functions in python:
How to fill histogram with gradient color fills a diverging gradient but each histogram is independent of the others so comparison between histograms is somewhat void. Using the figure below each histogram should be relative to the others. Furthermore, it does not use the seaborn facet grid, which is the central question here.
How to generate series of histograms doesn't plot histograms. It just fills the area under a curve.
I've found a few images displaying what I'm hoping to execute but they all seem to be generated in R with nothing in python. My assumption is the functionality doesn't exist as yet using seaborn and I'll have to use R but I think this will be applicable for many users.
Using the code below, we can change adjust the gradient using hue to either row or col but this doesn't consider the area under the curve.
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
# Create the data
rs = np.random.RandomState(1979)
x = rs.randn(120)
g = np.tile(list("ABCD"), 30)
h = np.tile(list("XYZ"), 40)
# Generate df
df = pd.DataFrame(dict(x = x, g = g, h = h))
# Initialize the FacetGrid object
pal = sns.cubehelix_palette(4, rot = -0.25, light = 0.7)
g = sns.FacetGrid(df, col = 'h', hue = 'h', row = 'g', aspect = 3, height= 1, palette = pal)
# Draw the densities
g = g.map(sns.kdeplot, 'x', shade = True, alpha = 0.8, lw = 1, bw = 0.8)
g = g.map(sns.kdeplot, 'x', color= 'w', lw = 1, bw = 0.8)
g = g.map(plt.axhline, y = 0, lw = 1)
# Adjust title and axis labels directly
g.axes[0,0].set_ylabel('L 1')
g.axes[1,0].set_ylabel('L 2')
g.axes[2,0].set_ylabel('L 3')
g.axes[3,0].set_ylabel('L 4')
g.axes[0,0].set_title('Top 1')
g.axes[0,1].set_title('Top 2')
g.axes[0,2].set_title('Top 3')
g.axes[1,0].set_title('')
g.axes[1,1].set_title('')
g.axes[1,2].set_title('')
g.axes[2,0].set_title('')
g.axes[2,1].set_title('')
g.axes[2,2].set_title('')
g.axes[3,0].set_title('')
g.axes[3,1].set_title('')
g.axes[3,2].set_title('')
g.set_axis_labels(x_var = 'Total Amount')
g.set(yticks = [])
Out:
There is a gradient that can be adjusted for row or col but I'm hoping to pass this gradient to the area underneath each histogram curve. Similar to the figure above. So the area underneath each curve would be lighter when lower than zero and darker when higher than zero.
Even adjusting the area under the curve to the median value may suffice.
You can create an image gradient, and use the histogram itself as a clipping path for the image, so that the only visible part is the part under the curve.
As such, you can play around with any cmaps and normalization that are available when creating images.
Here is a quick example:
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
# Create the data
rs = np.random.RandomState(1979)
x = rs.randn(120)
g = np.tile(list("ABCD"), 30)
h = np.tile(list("XYZ"), 40)
# Generate df
df = pd.DataFrame(dict(x = x, g = g, h = h))
# Initialize the FacetGrid object
pal = sns.cubehelix_palette(4, rot = -0.25, light = 0.7)
g = sns.FacetGrid(df, col = 'h', hue = 'h', row = 'g', aspect = 3, height= 1, palette = pal)
# Draw the densities
g = g.map(sns.kdeplot, 'x', shade = True, alpha = 0.8, lw = 1, bw = 0.8)
g = g.map(sns.kdeplot, 'x', color= 'w', lw = 1, bw = 0.8)
g = g.map(plt.axhline, y = 0, lw = 1)
for ax in g.axes.flat:
ax.set_title("")
# Adjust title and axis labels directly
for i in range(4):
g.axes[i,0].set_ylabel('L {:d}'.format(i))
for i in range(3):
g.axes[0,i].set_title('Top {:d}'.format(i))
# generate a gradient
cmap = 'coolwarm'
x = np.linspace(0,1,100)
for ax in g.axes.flat:
im = ax.imshow(np.vstack([x,x]), aspect='auto', extent=[*ax.get_xlim(), *ax.get_ylim()], cmap=cmap, zorder=10)
path = ax.collections[0].get_paths()[0]
patch = matplotlib.patches.PathPatch(path, transform=ax.transData)
im.set_clip_path(patch)
g.set_axis_labels(x_var = 'Total Amount')
g.set(yticks = [])
I am trying to create an animation of a scatterplot as well as a 2d Histogram. I can get the scatter plot working. I can also create individual stills of the 2d Histogram but cannot get it to animate with the scatter plot.
I can create some mock data if that would help. Please find code below.
import numpy as np
import matplotlib.pyplot as plt
import csv
import matplotlib.animation as animation
#Create empty lists
visuals = [[],[],[]]
#This dataset contains XY coordinates from 21 different players derived from a match
with open('Heatmap_dataset.csv') as csvfile :
readCSV = csv.reader(csvfile, delimiter=',')
n=0
for row in readCSV :
if n == 0 :
n+=1
continue
#All I'm doing here is appending all the X-Coordinates and all the Y-Coordinates. As the data is read across the screen, not down.
visuals[0].append([float(row[3]),float(row[5]),float(row[7]),float(row[9]),float(row[11]),float(row[13]),float(row[15]),float(row[17]),float(row[19]),float(row[21]),float(row[23]),float(row[25]),float(row[27]),float(row[29]),float(row[31]),float(row[33]),float(row[35]),float(row[37]),float(row[39]),float(row[41]),float(row[43])])
visuals[1].append([float(row[2]),float(row[4]),float(row[6]),float(row[8]),float(row[10]),float(row[12]),float(row[14]),float(row[16]),float(row[18]),float(row[20]),float(row[22]),float(row[24]),float(row[26]),float(row[28]),float(row[30]),float(row[32]),float(row[34]),float(row[36]),float(row[38]),float(row[40]),float(row[42])])
visuals[2].append([1,2])
#Create a list that contains all the X-Coordinates and all the Y-Coordinates. The 2nd list indicates the row. So visuals[1][100] would be the 100th row.
Y = visuals[1][0]
X = visuals[0][0]
fig, ax = plt.subplots(figsize = (8,8))
plt.grid(False)
# Create scatter plot
scatter = ax.scatter(visuals[0][0], visuals[1][0], c=['white'], alpha = 0.7, s = 20, edgecolor = 'black', zorder = 2)
#Create 2d Histogram
data = (X, Y)
data,x,y,p = plt.hist2d(X,Y, bins = 15, range = np.array([(-90, 90), (0, 140)]))
#Smooth with filter
im = plt.imshow(data.T, interpolation = 'gaussian', origin = 'lower', extent = [-80,80,0,140])
ax.set_ylim(0,140)
ax.set_xlim(-85,85)
#Define animation.
def animate(i) :
scatter.set_offsets([[[[[[[[[[[[[[[[[[[[[visuals[0][0+i][0], visuals[1][0+i][0]], [visuals[0][0+i][1], visuals[1][0+i][1]], [visuals[0][0+i][2], visuals[1][0+i][2]], [visuals[0][0+i][3], visuals[1][0+i][3]], [visuals[0][0+i][4], visuals[1][0+i][4]],[visuals[0][0+i][5], visuals[1][0+i][5]], [visuals[0][0+i][6], visuals[1][0+i][6]], [visuals[0][0+i][7], visuals[1][0+i][7]], [visuals[0][0+i][8], visuals[1][0+i][8]], [visuals[0][0+i][9], visuals[1][0+i][9]], [visuals[0][0+i][10], visuals[1][0+i][10]], [visuals[0][0+i][11], visuals[1][0+i][11]], [visuals[0][0+i][12], visuals[1][0+i][12]], [visuals[0][0+i][13], visuals[1][0+i][13]], [visuals[0][0+i][14], visuals[1][0+i][14]], [visuals[0][0+i][15], visuals[1][0+i][15]], [visuals[0][0+i][16], visuals[1][0+i][16]], [visuals[0][0+i][17], visuals[1][0+i][17]], [visuals[0][0+i][18], visuals[1][0+i][18]], [visuals[0][0+i][19], visuals[1][0+i][19]], [visuals[0][0+i][20], visuals[1][0+i][20]]]]]]]]]]]]]]]]]]]]]])
# This is were I'm having trouble...How do I animate the image derived from the 2d histogram
im.set_array[i+1]
ani = animation.FuncAnimation(fig, animate, np.arange(0,1000),
interval = 100, blit = False)
The image can be updated with im.set_data(data), where you need to call hist2d to get the updated data to pass to im. As a minimal example,
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
X = np.random.randn(100000)
Y = np.random.randn(100000) + 5
fig, ax = plt.subplots(figsize = (8,8))
#Create 2d Histogram
data,x,y = np.histogram2d(X,Y, bins = 15)
#Smooth with filter
im = plt.imshow(data.T, interpolation = 'gaussian', origin = 'lower')
#Define animation.
def animate(i) :
X = np.random.randn(100000)
Y = np.random.randn(100000) + 5
data,x,y = np.histogram2d(X,Y, bins = 15)
im.set_data(data)
ani = animation.FuncAnimation(fig, animate, np.arange(0,1000),
interval = 100, blit = False)
plt.show()
I have implemented one algorithm (RLSR) which there are two regularization factor. Based on different value of this two factors my cost function decreases or increase. Right now I visualize my error with plt.scatter by passing the error as the color which result :
but the problem here is my values in y-axis are very small so as you can see they overlap and I can not see some part of my results.
alpha_list=[1e-11,1e-10,1e-10,5*1e-10,8*1e-10,1e-8,1e-8,5*1e-8,8*1e-6,1e-6,1e-6,5*1e-6,8*1e-6,1e-4,1e-4,5*1e-4,8*1e-4,1e-3,1e-3,5*1e-3,6*1e-3,8*1e-3]
I tried to decrease the transparency but It didn't help much!
and this is how I implemented it :
eigenvalues,alphaa = np.meshgrid(eigRange,alpha_list )
fig = plt.figure()
DatavmaxTrain = np.max(normCostTrain)
DatavminTrain = np.min(normCostTrain)
DatavmaxTest = np.max(normCostTest)
DatavminTest = np.min(normCostTest)
plt.subplot(211)
plt.scatter(eigenvalues,alphaa,s=130, c=normCostTrain,cmap=cm.PuOr, vmin=DatavminTrain, vmax=DatavmaxTrain, alpha=0.70) #-----for train
cb1=plt.colorbar()
cb1.set_label("normalized square error")
plt.title("Train ")
plt.xlabel("No. of Eigenvalues")
plt.ylabel("Regualrization parameter")
So I am looking for a better way to visualize my data.
Thanks
How about plotting the log of the alpha_list values?
alpha_list = np.log(alpha_list)
There is still some overlap, but at least the values are more evenly spread out:
import matplotlib.pyplot as plt
import numpy as np
alpha_list=[1e-11,1e-10,1e-10,5*1e-10,8*1e-10,1e-8,1e-8,5*1e-8,8*1e-6,1e-6,1e-6,5*1e-6,8*1e-6,1e-4,1e-4,5*1e-4,8*1e-4,1e-3,1e-3,5*1e-3,6*1e-3,8*1e-3]
alpha_list = np.log(alpha_list)
eigRange = np.linspace(0,19,20)
eigenvalues,alphaa = np.meshgrid(eigRange,alpha_list )
normCostTrain = np.random.random((len(alpha_list),len(eigRange)))
fig = plt.figure()
DatavmaxTrain = np.max(normCostTrain)
DatavminTrain = np.min(normCostTrain)
plt.scatter(eigenvalues,alphaa,s = 130, c=normCostTrain,cmap=plt.get_cmap('PuOr'),
vmin=DatavminTrain, vmax=DatavmaxTrain, alpha=0.70) #-----for train
cb1=plt.colorbar()
cb1.set_label("normalized square error")
plt.title("Train ")
plt.xlabel("No. of Eigenvalues")
plt.ylabel("Log(Regularization parameter)")
plt.show()
yields
Here is an example of a 3D scatter plot of the same data, with the z-axis (and the color) are both used to represent the "normalized square error".
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.cm as cm
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')
alpha_list = [1e-11, 1e-10, 1e-10, 5*1e-10, 8*1e-10, 1e-8, 1e-8, 5*1e-8, 8*1e-6,
1e-6, 1e-6, 5*1e-6, 8*1e-6, 1e-4, 1e-4, 5*1e-4, 8*1e-4, 1e-3, 1e-3,
5*1e-3, 6*1e-3, 8*1e-3]
alpha_list = np.log(alpha_list)
eigRange = np.linspace(0, 19, 20)
eigenvalues, alphaa = np.meshgrid(eigRange, alpha_list )
eigenvalues = eigenvalues.ravel()
alphaa = alphaa.ravel()
normCostTrain = np.random.random((len(alpha_list), len(eigRange))).ravel()
DatavmaxTrain = np.max(normCostTrain)
DatavminTrain = np.min(normCostTrain)
PuOr = plt.get_cmap('PuOr')
ax.scatter(eigenvalues, alphaa, normCostTrain,
c = normCostTrain.ravel(),
s = 30,
cmap = PuOr,
vmin = DatavminTrain,
vmax = DatavmaxTrain,
alpha = 0.70
) #-----for train
m = cm.ScalarMappable(cmap = PuOr)
m.set_array(normCostTrain)
cb1 = plt.colorbar(m)
cb1.set_label("normalized square error")
plt.title("Train ")
ax.set_xlabel("No. of Eigenvalues")
ax.set_ylabel("Log(Regularization parameter)")
ax.set_zlabel("normalized square error")
plt.show()
I'm not sure if this is an improvement. The points are a bit jumbled together, but are distinguishable if you drag the mouse to rotate the plot.