简体   繁体   中英

matplotlib inset with '%H:%M' x-axis

I am unable to successfully plot a 'zoomed' inset with matplotlib.

在此处输入图片说明

The x-axis is originally a UTC datetime column; transformed to count from 0 (zero) and to display: matplotlib.dates . Ideally I would like the the inset to only show the x-range from 00:30 to 00:45 (h:m) between a y-range of -0.5 to 1 (m). Those fancy lines that link the inset to the main plot would be pretty cool.

My attempt that fails:

import matplotlib.pyplot as plt
import matplotlib.pylab as pl
import matplotlib.dates as md
from mpl_toolkits.axes_grid1.inset_locator import (inset_axes, InsetPosition, mark_inset)

colors = pl.cm.viridis(np.linspace(0,1,3))

# specify a date to use for the times
tdate = df['UTC']-df['UTC'][0]
zero = df['UTC'][0]
time = [zero + t for t in tdate]
# convert datetimes to numbers
zero = md.date2num(zero)
time = [t-zero for t in md.date2num(time)]
    
fig1 = plt.figure(figsize=(18,12), dpi=80)
fig1.suptitle('Position / Time', fontsize=14, fontweight='bold')
ax = fig1.add_subplot(2,1,1)
names = ['delta y','delta x','delta z']
      
# Make plots
for i in range(len(names)):
    ax.plot(time, df.iloc[:, 19+i], label=names[i], color=colors[i]) #tdate
    
ax.grid(True)
ax.set_ylabel('Absolute Error (m)')
ax.xaxis_date()
ax.xaxis.set_major_formatter(md.DateFormatter("%H:%M"))
ax.set_xlabel('Time (h:m)')
      
# Make legend
plt.legend(loc='upper right')

# Create a set of inset Axes
ax2 = plt.axes([100, 500, 0.5, 1])
# Manually set the position and relative size of the inset axes within ax1
ip = InsetPosition(ax, [0.4,0.4,0.5,0.5])
ax2.set_axes_locator(ip)

# do the time for the inset
indate = df[ (df.index >= 1800) & (df.index <= 2700)]
#tdate = df['UTC']-df['UTC'][0]
ztdate = indate['UTC'][2700]-indate['UTC'][1800]
zero = indate['UTC'][1800]
dtime = [zero + t for t in ztdate]
# convert datetimes to numbers
zero = md.date2num(zero)
dtime = [t-zero for t in md.date2num(dtime)]

# plot the inset
for i in range(len(names)):
    ax2.plot(dtime, df.iloc[19+i], label=names[i], color=colors[i]) #tdate
#ax2.legend(loc=0)
mark_inset(ax, ax2, loc1=2, loc2=4, fc="none", ec='0.5')

plt.xticks(visible=False)
plt.yticks(visible=False)

ax2.grid(True)
ax2.xaxis_date()
ax2.xaxis.set_major_formatter(md.DateFormatter("%H:%M"))

plt.show()

with the following error message - before it renders the plot above:

Traceback (most recent call last):

  File "<ipython-input-18-c3dd5e9ca2b0>", line 45, in <module>
    dtime = [zero + t for t in ztdate]

TypeError: 'Timedelta' object is not iterable

DataFrame here . Your help is appreciated.

The key is to trim the time (as a number) and the DataFrame:

t1 = time[1799:3000]
indate = df[(df.index >= 1800) & (df.index <= 3000)] 

then with:

import matplotlib.pyplot as plt
import matplotlib.pylab as pl
import matplotlib.dates as md
from mpl_toolkits.axes_grid1.inset_locator import (inset_axes, InsetPosition, mark_inset)

colors = pl.cm.viridis(np.linspace(0,1,3))
    
# Create a new figure of size 10x6 points, using 80 dots per inch
fig1 = plt.figure(figsize=(18, 12), dpi=80)
fig1.suptitle('Position / Time', fontsize=14, fontweight='bold')
ax = fig1.add_subplot(2,1,1)
names = ['delta y','delta x','delta z']

# specify a date to use for the times
tdate = df['UTC']-df['UTC'][0]
zero = df['UTC'][0]
time = [zero + t for t in tdate]
# convert datetimes to numbers
zero = md.date2num(zero)
time = [t-zero for t in md.date2num(time)]

# Make plots
for i in range(len(names)):
    ax.plot(time, df.iloc[:, 19+i], label=names[i], color=colors[i]) #tdate
      
# Make legend
plt.legend(loc='upper right')
    
ax.grid(axis='y', linestyle='-', linewidth=0.3)
ax.set_ylabel('Absolute Error (m)')
ax.xaxis_date()
ax.xaxis.set_major_formatter(md.DateFormatter("%H:%M"))
ax.set_xlabel('Time (h:m)')
          
# Make legend    
plt.legend(loc='upper right')
    
# Create a set of inset Axes
ax2 = plt.axes([0, 0, 1, 1])
# Manually set the position and relative size of the inset axes within ax1
ip = InsetPosition(ax, [0.4,0.4,0.5,0.5])
ax2.set_axes_locator(ip)
    
# do the time for the inset
t1 = time[1799:3000]
indate = df[(df.index >= 1800) & (df.index <= 3000)]
    
# plot the inset
for i in range(len(names)):
        ax2.plot(t1, indate.iloc[:, 19+i], color=colors[i])#label=names[i]) #tdate

# -- https://stackoverflow.com/questions/44715968/matplotlib-change-style-of-inset-elements-singularly
plt.setp(list(ax2.spines.values()), linewidth=0.5, linestyle="--")
box, c1, c2 = mark_inset(ax, ax2, loc1=2, loc2=3, fc="none",  lw=0.3, ec='0.5')
plt.setp([c1,c2], linestyle=":")
    
ax2.grid(axis='y', linestyle='-') #True
ax2.xaxis_date()
ax2.xaxis.set_major_formatter(md.DateFormatter("%H:%M"))
    
plt.show()

result: 在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM