How to plot events with minute precision on hourly plots using matplotlib? - python-3.x

I have an hourly plot generated with matplotlib. I need to plot an event which goes for example, from 09:00 to 10:45. When I try to do it, using axvspan I obtain a bar from 9:00 to 10:00. How could I obtain the longer one?
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import datetime as dt
import pandas as pd
now_date = dt.datetime(2018,10,1,9)
d_tw_ini = now_date - dt.timedelta(hours = 1)
d_tw_fin = now_date + dt.timedelta(hours = 3)
dts = pd.date_range(start=d_tw_ini, end=d_tw_fin, freq='1H', name='ini', closed='left')
data=pd.DataFrame({'val':[0.5,0.4,0.7,0.9]})
ev1=[dt.datetime(2018,10,1,9,5),dt.datetime(2018,10,1,10,50)]
data['t']=dts.values
data.set_index('t',inplace=True)
fig = plt.figure()
gs = GridSpec(1, 1)
ax_1 = fig.add_subplot(gs[0, 0])
data.plot(ax=ax_1, y='val')
ax_1.axvspan(ev1[0],ev1[1], alpha=0.3, color= 'red')
Result

Juan, it looks when you used pandas to plot, the hourly indexing seems to cause issues with how axvspan gets plotted.
I replaced
data.plot(ax=ax_1, y='val')
with
ax_1.plot(data.index, data['val'])
which generates the image below, but unfortunately you lose the automated x-axis formatting.
Adding the two lines below will result in the same date formatting as your example.
ax_1.set_xticks([x for x in data.index])
ax_1.set_xticklabels([str(x)[11:16] for x in data.index])
Below is the full code to produce the above plot.
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import datetime as dt
import pandas as pd
now_date = dt.datetime(2018,10,1,9)
d_tw_ini = now_date - dt.timedelta(hours = 1)
d_tw_fin = now_date + dt.timedelta(hours = 3)
dts = pd.date_range(start=d_tw_ini, end=d_tw_fin, freq='1h', name='ini',
closed='left')
data=pd.DataFrame({'val':[0.5,0.4,0.7,0.9]})
ev1=[dt.datetime(2018,10,1,9,5,0),dt.datetime(2018,10,1,10,50,0)]
data['t']=dts.values
data.set_index('t',inplace=True)
fig = plt.figure()
gs = GridSpec(1, 1)
ax_1 = fig.add_subplot(gs[0, 0])
# modified section below
ax_1.plot(data.index, data['val'])
ax_1.axvspan(ev1[0],ev1[1], alpha=0.3, color= 'red')
ax_1.set_xticks([x for x in data.index])
ax_1.set_xticklabels([str(x)[11:16] for x in data.index])
plt.show()

Related

How to show all dates in matplotlib from excel import?

I have a code in python 3.11 for a contour plot generating from an excel table using matplotlib. The result shows only first days of months on the x axis (for example 1.6.2022, 1.7.2022 ...). I want all days from the excel source table. Her's the code:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import Normalize
import pandas as pd
import matplotlib.dates as mdates
# import data from excel file
df = pd.read_excel('temperature_data.xlsx', index_col=0)
# Assign columns to variables
time = df.columns
depth = df.index
temperature = df.to_numpy()
# Creating the graph
fig, ax = plt.subplots()
min_temp = temperature.min()
max_temp = temperature.max()
cs = plt.contourf(time, depth, temperature, levels=np.arange(round(min_temp), round(max_temp)+2, 2), cmap='coolwarm', vmin=min_temp, vmax=max_temp)
cs2 = plt.contour(time, depth, temperature, levels=np.arange(round(min_temp), round(max_temp)+2, 2), colors='black')
plt.gca().invert_yaxis()
plt.clabel(cs2, inline=1, fontsize=10, fmt='%d')
plt.title('Teplota vody [°C]')
plt.xticks(rotation=90, ha='right')
ax.xaxis.set_major_formatter(mdates.DateFormatter('%d.%m.%Y'))
#ax.set_xlim(df.index.min(), df.index.max())
#ax.set_xlabel('Time')
ax.set_ylabel('hloubka [m]')
norm = Normalize(vmin=min_temp, vmax=max_temp)
plt.colorbar(cs, cmap='coolwarm', norm=norm)
plt.show()
Thank you for your help.

How to add color and legend by points' label one by one in python?

