簡體   English   中英

突出顯示 Matplotlib 散點圖 Plot 中的數據間隙 (NaN)

[英]Highlight data gaps (NaN) in Matplotlib Scatter Plot

我正在繪制來自 matplotlib 中 pandas 的一些基於時間的數據(可以是數萬行),我想突出顯示數據中存在 NaN 的時段。 我認為實現這一點的方法是使用 axvspan 在 plot 上繪制一個紅色框,在有數據間隙的地方開始和停止。 我確實考慮過每次使用 axvline 有一個 NaN 時只畫一條垂直線,但這可能會在 plot 上創建數千個對象,並導致生成的 PNG 需要很長時間才能寫入。 所以我認為使用 axvspan 更合適。 但是,我遇到的困難是找到 NaN 組的開始和停止索引。

下面的代碼不是來自我的實際代碼,它只是一個基本模型,用於顯示我想要實現的目標。

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()

結果看起來像下面的模型: 在此處輸入圖像描述

其他可視化間隙的建議也將不勝感激。 比如一條不同顏色的直線使用某種填充連接跨越間隙的數據?

要查找 NaN 組的開始和停止索引,您可以首先創建一個變量來保存 boolean 值,其中colNaN 使用此變量,您可以找到validNaN值之間存在轉換的行。 這可以使用shift (在數據幀上移動一行)和ne來完成,這樣您可以比較兩個連續的行並確定值交替的位置。 之后,應用cumsum創建不同組的validNaN值的連續數據。

現在,僅使用具有NaN值的行 ( df[is_nan] ) 使用groupbyn_groups來收集同一組內的間隙。 接下來,應用aggregate以返回單個元組,其中包含每個組的開始和結束時間戳。 這里使用DateOffset是為了將矩形顯示擴展到所需圖像 output 之后的相鄰點。 您現在可以使用['col'].values訪問聚合返回的aggregate並將其轉換為列表。

...
...
df = df.set_index('idx')
print(df)

# Code to find the start index and stop index of the groups of NaNs
is_nan = df['col'].isna()
n_groups = is_nan.ne(is_nan.shift()).cumsum()
gap_list = df[is_nan].groupby(n_groups).aggregate(
    lambda x: (
        x.index[0] + pd.DateOffset(days=-1),
        x.index[-1] + pd.DateOffset(days=+1)
    )
)["col"].values

# resuls in list which contains tuples of each gap start and stop datetime
gaps = gap_list

plt.plot(df.index, df['col'], marker='o' )
plt.xticks(df.index, rotation=45)

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

plt.grid()
plt.show()

plot_nan_gaps

我們可以使用fill_between來突出顯示區域。 但是,定義數據所在的部分比定義沒有數據的部分要容易得多,而不會與現有數據點產生差距。 因此,我們只需突出顯示整個繪圖區域,然后覆蓋數據為白色的區域,然后是 plot:

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()

樣品 output:

在此處輸入圖像描述

您可以遍歷由df['col'].isna()給出的 boolean 值的枚舉列表,並將每個 boolean 值與前一個值進行比較,以stops的時間戳starts 這是一個基於您的代碼示例的示例,其中 plot 是使用pandas 繪制 function生成的:

import numpy as np               # v 1.19.2
import pandas as pd              # v 1.2.3
import matplotlib.pyplot as plt  # v 3.3.4

days = pd.date_range('2021-03-08', periods=14, 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(dict(col=data), index=days)

ax = df.plot(y='col', marker='.', figsize=(8,4))

# Generate lists of starts and stops timestamps for gaps in time series,
# assuming that the first and last data points are not NaNs
starts, stops = [], []
for idx, isna in enumerate(df['col'].isna()):
    if isna != df['col'].isna()[idx-1] and isna:
        starts.append(df.index[idx-1])
    elif isna != df['col'].isna()[idx-1] and not isna:
        stops.append(df.index[idx])

# Plot red vertical spans for gaps in time series
for start, stop in zip(starts, stops): 
    ax.axvspan(start, stop, facecolor='r', alpha=0.3)

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)

之間的填充在 nans 所在的位置創建了我的陰影塊。 將它們乘以 data.max() 允許它們跨越整個 y 軸。 Step='mid' 將兩側對齊。 Linewidth=0 當數據為 0(不是 NaN)時隱藏紅線。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM