【问题标题】:Pandas, check if timestamp value exists in resampled 30 min time bin of datetimeindexPandas,检查时间戳值是否存在于 datetimeindex 的重新采样的 30 分钟时间箱中
【发布时间】:2015-12-02 20:18:36
【问题描述】:

我在 pandas 中创建了一个重采样数据框 (DF1),带有 datetimeindex。我有一个带有datetimeindex 和time 列的单独数据框(DF2)。如果 DF2 中的 time 实例落在 DF1 中 datetimeindex 的 30 分钟 bin 内。我想用 DF1 中 30 分钟 bin 中的适当 speed 标记 DF2 中 time 的每个实例。

DF1

                   boat_id      speed
time                                      
2015-01-13 09:00:00   28.000000   0.000000
2015-01-13 09:30:00   28.000000   0.723503
2015-01-13 10:00:00   28.000000   2.239399

DF2

                      id  boat_id                 time  state     
time                                                                          
2015-01-18 16:09:03   319437       28  2015-01-18 16:09:03      2    
2015-01-18 16:18:43   319451       28  2015-01-18 16:18:43      0    
2015-03-01 09:39:51   507108       31  2015-03-01 09:39:51      1    
2015-03-01 09:40:58   507109       31  2015-03-01 09:40:58      0 

想要的结果

                      id  boat_id                 time      state   speed
time                                                                          
2015-01-18 16:09:03   319437       28  2015-01-18 16:09:03      2 nan   
2015-01-18 16:18:43   319451       28  2015-01-18 16:18:43      0 nan   
2015-03-01 09:39:51   507108       31  2015-03-01 09:39:51      1 2.239399   
2015-03-01 09:40:58   507109       31  2015-03-01 09:40:58      0 2.239399

我创建了这个脚本来尝试执行此操作,但我认为它失败了,因为 DF1 的 datetimeindex 是不可变的,因此我的 timedelta 请求不会为块创建起点。我的一个想法是是否可以将 DF1 的 datetimeindex 复制到一个新列中,其中对象是可变的,但我还没有管理它,所以我不能 100% 确定逻辑。我很乐意修补,但目前我已经停滞了一段时间,所以希望其他人可能有一些想法。提前致谢。

for row in DF1.iterrows():
    for dfrow in DF2.iterrows():
        if dfrow[0] > row[0] - dt.timedelta(minutes=30) and dfrow[0] < row[0]:
            df['test'] =  row[1]

【问题讨论】:

  • 你为什么不试试between_time()?
  • @Kartik,感谢您的建议,我尝试了for row in DF1.iterrows(): for dfrow in DF2.iterrows(): if dfrow['time'] == DF1[pd.DataFrame.between_time(row[0] - dt.timedelta(minutes=15), row[0])]: df['test'] = row[1],但得到了结果TypeError: tuple indices must be integers, not str 如果我使用int 而不是字符串,我会得到IndexError: tuple index out of range 或TypeError: unbound method between_time() must be called with DataFrame instance as first argument (got Timestamp instance instead) 我错过了什么?
  • 我想知道它是否与数据帧中的标题有关?
  • 老兄!那是错的!等等,我来回答……

标签: python pandas


【解决方案1】:

迭代的性能非常低。更好的是使用矢量化解决方案。我使用了两次函数merge。 Docs.

输入:

print df1
                     boat_id     speed
time                                  
2015-03-01 09:00:00       28  0.000000
2015-03-01 09:30:00       28  0.723503
2015-03-01 10:00:00       28  2.239399

print df2
                         id  boat_id                time  state
time                                                           
2015-01-18 16:09:03  319437       28 2015-01-18 16:09:03      2
2015-01-18 16:18:43  319451       28 2015-01-18 16:18:43      0
2015-03-01 09:39:51  507108       31 2015-03-01 09:39:51      1
2015-03-01 09:40:58  507109       31 2015-03-01 09:40:58      0

我重置了两个数据框的索引并创建了由1 填充的辅助列i。

df1 = df1.reset_index()
df2 = df2.reset_index(drop=True)
df1['i'] =  df2['i'] = 1
print df1
                 time  boat_id     speed  i
0 2015-03-01 09:00:00       28  0.000000  1
1 2015-03-01 09:30:00       28  0.723503  1
2 2015-03-01 10:00:00       28  2.239399  1
print df2
       id  boat_id                time  state  i
0  319437       28 2015-01-18 16:09:03      2  1
1  319451       28 2015-01-18 16:18:43      0  1
2  507108       31 2015-03-01 09:39:51      1  1
3  507109       31 2015-03-01 09:40:58      0  1

然后我通过帮助列 i 合并了两个数据框。

df = df2.merge(df1, on='i', how='left')
df = df.rename(columns={'time_y':'Bin_time', 'time_x':'time'})
print df
        id  boat_id_x                time  state  i            Bin_time  \
0   319437         28 2015-01-18 16:09:03      2  1 2015-03-01 09:00:00   
1   319437         28 2015-01-18 16:09:03      2  1 2015-03-01 09:30:00   
2   319437         28 2015-01-18 16:09:03      2  1 2015-03-01 10:00:00   
3   319451         28 2015-01-18 16:18:43      0  1 2015-03-01 09:00:00   
4   319451         28 2015-01-18 16:18:43      0  1 2015-03-01 09:30:00   
5   319451         28 2015-01-18 16:18:43      0  1 2015-03-01 10:00:00   
6   507108         31 2015-03-01 09:39:51      1  1 2015-03-01 09:00:00   
7   507108         31 2015-03-01 09:39:51      1  1 2015-03-01 09:30:00   
8   507108         31 2015-03-01 09:39:51      1  1 2015-03-01 10:00:00   
9   507109         31 2015-03-01 09:40:58      0  1 2015-03-01 09:00:00   
10  507109         31 2015-03-01 09:40:58      0  1 2015-03-01 09:30:00   
11  507109         31 2015-03-01 09:40:58      0  1 2015-03-01 10:00:00   

    boat_id_y     speed  
0          28  0.000000  
1          28  0.723503  
2          28  2.239399  
3          28  0.000000  
4          28  0.723503  
5          28  2.239399  
6          28  0.000000  
7          28  0.723503  
8          28  2.239399  
9          28  0.000000  
10         28  0.723503  
11         28  2.239399  

输出按 bin 时间过滤:

df = df[((df.time >= (df.Bin_time - dt.timedelta(minutes=30))) & (df.time <= df.Bin_time ))]
df = df.drop(['Bin_time', 'id', 'boat_id_x', 'boat_id_y','state', 'i' ], axis=1 )
print df
                  time     speed
8  2015-03-01 09:39:51  2.239399
11 2015-03-01 09:40:58  2.239399

并且df 由列time 与数据框df2 合并。

df = df2.merge(df, on='time', how='left').reset_index(drop=True)
df = df.drop([ 'i' ], axis=1 )
print df
       id  boat_id                time  state     speed
0  319437       28 2015-01-18 16:09:03      2       NaN
1  319451       28 2015-01-18 16:18:43      0       NaN
2  507108       31 2015-03-01 09:39:51      1  2.239399
3  507109       31 2015-03-01 09:40:58      0  2.239399

比较矢量化和索引方法,您可以在类似的答案here 中找到。

【讨论】:

  • 感谢您的解决方案,我只是想让它工作,当您删除索引时,时间列将恢复为 pandas 对象而不是datetime64,所以timedelta 不起作用.但我理解你的逻辑,我对解决方案充满希望。
  • 太棒了,这是我在上面概述的timedelta 问题。
  • 我发现的一个问题是在i 上使用合并功能需要很长时间和大量内存。使用df.info 我可以看到大小为~50mb=df2 和~1mb=df1。您能否建议一种可以创建相同连接的替代连接方法?我试过concat 和append。
  • reddit.com/r/Python/comments/33q3gy/pandas_slow_merges 此链接显示,当合并单个值时,DF 大小会膨胀并持续下去。我将上面的代码更改为在 ID 字段上进行合并,它快了一百万倍……大约。
【解决方案2】:

适应这个:

for i in range(1, len(DF1.index)):
    DF2.between_time(DF1.index[i-1], DF1.index[i], include_start=True,
                     include_end=True).loc[:,'speed'] = DF1.loc[DF1.index[i],'speed']

在以下人员的帮助下:how to use dataframe between_time() function

【讨论】:

  • 感谢@kartik,我尝试并收到了此错误消息。 ValueError: Location based indexing can only have [integer, integer slice (START point is INCLUDED, END point is EXCLUDED), listlike of integers, boolean array] types 我试过使用 between_time 来选择索引,我想知道它是否专门用于时间而不是日期。
  • 没有。我认为在 iloc 中删除逗号后的冒号就可以了。它可能抱怨在冒号的两边都没有找到任何整数。试试.iloc[i-1,] 和.iloc[i,] 而不是.iloc[i-1,:] 和.iloc[i,:]...
  • 再次感谢您。 for i in range(1, len(fi.index)): df.between_time(fi.iloc[i-1,].index, fi.iloc[i,].index, include_start=True, include_end=True)['speed'] = fi.iloc[i,'speed']同样的错误信息
  • 我想通了...抱歉一开始没有考虑清楚。 .loc[] 将在仅选择一行时返回一个系列,并将索引作为名称。但是有一种更简单的方法,而不是使用.iloc...我正在相应地编辑我的答案。请看一下,如果它不起作用,请告诉我。
  • 还有,这其实很尴尬。我将'speed' 传递给.iloc[],这就是错误的原因。对不起。
猜你喜欢
  • 2022-01-22
  • 1970-01-01
  • 2014-03-19
  • 2018-08-15
  • 1970-01-01
  • 1970-01-01
  • 2019-10-20
  • 1970-01-01
  • 2011-06-25
相关资源
最近更新 更多