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

时间:2017-10-25 15:57:16

标签: python pandas numpy multi-index reindex

问题和我想要的

我有一个数据文件,包含从多个传感器异步读取的时间序列。基本上对于我的文件中的每个数据元素,我都有一个传感器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())

level=1基于Multi-Indexed fillna in Pandas,但我没有再次对数据框进行任何可见的更改,我认为因为我现在没有任何我希望我的价值观去的地方。

到目前为止

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,)]

但这有两个问题:

  1. 它让我脱离了熊猫世界,我现在必须手动维护我的传感器ID,时间索引等以及我的特征向量(实际的data列不仅仅是一列而是一吨来自传感器套件的值。)
  2. 考虑到列的数量和实际数据集的大小,在我的真实示例中实现这将是笨重和不优雅的。我更喜欢在熊猫中做一种方式。
  3. 应用程序

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

    感谢您的帮助!

3 个答案:

答案 0 :(得分:2)

以下是一种方法,使用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

答案 1 :(得分:1)

您可以拥有每个传感器的最后读数字典。你必须选择一些初始值;最合乎逻辑的选择可能是将最早的阅读回填到较早的时间。填好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]]。然后,您的神经网络可以决定如何根据数据的大小对数据进行加权。

答案 2 :(得分:0)

我得到了以下内容,我认为对于这样的情况非常通用,其中您要填充值的时间索引是具有两个索引的多索引中的第二个:

# 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的函数中,该函数将dataframe作为参数并返回已清理的数据框。