Python 在Matplotlib散点图中高亮显示数据间隙(NaN)

Python 在Matplotlib散点图中高亮显示数据间隙(NaN),python,python-3.x,pandas,numpy,matplotlib,Python,Python 3.x,Pandas,Numpy,Matplotlib,我正在matplotlib中绘制pandas的一些基于时间的数据(可以是上万行),我想突出显示数据中存在NaN的时段。我认为实现这一点的方法是使用axvspan在绘图上绘制一个红色框,在有数据间隙的地方开始和停止。我确实考虑过在每次使用axvline生成NaN时只绘制一条垂直线,但这可能会在绘图上创建数千个对象,并导致生成的PNG需要很长时间才能写入。因此,我认为使用axvspan更合适。然而,我陷入困境的地方是寻找南部组的开始和停止指数 下面的代码不是来自我的实际代码,只是一个基本的模型,展示

我正在matplotlib中绘制pandas的一些基于时间的数据(可以是上万行),我想突出显示数据中存在NaN的时段。我认为实现这一点的方法是使用axvspan在绘图上绘制一个红色框,在有数据间隙的地方开始和停止。我确实考虑过在每次使用axvline生成NaN时只绘制一条垂直线,但这可能会在绘图上创建数千个对象,并导致生成的PNG需要很长时间才能写入。因此,我认为使用axvspan更合适。然而,我陷入困境的地方是寻找南部组的开始和停止指数

下面的代码不是来自我的实际代码,只是一个基本的模型,展示了我试图实现的目标

import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import matplotlib.pyplot as plt

days = pd.date_range(datetime.now(), datetime.now() + timedelta(13), freq='D')
data = [2,2.3,3,np.nan, np.nan,4.7,3.4,3.1,2.7,np.nan,np.nan,np.nan,4,4.5]
df = pd.DataFrame({'idx': days, 'col': data})
df = df.set_index('idx')
print(df)

#Code to find the start index and stop index of the groups of NaNs
# resuls in list which contains lists of each gap start and stop datetime
gaps = []

plt.plot(df.index, df['col'])

for gap in gaps: 
    plt.axvspan(gap[0], gap[1], facecolor='r', alpha=0.5)

plt.show()
结果类似于下面的模型:


还将赞赏其他关于可视化差距的建议。例如,一条不同颜色的直线,使用某种类型的fillna连接间隙中的数据?

要查找NaN组的开始和停止索引,您可以首先创建一个变量来保存布尔值,其中
NaN
。使用此变量,您可以找到在
valid
NaN
值之间存在转换的行。这可以使用(使数据帧上的一行错位)和
ne
来完成,这样您就可以比较两个连续的行并确定值的交替位置。然后,应用创建
valid
NaN
值的不同连续数据组

现在,仅使用具有
NaN
值的行(
df[is_NaN]
)使用
groupby
n_groups
来收集同一组内的间隙。接下来,apply返回一个元组,其中包含每个组的开始和结束时间戳。此处使用的
DateOffset
是将矩形显示扩展到所需图像输出后的相邻点。现在,您可以使用
['col'].values
访问聚合返回的数据帧,并将其转换为列表