I want to divide and color points,val_lab(611,3) by their labels,pred_LAB(611,)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = plt.axes(projection = '3d')
ax.set_xlabel('L')
ax.set_ylabel('A')
ax.set_zlabel('B')
for i in range(0, len(val_lab)):
ax.scatter3D(
val_lab[i,0],
val_lab[i,1],
val_lab[i,2],
s = 8,
marker='o',
c = pred_LAB
#cmap = 'rainbow'
)
#ax.legend(*points.legend_elements(), title = 'clusters')
plt.show()
The problem is it shows error,
c' argument has 611 elements, which is not acceptable for use with 'x'
with size 1, 'y' with size 1.
However, if the dataset only have ten points,it can show the figure correctly, I don't know how to solve this problem, besides, how to add legend of this figure?
In your solution you would want to replace c = pred_LAB with c = pred_LAB[i]. But you do not have to use a for loop to plot the data. You can just use the following:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# generate random input data
val_lab = np.random.randint(0,10,(611,3))
pred_LAB = np.random.uniform(0,1, (611,))
# plot data
fig = plt.figure()
ax = plt.axes(projection = '3d')
ax.set_xlabel('L')
ax.set_ylabel('A')
ax.set_zlabel('B')
# create one 3D scatter plot - no for loop
ax.scatter3D(
val_lab[:,0],
val_lab[:,1],
val_lab[:,2],
s = 8,
marker='o',
c = pred_LAB,
cmap = 'rainbow',
label='my points'
)
# add legend
plt.legend()
plt.show()

colour map grids based on value in pandas dataframe

I want to fill the gridded map with colors based on the value of interest. A sample data is here:
import pandas as pd
df = pd.DataFrame()
df['lon'] = [100,105,110,115,120,125,130]
df['lat'] = [38,40,42,44,46,48,50]
df['value'] = [1,2,3,4,5,6,7]
Specifically, is it possible to do this with Cartopy? I found a similar question here:https://stackoverflow.com/questions/53412785/plotting-pandas-csv-data-onto-cartopy-map. But that post was to plot scattered points, I need to fill the grids with colors.
I myself tried:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
lon, lat = np.meshgrid(df['lon'], df['lat'])
fig = plt.figure(figsize=[15,15])
ax = plt.axes(projection=ccrs.PlateCarree())
ax.pcolormesh(lon,lat,df['variable'],latlon=True,cmap='jet')
plt.show()
The error is at ax.pcolormesh(...), it says "not enough values to unpack (expected 2, got 1)"
Many thanks for your help.
For discrete data you can create rectangular patches for each point. Here is a possible solution for your sample data set. Each row of data (lat, long, value) is used to create a rectangular patch. The value is normalized by dividing with max(value) to enable using colormap for coloring the patches.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import matplotlib.patches as mpatches
def make_rect(clon, clat, dlon, dlat):
lon_min = clon - dlon/2.
lat_min = clat - dlat/2.
lon_max = clon + dlon/2.
lat_max = clat + dlat/2.
# clockwise from LL
#lons = [lon_min, lon_min, lon_max, lon_max, lon_min]
#lats = [lat_min, lat_max, lat_max, lat_min, lat_min]
ll = [lon_min,lat_min]
ul = [lon_min,lat_max]
ur = [lon_max,lat_max]
lr = [lon_max,lat_min]
return [ll, ul, ur, lr, ll]
df = pd.DataFrame()
df['lon'] = [100,105,110,115,120,125,130]
df['lat'] = [38,40,42,44,46,48,50]
df['value'] = [1,2,3,4,5,6,7] # not suffice for meshgrid plot
# The colormap to use.
cm = plt.cm.get_cmap('jet')
fig = plt.figure(figsize=[8,6])
ax = plt.axes(projection=ccrs.PlateCarree(), extent=[95, 134, 35, 52])
# plot the red dots using the available data
# comment out if not needed
ax.plot(df['lon'], df['lat'], 'ro')
# plot rectangular patches at the data points
dlon, dlat = 5, 2 #spacings between data points
for lon1, lat1, val1 in zip(df['lon'], df['lat'], df['value']):
pcorners = make_rect(lon1, lat1, dlon, dlat)
poly = mpatches.Polygon(pcorners, ec='gray', fill=True, lw=0.25, \
fc=cm(val1 / max(df['value'])), transform=ccrs.PlateCarree())
ax.add_patch(poly)
ax.gridlines(draw_labels=True)
plt.show()
The output plot:

Data series animation with subplots using GridSpec vs pos

I have a number of data series which I want to save as a video. The different data series should appear one by one, keeping the former series. In the end, all the series is shown in the plot. I want to do this with multiple data series/subplots in a synchronized way. I want the subplots to have different sizes, so - as far as I have understood - I need to do this with GridSpec. However, with GridSpec I am not able to keep the old series while adding new ones (I only get the last data series).
An example of my code is (showing the principle - my actual script is more complex):
Working with index/pos (but only equal size on subplots):
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as anim
figure = plt.figure()
data = np.array([1,2,3,4,5])
set = np.array([1,2,3,4])
def make_frame(i):
ax1 = plt.subplot(121)
x = data
y = i*x
ax1.set_ylim(0,25)
ax1.plot(x,y)
ax2 = plt.subplot((122), sharey = ax1)
a = data*2
b = i/x*3
ax2.plot(a,b)
plt.pause(1)
ani = anim.FuncAnimation(figure, make_frame, frames = set, repeat = False)
plt.show()
Not keeping old series (but with ability of adjust number of columns/rows for each plot):
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as anim
from matplotlib.gridspec import GridSpec
figure = plt.figure()
data = np.array([1,2,3,4,5])
set = np.array([1,2,3,4])
print(data,set)
def make_frame(i):
gs1 = GridSpec(1,2)
ax1 = plt.subplot(gs1[0,0])
x = data
y = i*x
ax1.plot(x,y)
ax2 = plt.subplot(gs1[0,1], sharey = ax1)
a = data
b = i/x*3
ax2.plot(a,b)
ax1.set_ylim(0,25)
plt.pause(0.5)
ani = anim.FuncAnimation(figure, make_frame, frames = set, repeat = False)
plt.show()
How can I keep the "plotting history" in the GridSpec solution?
You need to define the gridspec and the subplots outside of the animating function. Else they are recreated from scratch for each frame.
Also, do not use plt.pause in an animation, but instead use the interval argument. And don't use the names of python functions (set) as variable names.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as anim
from matplotlib.gridspec import GridSpec
data = np.array([1,2,3,4,5])
seti = np.array([1,2,3,4])
gs1 = GridSpec(1,2)
figure = plt.figure()
ax1 = plt.subplot(gs1[0,0])
ax2 = plt.subplot(gs1[0,1], sharey = ax1)
ax1.set_ylim(0,25)
print(data,seti)
def make_frame(i):
x = data
y = i*x
ax1.plot(x,y)
a = data
b = 3.*i/x
ax2.plot(a,b)
ani = anim.FuncAnimation(figure, make_frame, frames = seti,
interval =500, repeat = False)
plt.show()

Having xticks to display Months in a Seaborn regplot with Pandas

I can not figure how to make the xticks to display Months.
For a reproducible example, my data is:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
json = '{"index":{"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16},"Date":{"0":1516147200000,"1":1516752000000,"2":1517788800000,"3":1520208000000,"4":1520985600000,"5":1522281600000,"6":1522886400000,"7":1523404800000,"8":1523491200000,"9":1524096000000,"10":1525305600000,"11":1525737600000,"12":1526428800000,"13":1527811200000,"14":1533686400000,"15":1534377600000,"16":1534809600000},"FB":{"0":0.978943931,"1":1.0282769543,"2":0.999118052,"3":0.994377665,"4":1.0152684601,"5":0.880773866,"6":0.8782934503,"7":0.91676777,"8":0.9032631287,"9":0.9265792518,"10":0.959210704,"11":0.9862198213,"12":1.0098114818,"13":1.0692867773,"14":1.0207253613,"15":0.962958874,"16":0.9514937543},"month":{"0":1,"1":1,"2":2,"3":3,"4":3,"5":3,"6":4,"7":4,"8":4,"9":4,"10":5,"11":5,"12":5,"13":6,"14":8,"15":8,"16":8}}'
toy_data = pd.read_json(json)
fig = plt.figure(figsize=(10,5))
ax = fig.add_subplot(111)
ax.set_title('Share Price Facebook, Google and the SP500')
sns.regplot( x = 'index', y = 'FB', data = toy_data , label = 'FB', fit_reg = True)
plt.show()
I would like to ammend the code so that in the x axis appear the Months of the Range of Observations. That is the tick labels 0 and 1 would be replaced with 'Jan' (appearing once), the tick label 2 would be replaced with 'Feb', the tick labels 3, 4, 5 with 'March' (appearing once), etc.
You can convert your dates to numbers and use those numbers as the x input for the regplot. Then you can format your ticklabels as dates.
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import seaborn as sns
json = '{"index":{"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16},"Date":{"0":1516147200000,"1":1516752000000,"2":1517788800000,"3":1520208000000,"4":1520985600000,"5":1522281600000,"6":1522886400000,"7":1523404800000,"8":1523491200000,"9":1524096000000,"10":1525305600000,"11":1525737600000,"12":1526428800000,"13":1527811200000,"14":1533686400000,"15":1534377600000,"16":1534809600000},"FB":{"0":0.978943931,"1":1.0282769543,"2":0.999118052,"3":0.994377665,"4":1.0152684601,"5":0.880773866,"6":0.8782934503,"7":0.91676777,"8":0.9032631287,"9":0.9265792518,"10":0.959210704,"11":0.9862198213,"12":1.0098114818,"13":1.0692867773,"14":1.0207253613,"15":0.962958874,"16":0.9514937543}}'
df = pd.read_json(json)
df["Date2"] = mdates.date2num(pd.to_datetime(df["Date"]))
fig = plt.figure(figsize=(10,5))
ax = fig.add_subplot(111)
ax.set_title('Share Price Facebook, Google and the SP500')
sns.regplot( x = 'Date2', y = 'FB', data = df , label = 'FB', fit_reg = True, ax=ax)
loc = mdates.AutoDateLocator()
ax.xaxis.set_major_locator(loc)
ax.xaxis.set_major_formatter(mdates.AutoDateFormatter(loc))
plt.show()

Resources