在多索引数据框中填充缺失的时间值

Posted

技术标签:

【中文标题】在多索引数据框中填充缺失的时间值【英文标题】:Filling missing time values in a multi-indexed dataframe 【发布时间】:2018-04-06 19:32:39 【问题描述】:

问题和我想要什么

我有一个数据文件,其中包含从多个传感器异步读取的时间序列。基本上,对于我文件中的每个数据元素,我都有一个传感器 ID 和读取它的时间,但我并不总是每次都有所有传感器,并且读取时间可能不均匀。比如:

ID,time,data
0,0,1
1,0,2
2,0,3
0,1,4
2,1,5  # skip some sensors for some time steps
0,2,6
2,2,7
2,3,8
1,5,9  # skip some time steps
2,5,10

重要提示实际的time 列是日期时间类型。

我想要的是能够在该传感器不存在的任何时间步长内为每个传感器保持零阶保持(前向填充)值,并将任何未读取的传感器设置为零或回填最早的时间步。我想要的是一个看起来像是从以下位置读取的数据框:

ID,time,data
0,0,1
1,0,2
2,0,3
0,1,4
1,1,2  # ID 1 hold value from time step 0
2,1,5
0,2,6
1,2,2  # ID 1 still holding
2,2,7
0,3,6  # ID 0 holding
1,3,2  # ID 1 still holding
2,3,8
0,5,6  # ID 0 still holding, can skip totally missing time steps
1,5,9  # ID 1 finally updates
2,5,10

迄今为止的熊猫尝试

我初始化我的数据框并设置我的索引:

df = pd.read_csv(filename, dtype=np.int)
df.set_index(['ID', 'time'], inplace=True)

我试图搞砸类似的事情:

filled = df.reindex(method='ffill')

或类似的将各种值传递给index 关键字参数,如df.index['time'] 等。这总是会引发错误,因为我传递了无效的关键字参数,或者对数据框不可见。我认为它没有认识到我正在寻找的数据是“缺失的”。

我也试过了:

df.update(df.groupby(level=0).ffill())

或基于Multi-Indexed fillna in Pandas 的level=1,但我认为数据框没有任何可见的变化,我想是因为我目前没有任何我想要我的价值观去的地方。

到目前为止的 Numpy 尝试

我在使用 numpy 和非整数索引时遇到了一些运气,例如:

data = [np.array(df.loc[level].data) for level in df.index.levels[0]]
shapes = [arr.shape for arr in data]
print(shapes)
# [(3,), (2,), (5,)]
data = [np.array([arr[i] for i in np.linspace(0, arr.shape[0]-1, num=max(shapes)[0])]) for arr in data]
print([arr.shape for arr in data])
# [(5,), (5,), (5,)]

但这有两个问题:

    它把我带出了熊猫世界,我现在必须手动维护我的传感器 ID、时间索引等以及我的特征向量(实际的 data 列不仅仅是一列,而是大量的值来自传感器套件)。 考虑到列数和实际数据集的大小,在我的实际示例中实现这将是笨重且不优雅的。我更喜欢在熊猫中这样做。

应用程序

最终,这只是训练循环神经网络的数据清理步骤,对于每个时间步,我需要提供一个始终具有相同结构的特征向量(每个时间步的每个传感器 ID 的一组测量值)。

感谢您的帮助!

【问题讨论】:

【参考方案1】:

这是一种方法,使用 reindexcategory

df.time=df.time.astype('category',categories =[0,1,2,3,4,5])
new_df=df.groupby('time',as_index=False).apply(lambda x : x.set_index('ID').reindex([0,1,2])).reset_index()
new_df['data']=new_df.groupby('ID')['data'].ffill()
new_df.drop('time',1).rename(columns='level_0':'time')
Out[311]: 
    time  ID  data
0      0   0   1.0
1      0   1   2.0
2      0   2   3.0
3      1   0   4.0
4      1   1   2.0
5      1   2   5.0
6      2   0   6.0
7      2   1   2.0
8      2   2   7.0
9      3   0   6.0
10     3   1   2.0
11     3   2   8.0
12     4   0   6.0
13     4   1   2.0
14     4   2   8.0
15     5   0   6.0
16     5   1   9.0
17     5   2  10.0

【讨论】:

太糟糕了,我只是被重新索引所困 @Bharathshetty 这很正常,伙计,我们总是卡在一些简单的点上,我也是。 这是一个很好的方法,但实际时间值都是跨越一整天的毫秒时钟值,因此为每个可能的时间定义类别可能不现实。有没有办法修改它以使用数据集的所有可用日期时间值的排序集? @Engineero 你可以在你的真实数据集上尝试一下,因为我看不到那些特殊情况。【参考方案2】:

您可以拥有每个传感器最后读数的字典。您必须选择一些初始值;最合乎逻辑的选择可能是将最早的阅读内容回填到较早的时间。填写完last_reading 字典后,您可以按时间对所有读数进行排序,为每个读数更新字典,然后根据字典填写行。因此,在您初始化 last_reading 字典之后:

last_time = readings[1][time]
for reading in readings:
   if reading[time] > last_time:
      for ID in ID_list:
         df.loc[last_time,ID] = last_reading[ID]
      last_time = reading[time]
   last_reading[reading[ID]] = reading[data]
#the above for loop doesn't update for the last time
#so you'll have to handle that separately
for ID in ID_list:
    df.loc[last_time,ID] = last_reading[ID]
    last_time = reading[time]

这假设您对于每个时间/传感器对只有一个读数,并且“读数”是按时间排序的字典列表。它还假设 df 具有不同的传感器作为列和不同的时间作为索引。否则,根据需要调整代码。您也可以通过一次更新整行而不是使用 for 循环来对其进行更多优化,但我不想确保我的 Pandas 语法正确。

但是,查看应用程序时,您可能希望数据框中的每个单元格不是数字,而是最后一个值和读取时间的元组,因此将 last_reading[reading[ID]] = reading[data] 替换为 last_reading[reading[ID]] = [reading[data],reading[time]]。然后,您的神经网络可以根据数据的年龄决定如何对数据进行加权。

【讨论】:

【参考方案3】:

我让它与以下内容一起使用,我认为这对于像这样的任何情况都很普遍,其中您要填充值的时间索引是具有两个索引的多索引中的第二个:

# Remove duplicate time indices (happens some in the dataset, pandas freaks out).
df = df[~df.index.duplicated(keep='first')]

# Unstack the dataframe and fill values per serial number forward, backward.
df = df.unstack(level=0)
df.update(df.ffill())  # first ZOH forward
df.update(df.bfill())  # now back fill values that are not seen at the beginning

# Restack the dataframe and re-order the indices.
df = df.stack(level=1)
df = df.swaplevel()

这得到了我想要的,尽管如果有人知道这样做的好方法,我希望能够保留重复的时间条目。

如果对于特定应用程序而言,从零开始看不见的值更可取,您也可以使用df.update(df.fillna(0)) 而不是回填。

我将上面的代码块放在一个名为clean_df 的函数中,该函数将数据帧作为参数并返回清理后的数据帧。

【讨论】:

以上是关于在多索引数据框中填充缺失的时间值的主要内容,如果未能解决你的问题,请参考以下文章

在缺少日期的多索引数据框中移动列

如何用其他数据框的值填充缺失值

Python,pandas:如何从对称的多索引数据框中提取值

在多索引熊猫数据框中使用 groupby 时计算时间和空间梯度

如何更改多索引数据框中的索引

访问熊猫数据框中内部多索引级别的最后一个元素