。。。
...
df=df.set_索引('idx')
打印(df)
#用于查找NAN组的开始索引和停止索引的代码
is_nan=df['col'].isna()
n_groups=is_nan.ne(is_nan.shift()).cumsum()
gap\u list=df[is\u nan].groupby(n个组).聚合(
λx:(
x、 索引[0]+pd.DateOffset(天数=-1),
x、 索引[-1]+pd.DateOffset(天数=+1)
)
)[“col”]值
#结果在列表中,该列表包含每个间隙开始和停止日期时间的元组
间隙=间隙列表
plt.plot(df.index,df['col'],marker='o')
plt.xticks(测向指数,旋转=45)
对于间隙中的间隙:
plt.axvspan(间隙[0],间隙[1],面颜色=r',α=0.5)
plt.grid()
plt.show()

您可以循环查看
df['col'].isna()给出的布尔值枚举列表,并将每个布尔值与前一个布尔值进行比较,以选择间隙的
开始
停止
的时间戳。以下是一个基于您的代码示例的示例,其中使用以下命令生成绘图:

将numpy作为np导入#v 1.19.2
作为pd#v 1.2.3进口大熊猫
将matplotlib.pyplot作为plt#v 3.3.4导入
天数=pd.日期范围('2021-03-08',周期=14,频率=D')
数据=[2,2.3,3,np.nan,np.nan,4.7,3.4,3.1,2.7,np.nan,np.nan,np.nan,4,4.5]
df=pd.DataFrame(dict(col=data),index=days)
ax=df.plot(y='col',marker=',figsize=(8,4))
#为时间序列中的间隔生成开始和停止时间戳列表,
#假设第一个和最后一个数据点不是NAN
开始,停止=[],[]
对于idx,枚举中的isna(df['col'].isna()):
如果是isna!=df['col'].isna()[idx-1]和isna:
start.append(df.index[idx-1])
伊里夫·伊斯纳df['col'].isna()[idx-1]而非isna:
stops.append(df.index[idx])
#为时间序列中的间隔绘制红色垂直跨度
对于开始,在zip中停止(开始,停止):
ax.axvspan(开始、停止、facecolor='r',alpha=0.3)
plt.show()
我们可以使用来突出显示区域。但是,定义有数据的零件要比没有数据的零件容易得多,而不会与现有数据点产生间隙。因此,我们只需高亮显示整个绘图区域,然后覆盖数据为白色的区域,然后进行绘图:

import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import matplotlib.pyplot as plt


days = pd.date_range(datetime.now(), datetime.now() + timedelta(13), freq='D')
data = [2,2.3,3,np.nan, np.nan,4.7,3.4,3.1,2.7,np.nan,np.nan,np.nan,4,4.5]
df = pd.DataFrame({'idx': days, 'col': data})
df = df.set_index('idx')


fig, ax = plt.subplots()
ax.fill_between(df.index, df.col.min(), df.col.max(), where=df.col, facecolor="lightblue", alpha=0.5)
ax.fill_between(df.index, df.col.min(), df.col.max(), where=np.isfinite(df.col), facecolor="white", alpha=1)
ax.plot(df.index, df.col)

ax.xaxis.set_tick_params(rotation=45)
plt.tight_layout()
plt.show()
样本输出:


最后,我从提供的答案中的a、B和C栏中选取了一些内容,感谢您的反馈。对于真实世界的数据(数十万行),构建开始-停止列表的速度非常慢。因为我不需要数字答案,只需要一个直观的答案,所以我单独使用matplotlib,并使用以下代码:

ax[i].fill_between(data.index, 0, (is_nan*data.max()), color='r', step='mid', linewidth='0')
ax[i].plot(data.index, data, color='b', linestyle='-', marker=',', label=ylabel)

中间的填充将在南的位置创建着色块。将它们与data.max()相乘,可以跨越整个y轴。将“中间”方块从侧面移开。Linewidth=0在数据为0(非NaN)时隐藏红线。

感谢您的响应,我以前没有看到fill_between,除非我尝试运行上述代码时,它会抛出ax.fiull_between的执行选项:
发生异常:TypeError不支持的操作数类型(&):'float'和'bool'文件“C:\gap_test.py”,第27行,在ax.fill_中间(df.index,df.col.min(),df.col.max(),其中=df.col,facecolor=“lightblue”,alpha=0.5)
您的matplotlib版本是什么?我想大概是2.x吧。这应在问题中加以说明;3.3.3. 是当前版本。Matplotlib版本是3.1.0,真的不知道为什么它不起作用,但能够从使用fill_between中获得灵感,以实现我需要的外观。