如何在 Pandas Dataframe 中汇总时间序列数据中的缺失值?
How to summarize missing values in time series data in a Pandas Dataframe?
我有一个如下所示的时间序列数据集:
如图所示,通道值有三列与同一组时间戳配对。
每个通道都有一组 NaN 值。
我的objective是创建这些NaN值的总结如下:
我的方法(低效):先创建一个for循环遍历每个通道列,然后再创建一个嵌套for循环遍历通道的每一行。然后当它偶然发现 NaN 值集时,它可以以单独的行(或列表)的形式注册开始时间戳、结束时间戳和持续时间,我最终可以将它们堆叠在一起作为最终输出。
但我的逻辑似乎效率很低而且很慢,尤其是考虑到我的原始数据集有 200 个通道列和 10k 行。我敢肯定 Python.
中应该有比这更好的方法
任何人都可以帮我解决这个问题 - 在 Python 中使用 Pandas 吗?
使用:
inds = df[df['g'].isna()].index.to_list()
gs = []
s = 0
for i, x in enumerate(inds):
if i<len(inds)-1:
if x+1!=inds[i+1]:
gs.append(inds[s:i+1])
s = i+1
else:
gs.append(inds[s:i+1])
ses = []
for g in gs:
ses.append([df.iloc[g[0]]['date'], df.iloc[g[-1]+1]['date']])
res = pd.DataFrame(ses, columns = ['st', 'et'])
res['d'] = res['et']-res['st']
以及更有效的解决方案:
import pandas as pd
import numpy as np
df = pd.DataFrame({'date':pd.date_range('2021-01-01', '2021-12-01', 12), 'g':range(12)})
df['g'].loc[0:3]=np.nan
df['g'].loc[5:7]=np.nan
inds = df[df['g'].isna().astype(int).diff()==-1].index+1
pd.DataFrame([(x.iloc[0]['date'], x.iloc[-1]['date']) for x in np.array_split(df, inds) if np.isnan(x['g'].iloc[0])])
使用DataFrame.melt
重塑DataFrame,然后通过缺失值和缺失后的下一个值过滤连续的组,并通过聚合min
和max
值创建新的DataFrame
:
df['date_time'] = pd.to_datetime(df['date_time'])
df1 = df.melt('date_time', var_name='Channel No.')
m = df1['value'].shift(fill_value=False).notna() #
mask = df1['value'].isna() | ~m
df1 = (df1.groupby([m.cumsum()[mask], 'Channel No.'])
.agg(Starting_Timestamp = ('date_time','min'),
Ending_Timestamp = ('date_time','max'))
.assign(Duration = lambda x: x['Ending_Timestamp'].sub(x['Starting_Timestamp']))
.droplevel(0)
.reset_index()
)
print (df1)
Channel No. Starting_Timestamp Ending_Timestamp Duration
0 Channel_1 2019-09-19 10:59:00 2019-09-19 14:44:00 0 days 03:45:00
1 Channel_1 2019-09-19 22:14:00 2019-09-19 23:29:00 0 days 01:15:00
2 Channel_2 2019-09-19 13:59:00 2019-09-19 19:44:00 0 days 05:45:00
3 Channel_3 2019-09-19 10:59:00 2019-09-19 12:44:00 0 days 01:45:00
4 Channel_3 2019-09-19 15:14:00 2019-09-19 16:44:00 0 days 01:30:00
我有一个如下所示的时间序列数据集:
如图所示,通道值有三列与同一组时间戳配对。 每个通道都有一组 NaN 值。
我的objective是创建这些NaN值的总结如下:
我的方法(低效):先创建一个for循环遍历每个通道列,然后再创建一个嵌套for循环遍历通道的每一行。然后当它偶然发现 NaN 值集时,它可以以单独的行(或列表)的形式注册开始时间戳、结束时间戳和持续时间,我最终可以将它们堆叠在一起作为最终输出。
但我的逻辑似乎效率很低而且很慢,尤其是考虑到我的原始数据集有 200 个通道列和 10k 行。我敢肯定 Python.
中应该有比这更好的方法任何人都可以帮我解决这个问题 - 在 Python 中使用 Pandas 吗?
使用:
inds = df[df['g'].isna()].index.to_list()
gs = []
s = 0
for i, x in enumerate(inds):
if i<len(inds)-1:
if x+1!=inds[i+1]:
gs.append(inds[s:i+1])
s = i+1
else:
gs.append(inds[s:i+1])
ses = []
for g in gs:
ses.append([df.iloc[g[0]]['date'], df.iloc[g[-1]+1]['date']])
res = pd.DataFrame(ses, columns = ['st', 'et'])
res['d'] = res['et']-res['st']
以及更有效的解决方案:
import pandas as pd
import numpy as np
df = pd.DataFrame({'date':pd.date_range('2021-01-01', '2021-12-01', 12), 'g':range(12)})
df['g'].loc[0:3]=np.nan
df['g'].loc[5:7]=np.nan
inds = df[df['g'].isna().astype(int).diff()==-1].index+1
pd.DataFrame([(x.iloc[0]['date'], x.iloc[-1]['date']) for x in np.array_split(df, inds) if np.isnan(x['g'].iloc[0])])
使用DataFrame.melt
重塑DataFrame,然后通过缺失值和缺失后的下一个值过滤连续的组,并通过聚合min
和max
值创建新的DataFrame
:
df['date_time'] = pd.to_datetime(df['date_time'])
df1 = df.melt('date_time', var_name='Channel No.')
m = df1['value'].shift(fill_value=False).notna() #
mask = df1['value'].isna() | ~m
df1 = (df1.groupby([m.cumsum()[mask], 'Channel No.'])
.agg(Starting_Timestamp = ('date_time','min'),
Ending_Timestamp = ('date_time','max'))
.assign(Duration = lambda x: x['Ending_Timestamp'].sub(x['Starting_Timestamp']))
.droplevel(0)
.reset_index()
)
print (df1)
Channel No. Starting_Timestamp Ending_Timestamp Duration
0 Channel_1 2019-09-19 10:59:00 2019-09-19 14:44:00 0 days 03:45:00
1 Channel_1 2019-09-19 22:14:00 2019-09-19 23:29:00 0 days 01:15:00
2 Channel_2 2019-09-19 13:59:00 2019-09-19 19:44:00 0 days 05:45:00
3 Channel_3 2019-09-19 10:59:00 2019-09-19 12:44:00 0 days 01:45:00
4 Channel_3 2019-09-19 15:14:00 2019-09-19 16:44:00 0 days 01:30:00